mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-17 05:41:25 +02:00
Remove postfixadmin migration script.
This commit is contained in:
@@ -6,56 +6,4 @@ Migrating from other software
|
||||
PostfixAdmin
|
||||
************
|
||||
|
||||
Modoboa provides a simple script to migrate an
|
||||
existing `PostfixAdmin (version 2.3.3+)
|
||||
<http://postfixadmin.sourceforge.net/>`_ 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" : "<engine>",
|
||||
"NAME" : "<database name>",
|
||||
"USER" : "<database user>",
|
||||
"PASSWORD" : "<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 <modoboa_site>
|
||||
$ python manage.py migrate_from_postfixadmin -s <password scheme>
|
||||
|
||||
``<password scheme>`` 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 <https://github.com/modoboa/modoboa-pfxadmin-migrate>`_.
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user