diff --git a/util/vm/build.py b/util/vm/build.py index 1cac35a..bcd8726 100755 --- a/util/vm/build.py +++ b/util/vm/build.py @@ -28,15 +28,16 @@ Basic idea: import os from os import stat, path -from stat import ST_MODE +from stat import ST_MODE, ST_SIZE from os.path import abspath -from sys import exit, stdout +from sys import exit, stdout, argv import re from glob import glob from subprocess import check_output, call, Popen from tempfile import mkdtemp from time import time, strftime, localtime import argparse +from distutils.spawn import find_executable pexpect = None # For code check - imported dynamically @@ -46,6 +47,7 @@ TIMEOUT=600 # Some configuration LogToConsole = False # VM output to console rather than log file SaveQCOW2 = False # Save QCOW2 image rather than deleting it +NoKVM = False # Don't use kvm and use emulation instead VMImageDir = os.environ[ 'HOME' ] + '/vm-images' @@ -104,6 +106,10 @@ def run( cmd, **kwargs ): "Convenient interface to check_output" log( '-', cmd ) cmd = cmd.split() + arg0 = cmd[ 0 ] + if not find_executable( arg0 ): + raise Exception( 'Cannot find executable "%s";' % arg0 + + 'you might try %s --depend' % argv[ 0 ] ) return check_output( cmd, **kwargs ) @@ -112,6 +118,9 @@ def srun( cmd, **kwargs ): return run( 'sudo ' + cmd, **kwargs ) +# BL: we should probably have a "checkDepend()" which +# checks to make sure all dependencies are satisfied! + def depend(): "Install package dependencies" log( '* Installing package dependencies' ) @@ -334,8 +343,12 @@ def installUbuntu( iso, image, logfilename='install.log' ): srun( 'mount %s %s' % ( iso, mnt ) ) kernel = path.join( mnt, 'install/vmlinuz' ) initrd = path.join( mnt, 'install/initrd.gz' ) + if NoKVM: + accel = 'tcg' + else: + accel = 'kvm' cmd = [ 'sudo', kvm, - '-machine', 'accel=kvm', + '-machine', 'accel=%s' % accel, '-nographic', '-netdev', 'user,id=mnbuild', '-device', 'virtio-net,netdev=mnbuild', @@ -384,8 +397,12 @@ def boot( cow, kernel, initrd, logfile ): global pexpect import pexpect arch = archFor( kernel ) + if NoKVM: + accel = 'tcg' + else: + accel = 'kvm' cmd = [ 'sudo', 'qemu-system-' + arch, - '-machine accel=kvm', + '-machine accel=%s' % accel, '-nographic', '-netdev user,id=mnbuild', '-device virtio-net,netdev=mnbuild', @@ -478,47 +495,109 @@ def convert( cow, basename ): return vmdk -# Template for virt-image(5) file +# Template for OVF - a very verbose format! +# In the best of all possible worlds, we might use an XML +# library to generate this, but a template is easier and +# possibly more concise! -VirtImageXML = """ - - - %s - - - - %s - - - - - - - - 1 - %s - - - - - - - - +OVFTemplate = """ + + + + + + +Virtual disk information + + + +The list of logical networks + +The nat network + + + +A Mininet Virtual Machine (%s) +mininet-vm + +Virtual hardware requirements + +hertz * 10^6 +Number of Virtual CPUs +1 virtual CPU(s) +1 +3 +1 + + +byte * 2^20 +Memory Size +%dMB of memory +2 +4 +%d + + +0 +scsiController0 +SCSI Controller +scsiController0 +4 +lsilogic +6 + + +0 +disk1 +ovf:/disk/vmdisk1 +11 +4 +17 + + +2 +true +nat +E1000 ethernet adapter on nat +ethernet0 +12 +E1000 +10 + + +0 +usb +USB Controller +usb +9 +23 + + + + """ -def genVirtImage( name, mem, diskname, disksize ): - "Generate and return virt-image file name.xml" - # Our strategy is going to be: create a - # virt-image file and then use virt-convert to convert - # it to an .ovf file - xmlfile = name + '.xml' - arch = archFor( name ) - xmltext = VirtImageXML % ( name, arch, mem, diskname, disksize ) - with open( xmlfile, 'w+' ) as f: +def generateOVF( name, diskname, disksize, mem=1024 ): + """Generate (and return) OVF file "name.ovf" + name: root name of OVF file to generate + diskname: name of disk file + disksize: size of virtual disk in bytes + mem: VM memory size in MB""" + ovf = name + '.ovf' + filesize = stat( diskname )[ ST_SIZE ] + # OVFTemplate uses the memory size twice in a row + xmltext = OVFTemplate % ( diskname, filesize, disksize, name, mem, mem ) + with open( ovf, 'w+' ) as f: f.write( xmltext ) - return xmlfile + return ovf def qcow2size( qcow2 ): @@ -560,8 +639,8 @@ def build( flavor='raring32server' ): log( '* Removing qcow2 volume', volume ) os.remove( volume ) log( '* Converted VM image stored as', abspath( vmdk ) ) - vimage = genVirtImage( flavor, mem=512, diskname=vmdk, disksize=size ) - log( '* Generated virtimage file as', vimage ) + ovf = generateOVF( diskname=vmdk, disksize=size, name=flavor, mem=1024 ) + log( '* Generated OVF descriptor file', ovf ) end = time() elapsed = end - start log( '* Results logged to', abspath( logfile.name ) ) @@ -576,7 +655,7 @@ def buildFlavorString(): def parseArgs(): "Parse command line arguments and run" - global LogToConsole + global LogToConsole, NoKVM parser = argparse.ArgumentParser( description='Mininet VM build script', epilog=buildFlavorString() ) parser.add_argument( '-v', '--verbose', action='store_true', @@ -589,6 +668,8 @@ def parseArgs(): help='clean up leftover build junk (e.g. qemu-nbd)' ) parser.add_argument( '-q', '--qcow2', action='store_true', help='save qcow2 image rather than deleting it' ) + parser.add_argument( '-n', '--nokvm', action='store_true', + help="Don't use kvm - use tcg emulation instead" ) parser.add_argument( 'flavor', nargs='*', help='VM flavor(s) to build (e.g. raring32server)' ) args = parser.parse_args() @@ -600,6 +681,8 @@ def parseArgs(): cleanup() if args.verbose: LogToConsole = True + if args.nokvm: + NoKVM = True for flavor in args.flavor: if flavor not in isoURLs: print "Unknown build flavor:", flavor