Blame SOURCES/mailman-migrate-fhs

7812c9
#!/usr/bin/python
7812c9
7812c9
import sys
7812c9
import os
7812c9
import re
7812c9
import shutil
7812c9
import getopt
7812c9
from stat import *
7812c9
7812c9
#------------------------------------------------------------------------------
7812c9
7812c9
# Command Line Args
7812c9
doit = True
7812c9
verbose = False
7812c9
quiet = False
7812c9
warn = False
7812c9
force = False
7812c9
print_mapping = False
7812c9
remove_files = False
7812c9
remove_installation = False
7812c9
7812c9
# Scan Results
7812c9
existing_files = {}
7812c9
non_existing_files = {}
7812c9
7812c9
# Directory and File mappings
7812c9
7812c9
# This is the complete directory map, it includes both data files
7812c9
# and run-time files
7812c9
dir_map = {
7812c9
    '/var/mailman'				: '/var/lib/mailman',
7812c9
    '/var/mailman/Mailman'			: '/usr/lib/mailman/Mailman',
7812c9
    '/var/mailman/archives'			: '/var/lib/mailman/archives',
7812c9
    '/var/mailman/bin'				: '/usr/lib/mailman/bin',
7812c9
    '/var/mailman/cgi-bin'			: '/usr/lib/mailman/cgi-bin',
7812c9
    '/var/mailman/cron'				: '/usr/lib/mailman/cron',
7812c9
    '/var/mailman/data'				: '/var/lib/mailman/data',
7812c9
    '/var/mailman/lists'			: '/var/lib/mailman/lists',
7812c9
    '/var/mailman/locks'			: '/var/lock/mailman',
7812c9
    '/var/mailman/logs'				: '/var/log/mailman',
7812c9
    '/var/mailman/mail'				: '/usr/lib/mailman/mail',
7812c9
    '/var/mailman/messages'			: '/usr/lib/mailman/messages',
7812c9
    '/var/mailman/pythonlib'			: '/usr/lib/mailman/pythonlib',
7812c9
    '/var/mailman/qfiles'			: '/var/spool/mailman',
7812c9
    '/var/spool/mailman/qfiles'			: '/var/spool/mailman',
7812c9
    '/var/mailman/scripts'			: '/usr/lib/mailman/scripts',
7812c9
    '/var/mailman/spam'				: '/var/lib/mailman/spam',
7812c9
    '/var/mailman/templates'			: '/usr/lib/mailman/templates',
7812c9
    '/var/mailman/tests'			: '/usr/lib/mailman/tests'
7812c9
}
7812c9
7812c9
# These are directories that contain data files the user may
7812c9
# want to preserve from an old installation and should be copied
7812c9
# into the new directory location.
7812c9
data_dir_map = {
7812c9
    '/var/mailman/archives'			: '/var/lib/mailman/archives',
7812c9
    '/var/mailman/data'				: '/var/lib/mailman/data',
7812c9
    '/var/mailman/lists'			: '/var/lib/mailman/lists',
7812c9
    '/var/mailman/logs'				: '/var/log/mailman',
7812c9
    '/var/mailman/qfiles'			: '/var/spool/mailman',
7812c9
    '/var/spool/mailman/qfiles'			: '/var/spool/mailman',
7812c9
    '/var/mailman/spam'				: '/var/lib/mailman/spam',
7812c9
}
7812c9
7812c9
# These are mappings for individual files. They represent files that
7812c9
# cannot be mapped via their parent dirctories, they must be treated
7812c9
# individually.
7812c9
file_map = {
7812c9
    '/var/mailman/data/adm.pw'			: '/etc/mailman/adm.pw',
7812c9
    '/var/mailman/data/creator.pw'		: '/etc/mailman/creator.pw',
7812c9
    '/var/mailman/data/aliases'			: '/etc/mailman/aliases',
7812c9
    '/var/mailman/data/virtual-mailman'		: '/etc/mailman/virtual-mailman',
7812c9
    '/var/mailman/data/sitelist.cfg'		: '/etc/mailman/sitelist.cfg',
7812c9
    '/var/mailman/data/master-qrunner.pid'	: '/var/run/mailman/master-qrunner.pid'
7812c9
}
7812c9
7812c9
#------------------------------------------------------------------------------
7812c9
7812c9
def DumpMapping():
7812c9
    '''Print out the directory and file mappings'''
