From f605a4e430fcc4188aa1dd55fd642c80cc49f516 Mon Sep 17 00:00:00 2001 From: Bob Lantz <rlantz@cs.stanford.edu> Date: Fri, 23 Aug 2013 20:33:25 -0700 Subject: [PATCH] Works, more or less. --- util/vm/build.py | 210 +++++++++++++++++++++++++++-------------------- 1 file changed, 121 insertions(+), 89 deletions(-) diff --git a/util/vm/build.py b/util/vm/build.py index 26bf7462..fd7325de 100755 --- a/util/vm/build.py +++ b/util/vm/build.py @@ -32,7 +32,6 @@ from os.path import abspath from sys import exit, argv from glob import glob -from urllib import urlretrieve from subprocess import check_output, call, Popen from tempfile import mkdtemp from time import time, strftime, localtime @@ -117,15 +116,9 @@ def findiso( flavor ): url = isoURLs[ flavor ] name = path.basename( url ) iso = path.join( VMImageDir, name ) - if path.exists( iso ): - # Detect race condition with multiple builds - perms = stat( iso )[ ST_MODE ] & 0777 - if perms != 0444: - raise Exception( 'Error - %s is writable ' % iso + - '; are multiple builds running?' ) - else: + if not path.exists( iso ) or ( stat( iso )[ ST_MODE ] & 0777 != 0444 ): log( '* Retrieving', url ) - urlretrieve( url, iso ) + run( 'curl -C - -o %s %s' % ( iso, url ) ) # Write-protect iso, signaling it is complete log( '* Write-protecting iso', iso) os.chmod( iso, 0444 ) @@ -156,30 +149,30 @@ def detachNBD( nbd ): srun( 'qemu-nbd -d ' + nbd ) -def kernelpath( flavor ): - "Return kernel path for flavor" - return path.join( VMImageDir, flavor + '-vmlinuz' ) - - -def extractKernel( image, kernel ): +def extractKernel( image, flavor ): "Extract kernel from base image" - nbd = attachNBD( image ) + kernel = path.join( VMImageDir, flavor + '-vmlinuz' ) + if path.exists( kernel ) and ( stat( image )[ ST_MODE ] & 0777 ) == 0444: + return kernel + log( '* Extracting kernel to', kernel ) + nbd = attachNBD( image, flags='-r' ) print srun( 'partx ' + nbd ) # Assume kernel is in partition 1/boot/vmlinuz*generic for now part = nbd + 'p1' mnt = mkdtemp() srun( 'mount %s %s' % ( part, mnt ) ) kernsrc = glob( '%s/boot/vmlinuz*generic' % mnt )[ 0 ] - run( 'cp %s %s' % ( kernsrc, kernel ) ) + run( 'sudo cp %s %s' % ( kernsrc, kernel ) ) + run( 'sudo chmod 0444 ' + kernel ) srun( 'umount ' + mnt ) run( 'rmdir ' + mnt ) - detachNBD( image ) + detachNBD( nbd ) + return kernel def findBaseImage( flavor, size='8G' ): "Return base VM image and kernel, creating them if needed" image = path.join( VMImageDir, flavor + '-base.img' ) - kernel = path.join( VMImageDir, flavor + '-vmlinuz' ) if path.exists( image ): # Detect race condition with multiple builds perms = stat( image )[ ST_MODE ] & 0777 @@ -193,79 +186,104 @@ def findBaseImage( flavor, size='8G' ): log( '* Creating image file', image ) run( 'qemu-img create %s %s' % ( image, size ) ) installUbuntu( iso, image ) - log( '* Extracting kernel to', kernel ) - extractKernel( image, kernel ) # Write-protect image, also signaling it is complete log( '* Write-protecting image', image) os.chmod( image, 0444 ) - log( '* Using base image', image ) + kernel = extractKernel( image, flavor ) + log( '* Using base image', image, 'and kernel', kernel ) return image, kernel +# Kickstart and Preseed files for Ubuntu/Debian installer +# +# Comments: this is really clunky and painful. If Ubuntu +# gets their act together and supports kickstart a bit better +# then we can get rid of preseed and even use this as a +# Fedora installer as well. +# +# Another annoying thing about Ubuntu is that it can't just +# install a normal system from the iso - it has to download +# junk from the internet, making this house of cards even +# more precarious. + +KickstartText =""" +#Generated by Kickstart Configurator +#platform=x86 + +#System language +lang en_US +#Language modules to install +langsupport en_US +#System keyboard +keyboard us +#System mouse +mouse +#System timezone +timezone America/Los_Angeles +#Root password +rootpw --disabled +#Initial user +user mininet --fullname "mininet" --password "mininet" +#Use text mode install +text +#Install OS instead of upgrade +install +#Use CDROM installation media +cdrom +#System bootloader configuration +bootloader --location=mbr +#Clear the Master Boot Record +zerombr yes +#Partition clearing information +clearpart --all --initlabel +#Automatic partitioning +autopart +#System authorization infomation +auth --useshadow --enablemd5 +#Firewall configuration +firewall --disabled +#Do not configure the X Window System +skipx +""" + +# Tell the Ubuntu/Debian installer to stop asking stupid questions + +PreseedText = """ +d-i mirror/country string manual +d-i mirror/http/hostname string mirrors.kernel.org +d-i mirror/http/directory string /ubuntu +d-i mirror/http/proxy string +d-i partman/confirm_write_new_label boolean true +d-i partman/choose_partition select finish +d-i partman/confirm boolean true +d-i partman/confirm_nooverwrite boolean true +d-i user-setup/allow-password-weak boolean true +d-i finish-install/reboot_in_progress note +d-i debian-installer/exit/poweroff boolean true +""" + def makeKickstartFloppy(): "Create and return kickstart floppy, kickstart, preseed" kickstart = 'ks.cfg' - kstext = '\n'.join( [ '#Generated by Kickstart Configurator', - '#platform=x86', - '#System language', - 'lang en_US', - '#Language modules to install', - 'langsupport en_US', - '#System keyboard', - 'keyboard us', - '#System mouse', - 'mouse', - '#System timezone', - 'timezone America/Los_Angeles', - '#Root password', - 'rootpw --disabled', - '#Initial user' - 'user mininet --fullname "mininet" --password "mininet"', - '#Use text mode install', - 'text', - '#Install OS instead of upgrade', - 'install', - '#Use CDROM installation media', - 'cdrom', - '#System bootloader configuration', - 'bootloader --location=mbr', - '#Clear the Master Boot Record', - 'zerombr yes', - '#Partition clearing information', - 'clearpart --all --initlabel', - '#Automatic partitioning', - 'autopart', - '#System authorization infomation', - 'auth --useshadow --enablemd5', - '#Firewall configuration', - 'firewall --disabled', - '#Do not configure the X Window System', - 'skipx', '' ] ) with open( kickstart, 'w' ) as f: - f.write( kstext ) + f.write( KickstartText ) preseed = 'ks.preseed' - pstext = '\n'.join( [ 'd-i partman/confirm_write_new_label boolean true', - 'd-i partman/choose_partition select finish', - 'd-i partman/confirm boolean true', - 'd-i partman/confirm_nooverwrite boolean true', - 'd-i user-setup/allow-password-weak boolean true' ] ) with open( preseed, 'w' ) as f: - f.write( pstext ) + f.write( PreseedText ) # Create floppy and copy files to it floppy = 'ksfloppy.img' - run( 'qemu-img create %s 1M' % floppy ) + run( 'qemu-img create %s 1440k' % floppy ) + run( 'mkfs -t msdos ' + floppy ) run( 'mcopy -i %s %s ::/' % ( floppy, kickstart ) ) run( 'mcopy -i %s %s ::/' % ( floppy, preseed ) ) - log( '* Created floppy image %s containing %s and %s' % - ( floppy, kickstart, preseed ) ) return floppy, kickstart, preseed def kvmFor( name ): "Guess kvm version for file name" - if 'amd64' in name: + if '64' in name: kvm = 'qemu-system-x86_64' - elif 'i386' in name: + elif 'i386' in name or '32' in name: kvm = 'qemu-system-i386' else: log( "Error: can't discern CPU for file name", name ) @@ -273,36 +291,49 @@ def kvmFor( name ): return kvm -def installUbuntu( iso, image ): +def installUbuntu( iso, image, logfilename='install.log' ): "Install Ubuntu from iso onto image" + global pexpect + import pexpect kvm = kvmFor( iso ) floppy, kickstart, preseed = makeKickstartFloppy() # Mount iso so we can use its kernel mnt = mkdtemp() srun( 'mount %s %s' % ( iso, mnt ) ) - kernel = mnt + 'install/vmlinuz' + srun( 'ls ' + mnt ) + kernel = path.join( mnt, 'install/vmlinuz' ) + initrd = path.join( mnt, 'install/initrd.gz' ) cmd = [ 'sudo', kvm, - '-machine accel=kvm', + '-machine', 'accel=kvm', '-nographic', - '-netdev user,id=mnbuild', - '-device virtio-net,netdev=mnbuild', - '-m 1024', - '-k en-us', - '-cdrom', iso, - '-drive file=%s,if=virtio' % image, + '-netdev', 'user,id=mnbuild', + '-device', 'virtio-net,netdev=mnbuild', + '-m', '1024', + '-k', 'en-us', '-fda', floppy, + '-drive', 'file=%s,if=virtio' % image, + '-cdrom', iso, '-kernel', kernel, - '-append "root=/dev/vda1 init=/sbin/init console=ttyS0' + - 'ks=floppy:/' + kickstart + - 'preseed/file=floppy://' + preseed + '"' ] - cmd = ' '.join( cmd ) + '-initrd', initrd, + '-append', + ' ks=floppy:/' + kickstart + + ' preseed/file=floppy://' + preseed + + ' console=ttyS0' ] + ubuntuStart = time() log( '* INSTALLING UBUNTU FROM', iso, 'ONTO', image ) - log( cmd ) - run( cmd ) + log( ' '.join( cmd ) ) + log( '* logging to', abspath( logfilename ) ) + logfile = open( logfilename, 'w' ) + vm = Popen( cmd, stdout=logfile, stderr=logfile ) + log( '* Waiting for installation to complete') + vm.wait() + logfile.close() + elapsed = time() - ubuntuStart # Unmount iso and clean up srun( 'umount ' + mnt ) run( 'rmdir ' + mnt ) log( '* UBUNTU INSTALLATION COMPLETED FOR', image ) + log( '* Ubuntu installation completed in %.2f seconds ' % elapsed ) def boot( cow, kernel, logfile ): @@ -356,6 +387,8 @@ def interact( vm ): vm.expect( prompt ) log( '* Running VM install script' ) vm.sendline( 'bash install-mininet-vm.sh' ) + vm.expect ( 'password for mininet: ' ) + vm.sendline( 'mininet' ) log( '* Waiting for script to complete... ' ) # Gigantic timeout for now ;-( vm.expect( 'Done preparing Mininet', timeout=3600 ) @@ -380,7 +413,7 @@ def interact( vm ): vm.sendline( 'sync; sudo shutdown -h now' ) log( '* Waiting for EOF/shutdown' ) vm.read() - log( '* Interaction complete' ) + log( '* Interaction complete' ) def cleanup(): @@ -412,14 +445,13 @@ def build( flavor='raring32server' ): vm = boot( volume, kernel, logfile ) interact( vm ) vmdk = convert( volume, basename=flavor ) - log( '* Converted VM image stored as', vmdk ) + log( '* Converted VM image stored as', abspath( vmdk ) ) end = time() elapsed = end - start log( '* Results logged to', abspath( logfile.name ) ) log( '* Completed in %.2f seconds' % elapsed ) log( '* %s VM build DONE!!!!! :D' % flavor ) - log( '* ' ) - os.chdir( '..' ) + os.chdir( '..' ) def listFlavors(): -- GitLab