summaryrefslogtreecommitdiff
path: root/Mailman/bin/qrunner.py
diff options
context:
space:
mode:
authorBarry Warsaw2008-02-27 01:26:18 -0500
committerBarry Warsaw2008-02-27 01:26:18 -0500
commita1c73f6c305c7f74987d99855ba59d8fa823c253 (patch)
tree65696889450862357c9e05c8e9a589f1bdc074ac /Mailman/bin/qrunner.py
parent3f31f8cce369529d177cfb5a7c66346ec1e12130 (diff)
downloadmailman-a1c73f6c305c7f74987d99855ba59d8fa823c253.tar.gz
mailman-a1c73f6c305c7f74987d99855ba59d8fa823c253.tar.zst
mailman-a1c73f6c305c7f74987d99855ba59d8fa823c253.zip
Diffstat (limited to 'Mailman/bin/qrunner.py')
-rw-r--r--Mailman/bin/qrunner.py277
1 files changed, 0 insertions, 277 deletions
diff --git a/Mailman/bin/qrunner.py b/Mailman/bin/qrunner.py
deleted file mode 100644
index b73643c9d..000000000
--- a/Mailman/bin/qrunner.py
+++ /dev/null
@@ -1,277 +0,0 @@
-# Copyright (C) 2001-2008 by the Free Software Foundation, Inc.
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
-# USA.
-
-import sys
-import signal
-import logging
-import optparse
-
-from Mailman import Version
-from Mailman import loginit
-from Mailman.configuration import config
-from Mailman.i18n import _
-from Mailman.initialize import initialize
-
-
-COMMASPACE = ', '
-QRUNNER_SHORTCUTS = {}
-log = None
-
-
-
-def r_callback(option, opt, value, parser):
- dest = getattr(parser.values, option.dest)
- parts = value.split(':')
- if len(parts) == 1:
- runner = parts[0]
- rslice = rrange = 1
- elif len(parts) == 3:
- runner = parts[0]
- try:
- rslice = int(parts[1])
- rrange = int(parts[2])
- except ValueError:
- parser.print_help()
- print >> sys.stderr, _('Bad runner specification: $value')
- sys.exit(1)
- else:
- parser.print_help()
- print >> sys.stderr, _('Bad runner specification: $value')
- sys.exit(1)
- dest.append((runner, rslice, rrange))
-
-
-
-def parseargs():
- parser = optparse.OptionParser(version=Version.MAILMAN_VERSION,
- usage=_("""\
-Run one or more qrunners, once or repeatedly.
-
-Each named runner class is run in round-robin fashion. In other words, the
-first named runner is run to consume all the files currently in its
-directory. When that qrunner is done, the next one is run to consume all the
-files in /its/ directory, and so on. The number of total iterations can be
-given on the command line.
-
-Usage: %prog [options]
-
--r is required unless -l or -h is given, and its argument must be one of the
-names displayed by the -l switch.
-
-Normally, this script should be started from mailmanctl. Running it
-separately or with -o is generally useful only for debugging.
-"""))
- parser.add_option('-r', '--runner',
- metavar='runner[:slice:range]', dest='runners',
- type='string', default=[],
- action='callback', callback=r_callback,
- help=_("""\
-Run the named qrunner, which must be one of the strings returned by the -l
-option. Optional slice:range if given, is used to assign multiple qrunner
-processes to a queue. range is the total number of qrunners for this queue
-while slice is the number of this qrunner from [0..range).
-
-When using the slice:range form, you must ensure that each qrunner for the
-queue is given the same range value. If slice:runner is not given, then 1:1
-is used.
-
-Multiple -r options may be given, in which case each qrunner will run once in
-round-robin fashion. The special runner `All' is shorthand for a qrunner for
-each listed by the -l option."""))
- parser.add_option('-o', '--once',
- default=False, action='store_true', help=_("""\
-Run each named qrunner exactly once through its main loop. Otherwise, each
-qrunner runs indefinitely, until the process receives signal."""))
- parser.add_option('-l', '--list',
- default=False, action='store_true',
- help=_('List the available qrunner names and exit.'))
- parser.add_option('-v', '--verbose',
- default=0, action='count', help=_("""\
-Display more debugging information to the logs/qrunner log file."""))
- parser.add_option('-s', '--subproc',
- default=False, action='store_true', help=_("""\
-This should only be used when running qrunner as a subprocess of the
-mailmanctl startup script. It changes some of the exit-on-error behavior to
-work better with that framework."""))
- parser.add_option('-C', '--config',
- help=_('Alternative configuration file to use'))
- opts, args = parser.parse_args()
- if args:
- parser.error(_('Unexpected arguments'))
- if not opts.runners and not opts.list:
- parser.error(_('No runner name given.'))
- return parser, opts, args
-
-
-
-def make_qrunner(name, slice, range, once=False):
- # Several conventions for specifying the runner name are supported. It
- # could be one of the shortcut names. Or the name is a full module path,
- # use it explicitly. If the name starts with a dot, it's a class name
- # relative to the Mailman.queue package.
- if name in QRUNNER_SHORTCUTS:
- classpath = QRUNNER_SHORTCUTS[name]
- elif name.startswith('.'):
- classpath = 'Mailman.queue' + name
- else:
- classpath = name
- modulename, classname = classpath.rsplit('.', 1)
- try:
- __import__(modulename)
- except ImportError, e:
- if opts.subproc:
- # Exit with SIGTERM exit code so the master watcher won't try to
- # restart us.
- print >> sys.stderr, _('Cannot import runner module: $modulename')
- print >> sys.stderr, e
- sys.exit(signal.SIGTERM)
- else:
- raise
- qrclass = getattr(sys.modules[modulename], classname)
- if once:
- # Subclass to hack in the setting of the stop flag in _doperiodic()
- class Once(qrclass):
- def _doperiodic(self):
- self.stop()
- qrunner = Once(slice, range)
- else:
- qrunner = qrclass(slice, range)
- return qrunner
-
-
-
-def set_signals(loop):
- """Set up the signal handlers.
-
- Signals caught are: SIGTERM, SIGINT, SIGUSR1 and SIGHUP. The latter is
- used to re-open the log files. SIGTERM and SIGINT are treated exactly the
- same -- they cause qrunner to exit with no restart from the master.
- SIGUSR1 also causes qrunner to exit, but the master watcher will restart
- it in that case.
-
- :param loop: A loop queue runner instance.
- """
- def sigterm_handler(signum, frame):
- # Exit the qrunner cleanly
- loop.stop()
- loop.status = signal.SIGTERM
- log.info('%s qrunner caught SIGTERM. Stopping.', loop.name())
- signal.signal(signal.SIGTERM, sigterm_handler)
- def sigint_handler(signum, frame):
- # Exit the qrunner cleanly
- loop.stop()
- loop.status = signal.SIGINT
- log.info('%s qrunner caught SIGINT. Stopping.', loop.name())
- signal.signal(signal.SIGINT, sigint_handler)
- def sigusr1_handler(signum, frame):
- # Exit the qrunner cleanly
- loop.stop()
- loop.status = signal.SIGUSR1
- log.info('%s qrunner caught SIGUSR1. Stopping.', loop.name())
- signal.signal(signal.SIGUSR1, sigusr1_handler)
- # SIGHUP just tells us to rotate our log files.
- def sighup_handler(signum, frame):
- loginit.reopen()
- log.info('%s qrunner caught SIGHUP. Reopening logs.', loop.name())
- signal.signal(signal.SIGHUP, sighup_handler)
-
-
-
-def main():
- global log, opts
-
- parser, opts, args = parseargs()
- # If we're not running as a subprocess of mailmanctl, then we'll log to
- # stderr in addition to logging to the log files. We do this by passing a
- # value of True to propagate, which allows the 'mailman' root logger to
- # see the log messages.
- initialize(opts.config, propagate_logs=not opts.subproc)
- log = logging.getLogger('mailman.qrunner')
-
- # Calculate the qrunner shortcut names.
- for qrunnerpath, slices in config.qrunners.items():
- classname = qrunnerpath.rsplit('.', 1)[1]
- if classname.endswith('Runner'):
- shortname = classname[:-6].lower()
- else:
- shortname = classname
- QRUNNER_SHORTCUTS[shortname] = qrunnerpath
-
- if opts.list:
- prefixlen = max(len(shortname) for shortname in QRUNNER_SHORTCUTS)
- for shortname in sorted(QRUNNER_SHORTCUTS):
- runnername = QRUNNER_SHORTCUTS[shortname]
- shortname = (' ' * (prefixlen - len(shortname))) + shortname
- print _('$shortname runs the $runnername qrunner')
- sys.exit(0)
-
- # Fast track for one infinite runner
- if len(opts.runners) == 1 and not opts.once:
- qrunner = make_qrunner(*opts.runners[0])
- class Loop:
- status = 0
- def __init__(self, qrunner):
- self._qrunner = qrunner
- def name(self):
- return self._qrunner.__class__.__name__
- def stop(self):
- self._qrunner.stop()
- loop = Loop(qrunner)
- set_signals(loop)
- # Now start up the main loop
- log.info('%s qrunner started.', loop.name())
- qrunner.run()
- log.info('%s qrunner exiting.', loop.name())
- else:
- # Anything else we have to handle a bit more specially
- qrunners = []
- for runner, rslice, rrange in opts.runners:
- qrunner = make_qrunner(runner, rslice, rrange, once=True)
- qrunners.append(qrunner)
- # This class is used to manage the main loop
- class Loop:
- status = 0
- def __init__(self):
- self._isdone = False
- def name(self):
- return 'Main loop'
- def stop(self):
- self._isdone = True
- def isdone(self):
- return self._isdone
- loop = Loop()
- set_signals(loop)
- log.info('Main qrunner loop started.')
- while not loop.isdone():
- for qrunner in qrunners:
- # In case the SIGTERM came in the middle of this iteration
- if loop.isdone():
- break
- if opts.verbose:
- log.info('Now doing a %s qrunner iteration',
- qrunner.__class__.__bases__[0].__name__)
- qrunner.run()
- if opts.once:
- break
- log.info('Main qrunner loop exiting.')
- # All done
- sys.exit(loop.status)
-
-
-
-if __name__ == '__main__':
- main()