7812c9
    print "Directory Mapping:"
7812c9
    for key in dir_map.keys():
7812c9
        print "%s --> %s" %(key, dir_map[key])
7812c9
7812c9
    print "\nFile Mapping:"
7812c9
    for key in file_map.keys():
7812c9
        print "%s --> %s" %(key, file_map[key])
7812c9
7812c9
def RecordFile(src, dst):
7812c9
    '''If the src files (old) exists record this as a potential
7812c9
    file operation. File operations are grouped into two sets,
7812c9
    those where the dst (new) files exists and those where it does not
7812c9
    exist. This is done to prevent overwriting files'''
7812c9
    
7812c9
    global existing_files, non_existing_files
7812c9
7812c9
    if not os.path.exists(src):
7812c9
        return
7812c9
7812c9
    if existing_files.has_key(src):
7812c9
        if warn:
7812c9
            print "WARNING: src file already seen (%s) and has dst match: (%s)" % (src, dst)
7812c9
        return
7812c9
7812c9
    if non_existing_files.has_key(src):
7812c9
        if warn:
7812c9
            print "WARNING: src file already seen (%s) does not have dst match" % (src)
7812c9
        return
7812c9
7812c9
    if os.path.exists(dst):
7812c9
        existing_files[src] = dst
7812c9
    else:
7812c9
        non_existing_files[src] = dst
7812c9
7812c9
def GetCopyFiles(old_root, new_root):
7812c9
    '''Recursively generate a list of src files (old) in the old_root
7812c9
    and pair each of them with their new dst path name'''
7812c9
    
7812c9
    prefix_re = re.compile("^(%s)/*(.*)" % re.escape(old_root))
7812c9
    dst_files_existing = []
7812c9
    dst_files_non_existing = []
7812c9
    for root, dirs, files in os.walk(old_root):
7812c9
        match = prefix_re.match(root)
7812c9
        subdir = match.group(2)
7812c9
        for name in files:
7812c9
            oldpath = os.path.join(root, name)
7812c9
            newpath = os.path.join(new_root, subdir, name)
7812c9
            RecordFile(oldpath, newpath)
7812c9
7812c9
def CopyFile(src_path, dst_path):
7812c9
    '''Copy file, preserve its mode and ownership. If the dst directory
7812c9
    does not exist, create it preserving the mode and ownership of the
7812c9
    src direcotry'''
7812c9
    
7812c9
    if not doit:
7812c9
        print "cp %s %s" % (src_path, dst_path)
7812c9
        return
7812c9
    
7812c9
    src_dir = os.path.dirname(src_path)
7812c9
    dst_dir = os.path.dirname(dst_path)
7812c9
7812c9
    if not os.path.isdir(dst_dir):
7812c9
        if os.path.exists(dst_dir):
7812c9
            print "ERROR: dst dir exists, but is not directory (%s)" % dst_dir
7812c9
            return
7812c9
        st = os.stat(src_dir)
7812c9
        os.makedirs(dst_dir, st[ST_MODE])
7812c9
        os.chown(dst_dir, st[ST_UID], st[ST_GID])
7812c9
    
7812c9
    shutil.copy2(src_path, dst_path)
7812c9
    st = os.stat(src_path)
7812c9
    os.chown(dst_path, st[ST_UID], st[ST_GID])
7812c9
7812c9
def RemoveFile(path):
7812c9
    '''Remove the file'''
7812c9
    
7812c9
    if not os.path.exists(path):
7812c9
        if warn:
7812c9
            print "WARNING: attempt to remove non-existent file (%s)" % path
7812c9
        return
7812c9
7812c9
    if not os.path.isfile(path):
7812c9
        if warn:
7812c9
            print "WARNING: attempt to remove non-plain file (%s)" % path
7812c9
        return
7812c9
7812c9
    if not doit:
7812c9
        print "rm %s" % (path)
7812c9
        return
7812c9
7812c9
    os.unlink(path)
7812c9
    
7812c9
def RemoveDirs(top):
7812c9
    '''Delete everything reachable from the directory named in 'top',
7812c9
    assuming there are no symbolic links.
7812c9
    CAUTION:  This is dangerous!  For example, if top == '/', it
7812c9
    could delete all your disk files.'''
