[231] | 1 | #!/usr/bin/env python |
---|
| 2 | |
---|
| 3 | import sys |
---|
| 4 | import os |
---|
| 5 | import shutil |
---|
| 6 | import tempfile |
---|
| 7 | from subprocess import call, Popen |
---|
| 8 | |
---|
| 9 | # TODO: |
---|
| 10 | # . pick a loop device sanely |
---|
| 11 | # - adapt size of partition to fit volume |
---|
| 12 | # - fix hostname, root password |
---|
| 13 | # - (once debugging diminishes) umount, losetup -d, maybe rm -r |
---|
| 14 | # - avoid race conditions with other loop-device users, too (somehow) |
---|
| 15 | # - get numbers from actual source partition table rather than |
---|
| 16 | # as magic numbers based on what sipb-xen-make-iso does with etch. |
---|
| 17 | |
---|
| 18 | def losetup(source, offset=0): |
---|
| 19 | # XXX we avoid colliding with other instances of ourself, |
---|
| 20 | # but when it comes to other loop-device users we just |
---|
| 21 | # pick a range things don't seem to use and hope... |
---|
| 22 | lockfilename = '/tmp/losetup.lock' |
---|
| 23 | os.close(os.open(lockfilename, os.O_CREAT+os.O_EXCL)) #lock |
---|
| 24 | try: |
---|
| 25 | loopdevice = None |
---|
| 26 | for i in xrange(32,60): # totally arbitrary, just looks to be unused on black-mesa |
---|
| 27 | filename = '/dev/loop%d'%i |
---|
| 28 | if 0 == len(file(filename).read(1)): |
---|
| 29 | loopdevice = filename # it's empty |
---|
| 30 | break |
---|
| 31 | if loopdevice is not None: |
---|
| 32 | call(['losetup', '-o', str(offset), loopdevice, source]) |
---|
| 33 | finally: |
---|
| 34 | os.unlink(lockfilename) #unlock |
---|
| 35 | return loopdevice |
---|
| 36 | # XX this means we can duplicate 9 at once. since it takes around 30 seconds, |
---|
| 37 | # this seems like an adequate capacity. |
---|
| 38 | |
---|
| 39 | def duplicate_lv(source, dest): |
---|
| 40 | '''duplicate boot record, filesystem from LV source to LV dest |
---|
| 41 | |
---|
| 42 | source, dest should be device filenames. |
---|
| 43 | ''' |
---|
| 44 | # XXX this is very specific to what the etch sipb-xen-make-iso installer does. |
---|
| 45 | # XXXX also, it's specific to four gigs. |
---|
| 46 | # step 1: copy the MBR |
---|
| 47 | call(['dd', 'if='+source, 'bs=512', 'count=63', 'of='+dest]) |
---|
| 48 | # step 2: fix up partition table |
---|
| 49 | # XX actually do this; this is our opportunity to resize the filesystem |
---|
| 50 | # step 3: make the filesystem, and copy its contents in |
---|
| 51 | sourcefs = losetup(source, 32256) |
---|
| 52 | destfs = losetup(dest, 32256) |
---|
| 53 | call(['mkfs.ext3', '-b', '4096', destfs, '987989']) |
---|
| 54 | tmptree = tempfile.mkdtemp('', 'auto-install.dup.', '/tmp') |
---|
| 55 | os.mkdir(tmptree+"/source") |
---|
| 56 | call(['mount', '-t', 'ext3', '-o', 'ro', sourcefs, tmptree+"/source"]) |
---|
| 57 | os.mkdir(tmptree+"/dest") |
---|
| 58 | call(['mount', '-t', 'ext3', destfs, tmptree+"/dest"]) |
---|
| 59 | call(['cp', '-aT', tmptree+"/source", tmptree+"/dest"]) |
---|
| 60 | # step 4: twiddle what we want to twiddle |
---|
| 61 | # XX do this |
---|
| 62 | # step 5: make the swap area |
---|
| 63 | swapfs = losetup(dest, 4046870016) |
---|
| 64 | call(['mkswap', swapfs, str(240943)]) |
---|
| 65 | # call(['losetup', '-d', swapfs]) |
---|
| 66 | print 'losetup -d '+swapfs |
---|
| 67 | # step 6: clean up. |
---|
| 68 | # XX actually unmount etc (leaving it is for debugging) |
---|
| 69 | print 'umount '+tmptree+'/source' |
---|
| 70 | call(['umount', tmptree+"/dest"]) # needed to flush writes |
---|
| 71 | print 'losetup -d '+sourcefs |
---|
| 72 | print 'losetup -d '+destfs |
---|
| 73 | |
---|
| 74 | if __name__ == '__main__': |
---|
| 75 | duplicate_lv(*sys.argv[1:]) |
---|