From d4279559fa5cb2aa00adb2dec4c51848db9d5294 Mon Sep 17 00:00:00 2001 From: Bob Lantz <rlantz@cs.stanford.edu> Date: Thu, 5 Sep 2013 23:33:30 -0700 Subject: [PATCH] Add options; generate virtimage file (in progress) --- util/vm/build.py | 102 +++++++++++++++++++++++++++++++---------------- 1 file changed, 67 insertions(+), 35 deletions(-) diff --git a/util/vm/build.py b/util/vm/build.py index 3366cffc..1cac35af 100755 --- a/util/vm/build.py +++ b/util/vm/build.py @@ -30,7 +30,8 @@ from os import stat, path from stat import ST_MODE from os.path import abspath -from sys import exit, argv +from sys import exit, stdout +import re from glob import glob from subprocess import check_output, call, Popen from tempfile import mkdtemp @@ -42,6 +43,10 @@ # boot can be slooooow!!!! need to debug/optimize somehow TIMEOUT=600 +# Some configuration +LogToConsole = False # VM output to console rather than log file +SaveQCOW2 = False # Save QCOW2 image rather than deleting it + VMImageDir = os.environ[ 'HOME' ] + '/vm-images' isoURLs = { @@ -307,22 +312,22 @@ def makeKickstartFloppy(): return floppy, kickstart, preseed -def kvmFor( filepath ): - "Guess kvm version for file path" +def archFor( filepath ): + "Guess architecture for file path" name = path.basename( filepath ) if '64' in name: - kvm = 'qemu-system-x86_64' + arch = 'x86_64' elif 'i386' in name or '32' in name: - kvm = 'qemu-system-i386' + arch = 'i386' else: log( "Error: can't discern CPU for file name", name ) exit( 1 ) - return kvm + return arch def installUbuntu( iso, image, logfilename='install.log' ): "Install Ubuntu from iso onto image" - kvm = kvmFor( iso ) + kvm = 'qemu-system-' + archFor( iso ) floppy, kickstart, preseed = makeKickstartFloppy() # Mount iso so we can use its kernel mnt = mkdtemp() @@ -349,11 +354,15 @@ def installUbuntu( iso, image, logfilename='install.log' ): log( '* INSTALLING UBUNTU FROM', iso, 'ONTO', image ) log( ' '.join( cmd ) ) log( '* logging to', abspath( logfilename ) ) - logfile = open( logfilename, 'w' ) - vm = Popen( cmd, stdout=logfile, stderr=logfile ) + params = {} + if not LogToConsole: + logfile = open( logfilename, 'w' ) + params = { 'stdout': logfile, 'stderr': logfile } + vm = Popen( cmd, **params ) log( '* Waiting for installation to complete') vm.wait() - logfile.close() + if not LogToConsole: + logfile.close() elapsed = time() - ubuntuStart # Unmount iso and clean up srun( 'umount ' + mnt ) @@ -374,8 +383,8 @@ def boot( cow, kernel, initrd, logfile ): # pexpect might not be installed until after depend() is called global pexpect import pexpect - kvm = kvmFor( kernel ) - cmd = [ 'sudo', kvm, + arch = archFor( kernel ) + cmd = [ 'sudo', 'qemu-system-' + arch, '-machine accel=kvm', '-nographic', '-netdev user,id=mnbuild', @@ -478,7 +487,7 @@ def convert( cow, basename ): <domain> <boot type="hvm"> <guest> - <arch>%s/arch> + <arch>%s<arch> </guest> <os> <loader dev="hd"/> @@ -498,20 +507,30 @@ def convert( cow, basename ): </image> """ + 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' - xmltext = VirtImageXML % ( name, mem, diskname, disksize ) + arch = archFor( name ) + xmltext = VirtImageXML % ( name, arch, mem, diskname, disksize ) with open( xmlfile, 'w+' ) as f: f.write( xmltext ) return xmlfile +def qcow2size( qcow2 ): + "Return virtual disk size (in bytes) of qcow2 image" + output = check_output( [ 'file', qcow2 ] ) + assert 'QCOW' in output + bytes = int( re.findall( '(\d+) bytes', output )[ 0 ] ) + return bytes + + def build( flavor='raring32server' ): - "Build a Mininet VM" + "Build a Mininet VM; return vmdk and vdisk size" global LogFile start = time() date = strftime( '%y%m%d-%H-%M-%S', localtime()) @@ -528,14 +547,21 @@ def build( flavor='raring32server' ): volume = flavor + '.qcow2' run( 'qemu-img create -f qcow2 -b %s %s' % ( image, volume ) ) log( '* VM image for', flavor, 'created as', volume ) - logfile = open( flavor + '.log', 'w+' ) + if LogToConsole: + logfile = stdout + else: + logfile = open( flavor + '.log', 'w+' ) log( '* Logging results to', abspath( logfile.name ) ) vm = boot( volume, kernel, initrd, logfile ) interact( vm ) + size = qcow2size( volume ) vmdk = convert( volume, basename=flavor ) - log( '* Removing qcow2 volume', volume ) - os.remove( volume ) + if not SaveQCOW2: + 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 ) end = time() elapsed = end - start log( '* Results logged to', abspath( logfile.name ) ) @@ -543,44 +569,50 @@ def build( flavor='raring32server' ): log( '* %s VM build DONE!!!!! :D' % flavor ) os.chdir( '..' ) - -def listFlavors(): - "List valid build flavors" - print '\nvalid build flavors:', ' '.join( isoURLs ), '\n' +def buildFlavorString(): + "Return string listing valid build flavors" + return 'valid build flavors: %s' % ' '.join( sorted( isoURLs ) ) def parseArgs(): "Parse command line arguments and run" - parser = argparse.ArgumentParser( description='Mininet VM build script' ) - parser.add_argument( '--depend', action='store_true', + global LogToConsole + parser = argparse.ArgumentParser( description='Mininet VM build script', + epilog=buildFlavorString() ) + parser.add_argument( '-v', '--verbose', action='store_true', + help='send VM output to console rather than log file' ) + parser.add_argument( '-d', '--depend', action='store_true', help='install dependencies for this script' ) - parser.add_argument( '--list', action='store_true', + parser.add_argument( '-l', '--list', action='store_true', help='list valid build flavors' ) - parser.add_argument( '--clean', action='store_true', + parser.add_argument( '-c', '--clean', action='store_true', 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( 'flavor', nargs='*', - help='VM flavor to build (e.g. raring32server)' ) - args = parser.parse_args( argv ) + help='VM flavor(s) to build (e.g. raring32server)' ) + args = parser.parse_args() if args.depend: depend() if args.list: - listFlavors() + print buildFlavorString() if args.clean: cleanup() - flavors = args.flavor[ 1: ] - for flavor in flavors: + if args.verbose: + LogToConsole = True + for flavor in args.flavor: if flavor not in isoURLs: - parser.print_help() - listFlavors() + print "Unknown build flavor:", flavor + print buildFlavorString() break # try: build( flavor ) # except Exception as e: # log( '* BUILD FAILED with exception: ', e ) # exit( 1 ) - if not ( args.depend or args.list or args.clean or flavors ): + if not ( args.depend or args.list or args.clean or args.flavor ): parser.print_help() - listFlavors() + if __name__ == '__main__': parseArgs() -- GitLab