source: trunk/packages/sipb-xen-guest-installer/files/usr/sbin/sipb-xen-duplicate @ 319

Last change on this file since 319 was 245, checked in by price, 17 years ago

Prototype fast installer using a special guest.

Rather than poke directly in the boot sector, partition table,
etc, which caused so much pain last week (r231), we just dd
the entire disk. Then we poke at the bits that need to change
(like secrets, the hostname, the root password, the filesystem uuids).
Since this poking is distribution-dependent, we want it to be
supplied by the image, so to make that safe we run it in a VM.

The plan is

  1. source image is dd'd (in the host) to the target image
  2. special copying-installer image is booted, with access to target and to a small image containing its arguments
  3. copying-installer guest reads arguments, mounts target, runs a script with a well-known name in target, passing arguments.

Bits I haven't done yet include

  • frob grub config in the copying-installer image so it boots with init = the copying-installer script (doing this manually for testing)
  • install script into copying-installer, or into my example image, in a sane automated way (they're checked into partial/ here)
  • all the tasks the image's script ought to actually carry out
  • make this all available from remctl and the web pages.
  • Property svn:executable set to *
File size: 3.8 KB
Line 
1#!/usr/bin/env python2.5
2
3import sys
4import os
5import shutil
6import tempfile
7from subprocess import call, check_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
18def 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
39def 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
74def duplicate_by_vm(source, target, *args):
75  # source, target should be machine names
76  prefix = 'd_'
77  # 1. copy (by dd) source to target
78  source_device = '/dev/xenvg/' + prefix + source + '_hda'
79  target_device = '/dev/xenvg/' + prefix + target + '_hda'
80  #check_call(['/bin/dd', 'bs=1M', 'conv=nocreat',
81  #            'if='+source_device, 'of='+target_device])
82  # 2. prepare arguments volume
83  args_volume = prefix+target+'_args'
84  args_device = '/dev/xenvg/' + args_volume
85  check_call(['/sbin/lvcreate', 'xenvg', '--name', args_volume, '--size', '4K'])
86  file(args_device, 'w').write('\n'.join(args) + '\n')
87
88  copier_device = '/dev/xenvg/d_wert_hda'
89  check_call(['/usr/sbin/xm', 'create', 'sipb-database',
90              'machine_name='+target,
91              'disks=' + ' '.join(['phy:'+copier_device+',hda,w',
92                                   'phy:'+target_device+',hdc,w',
93                                   'phy:'+args_device+',hdd,w'])])
94  boot = 'c'
95
96  # XXX make this happen! check_call(['/sbin/lvremove', '-f', 'xenvg/'+args_volume])
97 
98
99if __name__ == '__main__':
100  duplicate_by_vm(*sys.argv[1:])
Note: See TracBrowser for help on using the repository browser.