#!/usr/bin/env python2.5

import sys
import os
import shutil
import tempfile
from subprocess import call, check_call, Popen

# TODO:
# . pick a loop device sanely
# - adapt size of partition to fit volume
# - fix hostname, root password
# - (once debugging diminishes) umount, losetup -d, maybe rm -r
# - avoid race conditions with other loop-device users, too (somehow)
# - get numbers from actual source partition table rather than
#   as magic numbers based on what sipb-xen-make-iso does with etch.

def losetup(source, offset=0):
  # XXX we avoid colliding with other instances of ourself,
  #     but when it comes to other loop-device users we just
  #     pick a range things don't seem to use and hope...
  lockfilename = '/tmp/losetup.lock'
  os.close(os.open(lockfilename, os.O_CREAT+os.O_EXCL)) #lock
  try:
    loopdevice = None
    for i in xrange(32,60): # totally arbitrary, just looks to be unused on black-mesa
      filename = '/dev/loop%d'%i
      if 0 == len(file(filename).read(1)):
        loopdevice = filename # it's empty
        break
    if loopdevice is not None:
      call(['losetup', '-o', str(offset), loopdevice, source])
  finally:
    os.unlink(lockfilename) #unlock
  return loopdevice
  # XX this means we can duplicate 9 at once.  since it takes around 30 seconds,
  # this seems like an adequate capacity.

def duplicate_lv(source, dest):
  '''duplicate boot record, filesystem from LV source to LV dest
  
  source, dest should be device filenames.
  '''
  # XXX this is very specific to what the etch sipb-xen-make-iso installer does.
  # XXXX also, it's specific to four gigs.
  # step 1: copy the MBR
  call(['dd', 'if='+source, 'bs=512', 'count=63', 'of='+dest])
  # step 2: fix up partition table
  # XX actually do this; this is our opportunity to resize the filesystem
  # step 3: make the filesystem, and copy its contents in
  sourcefs = losetup(source, 32256)
  destfs = losetup(dest, 32256)
  call(['mkfs.ext3', '-b', '4096', destfs, '987989'])
  tmptree = tempfile.mkdtemp('', 'auto-install.dup.', '/tmp')
  os.mkdir(tmptree+"/source")
  call(['mount', '-t', 'ext3', '-o', 'ro', sourcefs, tmptree+"/source"])
  os.mkdir(tmptree+"/dest")
  call(['mount', '-t', 'ext3', destfs, tmptree+"/dest"])
  call(['cp', '-aT', tmptree+"/source", tmptree+"/dest"])
  # step 4: twiddle what we want to twiddle
  # XX do this
  # step 5: make the swap area
  swapfs = losetup(dest, 4046870016)
  call(['mkswap', swapfs, str(240943)])
  # call(['losetup', '-d', swapfs])
  print 'losetup -d '+swapfs
  # step 6: clean up.
  # XX actually unmount etc (leaving it is for debugging)
  print 'umount '+tmptree+'/source'
  call(['umount', tmptree+"/dest"]) # needed to flush writes
  print 'losetup -d '+sourcefs
  print 'losetup -d '+destfs

def duplicate_by_vm(source, target, *args):
  # source, target should be machine names
  prefix = 'd_'
  # 1. copy (by dd) source to target
  source_device = '/dev/xenvg/' + prefix + source + '_hda'
  target_device = '/dev/xenvg/' + prefix + target + '_hda'
  #check_call(['/bin/dd', 'bs=1M', 'conv=nocreat',
  #            'if='+source_device, 'of='+target_device])
  # 2. prepare arguments volume
  args_volume = prefix+target+'_args'
  args_device = '/dev/xenvg/' + args_volume
  check_call(['/sbin/lvcreate', 'xenvg', '--name', args_volume, '--size', '4K'])
  file(args_device, 'w').write('\n'.join(args) + '\n')

  copier_device = '/dev/xenvg/d_wert_hda'
  check_call(['/usr/sbin/xm', 'create', 'sipb-database',
              'machine_name='+target,
              'disks=' + ' '.join(['phy:'+copier_device+',hda,w',
                                   'phy:'+target_device+',hdc,w',
                                   'phy:'+args_device+',hdd,w'])])
  boot = 'c'

  # XXX make this happen! check_call(['/sbin/lvremove', '-f', 'xenvg/'+args_volume])
  

if __name__ == '__main__':
  duplicate_by_vm(*sys.argv[1:])
