ecfa8bf7419e57e3ed3b08aa3dfe842341ed1ba6
[invirt/packages/invirt-autoinstaller.git] / files / usr / sbin / sipb-xen-lvcopy
1 #!/usr/bin/env python2.5
2
3 import sys
4 import os
5 import shutil
6 import tempfile
7 import time
8 from subprocess import call, check_call, Popen, PIPE
9
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
16   try:
17     loopdevice = None
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
22         break
23     if loopdevice is not None:
24       call(['losetup', '-o', str(offset), loopdevice, source])
25     else:
26       raise RuntimeError('out of loop devices for copying VM image: too many at once?')
27   finally:
28     os.unlink(lockfilename) #unlock
29   return loopdevice
30   # XX this means we can duplicate 9 at once.  since it takes around 30 seconds,
31   # this seems like an adequate capacity.
32
33 def duplicate_lv(source, dest):
34   '''OBSOLETE: duplicate boot record, filesystem from LV source to LV dest
35   
36   source, dest should be device filenames.
37
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.
40   '''
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
58   # XX do this
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
64   # step 6: clean up.
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
70
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')
78
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'])])
86
87   # XXX should check_call(['/sbin/lvremove', '-f', 'xenvg/'+args_volume])
88
89 def frob_copy_simple(target, hostname, rootpw):
90   """target should be an LV device filename
91
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...
94   """
95   # 1: mount filesystem
96   fs = losetup(target, 32256)
97   mntdir = tempfile.mkdtemp('', 'auto-install.frob.', '/tmp')
98   call(['mount', '-t', 'ext3', fs, mntdir])
99   # 2: do frobbing
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')
104   time.sleep(1)
105   p.stdin.write(rootpw+'\n')
106   p.stdin.close()
107   p.wait()
108   os.chdir(mntdir)
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')
112   # 2c: hostname.
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'])
119   # 3: clean up
120   os.chdir('/')
121   call(['umount', mntdir])
122   os.rmdir(mntdir)
123   call(['losetup', '-d', fs])
124
125 def duplicate_by_vm(source, target, rootpw, nodd=False):
126   # source, target should be machine names
127   prefix = 'd_'
128   # 1. copy (by dd) source to target
129   source_device = '/dev/xenvg/' + prefix + source + '_hda'
130   target_device = '/dev/xenvg/' + prefix + target + '_hda'
131   if not nodd:
132     check_call(['/bin/dd', 'bs=1M', 'conv=nocreat',
133                 'if='+source_device, 'of='+target_device])
134   # 2. frob target
135   frob_copy_simple(target_device, target, rootpw)
136
137 def main(*argv):
138   subcommand = argv[1]
139   args = argv[2:]
140   os.environ['PATH'] = '/usr/sbin:/usr/bin:/sbin:/bin'
141   if subcommand == 'lvcopy':
142     kwargs = {}
143     if args[0] == '--nodd':
144       args = args[1:]
145       kwargs['nodd'] = True
146     duplicate_by_vm(*args, **kwargs)
147   else:
148     print >>sys.stderr, argv[0]+": unknown subcommand: "+subcommand
149     return 2
150   return 0
151
152 if __name__ == '__main__':
153   sys.exit(main(*sys.argv))