Blame SOURCES/mailman-migrate-fhs

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