пятница, 28 сентября 2018 г.

Миграция пользователей почтового сервера между почтовыми доменами (Exim, postgresql)

Преамбула:
Несколько лет назад был настроен почтовый сервер по мотивам этой статьи он постоянно эволюционирует, но в рамках данной темы это не важно. На нем крутятся несколько почтовых доменов и вот, после появления очередного, некий ВЕЛИКИЙ ОПТИМИЗАТОР бизнес процессов заявил, что надо часть пользователей перевести в новый домен, чтобы почта, приходящая в старый продолжала ходить и чтобы пользователи вообще бы ничего не почувствовали при этом.
Как вариант можно бы было создать новых юзеров в новом домене, настроить переадрессацию и не париться. Но так как у нас клиенты работают по протоколу IMAP, почта хранится на сервере и юзеры хотят иметь доступ к своей старой почте а администратор не хочет разводить зверинец на сервере,- какой смысл в мертвых ящиках? Принято решение провести миграцию пользователей из домена в домен.
Исходные данные:

  1. Сервер Exim
  2. Пользователи хранятся в базе postgreSQL
  3. Почтовые ящики пользователей хранятся внутри папок с названиями доменов, которые в свою очередь хранятся в корневой папке, указанной в настройках юзера(так было у автора статьи - дает гипотетическую гибкость в настройках, которой я не пользуюсь)
Вот схема интересующей нас части базы данных:
Для осуществления миграции учетной записи из домена в домен необходимо:
  1. В таблице users_tb в записи пользователя изменить значение поля domain_id на id целевого домена.
  2. В таблице aliases_tb
    1. Удалить псевдоним для целевой учетной записи, если он существует.
    2. Создать псевдоним для исходной учетной записи, с указанием на целевую(аналог переадрессации )
    3. В случае, если на исходную учетную запись указывали другие алиасы, заменить указатели на целевую учетную запись
  3. Переместить каталог учетной записи из каталога исходного домена в каталог целевого домена.
  4. Изменить настройки почтового клиента пользователя.
Варианты решения:
  1. Делать руками для каждого пользователя
  2. Написать скрипт, который будет переносить одного пользователя
  3. Написать скрипт, который будет переносить группу/всех пользователей
Так, как ручной работы по настройке клиента все равно не избежать, но делать все чисто руками не хочется остановился на втором варианте (очень не хочется оказаться в ситуации, когда пользователи уже перенесены, а клиенты у них не настроены и что то произошло, требующее немедленного внимания вариант №2 минимизирует головную боль в этом случае).
Собственно получился вот такой скриптик на пайтоне:
#!/usr/bin/env python3.6
# -*- coding: utf8 -*-
import os,sys,postgresql,string
# Строка подключения к БД
db=postgresql.open('pq://dbuser:dbpassword@localhost:5432/basename')
#функция возвращающая пару значений id, domainname или None если нет такого домена
def get_domain(domain,domainList):
    for dom in domainList:
        if dom[1]==domain:
            return dom
#функция возращающая id, homedir(или None если в домене нет такого пользователя)
def get_user(username,domain):
    query=db.prepare("Select id,homedir From users_tb Where username=$1 and domain_id=$2")
    user=query(username,domain)
    if len(user)!=0:
        return user[0]

emails = sys.argv
domains=db.query("Select id, domainname from domains_tb order by id")
if len(emails) ==1:
    print("Arguments not found! (need email for migration)")
    exit()
srcEmail=emails[1].split("@")
curDomain=get_domain(srcEmail[1],domains)
if curDomain==None:
    print("The server not support domain "+srcEmail[1])
    exit()
curUser=get_user(srcEmail[0],curDomain[0])
if curUser==None:
    print("User "+sys.argv[1]+" not found")
    exit()
domains.remove(curDomain)
print("Current email "+sys.argv[1])
print("Select index for your target domain: ")
dim=[]
domainDict={a[0]:a[1] for a in domains}
message="Input your index("
for i in domains:
    print(i[0],"--->",i[1])
    dim.append(i[0])
    message+=str(i[0])+","
message+="):"
newDomainId=0
while newDomainId not in dim:
    newDomainId=int(input(message))

if get_user(srcEmail[0],newDomainId):
    print("User "+srcEmail[0]+"@"+domainDict[newDomainId]+" is exist ")
    exit()
userUpdate=db.prepare("Update users_tb set domain_id=$1 where id=$2")
aliasDelete=db.prepare("Delete from aliases_tb where aliasname=$1 and domain_id=$2")
aliasInsert=db.prepare("INSERT INTO aliases_tb VALUES(DEFAULT,$1,$2,$3,'true')")
userUpdate(newDomainId,curUser[0])
aliasDelete(srcEmail[0],newDomainId)
aliasInsert(srcEmail[0],curDomain[0],srcEmail[0]+"@"+domainDict[newDomainId])
curPath=curUser[1]+"/"+curDomain[1]+"/"+srcEmail[0]
targetPuth=curUser[1]+"/"+domainDict[newDomainId]+"/"+srcEmail[0]
os.rename(curPath,targetPuth)
print("OK")

Скрипт запускается с аргументом - адресом мигрируемого пользователя:
./migrate/py user@domain.com
Для универсальности сделал скрипт с некоторой интерактивностью.
Обрабатываются ситуации:

  1. Отсутствует аргумент
  2. Отсутствует исходный домен
  3. Отсутствует исходный пользователь
  4. Присутствует целевой пользователь
В случае появления ситуации выдается сообщение и прерывается работа скрипта.
Делать скрипт более навороченым нету смысла.
Сообщения на английском, так как рута не локализую.

В общем как то так.

Комментариев нет:

Отправить комментарий