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:]) |
---|