diff --git a/doc/additional/migrating.rst b/doc/additional/migrating.rst index 22acee28b..a094bbe77 100644 --- a/doc/additional/migrating.rst +++ b/doc/additional/migrating.rst @@ -6,56 +6,4 @@ Migrating from other software PostfixAdmin ************ -Modoboa provides a simple script to migrate an -existing `PostfixAdmin (version 2.3.3+) -`_ database to a Modoboa one. - -.. note:: - This script is only suitable for a new installation. - -First, you must follow the :ref:`installation` step to create a fresh -Modoboa database. - -Once done, edit the :file:`settings.py` file. First, add a new database -connection named ``pfxadmin`` into the ``DATABASES`` variable -corresponding to your PostfixAdmin setup:: - - DATABASES = { - "default" : { - # default connection definition - }, - "pfxadmin" : { - "ENGINE" : "", - "NAME" : "", - "USER" : "", - "PASSWORD" : "", - } - } - -This connection should correspond to the one defined in PostfixAdmin's -configuration file. - -Then, uncomment the line containing -``'modoboa.tools.pfxadmin_migrate'`` inside the ``MODOBOA_APPS`` -variable and save your changes. - -You are now ready to start the migration so run the following commands:: - - $ cd - $ python manage.py migrate_from_postfixadmin -s - -```` must be replaced by the scheme used within -postfixadmin (``crypt`` most of the time). - -Depending on how many domains/mailboxes your existing setup contains, -the migration can be long. Just wait for the script's ending. - -The procedure is over, edit the :file:`settings.py` file and: - -* remove the ``pfxadmin`` database connection from the ``DATABASES`` - variable -* remove the ``'modoboa.tools.pfxadmin_migrate',`` from the - ``MODOBOA_APPS`` variable - -You should be able to connect to Modoboa using the same credentials -you were using to connect to PostfixAdmin. +`Official repository `_. diff --git a/modoboa/tools/__init__.py b/modoboa/tools/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/modoboa/tools/pfxadmin_migrate/__init__.py b/modoboa/tools/pfxadmin_migrate/__init__.py deleted file mode 100755 index e69de29bb..000000000 diff --git a/modoboa/tools/pfxadmin_migrate/management/__init__.py b/modoboa/tools/pfxadmin_migrate/management/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/modoboa/tools/pfxadmin_migrate/management/commands/__init__.py b/modoboa/tools/pfxadmin_migrate/management/commands/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/modoboa/tools/pfxadmin_migrate/management/commands/migrate_from_postfixadmin.py b/modoboa/tools/pfxadmin_migrate/management/commands/migrate_from_postfixadmin.py deleted file mode 100644 index 4f22bf79b..000000000 --- a/modoboa/tools/pfxadmin_migrate/management/commands/migrate_from_postfixadmin.py +++ /dev/null @@ -1,290 +0,0 @@ -# coding: utf-8 - -"""Automatic postfixadmin to Modoboa migration -=========================================== - -This script provides an easy way to migrate an existing postfixadmin -database to a Modoboa one. - -As the two products do not share the same schema, some information -will be lost on the new database. Here is the list: - * Description, transport and backup MX for domains - * Logs - * Fetchmail table - -Domain administrators that manage more than one domain will not be -completly restored. They will only be administrator of the domain that -corresponds to their username. - -Vacation messages will not be migrated by this script. Users will have -to define new messages by using Modoboa's autoreply plugin. (if -activated) - -One last point about passwords. PostfixAdmin uses different hash -algorithms to store passwords so this script does not modify -them. They are copied "as is" in the new database. In order to use -them, use the ``--passwords-scheme`` to tell Modoboa which scheme was -used by PostfixAdmin. - -.. note:: - - Actually, it appears that the ``crypt`` scheme is compatible with - PostfixAdmin ``md5crypt`` algorithm... - -""" -from optparse import make_option -import re - -from django.contrib.auth.models import Group -from django.core.management.base import BaseCommand -from django.db import transaction - -import modoboa.core.models as core_models -import modoboa.extensions.admin.models as admin_models -from modoboa.lib.email_utils import split_mailbox -from modoboa.extensions.admin import grant_access_to_all_objects - -from modoboa.tools.pfxadmin_migrate import models as pf_models - - -def set_account_password(account, password, scheme): - """Correclty format the password before storing it. - - It seems postfixadmin sometimes prepend the scheme... but not - always :p - - """ - if re.search(r"\{[\w-]+\}", password): - account.password = password - else: - account.password = "{%s}%s" % (scheme.upper(), password) - - -class Command(BaseCommand): - - """Migration from postfixadmin.""" - - help = "This command eases the migration from a postfixadmin database." - option_list = BaseCommand.option_list + ( - make_option( - "-f", "--from", dest="_from", default="pfxadmin", - help="Name of postfixadmin db connection declared in settings.py" - "(default is pfxadmin)" - ), - make_option( - "-t", "--to", dest="_to", default="default", - help="Name of the Modoboa db connection declared in settings.py" - " (default is default)" - ), - make_option( - "-s", "--passwords-scheme", default="crypt", - help="Scheme used to crypt user passwords" - ) - ) - - def _migrate_dates(self, oldobj): - """Creates a new ObjectDates instance. - - Due to Django limitations, we only retrieve the creation date. The - modification date will be set to 'now' because the corresponding - field in Modoboa's schema has the 'auto_now' attribute to True. - (see https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.DateField). - - :param oldobj: the PostfixAdmin record which is beeing migrated. - """ - dates = admin_models.base.ObjectDates() - dates.save() - - dates.creation = oldobj.created - dates.save() - return dates - - def _migrate_domain_aliases(self, domain, options, creator): - """Migrate aliases of a single domain.""" - print "\tMigrating domain aliases" - old_domain_aliases = pf_models.AliasDomain.objects.using( - options["_from"] - ).filter(target_domain=domain.name) - for old_da in old_domain_aliases: - try: - target_domain = admin_models.Domain.objects \ - .using(options["_to"]).get(name=old_da.target_domain) - new_da = admin_models.DomainAlias() - new_da.name = old_da.alias_domain - new_da.target = target_domain - new_da.enabled = old_da.active - new_da.dates = self._migrate_dates(old_da) - new_da.save(using=options["_to"]) - new_da.post_create(creator) - except core_models.Domain.DoesNotExist: - print ("Warning: target domain %s does not exists, " - "not creating alias domain %s" - % old_da.target_domain, old_da.alias_domain) - continue - - def _migrate_mailbox_aliases(self, domain, options, creator): - """Migrate mailbox aliases of a single domain.""" - print "\tMigrating mailbox aliases" - old_aliases = pf_models.Alias.objects \ - .using(options["_from"]).filter(domain=domain.name) - for old_al in old_aliases: - if old_al.address == old_al.goto: - continue - new_al = admin_models.Alias() - local_part, tmp = split_mailbox(old_al.address) - if local_part is None or not len(local_part): - if tmp is None or not len(tmp): - print ( - "Warning: skipping alias %s (cannot retrieve local " - "part). You will need to recreate it manually." - % old_al.address - ) - continue - new_al.address = "*" - else: - new_al.address = local_part - new_al.domain = domain - new_al.enabled = old_al.active - extmboxes = [] - intmboxes = [] - for goto in old_al.goto.split(","): - try: - mb = admin_models.Mailbox.objects \ - .using(options["_to"]).get(user__username=goto) - except admin_models.Mailbox.DoesNotExist: - extmboxes += [goto] - else: - intmboxes += [mb] - new_al.dates = self._migrate_dates(old_al) - new_al.save( - int_rcpts=intmboxes, ext_rcpts=extmboxes, creator=creator, - using=options["_to"] - ) - - def _migrate_mailboxes(self, domain, options, creator): - """Migrate mailboxes of a single domain.""" - print "\tMigrating mailboxes" - old_mboxes = pf_models.Mailbox.objects \ - .using(options["_from"]).filter(domain=domain.name) - for old_mb in old_mboxes: - new_user = core_models.User() - new_user.username = old_mb.username - new_user.first_name = old_mb.name.partition(' ')[0] - new_user.last_name = old_mb.name.partition(' ')[2] - new_user.email = old_mb.username - new_user.is_active = old_mb.active - new_user.date_joined = old_mb.created - set_account_password( - new_user, old_mb.password, options["passwords_scheme"]) - new_user.dates = self._migrate_dates(old_mb) - new_user.save(creator=creator, using=options["_to"]) - new_user.set_role("SimpleUsers") - - new_mb = admin_models.Mailbox() - new_mb.user = new_user - new_mb.address = old_mb.local_part - new_mb.domain = domain - new_mb.dates = self._migrate_dates(old_mb) - new_mb.set_quota(old_mb.quota / 1024000, override_rules=True) - new_mb.save(creator=creator, using=options["_to"]) - - def _migrate_domain(self, pf_domain, options, creator): - """Single domain migration.""" - print "\nMigrating domain %s" % pf_domain.domain - newdom = admin_models.Domain() - newdom.name = pf_domain.domain - newdom.enabled = pf_domain.active - newdom.quota = pf_domain.maxquota - newdom.dates = self._migrate_dates(pf_domain) - newdom.save(creator=creator, using=options["_to"]) - self._migrate_domain_aliases(newdom, options, creator) - self._migrate_mailboxes(newdom, options, creator) - self._migrate_mailbox_aliases(newdom, options, creator) - self._migrate_admins(options, creator, pf_domain, newdom) - - def _migrate_admins(self, options, creator, pf_domain, newdom=None): - """Administrators migration. - - :param pf_domain: The ``pf_models.Domain`` to get the admins to migrate. - :param newdom: If None the admins will not be linked to any domain - and they will be SuperAdmins. Otherwise link them to - this domain and make them DomainAdmins. - """ - - if newdom is None: - print "\nMigrating super administrators" - else: - print "\tMigrating administrators" - - dagroup = Group.objects.using(options["_to"]).get(name="DomainAdmins") - for old_admin in pf_domain.admins.all(): - if newdom is None: - print "\tMigrating %s" % old_admin.username - - # In postfixadmin, it's possible to have an admin account - # and a user account with the same username but they are - # completely different entities, so they do not have a - # common password. In Modoboa this does not makes sense, so - # we actually merge these two entities into a single user - # if both are found. Question is, which password should we - # use?. The admin password is used to be on the secure - # side. - try: - user = core_models.User.objects \ - .using(options["_to"]).get(username=old_admin.username) - - if "SimpleUsers" == user.group: - print ( - "Warning: Found an admin account with the same " - "username as normal user '%s'. The existing user will " - "be promoted to admin and it's password changed to the " - "admin's account password. " % user.username - ) - - except core_models.User.DoesNotExist: - user = core_models.User() - user.username = old_admin.username - user.email = old_admin.username - user.is_active = old_admin.active - user.save(creator=creator, using=options["_to"]) - - user.date_joined = old_admin.modified - set_account_password( - user, old_admin.password, options["passwords_scheme"]) - - if newdom is None: - grant_access_to_all_objects(user, "SuperAdmins") - user.is_superuser = True - else: - user.groups.add(dagroup) - newdom.add_admin(user) - - user.save(using=options["_to"]) - - def _do_migration(self, options, creator_username='admin'): - """Run the complete migration.""" - pf_domains = pf_models.Domain.objects.using( - options["_from"]).exclude(domain='ALL') - creator = core_models.User.objects.using(options["_to"]).get( - username=creator_username) - for pf_domain in pf_domains: - - try: - # Skip this domain if it's an alias - is_domain_alias = pf_models.AliasDomain.objects \ - .using(options["_from"]).get(alias_domain=pf_domain.domain) - print "Info: %s looks like an alias domain, skipping it" \ - % pf_domain.domain - continue - except pf_models.AliasDomain.DoesNotExist: - self._migrate_domain(pf_domain, options, creator) - - # Handle the ALL domain - pf_domain = pf_models.Domain.objects.using( - options["_from"]).get(domain='ALL') - self._migrate_admins(options, creator, pf_domain) - - def handle(self, *args, **options): - """Command entry point.""" - with transaction.commit_on_success(): - self._do_migration(options) diff --git a/modoboa/tools/pfxadmin_migrate/models.py b/modoboa/tools/pfxadmin_migrate/models.py deleted file mode 100755 index f94e2f775..000000000 --- a/modoboa/tools/pfxadmin_migrate/models.py +++ /dev/null @@ -1,201 +0,0 @@ -# coding: utf-8 - -"""postfixadmin models.""" - -# This is an auto-generated Django model module. -# You'll have to do the following manually to clean this up: -# * Rearrange models' order -# * Make sure each model has one field with primary_key=True -# Feel free to rename the models, but don't rename db_table values or -# field names. -# -# Also note: You'll have to insert the output of 'django-admin.py -# sqlcustom [appname]' into your database. - -from django.db import models - - -class Admin(models.Model): - username = models.CharField(max_length=255, primary_key=True) - password = models.CharField(max_length=255) - created = models.DateTimeField() - modified = models.DateTimeField() - active = models.IntegerField() - - class Meta: - db_table = u'admin' - app_label = 'pfxadmin_migrate' - managed = False - - -class Alias(models.Model): - address = models.CharField(max_length=255, primary_key=True) - goto = models.TextField() - domain = models.CharField(max_length=255) - created = models.DateTimeField() - modified = models.DateTimeField() - active = models.IntegerField() - - class Meta: - db_table = u'alias' - app_label = 'pfxadmin_migrate' - managed = False - - -class AliasDomain(models.Model): - alias_domain = models.CharField(max_length=255, primary_key=True) - target_domain = models.CharField(max_length=255) - created = models.DateTimeField() - modified = models.DateTimeField() - active = models.IntegerField() - - class Meta: - db_table = u'alias_domain' - app_label = 'pfxadmin_migrate' - managed = False - - -class Config(models.Model): - id = models.IntegerField(primary_key=True) - name = models.CharField(unique=True, max_length=60) - value = models.CharField(max_length=60) - - class Meta: - db_table = u'config' - app_label = 'pfxadmin_migrate' - managed = False - - -class Domain(models.Model): - domain = models.CharField(max_length=255, primary_key=True) - description = models.CharField(max_length=255) - aliases = models.IntegerField() - mailboxes = models.IntegerField() - maxquota = models.BigIntegerField() - quota = models.BigIntegerField() - transport = models.CharField(max_length=255) - backupmx = models.IntegerField() - created = models.DateTimeField() - modified = models.DateTimeField() - active = models.IntegerField() - admins = models.ManyToManyField(Admin, through='DomainAdmins') - - class Meta: - db_table = u'domain' - app_label = 'pfxadmin_migrate' - managed = False - - -class DomainAdmins(models.Model): - username = models.ForeignKey(Admin, db_column='username') - domain = models.ForeignKey(Domain, db_column='domain') - created = models.DateTimeField(primary_key=True) - active = models.IntegerField() - - class Meta: - db_table = u'domain_admins' - app_label = 'pfxadmin_migrate' - managed = False - - -class Fetchmail(models.Model): - id = models.IntegerField(primary_key=True) - mailbox = models.CharField(max_length=255) - src_server = models.CharField(max_length=255) - src_auth = models.CharField(max_length=33, blank=True) - src_user = models.CharField(max_length=255) - src_password = models.CharField(max_length=255) - src_folder = models.CharField(max_length=255) - poll_time = models.IntegerField() - fetchall = models.IntegerField() - keep = models.IntegerField() - protocol = models.CharField(max_length=12, blank=True) - usessl = models.IntegerField() - extra_options = models.TextField(blank=True) - returned_text = models.TextField(blank=True) - mda = models.CharField(max_length=255) - date = models.DateTimeField() - - class Meta: - db_table = u'fetchmail' - app_label = 'pfxadmin_migrate' - managed = False - - -class Log(models.Model): - timestamp = models.DateTimeField() - username = models.CharField(max_length=255) - domain = models.CharField(max_length=255) - action = models.CharField(max_length=255) - data = models.TextField() - - class Meta: - db_table = u'log' - app_label = 'pfxadmin_migrate' - managed = False - - -class Mailbox(models.Model): - username = models.CharField(max_length=255, primary_key=True) - password = models.CharField(max_length=255) - name = models.CharField(max_length=255) - maildir = models.CharField(max_length=255) - quota = models.BigIntegerField() - local_part = models.CharField(max_length=255) - domain = models.CharField(max_length=255) - created = models.DateTimeField() - modified = models.DateTimeField() - active = models.IntegerField() - - class Meta: - db_table = u'mailbox' - app_label = 'pfxadmin_migrate' - managed = False - - -class Quota(models.Model): - username = models.CharField(max_length=255, primary_key=True) - path = models.CharField(max_length=100, primary_key=True) - current = models.BigIntegerField(null=True, blank=True) - - class Meta: - db_table = u'quota' - app_label = 'pfxadmin_migrate' - managed = False - - -class Quota2(models.Model): - username = models.CharField(max_length=100, primary_key=True) - bytes = models.BigIntegerField() - messages = models.IntegerField() - - class Meta: - db_table = u'quota2' - app_label = 'pfxadmin_migrate' - managed = False - - -class Vacation(models.Model): - email = models.CharField(max_length=255) - subject = models.CharField(max_length=255) - body = models.TextField() - cache = models.TextField() - domain = models.CharField(max_length=255) - created = models.DateTimeField() - active = models.IntegerField() - - class Meta: - db_table = u'vacation' - app_label = 'pfxadmin_migrate' - managed = False - - -class VacationNotification(models.Model): - on_vacation = models.ForeignKey(Vacation, db_column='on_vacation') - notified = models.CharField(max_length=255, primary_key=True) - notified_at = models.DateTimeField() - - class Meta: - db_table = u'vacation_notification' - app_label = 'pfxadmin_migrate' - managed = False