Added support for creating a volume rather than a raw partition.

This commit is contained in:
Bob Lantz
2013-08-21 21:18:11 -07:00
parent 85dfac5c06
commit 94954177e5
+90 -30
View File
@@ -41,22 +41,41 @@ Something to think about:
Maybe download the cloud image and customize it so that Maybe download the cloud image and customize it so that
it is an actual usable/bootable image??? it is an actual usable/bootable image???
More notes:
We really want a full, partitioned disk image!
This means we want to use the disk1.image file ???
However, this means that we will need to change the grub2
configuratin to use a serial console.
/etc/default/grub:
GRUB_TERMINAL=serial
GRUB_SERIAL_COMMAND="serial --unit=0 --speed=38400 --word=8 --parity=no --stop=1"
BOOT_IMAGE="console=ttyS0"
# grub2-mkconfig -o /boot/grub2/grub.cfg
by the way, we should use wget -c
""" """
import os import os
from os import stat from os import stat
from stat import ST_MODE from stat import ST_MODE
from os.path import exists, splitext from os.path import exists, splitext, abspath, realpath
from sys import exit, argv 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
from time import time from time import time
import argparse import argparse
pexpect = None # For code check - imported dynamically
# boot can be slooooow!!!! need to debug/optimize somehow # boot can be slooooow!!!! need to debug/optimize somehow
TIMEOUT=600 TIMEOUT=600
@@ -87,7 +106,6 @@ def srun( cmd, **kwargs ):
def depend(): def depend():
"Install packagedependencies" "Install packagedependencies"
print '* Installing package dependencies' print '* Installing package dependencies'
packages = ( )
run( 'sudo apt-get -y update' ) run( 'sudo apt-get -y update' )
run( 'sudo apt-get install -y' run( 'sudo apt-get install -y'
' kvm cloud-utils genisoimage qemu-kvm qemu-utils' ' kvm cloud-utils genisoimage qemu-kvm qemu-utils'
@@ -127,10 +145,9 @@ def fetchImage( image, path=None ):
"Fetch base VM image if it's not there already" "Fetch base VM image if it's not there already"
if not path: if not path:
path = imagePath( image ) path = imagePath( image )
tgz = path + '.tar.gz' tgz = path + '.disk1.img'
disk = path + '.img' disk = path + '.img'
kernel = path + '-vmlinuz-generic' kernel = path + '-vmlinuz-generic'
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 # Detect race condition with multiple builds
@@ -213,8 +230,11 @@ def addMininetUser( nbd ):
srun( 'e2fsck -y ' + nbd ) srun( 'e2fsck -y ' + nbd )
def connectCOWdevice( cow ): def attachNBD( cow, flags='' ):
"Attempt to connect a COW disk and return its nbd device" """Attempt to attach a COW disk image and return its nbd device
flags: additional flags for qemu-nbd (e.g. -r for readonly)"""
# qemu-nbd requires an absolute path
cow = abspath( cow )
print '* Checking for unused /dev/nbdX device ', print '* Checking for unused /dev/nbdX device ',
for i in range ( 0, 63 ): for i in range ( 0, 63 ):
nbd = '/dev/nbd%d' % i nbd = '/dev/nbd%d' % i
@@ -224,33 +244,70 @@ def connectCOWdevice( cow ):
continue continue
# Fails without -v for some annoying reason... # Fails without -v for some annoying reason...
print print
srun( 'qemu-nbd -c %s %s' % ( nbd, cow ) ) srun( 'qemu-nbd %s -c %s %s' % ( flags, nbd, cow ) )
return nbd return nbd
raise Exception( "Error: could not find unused /dev/nbdX device" ) raise Exception( "Error: could not find unused /dev/nbdX device" )
def disconnectCOWdevice( nbd ): def detachNBD( nbd ):
"Detatch an nbd device"
srun( 'qemu-nbd -d ' + nbd ) srun( 'qemu-nbd -d ' + nbd )
def makeCOWDisk( image ): def makeCOWDisk( image, dir='.' ):
"Create new COW disk for image" "Create new COW disk for image"
disk, kernel = fetchImage( image ) disk, kernel = fetchImage( image )
cow = NamedTemporaryFile( prefix=image + '-', suffix='.qcow2', cow = '%s/%s.qcow2' % ( dir, image )
dir='.' ).name
print '* Creating COW disk', cow print '* Creating COW disk', 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 max-part=64') srun( 'modprobe nbd max-part=64')
nbd = connectCOWdevice( cow ) nbd = attachNBD( cow )
srun( 'e2fsck -y ' + nbd ) srun( 'e2fsck -y ' + nbd )
srun( 'resize2fs ' + nbd ) srun( 'resize2fs ' + nbd )
addMininetUser( nbd ) addMininetUser( nbd )
disconnectCOWdevice( nbd ) detachNBD( nbd )
return cow, kernel return cow, kernel
def makeVolume( volume, cylinders=1000 ):
"""Create volume as a qcow2 and add a single boot partition
cylinders: number of ~8MB (255*63*512) cylinders in volume"""
heads, sectors, bytes = 255, 63, 512
size = cylinders * heads * sectors * bytes
print '* Creating volume of size', size
run( 'qemu-img create -f qcow2 %s %s' % ( volume, size ) )
print '* Partitioning volume'
# We need to mount it using qemu-nbd!!
nbd = attachNBD( volume )
# A bit hacky - we may change this to use parted(8) later
fdisk = Popen( [ 'sudo', 'fdisk', nbd ], stdin=PIPE )
cmds = 'x\nc\n%d\nr\no\nn\np\n1\n\n\na\n1\nw\n' % cylinders
fdisk.stdin.write( cmds )
fdisk.wait()
print '* Volume partition table:'
print srun( 'fdisk -l ' + nbd )
detachNBD( nbd )
def initPartition( partition, volume ):
"""Copy partition to volume-p1 and call addMininetUser"""
srcdev = attachNBD( partition, flags='-r' )
voldev = attachNBD( volume )
print srun( 'fdisk -l ' + voldev )
print srun( 'partx ' + voldev )
dstdev = voldev + 'p1'
print "* Copying partition from", srcdev, "to", dstdev
print srun( 'time dd if=%s of=%s bs=1M' % ( srcdev, dstdev ) )
print '* Resizing and adding Mininet user'
srun( 'resize2fs ' + dstdev )
srun( 'e2fsck -y ' + dstdev )
addMininetUser( dstdev )
detachNBD( voldev )
detachNBD( srcdev )
def boot( cow, kernel, tap ): 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
cow: COW disk path cow: COW disk path
@@ -276,7 +333,7 @@ def boot( cow, kernel, tap ):
'-k en-us', '-k en-us',
'-kernel', kernel, '-kernel', kernel,
'-drive file=%s,if=virtio' % cow, '-drive file=%s,if=virtio' % cow,
'-append "root=/dev/vda init=/sbin/init console=ttyS0" ' ] '-append "root=/dev/vda1 init=/sbin/init console=ttyS0" ' ]
cmd = ' '.join( cmd ) cmd = ' '.join( cmd )
print '* STARTING VM' print '* STARTING VM'
print cmd print cmd
@@ -315,7 +372,7 @@ def interact( vm ):
vm.expect( prompt ) vm.expect( prompt )
print '* Testing Mininet' print '* Testing Mininet'
vm.sendline( 'sudo mn --test pingall' ) vm.sendline( 'sudo mn --test pingall' )
if vm.expect( [ ' 0% dropped', pexpect.TIMEOUT ], timeout=30 ): if vm.expect( [ ' 0% dropped', pexpect.TIMEOUT ], timeout=45 ):
print '* Sanity check succeeded' print '* Sanity check succeeded'
else: else:
print '* Sanity check FAILED' print '* Sanity check FAILED'
@@ -343,34 +400,37 @@ def cleanup():
def convert( cow, basename ): def convert( cow, basename ):
"""Convert a qcow2 disk to a vmdk and put it a new directory """Convert a qcow2 disk to a vmdk and put it a new directory
basename: base name for output vmdk file""" basename: base name for output vmdk file"""
dir = mkdtemp( prefix=basename, dir='.' ) vmdk = basename + '.vmdk'
vmdk = '%s/%s.vmdk' % ( dir, basename )
print '* Converting qcow2 to vmdk' print '* Converting qcow2 to vmdk'
run( 'qemu-img convert -f qcow2 -O vmdk %s %s' % ( cow, vmdk ) ) run( 'qemu-img convert -f qcow2 -O vmdk %s %s' % ( cow, vmdk ) )
return vmdk return vmdk
def build( flavor='raring-server-amd64' ): def build( flavor='raring32server' ):
"Build a Mininet VM" "Build a Mininet VM"
start = time() start = time()
cow, kernel = makeCOWDisk( flavor ) dir = mkdtemp( prefix=flavor + '-result-', dir='.' )
print '* VM image for', flavor, 'created as', cow os.chdir( dir )
with NamedTemporaryFile( print '* Created working directory', dir
prefix='mn-build-%s-' % flavor, suffix='.log', dir='.' ) as logfile: image, kernel = fetchImage( flavor )
print '* Logging results to', logfile.name volume = flavor + '.qcow2'
vm = boot( cow, kernel, logfile ) makeVolume( volume )
initPartition( image, volume )
print '* VM image for', flavor, 'created as', volume
logfile = open( flavor + '.log', 'w+' )
print '* Logging results to', abspath( logfile.name )
vm = boot( volume, kernel, logfile )
vm.logfile_read = logfile vm.logfile_read = logfile
interact( vm ) interact( vm )
# cow is a temporary file and will go away when we quit! vmdk = convert( volume, basename=flavor )
# 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 print '* Converted VM image stored as', vmdk
end = time() end = time()
elapsed = end - start elapsed = end - start
print '* Results logged to', logfile.name print '* Results logged to', abspath( logfile.name )
print '* Completed in %.2f seconds' % elapsed print '* Completed in %.2f seconds' % elapsed
print '* %s VM build DONE!!!!! :D' % flavor print '* %s VM build DONE!!!!! :D' % flavor
print print
os.chdir( '..' )
def parseArgs(): def parseArgs():