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