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 @@
import os
from os import stat
from stat import ST_MODE
from os.path import exists, splitext
from sys import exit, argv
from glob import glob
from urllib import urlretrieve
from subprocess import check_output, call, Popen, PIPE
from tempfile import mkdtemp, NamedTemporaryFile
from sys import exit
from time import time
import argparse
# boot can be slooooow!!!! need to debug/optimize somehow
TIMEOUT=600
......@@ -60,7 +63,10 @@
VMImageDir = os.environ[ 'HOME' ] + '/vm-images'
ImageURLBase = {
'raring-server-amd64':
'raring32server':
'http://cloud-images.ubuntu.com/raring/current/'
'raring-server-cloudimg-i386',
'raring64server':
'http://cloud-images.ubuntu.com/raring/current/'
'raring-server-cloudimg-amd64'
}
......@@ -78,17 +84,17 @@ def srun( cmd, **kwargs ):
return run( 'sudo ' + cmd, **kwargs )
# Install necessary packages
print '* Installing package dependencies'
packages = ( )
run( 'sudo apt-get -y update' )
run( 'sudo apt-get install -y'
' kvm cloud-utils genisoimage qemu-kvm qemu-utils'
' e2fsprogs '
' landscape-client'
' python-setuptools' )
run( 'sudo easy_install pexpect' )
import pexpect
def depend():
"Install packagedependencies"
print '* Installing package dependencies'
packages = ( )
run( 'sudo apt-get -y update' )
run( 'sudo apt-get install -y'
' kvm cloud-utils genisoimage qemu-kvm qemu-utils'
' e2fsprogs '
' landscape-client'
' python-setuptools' )
run( 'sudo easy_install pexpect' )
def popen( cmd ):
......@@ -127,6 +133,11 @@ def fetchImage( image, path=None ):
floppy = path + '-floppy'
if exists( disk ) and exists( 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:
dir = os.path.dirname( path )
run( 'mkdir -p %s' % dir )
......@@ -136,13 +147,10 @@ def fetchImage( image, path=None ):
urlretrieve( url, tgz )
print '* Extracting', tgz
run( 'tar -C %s -xzf %s' % ( dir, tgz ) )
# Make sure Mininet user is there
os.chmod( disk, 0664 )
addMininetUser( disk )
# Write-protect disk image so it remains somewhat pristine;
# We will not use it directly but will use a COW disk
print '* Write-protecting disk image', disk
os.chmod( disk, 0444 )
# Write-protect disk image so it remains pristine;
# 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
......@@ -162,14 +170,14 @@ def disableCloud( bind ):
call( 'echo manual | sudo tee ' + override, shell=True )
def addMininetUser( img ):
"Add mininet user/group to root filesystem image"
print '* Adding mininet user to', img
def addMininetUser( nbd ):
"Add mininet user/group to filesystem"
print '* Adding mininet user to filesystem on device', nbd
# 1. We bind-mount / into a temporary directory, and
# then mount the volume's /etc and /home on top of it!
mnt = mkdtemp()
bind = mkdtemp()
srun( 'mount %s %s' % ( img, mnt ) )
srun( 'mount %s %s' % ( nbd, mnt ) )
srun( 'mount -B / ' + bind )
srun( 'mount -B %s/etc %s/etc' % ( mnt, bind ) )
srun( 'mount -B %s/home %s/home' % ( mnt, bind ) )
......@@ -202,66 +210,79 @@ def chroot( cmd ):
run( 'rmdir ' + bind )
run( 'rmdir ' + mnt )
# 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 ):
"Create new COW disk for image"
disk, kernel = fetchImage( image )
cow = disk + '.qcow2'
cow = NamedTemporaryFile( prefix=image + '-', suffix='.qcow2',
dir='.' ).name
print '* Creating COW disk', cow
remove( cow )
run( 'qemu-img create -f qcow2 -b %s %s' % ( disk, cow ) )
print '* Resizing COW disk and file system'
run( 'qemu-img resize %s +8G' % cow )
srun( 'modprobe nbd')
# Ideally we should check if it's being used...
srun( 'qemu-nbd -d /dev/nbd0' )
srun( 'qemu-nbd -c /dev/nbd0 ' + cow )
srun( 'e2fsck -fy /dev/nbd0' )
srun( 'resize2fs /dev/nbd0' )
srun( 'qemu-nbd -d /dev/nbd0' )
srun( 'modprobe nbd max-part=64')
nbd = connectCOWdevice( cow )
srun( 'e2fsck -y ' + nbd )
srun( 'resize2fs ' + nbd )
addMininetUser( nbd )
disconnectCOWdevice( nbd )
return cow, kernel
def boot( cow, kernel ):
def boot( cow, kernel, tap ):
"""Boot qemu/kvm with a COW disk and local/user data store
returns: popen object to qemu process"""
if 'amd64' in cow:
cow: COW disk path
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'
elif 'i386' in cow:
elif 'i386' in kernel:
kvm = 'qemu-system-i386'
else:
print "Error: can't discern CPU for image", cow
exit
# was -nographic
# was -net=%net
# ' -net nic,model=virtio'
# ' -netdev tap,id=mininet0,ifname=%s,script=no ' % tap +
exit( 1 )
cmd = [ 'sudo', kvm,
'-machine', 'accel=kvm',
'-machine accel=kvm',
'-nographic',
'-m', '512',
'-k', 'en-us',
'-netdev user,id=mnbuild',
'-device virtio-net,netdev=mnbuild',
'-m 1024',
'-k en-us',
'-kernel', kernel,
'-drive', 'file=%s,if=virtio' % cow,
'-append',
' "root=/dev/vda'
' init=/sbin/init'
# ' init=/usr/lib/cloud-init/uncloud-init'
# ' ds=nocloud'
# ' --verbose'
' console=ttyS0"'
]
'-drive file=%s,if=virtio' % cow,
'-append "root=/dev/vda init=/sbin/init console=ttyS0" ' ]
cmd = ' '.join( cmd )
print '* STARTING VM'
print cmd
vm = pexpect.spawn( cmd, timeout=TIMEOUT )
logfile = NamedTemporaryFile( prefix='mn-build-expect' )
print '* Logging results to', logfile.name
vm.logfile_read = logfile
return vm, logfile
return vm
def interact( vm ):
"Interact with vm, which is a pexpect object"
......@@ -288,7 +309,8 @@ def interact( vm ):
print '* Running VM install script'
vm.sendline( 'bash install-mininet-vm.sh' )
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'
vm.expect( prompt )
print '* Testing Mininet'
......@@ -312,18 +334,74 @@ def interact( vm ):
vm.read()
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