Blame SOURCES/mailman-migrate-fhs

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