#!/usr/bin/env python import sys import os import shutil import tempfile from subprocess import call, Popen # TODO: # . pick a loop device sanely # - adapt size of partition to fit volume # - fix hostname, root password # - (once debugging diminishes) umount, losetup -d, maybe rm -r # - avoid race conditions with other loop-device users, too (somehow) # - get numbers from actual source partition table rather than # as magic numbers based on what sipb-xen-make-iso does with etch. def losetup(source, offset=0): # XXX we avoid colliding with other instances of ourself, # but when it comes to other loop-device users we just # pick a range things don't seem to use and hope... lockfilename = '/tmp/losetup.lock' os.close(os.open(lockfilename, os.O_CREAT+os.O_EXCL)) #lock try: loopdevice = None for i in xrange(32,60): # totally arbitrary, just looks to be unused on black-mesa filename = '/dev/loop%d'%i if 0 == len(file(filename).read(1)): loopdevice = filename # it's empty break if loopdevice is not None: call(['losetup', '-o', str(offset), loopdevice, source]) finally: os.unlink(lockfilename) #unlock return loopdevice # XX this means we can duplicate 9 at once. since it takes around 30 seconds, # this seems like an adequate capacity. def duplicate_lv(source, dest): '''duplicate boot record, filesystem from LV source to LV dest source, dest should be device filenames. ''' # XXX this is very specific to what the etch sipb-xen-make-iso installer does. # XXXX also, it's specific to four gigs. # step 1: copy the MBR call(['dd', 'if='+source, 'bs=512', 'count=63', 'of='+dest]) # step 2: fix up partition table # XX actually do this; this is our opportunity to resize the filesystem # step 3: make the filesystem, and copy its contents in sourcefs = losetup(source, 32256) destfs = losetup(dest, 32256) call(['mkfs.ext3', '-b', '4096', destfs, '987989']) tmptree = tempfile.mkdtemp('', 'auto-install.dup.', '/tmp') os.mkdir(tmptree+"/source") call(['mount', '-t', 'ext3', '-o', 'ro', sourcefs, tmptree+"/source"]) os.mkdir(tmptree+"/dest") call(['mount', '-t', 'ext3', destfs, tmptree+"/dest"]) call(['cp', '-aT', tmptree+"/source", tmptree+"/dest"]) # step 4: twiddle what we want to twiddle # XX do this # step 5: make the swap area swapfs = losetup(dest, 4046870016) call(['mkswap', swapfs, str(240943)]) # call(['losetup', '-d', swapfs]) print 'losetup -d '+swapfs # step 6: clean up. # XX actually unmount etc (leaving it is for debugging) print 'umount '+tmptree+'/source' call(['umount', tmptree+"/dest"]) # needed to flush writes print 'losetup -d '+sourcefs print 'losetup -d '+destfs if __name__ == '__main__': duplicate_lv(*sys.argv[1:])