diff --git a/wcs/ctl/apply_timeouts.py b/wcs/ctl/apply_timeouts.py index a12495b64..6f96a80a3 100644 --- a/wcs/ctl/apply_timeouts.py +++ b/wcs/ctl/apply_timeouts.py @@ -16,25 +16,36 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA -import wcs.workflows as workflows +from qommon.ctl import Command, make_option -import publisher +class CmdApplyTimeouts(Command): + name = 'apply_timeouts' -def apply_timeouts(args): - i = 0 - while i < len(args): - if args[i] == '--extra': - publisher.WcsPublisher.register_extra_dir(args[i+1]) - i += 1 - elif args[i] == '--app-dir': - publisher.WcsPublisher.APP_DIR = args[i+1] - i += 1 - elif args[i] == '--data-dir': - publisher.WcsPublisher.DATA_DIR = args[i+1] - i += 1 - elif args[i] == '--silent': + def __init__(self): + Command.__init__(self, [ + make_option('--extra', metavar='DIR', action='append', + dest='extra', default=[]), + make_option('--app-dir', metavar='DIR', action='store', + dest='app_dir', default=None), + make_option('--data-dir', metavar='DIR', action='store', + dest='data_dir', default=None), + make_option('--silent', action='store_true', + dest='silent', default=False), + ]) + + def execute(self, options, args): + import wcs.workflows as workflows + import publisher + + if options.app_dir: + publisher.WcsPublisher.APP_DIR = options.app_dir + if options.data_dir: + publisher.WcsPublisher.DATA_DIR = options.data_dir + if options.silent: sys.stdout = file('/dev/null', 'w') sys.stderr = file('/dev/null', 'w') - i += 1 - pub = publisher.WcsPublisher.create_publisher() - workflows.apply_timeouts() + + pub = publisher.WcsPublisher.create_publisher() + workflows.apply_timeouts() + +CmdApplyTimeouts.register() diff --git a/wcs/ctl/clean_sessions.py b/wcs/ctl/clean_sessions.py index 1427e3465..b4c341380 100644 --- a/wcs/ctl/clean_sessions.py +++ b/wcs/ctl/clean_sessions.py @@ -20,8 +20,7 @@ import time import sys import os -import publisher -import sessions +from qommon.ctl import Command, make_option def clean_vhost_sessions(): manager = sessions.StorageSessionManager() @@ -36,15 +35,20 @@ def clean_vhost_sessions(): if session._access_time < one_week_ago or session._creation_time < one_month_ago: del manager[session.id] -def clean_sessions(args): - pub = publisher.WcsPublisher.create_publisher() - if '--single_host' in args: - clean_vhost_sessions() - else: +class CmdCleanSessions(Command): + name = 'clean_sessions' + + def execute(self, options, args): + import publisher + import sessions + + pub = publisher.WcsPublisher.create_publisher() + app_dir = pub.app_dir hostnames = os.listdir(app_dir) for hostname in hostnames: pub.app_dir = os.path.join(app_dir, hostname) clean_vhost_sessions() +CmdCleanSessions.register() diff --git a/wcs/ctl/export_settings.py b/wcs/ctl/export_settings.py index 44ccfee35..b758a1a41 100644 --- a/wcs/ctl/export_settings.py +++ b/wcs/ctl/export_settings.py @@ -19,19 +19,23 @@ import sys import os -import publisher +from qommon.ctl import Command, make_option -def export_settings(args): - stdout = sys.stdout - pub = publisher.WcsPublisher.create_publisher() +class CmdExportSettings(Command): + name = 'export_settings' - i = 0 - while i < len(args): - if args[i] == '--vhost': - pub.app_dir = os.path.join(pub.app_dir, args[i+1]) - i += 1 - i += 1 + def __init__(self): + Command.__init__(self, [ + make_option('--vhost', metavar='VHOST', action='store', + dest='vhost'), + ]) - pub.reload_cfg() - print >> stdout, pub.export_cfg() + def execute(self, options, args): + import publisher + stdout = sys.stdout + pub = publisher.WcsPublisher.create_publisher() + pub.app_dir = os.path.join(pub.app_dir, options.vhost) + pub.reload_cfg() + print >> stdout, pub.export_cfg() +CmdExportSettings.register() diff --git a/wcs/ctl/process_bounce.py b/wcs/ctl/process_bounce.py index 57204a9f9..5fa838810 100644 --- a/wcs/ctl/process_bounce.py +++ b/wcs/ctl/process_bounce.py @@ -24,59 +24,65 @@ import imp from Bouncers import BouncerAPI -import publisher - -from qommon.tokens import Token -from qommon.bounces import Bounce +from qommon.ctl import Command, make_option COMMA_SPACE = ', ' -def process_bounce(args): - try: - parser = email.Parser.Parser() - bouncers_dir = os.path.join(os.path.dirname(__file__), 'Bouncers') - sys.path.append(bouncers_dir) - msg = parser.parse(sys.stdin) - for modname in BouncerAPI.BOUNCE_PIPELINE: - __import__(modname) - addrs = sys.modules[modname].process(msg) - if addrs is BouncerAPI.Stop: - return # Stop means to ignore message - if addrs: - break - else: - return # didn't find any match +class CmdProcessBounce(Command): + name = 'process_bounce' + + def execute(self, options, args): + from qommon.tokens import Token + from qommon.bounces import Bounce + + import publisher try: - to = msg['To'] - local_part, server_part = to.split('@') - token_id = local_part.split('+')[1] - except (IndexError, KeyError): - return + parser = email.Parser.Parser() + bouncers_dir = os.path.join(os.path.dirname(__file__), 'Bouncers') + sys.path.append(bouncers_dir) + msg = parser.parse(sys.stdin) + for modname in BouncerAPI.BOUNCE_PIPELINE: + __import__(modname) + addrs = sys.modules[modname].process(msg) + if addrs is BouncerAPI.Stop: + return # Stop means to ignore message + if addrs: + break + else: + return # didn't find any match - pub = publisher.WcsPublisher.create_publisher() - pub.app_dir = os.path.join(pub.app_dir, server_part) + try: + to = msg['To'] + local_part, server_part = to.split('@') + token_id = local_part.split('+')[1] + except (IndexError, KeyError): + return - try: - token = Token.get(token_id) - except KeyError: - return + pub = publisher.WcsPublisher.create_publisher() + pub.app_dir = os.path.join(pub.app_dir, server_part) - if token.type != 'email-bounce': - return + try: + token = Token.get(token_id) + except KeyError: + return - token.remove_self() + if token.type != 'email-bounce': + return - bounce = Bounce() - bounce.arrival_time = time.time() - bounce.bounce_message = msg.as_string() - bounce.addrs = addrs - bounce.original_message = token.email_message - bounce.original_rcpts = token.email_rcpts - bounce.email_type = token.email_type - bounce.store() - except Exception, e: - import traceback - file('/tmp/bounces-error', 'a+').write(traceback.format_exc()) - sys.exit(1) + token.remove_self() + bounce = Bounce() + bounce.arrival_time = time.time() + bounce.bounce_message = msg.as_string() + bounce.addrs = addrs + bounce.original_message = token.email_message + bounce.original_rcpts = token.email_rcpts + bounce.email_type = token.email_type + bounce.store() + except Exception, e: + import traceback + file('/tmp/bounces-error', 'a+').write(traceback.format_exc()) + sys.exit(1) + +CmdProcessBounce.register() diff --git a/wcs/ctl/rebuild_indexes.py b/wcs/ctl/rebuild_indexes.py index 752b7bb39..1a79d5d42 100644 --- a/wcs/ctl/rebuild_indexes.py +++ b/wcs/ctl/rebuild_indexes.py @@ -20,22 +20,25 @@ import time import sys import os -import publisher - -from wcs.formdef import FormDef +from qommon.ctl import Command, make_option def rebuild_vhost_indexes(): FormDef.rebuild_indexes() -def rebuild_indexes(args): - pub = publisher.WcsPublisher.create_publisher() - if '--single_host' in args: - rebuild_vhost_indexes() - else: +class CmdRebuildIndexes(Command): + name = 'rebuild_indexes' + + def execute(self, options, args): + import publisher + from wcs.formdef import FormDef + + pub = publisher.WcsPublisher.create_publisher() + app_dir = pub.app_dir hostnames = os.listdir(app_dir) for hostname in hostnames: pub.app_dir = os.path.join(app_dir, hostname) rebuild_vhost_indexes() +CmdRebuildIndexes.register() diff --git a/wcs/ctl/start.py b/wcs/ctl/start.py index db4b4024f..734804073 100644 --- a/wcs/ctl/start.py +++ b/wcs/ctl/start.py @@ -21,48 +21,64 @@ import sys import qommon.scgi_server import quixote.server.simple_server -import publisher +from qommon.ctl import Command, make_option -def start(args): - run_kwargs = {} - run_kwargs['port'] = 3001 - run_kwargs['spawn_cron'] = True - run_function = qommon.scgi_server.run +class CmdStart(Command): + name = 'start' - i = 0 - while i < len(args): - if args[i] == '--port': - run_kwargs['port'] = int(args[i+1]) - i += 1 - elif args[i] == '--extra': - publisher.WcsPublisher.register_extra_dir(args[i+1]) - i += 1 - elif args[i] == '--handler-connection-limit': - run_kwargs['handler_connection_limit'] = int(args[i+1]) - i += 1 - elif args[i] == '--script-name': - run_kwargs['script_name'] = args[i+1] - i += 1 - elif args[i] == '--app-dir': - publisher.WcsPublisher.APP_DIR = args[i+1] - i += 1 - elif args[i] == '--data-dir': - publisher.WcsPublisher.DATA_DIR = args[i+1] - i += 1 - elif args[i] == '--http': + def __init__(self): + Command.__init__(self, [ + make_option('--port', metavar='PORT', action='store', + dest='port', default=3001), + make_option('--extra', metavar='DIR', action='append', + dest='extra', default=[]), + make_option('--handler-connection-limit', metavar='LIMIT', + action='store', + dest='handler_connection_limit', default=None), + make_option('--script-name', metavar='NAME', action='store', + dest='script_name', default=None), + make_option('--app-dir', metavar='DIR', action='store', + dest='app_dir', default=None), + make_option('--data-dir', metavar='DIR', action='store', + dest='data_dir', default=None), + make_option('--http', action='store_true', + dest='http', default=False), + make_option('--silent', action='store_true', + dest='silent', default=False), + ]) + + def execute(self, options, args): + import publisher + + run_kwargs = {} + run_kwargs['port'] = int(options.port) + run_kwargs['spawn_cron'] = True + run_function = qommon.scgi_server.run + for directory in options.extra: + publisher.WcsPublisher.register_extra_dir(directory) + if options.handler_connection_limit: + run_kwargs['handler_connection_limit'] = int(options.handler_connection_limit) + if options.script_name: + run_kwargs['script_name'] = options.script_name + if options.app_dir: + publisher.WcsPublisher.APP_DIR = options.app_dir + if options.data_dir: + publisher.WcsPublisher.DATA_DIR = options.data_dir + if options.http: run_function = qommon.scgi_server.http_run - elif args[i] == '--silent': + if options.silent: sys.stdout = file('/dev/null', 'w') sys.stderr = file('/dev/null', 'w') - i += 1 - try: - run_function(publisher.WcsPublisher.create_publisher, **run_kwargs) - except socket.error, e: - if e[0] == 98: - print >> sys.stderr, 'address already in use' + try: + run_function(publisher.WcsPublisher.create_publisher, **run_kwargs) + except socket.error, e: + if e[0] == 98: + print >> sys.stderr, 'address already in use' + sys.exit(1) + raise + except KeyboardInterrupt: sys.exit(1) - raise - except KeyboardInterrupt: - sys.exit(1) + +CmdStart.register() diff --git a/wcs/qommon/ctl.py b/wcs/qommon/ctl.py new file mode 100644 index 000000000..f21aff335 --- /dev/null +++ b/wcs/qommon/ctl.py @@ -0,0 +1,116 @@ +# w.c.s. - web application for online forms +# Copyright (C) 2005-2010 Entr'ouvert +# +# 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 optparse +from optparse import make_option +import sys +import os + +__all__ = [ + 'Command', + 'register_command', + ] + +import qommon +qommon._commands = {} + +class Command: + doc = '' + name = None + usage_args = '[ options ... ]' + + def __init__(self, options=[]): + self.options = options + + def run(self, args): + options, args = self.parse_args(args) + return self.execute(options, args) + + def parse_args(self, args): + self.parser = optparse.OptionParser( + usage='%%prog %s %s' % (self.name, _(self.usage_args)), + description=self.doc) + self.parser.add_options(self.options) + return self.parser.parse_args(args) + + def execute(self, options, args): + """The body of the command""" + raise NotImplementedError + + def register(cls): + qommon._commands[cls.name] = cls + register = classmethod(register) + +class Ctl: + def __init__(self, cmd_prefixes=[]): + self.cmd_prefixes = cmd_prefixes + self.parser = optparse.OptionParser( + usage='%prog [ -f config ] command [ options ... ]', + add_help_option=False) + self.parser.disable_interspersed_args() + self.parser.add_option('-f', '--file', action='store', metavar='CONFIG', + type='string', dest='configfile', + help=_('use a non default configuration file')) + self.parser.add_option('--help', action='callback', + callback=self.print_help, + help=_("Display this help and exit")) + + def print_help(self, *args): + self.parser.print_help() + print + for cmd_prefix in self.cmd_prefixes: + if not cmd_prefix in sys.modules: + __import__(cmd_prefix) + mod = sys.modules.get(cmd_prefix) + if not mod: + continue + if os.path.isdir(mod.__file__): + cmddir = os.path.abspath(mod.__file__) + else: + cmddir = os.path.abspath(os.path.dirname(mod.__file__)) + for fname in os.listdir(os.path.join(cmddir)): + name, ext = os.path.splitext(fname) + if not ext == '.py': + continue + if name.startswith('_'): + continue + try: + __import__('%s.%s' % (cmd_prefix, name)) + except ImportError: + pass + + commands = [(x.name, x.doc) for x in qommon._commands.values()] + commands.sort() + print 'Available commands:' + for name, description in commands: + print ' %-15s %s' % (name, description) + sys.exit(0) + + def run(self, args): + options, args = self.parser.parse_args(args) + command, args = args[0], args[1:] + if command not in qommon._commands: + for cmd_prefix in self.cmd_prefixes: + try: + __import__('%s.%s' % (cmd_prefix, command)) + except ImportError: + pass + command_class = qommon._commands[command] + cmd = command_class() + return cmd.run(args) + diff --git a/wcsctl.py b/wcsctl.py index 3a103baf9..7ead8cadf 100755 --- a/wcsctl.py +++ b/wcsctl.py @@ -2,41 +2,8 @@ import sys -def print_usage(): - print 'Usage: wcsctl.py command [...]' - print '' - print 'Commands:' - print ' start start server' - print ' clean_sessions clean old sessions' - print ' rebuild_indexes rebuild database indexes' - print ' export_settings export settings' - print ' apply_timeouts expire forms with timeout state' +import wcs.qommon.ctl - -if len(sys.argv) < 2: - print_usage() - sys.exit(1) -else: - command = sys.argv[1] - - if command == 'start': - from wcs.ctl.start import start - start(sys.argv[2:]) - elif command == 'clean_sessions': - from wcs.ctl.clean_sessions import clean_sessions - clean_sessions(sys.argv[2:]) - elif command == 'rebuild_indexes': - from wcs.ctl.rebuild_indexes import rebuild_indexes - rebuild_indexes(sys.argv[2:]) - elif command == 'process_bounce': - from wcs.ctl.process_bounce import process_bounce - process_bounce(sys.argv[2:]) - elif command == 'export_settings': - from wcs.ctl.export_settings import export_settings - export_settings(sys.argv[2:]) - elif command == 'apply_timeouts': - from wcs.ctl.apply_timeouts import apply_timeouts - apply_timeouts(sys.argv[2:]) - else: - print_usage() +ctl = wcs.qommon.ctl.Ctl(cmd_prefixes=['wcs.ctl']) +ctl.run(sys.argv[1:])