7812c9
    for root, dirs, files in os.walk(top, topdown=False):
7812c9
        for name in files:
7812c9
            path = os.path.join(root, name)
7812c9
            if not doit:
7812c9
                print "rm %s" % (path)
7812c9
            else:
7812c9
                os.remove(path)
7812c9
        for name in dirs:
7812c9
            path = os.path.join(root, name)
7812c9
            if not doit:
7812c9
                print "rmdir %s" % (path)
7812c9
            else:
7812c9
                os.rmdir(path)
7812c9
7812c9
def Usage():
7812c9
    print """
7812c9
This script will help you copy mailman data files from the old
7812c9
directory structure to the new FHS directory structure.
7812c9
7812c9
Mailman should not be running when you perform this!
7812c9
/sbin/service mailman stop
7812c9
7812c9
This script is conservative, by default it will not overwrite
7812c9
any file in the new directory on the assumption it is most recent
7812c9
and most correct. If you want to force overwrites use -f.
7812c9
7812c9
Files are copied to the new directories, if you want to remove the
7812c9
old data files use -r. Hint: copy first and test, once everything is
7812c9
working remove the old files with -r. If you want to remove the entire
7812c9
old installation use -R
7812c9
7812c9
migrate [-f] [-n] [-q] [-v] [-w] [-m] [-r] [-R]
7812c9
-n don't execute, but show what would be done
7812c9
-f force destination overwrites
7812c9
-m print mapping
7812c9
-r remove old data files
7812c9
-R remove entire old installation
7812c9
-q be quiet
7812c9
-v be verbose
7812c9
-w print warnings
7812c9
-h help
7812c9
"""
7812c9
7812c9
#------------------------------------------------------------------------------
7812c9
7812c9
try:
7812c9
    opts, args = getopt.getopt(sys.argv[1:], "nfvmqwhrR")
7812c9
    for o, a in opts:
7812c9
        if o == "-n":
7812c9
            doit = False
7812c9
        elif o == "-f":
7812c9
            force = True
7812c9
        elif o == "-v":
7812c9
            verbose = True
7812c9
        elif o == "-m":
7812c9
            print_mapping = True
7812c9
        elif o == "-q":
7812c9
            quiet = True
7812c9
        elif o == "-w":
7812c9
            warn = True
7812c9
        elif o == "-r":
7812c9
            remove_files = True
7812c9
        elif o == "-R":
7812c9
            remove_installation = True
7812c9
        elif o == "-h":
7812c9
            Usage()
7812c9
            sys.exit(1)
7812c9
except getopt.GetoptError, err:
7812c9
    print err
7812c9
    Usage()
7812c9
    sys.exit(1)
7812c9
7812c9
7812c9
if print_mapping:
7812c9
    DumpMapping()
7812c9
    sys.exit(0)
7812c9
7812c9
# Generate file list
7812c9
for src_dir in data_dir_map.keys():
7812c9
    GetCopyFiles(src_dir, dir_map[src_dir])
7812c9
7812c9
for src_file in file_map.keys():
7812c9
    RecordFile(src_file, file_map[src_file])
7812c9
7812c9
7812c9
# Copy files
7812c9
for src in non_existing_files:
7812c9
    dst = non_existing_files[src]
7812c9
    CopyFile(src, dst)
7812c9
7812c9
if force:
7812c9
    for src in existing_files:
7812c9
        dst = existing_files[src]
7812c9
        CopyFile(src, dst)
7812c9
else:
7812c9
    if len(existing_files) > 0 and not quiet:
7812c9
        print "\nThe following files already exist in the destination, they will NOT be copied"
7812c9
        print "To force overwriting invoke with -f\n"
7812c9
        for src in existing_files:
7812c9
            dst = existing_files[src]
7812c9
            print "# cp %s %s" %(src, dst)
7812c9
7812c9
# Remove old files
7812c9
if remove_files:
7812c9
    for src in existing_files:
7812c9
        RemoveFile(src)
7812c9
    for src in non_existing_files:
7812c9
        RemoveFile(src)
7812c9
7812c9
if remove_installation:
7812c9
    for old_dir in dir_map.keys():
7812c9
        RemoveDirs(old_dir)
7812c9
7812c9
7812c9
sys.exit(0)    
7812c9