Skip to content
Snippets Groups Projects
Commit 85dfac5c authored by Bob Lantz's avatar Bob Lantz
Browse files

Many more improvements.

parent 860bcc02
No related branches found
No related tags found
No related merge requests found
...@@ -46,13 +46,16 @@ ...@@ -46,13 +46,16 @@
import os import os
from os import stat
from stat import ST_MODE
from os.path import exists, splitext from os.path import exists, splitext
from sys import exit, argv
from glob import glob from glob import glob
from urllib import urlretrieve from urllib import urlretrieve
from subprocess import check_output, call, Popen, PIPE from subprocess import check_output, call, Popen, PIPE
from tempfile import mkdtemp, NamedTemporaryFile from tempfile import mkdtemp, NamedTemporaryFile
from sys import exit
from time import time from time import time
import argparse
# boot can be slooooow!!!! need to debug/optimize somehow # boot can be slooooow!!!! need to debug/optimize somehow
TIMEOUT=600 TIMEOUT=600
...@@ -60,7 +63,10 @@ ...@@ -60,7 +63,10 @@
VMImageDir = os.environ[ 'HOME' ] + '/vm-images' VMImageDir = os.environ[ 'HOME' ] + '/vm-images'
ImageURLBase = { ImageURLBase = {
'raring-server-amd64': 'raring32server':
'http://cloud-images.ubuntu.com/raring/current/'
'raring-server-cloudimg-i386',
'raring64server':
'http://cloud-images.ubuntu.com/raring/current/' 'http://cloud-images.ubuntu.com/raring/current/'
'raring-server-cloudimg-amd64' 'raring-server-cloudimg-amd64'
} }
...@@ -78,17 +84,17 @@ def srun( cmd, **kwargs ): ...@@ -78,17 +84,17 @@ def srun( cmd, **kwargs ):
return run( 'sudo ' + cmd, **kwargs ) return run( 'sudo ' + cmd, **kwargs )
# Install necessary packages def depend():
print '* Installing package dependencies' "Install packagedependencies"
packages = ( ) print '* Installing package dependencies'
run( 'sudo apt-get -y update' ) packages = ( )
run( 'sudo apt-get install -y' run( 'sudo apt-get -y update' )
' kvm cloud-utils genisoimage qemu-kvm qemu-utils' run( 'sudo apt-get install -y'
' e2fsprogs ' ' kvm cloud-utils genisoimage qemu-kvm qemu-utils'
' landscape-client' ' e2fsprogs '
' python-setuptools' ) ' landscape-client'
run( 'sudo easy_install pexpect' ) ' python-setuptools' )
import pexpect run( 'sudo easy_install pexpect' )
def popen( cmd ): def popen( cmd ):
...@@ -127,6 +133,11 @@ def fetchImage( image, path=None ): ...@@ -127,6 +133,11 @@ def fetchImage( image, path=None ):
floppy = path + '-floppy' floppy = path + '-floppy'
if exists( disk ) and exists( kernel ): if exists( disk ) and exists( kernel ):
print '* Found', disk, 'and', kernel print '* Found', disk, 'and', kernel
# Detect race condition with multiple builds
perms = stat( disk )[ ST_MODE ] & 0777
if perms != 0444:
raise Exception( 'Error - %s is writable ' % disk +
'; are multiple builds running?' )
else: else:
dir = os.path.dirname( path ) dir = os.path.dirname( path )
run( 'mkdir -p %s' % dir ) run( 'mkdir -p %s' % dir )
...@@ -136,13 +147,10 @@ def fetchImage( image, path=None ): ...@@ -136,13 +147,10 @@ def fetchImage( image, path=None ):
urlretrieve( url, tgz ) urlretrieve( url, tgz )
print '* Extracting', tgz print '* Extracting', tgz
run( 'tar -C %s -xzf %s' % ( dir, tgz ) ) run( 'tar -C %s -xzf %s' % ( dir, tgz ) )
# Make sure Mininet user is there # Write-protect disk image so it remains pristine;
os.chmod( disk, 0664 ) # We will not use it directly but will use a COW disk
addMininetUser( disk ) print '* Write-protecting disk image', disk
# Write-protect disk image so it remains somewhat pristine; os.chmod( disk, 0444 )
# We will not use it directly but will use a COW disk
print '* Write-protecting disk image', disk
os.chmod( disk, 0444 )
return disk, kernel return disk, kernel
...@@ -162,14 +170,14 @@ def disableCloud( bind ): ...@@ -162,14 +170,14 @@ def disableCloud( bind ):
call( 'echo manual | sudo tee ' + override, shell=True ) call( 'echo manual | sudo tee ' + override, shell=True )
def addMininetUser( img ): def addMininetUser( nbd ):
"Add mininet user/group to root filesystem image" "Add mininet user/group to filesystem"
print '* Adding mininet user to', img print '* Adding mininet user to filesystem on device', nbd
# 1. We bind-mount / into a temporary directory, and # 1. We bind-mount / into a temporary directory, and
# then mount the volume's /etc and /home on top of it! # then mount the volume's /etc and /home on top of it!
mnt = mkdtemp() mnt = mkdtemp()
bind = mkdtemp() bind = mkdtemp()
srun( 'mount %s %s' % ( img, mnt ) ) srun( 'mount %s %s' % ( nbd, mnt ) )
srun( 'mount -B / ' + bind ) srun( 'mount -B / ' + bind )
srun( 'mount -B %s/etc %s/etc' % ( mnt, bind ) ) srun( 'mount -B %s/etc %s/etc' % ( mnt, bind ) )
srun( 'mount -B %s/home %s/home' % ( mnt, bind ) ) srun( 'mount -B %s/home %s/home' % ( mnt, bind ) )
...@@ -202,66 +210,79 @@ def chroot( cmd ): ...@@ -202,66 +210,79 @@ def chroot( cmd ):
run( 'rmdir ' + bind ) run( 'rmdir ' + bind )
run( 'rmdir ' + mnt ) run( 'rmdir ' + mnt )
# 4. Just to make sure, we check the filesystem # 4. Just to make sure, we check the filesystem
run( 'e2fsck -y ' + img ) srun( 'e2fsck -y ' + nbd )
def connectCOWdevice( cow ):
"Attempt to connect a COW disk and return its nbd device"
print '* Checking for unused /dev/nbdX device ',
for i in range ( 0, 63 ):
nbd = '/dev/nbd%d' % i
print i,
# Check whether someone's already messing with that device
if call( [ 'pgrep', '-f', nbd ] ) == 0:
continue
# Fails without -v for some annoying reason...
print
srun( 'qemu-nbd -c %s %s' % ( nbd, cow ) )
return nbd
raise Exception( "Error: could not find unused /dev/nbdX device" )
def disconnectCOWdevice( nbd ):
srun( 'qemu-nbd -d ' + nbd )
def makeCOWDisk( image ): def makeCOWDisk( image ):
"Create new COW disk for image" "Create new COW disk for image"
disk, kernel = fetchImage( image ) disk, kernel = fetchImage( image )
cow = disk + '.qcow2' cow = NamedTemporaryFile( prefix=image + '-', suffix='.qcow2',
dir='.' ).name
print '* Creating COW disk', cow print '* Creating COW disk', cow
remove( cow )
run( 'qemu-img create -f qcow2 -b %s %s' % ( disk, cow ) ) run( 'qemu-img create -f qcow2 -b %s %s' % ( disk, cow ) )
print '* Resizing COW disk and file system' print '* Resizing COW disk and file system'
run( 'qemu-img resize %s +8G' % cow ) run( 'qemu-img resize %s +8G' % cow )
srun( 'modprobe nbd') srun( 'modprobe nbd max-part=64')
# Ideally we should check if it's being used... nbd = connectCOWdevice( cow )
srun( 'qemu-nbd -d /dev/nbd0' ) srun( 'e2fsck -y ' + nbd )
srun( 'qemu-nbd -c /dev/nbd0 ' + cow ) srun( 'resize2fs ' + nbd )
srun( 'e2fsck -fy /dev/nbd0' ) addMininetUser( nbd )
srun( 'resize2fs /dev/nbd0' ) disconnectCOWdevice( nbd )
srun( 'qemu-nbd -d /dev/nbd0' )
return cow, kernel return cow, kernel
def boot( cow, kernel ): def boot( cow, kernel, tap ):
"""Boot qemu/kvm with a COW disk and local/user data store """Boot qemu/kvm with a COW disk and local/user data store
returns: popen object to qemu process""" cow: COW disk path
if 'amd64' in cow: kernel: kernel path
tap: tap device to connect to VM
returns: pexpect object to qemu process"""
# pexpect might not be installed until after depend() is called
global pexpect
import pexpect
if 'amd64' in kernel:
kvm = 'qemu-system-x86_64' kvm = 'qemu-system-x86_64'
elif 'i386' in cow: elif 'i386' in kernel:
kvm = 'qemu-system-i386' kvm = 'qemu-system-i386'
else: else:
print "Error: can't discern CPU for image", cow print "Error: can't discern CPU for image", cow
exit exit( 1 )
# was -nographic
# was -net=%net
# ' -net nic,model=virtio'
# ' -netdev tap,id=mininet0,ifname=%s,script=no ' % tap +
cmd = [ 'sudo', kvm, cmd = [ 'sudo', kvm,
'-machine', 'accel=kvm', '-machine accel=kvm',
'-nographic', '-nographic',
'-m', '512', '-netdev user,id=mnbuild',
'-k', 'en-us', '-device virtio-net,netdev=mnbuild',
'-m 1024',
'-k en-us',
'-kernel', kernel, '-kernel', kernel,
'-drive', 'file=%s,if=virtio' % cow, '-drive file=%s,if=virtio' % cow,
'-append', '-append "root=/dev/vda init=/sbin/init console=ttyS0" ' ]
' "root=/dev/vda'
' init=/sbin/init'
# ' init=/usr/lib/cloud-init/uncloud-init'
# ' ds=nocloud'
# ' --verbose'
' console=ttyS0"'
]
cmd = ' '.join( cmd ) cmd = ' '.join( cmd )
print '* STARTING VM' print '* STARTING VM'
print cmd print cmd
vm = pexpect.spawn( cmd, timeout=TIMEOUT ) vm = pexpect.spawn( cmd, timeout=TIMEOUT )
logfile = NamedTemporaryFile( prefix='mn-build-expect' ) return vm
print '* Logging results to', logfile.name
vm.logfile_read = logfile
return vm, logfile
def interact( vm ): def interact( vm ):
"Interact with vm, which is a pexpect object" "Interact with vm, which is a pexpect object"
...@@ -288,7 +309,8 @@ def interact( vm ): ...@@ -288,7 +309,8 @@ def interact( vm ):
print '* Running VM install script' print '* Running VM install script'
vm.sendline( 'bash install-mininet-vm.sh' ) vm.sendline( 'bash install-mininet-vm.sh' )
print '* Waiting for script to complete... ' print '* Waiting for script to complete... '
vm.expect( 'Done preparing Mininet' ) # Gigantic timeout for now ;-(
vm.expect( 'Done preparing Mininet', timeout=3600 )
print '* Completed successfully' print '* Completed successfully'
vm.expect( prompt ) vm.expect( prompt )
print '* Testing Mininet' print '* Testing Mininet'
...@@ -312,18 +334,74 @@ def interact( vm ): ...@@ -312,18 +334,74 @@ def interact( vm ):
vm.read() vm.read()
print '* Interaction complete' print '* Interaction complete'
image = 'raring-server-amd64'
start = time()
cow, kernel = makeCOWDisk( image )
print '* VM image for', image, 'created as', cow
vm, logfile = boot( cow, kernel )
interact( vm )
logfile.close()
end = time()
elapsed = end - start
print '* Results logged to', logfile.name
print '* Completed in %.2f seconds' % elapsed
print '* DONE!!!!! :D'
def cleanup():
"Clean up leftover qemu-nbd processes and other junk"
call( 'sudo pkill -9 qemu-nbd', shell=True )
def convert( cow, basename ):
"""Convert a qcow2 disk to a vmdk and put it a new directory
basename: base name for output vmdk file"""
dir = mkdtemp( prefix=basename, dir='.' )
vmdk = '%s/%s.vmdk' % ( dir, basename )
print '* Converting qcow2 to vmdk'
run( 'qemu-img convert -f qcow2 -O vmdk %s %s' % ( cow, vmdk ) )
return vmdk
def build( flavor='raring-server-amd64' ):
"Build a Mininet VM"
start = time()
cow, kernel = makeCOWDisk( flavor )
print '* VM image for', flavor, 'created as', cow
with NamedTemporaryFile(
prefix='mn-build-%s-' % flavor, suffix='.log', dir='.' ) as logfile:
print '* Logging results to', logfile.name
vm = boot( cow, kernel, logfile )
vm.logfile_read = logfile
interact( vm )
# cow is a temporary file and will go away when we quit!
# We convert it to a .vmdk which can be used in most VMMs
vmdk = convert( cow, basename=flavor )
print '* Converted VM image stored as', vmdk
end = time()
elapsed = end - start
print '* Results logged to', logfile.name
print '* Completed in %.2f seconds' % elapsed
print '* %s VM build DONE!!!!! :D' % flavor
print
def parseArgs():
"Parse command line arguments and run"
parser = argparse.ArgumentParser( description='Mininet VM build script' )
parser.add_argument( '--depend', action='store_true',
help='Install dependencies for this script' )
parser.add_argument( '--list', action='store_true',
help='list valid build flavors' )
parser.add_argument( '--clean', action='store_true',
help='clean up leftover build junk (e.g. qemu-nbd)' )
parser.add_argument( 'flavor', nargs='*',
help='VM flavor to build (e.g. raring32server)' )
args = parser.parse_args( argv )
if args.depend:
depend()
if args.list:
print 'valid build flavors:', ' '.join( ImageURLBase )
if args.clean:
cleanup()
flavors = args.flavor[ 1: ]
for flavor in flavors:
if flavor not in ImageURLBase:
parser.print_help()
# try:
build( flavor )
# except Exception as e:
# print '* BUILD FAILED with exception: ', e
# exit( 1 )
if not ( args.depend or args.list or args.clean or flavors ):
parser.print_help()
if __name__ == '__main__':
parseArgs()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment