1 #!/usr/bin/env python2.5
8 from subprocess import call, check_call, Popen, PIPE
10 def losetup(source, offset=0):
11 # XXX we avoid colliding with other instances of ourself,
12 # but when it comes to other loop-device users we just
13 # pick a range things don't seem to use and hope...
14 lockfilename = '/tmp/losetup.lock'
15 os.close(os.open(lockfilename, os.O_CREAT+os.O_EXCL)) #lock
18 for i in xrange(32,60): # totally arbitrary, just looks to be unused on black-mesa
19 filename = '/dev/loop%d'%i
20 if 0 == len(file(filename).read(1)):
21 loopdevice = filename # it's empty
23 if loopdevice is not None:
24 call(['losetup', '-o', str(offset), loopdevice, source])
26 raise RuntimeError('out of loop devices for copying VM image: too many at once?')
28 os.unlink(lockfilename) #unlock
30 # XX this means we can duplicate 9 at once. since it takes around 30 seconds,
31 # this seems like an adequate capacity.
33 def duplicate_lv(source, dest):
34 '''OBSOLETE: duplicate boot record, filesystem from LV source to LV dest
36 source, dest should be device filenames.
38 Now we just dd. Doesn't support resizing, but it's easier,
39 especially if we allow the partition table to vary between images.
41 # XXX this is very specific to what the etch sipb-xen-make-iso installer does.
42 # XXXX also, it's specific to four gigs.
43 # step 1: copy the MBR
44 call(['dd', 'if='+source, 'bs=512', 'count=63', 'of='+dest])
45 # step 2: fix up partition table
46 # XX actually do this; this is our opportunity to resize the filesystem
47 # step 3: make the filesystem, and copy its contents in
48 sourcefs = losetup(source, 32256)
49 destfs = losetup(dest, 32256)
50 call(['mkfs.ext3', '-b', '4096', destfs, '987989'])
51 tmptree = tempfile.mkdtemp('', 'auto-install.dup.', '/tmp')#yes, args backward
52 os.mkdir(tmptree+"/source")
53 call(['mount', '-t', 'ext3', '-o', 'ro', sourcefs, tmptree+"/source"])
54 os.mkdir(tmptree+"/dest")
55 call(['mount', '-t', 'ext3', destfs, tmptree+"/dest"])
56 call(['cp', '-aT', tmptree+"/source", tmptree+"/dest"])
57 # step 4: twiddle what we want to twiddle
59 # step 5: make the swap area
60 swapfs = losetup(dest, 4046870016)
61 call(['mkswap', swapfs, str(240943)])
62 # call(['losetup', '-d', swapfs])
63 print 'losetup -d '+swapfs
65 # XX actually unmount etc (leaving it is for debugging)
66 print 'umount '+tmptree+'/source'
67 call(['umount', tmptree+"/dest"]) # needed to flush writes
68 print 'losetup -d '+sourcefs
69 print 'losetup -d '+destfs
71 def frob_copy(target, *args):
72 '''UNUSED: maybe we'll use this someday; it does isolate the frobber.'''
73 # 1. prepare arguments volume
74 args_volume = prefix+target+'_args'
75 args_device = '/dev/xenvg/' + args_volume
76 check_call(['/sbin/lvcreate', 'xenvg', '--name', args_volume, '--size', '4K'])
77 file(args_device, 'w').write('\n'.join(args) + '\n')
79 # 2. invoke frobber vm
80 copier_device = '/dev/xenvg/d_wert_hda'
81 check_call(['/usr/sbin/xm', 'create', 'sipb-database',
82 'machine_name='+target,
83 'disks=' + ' '.join(['phy:'+copier_device+',hda,w',
84 'phy:'+target_device+',hdc,w',
85 'phy:'+args_device+',hdd,w'])])
87 # XXX should check_call(['/sbin/lvremove', '-f', 'xenvg/'+args_volume])
89 def frob_copy_simple(target, hostname, rootpw):
90 """target should be an LV device filename
92 This is highly specific to the etch install produced by sipb-xen-make-iso.
93 Generalizing and customizing to other distros is left to the future...
96 fs = losetup(target, 32256)
97 mntdir = tempfile.mkdtemp('', 'auto-install.frob.', '/tmp')
98 call(['mount', '-t', 'ext3', fs, mntdir])
100 # 2a: (printf "%s\n" "$ROOTPW"; sleep .3; printf "%s\n" "$ROOTPW")
101 # | /usr/sbin/chroot "$TARGET" /usr/bin/passwd root
102 p = Popen(['/usr/sbin/chroot', mntdir, '/usr/bin/passwd', 'root'], stdin=PIPE)
103 p.stdin.write(rootpw+'\n')
105 p.stdin.write(rootpw+'\n')
109 # 2b: clear generated file that has eth0's old MAC address
110 # rm $TARGET/etc/udev/rules.d/z25_persistent-net.rules
111 os.unlink('etc/udev/rules.d/z25_persistent-net.rules')
113 # XX Use nullmailer in image, not exim4. (Fewer copies of hostname.)
114 # 2c1: rm /var/lib/dhcp3/dhclient.eth0.leases
115 os.unlink('var/lib/dhcp3/dhclient.eth0.leases')
116 # 2c2: /etc/hosts, /etc/hostname; /etc/motd? /etc/mailname?
117 call(['perl', '-i', '-pe', 's/ice3.servers.csail.mit.edu/'+hostname+'/g',
118 'etc/hosts', 'etc/hostname', 'etc/motd', 'etc/mailname'])
121 call(['umount', mntdir])
123 call(['losetup', '-d', fs])
125 def duplicate_by_vm(source, target, rootpw, nodd=False):
126 # source, target should be machine names
128 # 1. copy (by dd) source to target
129 source_device = '/dev/xenvg/' + prefix + source + '_hda'
130 target_device = '/dev/xenvg/' + prefix + target + '_hda'
132 check_call(['/bin/dd', 'bs=1M', 'conv=nocreat',
133 'if='+source_device, 'of='+target_device])
135 frob_copy_simple(target_device, target, rootpw)
140 os.environ['PATH'] = '/usr/sbin:/usr/bin:/sbin:/bin'
141 if subcommand == 'lvcopy':
143 if args[0] == '--nodd':
145 kwargs['nodd'] = True
146 duplicate_by_vm(*args, **kwargs)
148 print >>sys.stderr, argv[0]+": unknown subcommand: "+subcommand
152 if __name__ == '__main__':
153 sys.exit(main(*sys.argv))