Added --test option to boot and test a VM
This commit is contained in:
committed by
Brian O'Connor
parent
9e725cb28a
commit
501a164eba
+95
-32
@@ -34,7 +34,7 @@ from sys import exit, stdout, argv
|
|||||||
import re
|
import re
|
||||||
from glob import glob
|
from glob import glob
|
||||||
from subprocess import check_output, call, Popen
|
from subprocess import check_output, call, Popen
|
||||||
from tempfile import mkdtemp
|
from tempfile import mkdtemp, NamedTemporaryFile
|
||||||
from time import time, strftime, localtime
|
from time import time, strftime, localtime
|
||||||
import argparse
|
import argparse
|
||||||
from distutils.spawn import find_executable
|
from distutils.spawn import find_executable
|
||||||
@@ -51,6 +51,8 @@ NoKVM = False # Don't use kvm and use emulation instead
|
|||||||
|
|
||||||
VMImageDir = os.environ[ 'HOME' ] + '/vm-images'
|
VMImageDir = os.environ[ 'HOME' ] + '/vm-images'
|
||||||
|
|
||||||
|
Prompt = '\$ ' # Shell prompt that pexpect will wait for
|
||||||
|
|
||||||
isoURLs = {
|
isoURLs = {
|
||||||
'precise32server':
|
'precise32server':
|
||||||
'http://mirrors.kernel.org/ubuntu-releases/12.04/'
|
'http://mirrors.kernel.org/ubuntu-releases/12.04/'
|
||||||
@@ -141,8 +143,11 @@ def popen( cmd ):
|
|||||||
|
|
||||||
|
|
||||||
def remove( fname ):
|
def remove( fname ):
|
||||||
"rm -f fname"
|
"Remove a file, ignoring errors"
|
||||||
return run( 'rm -f %s' % fname )
|
try:
|
||||||
|
os.remove( fname )
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def findiso( flavor ):
|
def findiso( flavor ):
|
||||||
@@ -186,10 +191,10 @@ def detachNBD( nbd ):
|
|||||||
srun( 'qemu-nbd -d ' + nbd )
|
srun( 'qemu-nbd -d ' + nbd )
|
||||||
|
|
||||||
|
|
||||||
def extractKernel( image, flavor ):
|
def extractKernel( image, flavor, imageDir=VMImageDir ):
|
||||||
"Extract kernel and initrd from base image"
|
"Extract kernel and initrd from base image"
|
||||||
kernel = path.join( VMImageDir, flavor + '-vmlinuz' )
|
kernel = path.join( imageDir, flavor + '-vmlinuz' )
|
||||||
initrd = path.join( VMImageDir, flavor + '-initrd' )
|
initrd = path.join( imageDir, flavor + '-initrd' )
|
||||||
if path.exists( kernel ) and ( stat( image )[ ST_MODE ] & 0777 ) == 0444:
|
if path.exists( kernel ) and ( stat( image )[ ST_MODE ] & 0777 ) == 0444:
|
||||||
# If kernel is there, then initrd should also be there
|
# If kernel is there, then initrd should also be there
|
||||||
return kernel, initrd
|
return kernel, initrd
|
||||||
@@ -384,7 +389,7 @@ def installUbuntu( iso, image, logfilename='install.log' ):
|
|||||||
raise Exception( 'Ubuntu installation returned error %d' %
|
raise Exception( 'Ubuntu installation returned error %d' %
|
||||||
vm.returncode )
|
vm.returncode )
|
||||||
log( '* UBUNTU INSTALLATION COMPLETED FOR', image )
|
log( '* UBUNTU INSTALLATION COMPLETED FOR', image )
|
||||||
log( '* Ubuntu installation completed in %.2f seconds ' % elapsed )
|
log( '* Ubuntu installation completed in %.2f seconds' % elapsed )
|
||||||
|
|
||||||
|
|
||||||
def boot( cow, kernel, initrd, logfile ):
|
def boot( cow, kernel, initrd, logfile ):
|
||||||
@@ -419,9 +424,8 @@ def boot( cow, kernel, initrd, logfile ):
|
|||||||
return vm
|
return vm
|
||||||
|
|
||||||
|
|
||||||
def interact( vm ):
|
def login( vm ):
|
||||||
"Interact with vm, which is a pexpect object"
|
"Log in to vm (pexpect object)"
|
||||||
prompt = '\$ '
|
|
||||||
log( '* Waiting for login prompt' )
|
log( '* Waiting for login prompt' )
|
||||||
vm.expect( 'login: ' )
|
vm.expect( 'login: ' )
|
||||||
log( '* Logging in' )
|
log( '* Logging in' )
|
||||||
@@ -431,6 +435,41 @@ def interact( vm ):
|
|||||||
log( '* Sending password' )
|
log( '* Sending password' )
|
||||||
vm.sendline( 'mininet' )
|
vm.sendline( 'mininet' )
|
||||||
log( '* Waiting for login...' )
|
log( '* Waiting for login...' )
|
||||||
|
|
||||||
|
|
||||||
|
def sanityTest( vm ):
|
||||||
|
"Run Mininet sanity test (pingall) in vm"
|
||||||
|
vm.sendline( 'sudo mn --test pingall' )
|
||||||
|
if vm.expect( [ ' 0% dropped', pexpect.TIMEOUT ], timeout=45 ) == 0:
|
||||||
|
log( '* Sanity check OK' )
|
||||||
|
else:
|
||||||
|
log( '* Sanity check FAILED' )
|
||||||
|
|
||||||
|
|
||||||
|
def coreTest( vm, prompt=Prompt ):
|
||||||
|
"Run core tests (make test) in VM"
|
||||||
|
log( '* Making sure cgroups are mounted' )
|
||||||
|
vm.sendline( 'sudo service cgroup-lite restart' )
|
||||||
|
vm.expect( prompt )
|
||||||
|
vm.sendline( 'sudo cgroups-mount' )
|
||||||
|
vm.expect( prompt )
|
||||||
|
log( '* Running make test' )
|
||||||
|
vm.sendline( 'cd ~/mininet; sudo make test' )
|
||||||
|
# We should change "make test" to report the number of
|
||||||
|
# successful and failed tests. For now, we have to
|
||||||
|
# know the time for each test, which means that this
|
||||||
|
# script will have to change as we add more tests.
|
||||||
|
for test in range( 0, 2 ):
|
||||||
|
if vm.expect( [ 'OK', 'FAILED', pexpect.TIMEOUT ], timeout=180 ) == 0:
|
||||||
|
log( '* Test', test, 'OK' )
|
||||||
|
else:
|
||||||
|
log( '* Test', test, 'FAILED' )
|
||||||
|
|
||||||
|
|
||||||
|
def interact( vm, prompt=Prompt ):
|
||||||
|
"Interact with vm, which is a pexpect object"
|
||||||
|
login( vm )
|
||||||
|
log( '* Waiting for login...' )
|
||||||
vm.expect( prompt )
|
vm.expect( prompt )
|
||||||
log( '* Sending hostname command' )
|
log( '* Sending hostname command' )
|
||||||
vm.sendline( 'hostname' )
|
vm.sendline( 'hostname' )
|
||||||
@@ -451,28 +490,9 @@ def interact( vm ):
|
|||||||
log( '* Completed successfully' )
|
log( '* Completed successfully' )
|
||||||
vm.expect( prompt )
|
vm.expect( prompt )
|
||||||
log( '* Testing Mininet' )
|
log( '* Testing Mininet' )
|
||||||
vm.sendline( 'sudo mn --test pingall' )
|
sanityTest( vm )
|
||||||
if vm.expect( [ ' 0% dropped', pexpect.TIMEOUT ], timeout=45 ) == 0:
|
|
||||||
log( '* Sanity check OK' )
|
|
||||||
else:
|
|
||||||
log( '* Sanity check FAILED' )
|
|
||||||
vm.expect( prompt )
|
vm.expect( prompt )
|
||||||
log( '* Making sure cgroups are mounted' )
|
coreTest( vm )
|
||||||
vm.sendline( 'sudo service cgroup-lite restart' )
|
|
||||||
vm.expect( prompt )
|
|
||||||
vm.sendline( 'sudo cgroups-mount' )
|
|
||||||
vm.expect( prompt )
|
|
||||||
log( '* Running make test' )
|
|
||||||
vm.sendline( 'cd ~/mininet; sudo make test' )
|
|
||||||
# We should change "make test" to report the number of
|
|
||||||
# successful and failed tests. For now, we have to
|
|
||||||
# know the time for each test, which means that this
|
|
||||||
# script will have to change as we add more tests.
|
|
||||||
for test in range( 0, 2 ):
|
|
||||||
if vm.expect( [ 'OK', 'FAILED', pexpect.TIMEOUT ], timeout=180 ) == 0:
|
|
||||||
log( '* Test', test, 'OK' )
|
|
||||||
else:
|
|
||||||
log( '* Test', test, 'FAILED' )
|
|
||||||
vm.expect( prompt )
|
vm.expect( prompt )
|
||||||
log( '* Shutting down' )
|
log( '* Shutting down' )
|
||||||
vm.sendline( 'sync; sudo shutdown -h now' )
|
vm.sendline( 'sync; sudo shutdown -h now' )
|
||||||
@@ -649,6 +669,44 @@ def build( flavor='raring32server' ):
|
|||||||
log( '* %s VM build DONE!!!!! :D' % flavor )
|
log( '* %s VM build DONE!!!!! :D' % flavor )
|
||||||
os.chdir( '..' )
|
os.chdir( '..' )
|
||||||
|
|
||||||
|
|
||||||
|
def bootAndTest( image, tests=None ):
|
||||||
|
"""Boot and test VM
|
||||||
|
tests: list of tests (default: sanityTest, coreTest)"""
|
||||||
|
bootTestStart = time()
|
||||||
|
if tests is None:
|
||||||
|
tests = [ sanityTest, coreTest ]
|
||||||
|
basename = path.basename( image )
|
||||||
|
image = abspath( image )
|
||||||
|
tmpdir = mkdtemp( prefix='test-' + basename )
|
||||||
|
cow = path.join( tmpdir, image + '-cow.qcow2' )
|
||||||
|
log( '* Creating COW disk' )
|
||||||
|
run( 'qemu-img create -f qcow2 -b %s %s' % ( image, cow ) )
|
||||||
|
log( '* Extracting kernel and initrd' )
|
||||||
|
kernel, initrd = extractKernel( image, flavor=basename, imageDir=tmpdir )
|
||||||
|
if LogToConsole:
|
||||||
|
logfile = stdout
|
||||||
|
else:
|
||||||
|
logfile = NamedTemporaryFile( prefix=image, delete=False )
|
||||||
|
log( '* Logging VM output to', logfile.name )
|
||||||
|
vm = boot( cow=cow, kernel=kernel, initrd=initrd, logfile=logfile )
|
||||||
|
prompt = '\$ '
|
||||||
|
login( vm )
|
||||||
|
log( '* Waiting for VM boot and login' )
|
||||||
|
vm.expect( prompt )
|
||||||
|
for test in tests:
|
||||||
|
test( vm )
|
||||||
|
vm.expect( prompt )
|
||||||
|
log( '* Shutting down' )
|
||||||
|
vm.sendline( 'sudo shutdown -h now ' )
|
||||||
|
log( '* Waiting for shutdown' )
|
||||||
|
vm.wait()
|
||||||
|
log( '* Removing temporary dir', tmpdir )
|
||||||
|
srun( 'rm -rf ' + tmpdir )
|
||||||
|
elapsed = time() - bootTestStart
|
||||||
|
log( '* Boot and test completed in %.2f seconds' % elapsed )
|
||||||
|
|
||||||
|
|
||||||
def buildFlavorString():
|
def buildFlavorString():
|
||||||
"Return string listing valid build flavors"
|
"Return string listing valid build flavors"
|
||||||
return 'valid build flavors: %s' % ' '.join( sorted( isoURLs ) )
|
return 'valid build flavors: %s' % ' '.join( sorted( isoURLs ) )
|
||||||
@@ -671,6 +729,8 @@ def parseArgs():
|
|||||||
help='save qcow2 image rather than deleting it' )
|
help='save qcow2 image rather than deleting it' )
|
||||||
parser.add_argument( '-n', '--nokvm', action='store_true',
|
parser.add_argument( '-n', '--nokvm', action='store_true',
|
||||||
help="Don't use kvm - use tcg emulation instead" )
|
help="Don't use kvm - use tcg emulation instead" )
|
||||||
|
parser.add_argument( '-t', '--test', metavar='image', action='append',
|
||||||
|
help='Boot and test a VM image' )
|
||||||
parser.add_argument( 'flavor', nargs='*',
|
parser.add_argument( 'flavor', nargs='*',
|
||||||
help='VM flavor(s) to build (e.g. raring32server)' )
|
help='VM flavor(s) to build (e.g. raring32server)' )
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
@@ -694,7 +754,10 @@ def parseArgs():
|
|||||||
# except Exception as e:
|
# except Exception as e:
|
||||||
# log( '* BUILD FAILED with exception: ', e )
|
# log( '* BUILD FAILED with exception: ', e )
|
||||||
# exit( 1 )
|
# exit( 1 )
|
||||||
if not ( args.depend or args.list or args.clean or args.flavor ):
|
for image in args.test:
|
||||||
|
bootAndTest( image )
|
||||||
|
if not ( args.depend or args.list or args.clean or args.flavor
|
||||||
|
or args.test ):
|
||||||
parser.print_help()
|
parser.print_help()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user