diff --git a/modoboa/core/commands/deploy.py b/modoboa/core/commands/deploy.py index e7073ecd1..3a58161af 100644 --- a/modoboa/core/commands/deploy.py +++ b/modoboa/core/commands/deploy.py @@ -1,9 +1,26 @@ import sys import os import shutil +import getpass +import subprocess from django.core import management +from django.template import Context, Template from modoboa.core.management import Command +dbconn_tpl = """ + '{{ conn_name }}': { + 'ENGINE': 'django.db.backends.{{ dbtype }}', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'. + 'NAME': '{{ dbname }}', # Or path to database file if using sqlite3. + 'USER': '{{ username }}', # Not used with sqlite3. + 'PASSWORD': '{{ password }}', # Not used with sqlite3. + 'HOST': '{{ dbhost }}', # Set to empty string for localhost. Not used with sqlite3. + 'PORT': '', # Set to empty string for default. Not used with sqlite3.{% if dbtype == 'mysql' %} + 'OPTIONS' : { + "init_command" : 'SET foreign_key_checks = 0;', + },{% endif %} + }, +""" + class DeployCommand(Command): help = "Create a fresh django project (calling startproject) and apply Modoboa specific settings." @@ -11,23 +28,88 @@ class DeployCommand(Command): super(DeployCommand, self).__init__(*args, **kwargs) self._parser.add_argument('name', type=str, help='The name of your Modoboa instance') + self._parser.add_argument('--with-amavis', action='store_true', default=False, + help='Include amavis configuration') + self._parser.add_argument('--run-syncdb', action='store_true', default=False, + help='Run django syncdb command') + self._parser.add_argument('--run-collectstatic', action='store_true', default=False, + help='Run django collectstatic command') + def _exec_django_command(self, name, cwd, *args): + """Run a django command for the freshly created project + + :param name: the command name + :param cwd: the directory where the command must be executed + """ + cmd = 'python manage.py %s %s' % (name, " ".join(args)) + if not self._verbose: + p = subprocess.Popen(cmd, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + shell=True, cwd=cwd) + output = p.communicate() + else: + p = subprocess.Popen(cmd, shell=True, cwd=cwd) + p.wait() + output = None + if p.returncode: + if output: + print >>sys.stderr, "\n".join(filter(lambda l: l != None, output)) + print >>sys.stderr, "%s failed, check your configuration" % cmd + + def ask_db_info(self, name='default'): + """Prompt the user for database information + + Gather all information required to create a new database + connection (into settings.py). + + :param name: the connection name + """ + print "Configuring database connection: %s" % name + info = {'conn_name' : name} + info['dbtype'] = raw_input('Database type (mysql or postgres): ') + if info['dbtype'] not in ['mysql', 'postgres']: + info['dbtype'] = 'mysql' + if info['dbtype'] == 'postgres': + info['dbtype'] = 'postgresql_psycopg2' + info['dbhost'] = raw_input('Database host (default: localhost): ') + if info['dbhost'] == '': + info['dbhost'] = 'localhost' + info['dbname'] = raw_input('Database name: ') + info['username'] = raw_input('Username: ') + info['password'] = getpass.getpass('Password: ') + return info + def handle(self, parsed_args): management.call_command('startproject', parsed_args.name, verbosity=False) if os.path.exists("%(name)s/%(name)s" % {'name' : parsed_args.name}): # Django 1.4+ path = "%(name)s/%(name)s" % {'name' : parsed_args.name} sys.path.append(parsed_args.name) + django14 = True else: path = parsed_args.name sys.path.append(".") + django14 = False + + t = Template(dbconn_tpl) + default_conn = t.render(Context(self.ask_db_info())) + amavis_conn = t.render(Context(self.ask_db_info('amavis'))) if parsed_args.with_amavis \ + else None + mod = __import__(parsed_args.name, globals(), locals(), ['settings']) tpl = self._render_template("%s/settings.py" % self._templates_dir, { - 'secret_key' : mod.settings.SECRET_KEY, - 'name' : parsed_args.name - }) + 'default_conn' : default_conn, 'amavis_conn' : amavis_conn, + 'secret_key' : mod.settings.SECRET_KEY, + 'name' : parsed_args.name, 'django14' : django14 + }) fp = open("%s/settings.py" % path, "w") fp.write(tpl) fp.close() shutil.copyfile("%s/urls.py" % self._templates_dir, "%s/urls.py" % path) + os.mkdir("%s/media" % path) + if parsed_args.run_syncdb: + self._exec_django_command("syncdb", parsed_args.name, '--migrate', '--noinput') + + if parsed_args.run_collectstatic: + self._exec_django_command("collectstatic", parsed_args.name, '--noinput') diff --git a/modoboa/core/management.py b/modoboa/core/management.py index 2e8874e64..105b5bcde 100644 --- a/modoboa/core/management.py +++ b/modoboa/core/management.py @@ -13,9 +13,10 @@ class Command(object): """ help = "No help available." - def __init__(self, commands, **kwargs): + def __init__(self, commands, verbose=False, **kwargs): self._commands = commands self._parser = argparse.ArgumentParser() + self._verbose = verbose if not settings.configured: settings.configure() self._templates_dir = "%s/templates" % os.path.dirname(__file__) @@ -74,6 +75,8 @@ def handle_command_line(): epilog="""Available commands: %s """ % "\n".join(map(lambda c: "\t" + c, sorted(commands)))) + parser.add_argument('--verbose', action='store_true', + help='Activate verbose output') parser.add_argument('command', type=str, help='A valid command name') (args, remaining) = parser.parse_known_args() @@ -82,4 +85,4 @@ def handle_command_line(): print >>sys.stderr, "Unknown command '%s'" % args.command sys.exit(1) - commands[args.command](commands).run(remaining) + commands[args.command](commands, verbose=args.verbose).run(remaining) diff --git a/modoboa/core/templates/settings.py b/modoboa/core/templates/settings.py index 091b50511..3ca4b3ba5 100644 --- a/modoboa/core/templates/settings.py +++ b/modoboa/core/templates/settings.py @@ -1,4 +1,5 @@ # Django settings for toto project. +import os DEBUG = True TEMPLATE_DEBUG = DEBUG @@ -11,28 +12,21 @@ ADMINS = ( MANAGERS = ADMINS -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'. - 'NAME': '', # Or path to database file if using sqlite3. - 'USER': '', # Not used with sqlite3. - 'PASSWORD': '', # Not used with sqlite3. - 'HOST': '', # Set to empty string for localhost. Not used with sqlite3. - 'PORT': '', # Set to empty string for default. Not used with sqlite3. - }, +DATABASES = { {{ default_conn|safe }} # "pfxadmin" : { # "ENGINE" : "django.db.backends.", # "NAME" : "", # "USER" : "", # "PASSWORD" : "" - # }, + # },{% if not amavis_conn %} # "amavis": { # "ENGINE" : "django.db.backends.", # "HOST" : "", # "NAME" : "", # "USER" : "", # "PASSWORD" : "" - # } + # }{% else %} + {{ amavis_conn|safe }}{% endif %} } DATABASE_ROUTERS = ["modoboa.extensions.amavis.dbrouter.AmavisRouter"] @@ -153,7 +147,7 @@ INSTALLED_APPS = ( 'modoboa.extensions.postfix_autoreply', 'modoboa.extensions.webmail', 'modoboa.extensions.stats', - 'modoboa.extensions.amavis', + {% if not amavis_conn %}#{% endif %}'modoboa.extensions.amavis', 'modoboa.extensions.sievefilters', # Extra tools @@ -173,15 +167,15 @@ AUTHENTICATION_BACKENDS = ( LOGGING = { 'version': 1, 'disable_existing_loggers': False, - 'filters': { + {% if django14 %}'filters': { 'require_debug_false': { '()': 'django.utils.log.RequireDebugFalse' } - }, + },{% endif %} 'handlers': { 'mail_admins': { - 'level': 'ERROR', - 'filters': ['require_debug_false'], + 'level': 'ERROR',{% if django14 %} + 'filters': ['require_debug_false'],{% endif %} 'class': 'django.utils.log.AdminEmailHandler' } },