mininet / util / vm / build.py @ 6be4bfd0
History | View | Annotate | Download (35.3 KB)
1 |
#!/usr/bin/python
|
---|---|
2 |
|
3 |
"""
|
4 |
build.py: build a Mininet VM
|
5 |
|
6 |
Basic idea:
|
7 |
|
8 |
prepare
|
9 |
-> create base install image if it's missing
|
10 |
- download iso if it's missing
|
11 |
- install from iso onto image
|
12 |
|
13 |
build
|
14 |
-> create cow disk for new VM, based on base image
|
15 |
-> boot it in qemu/kvm with text /serial console
|
16 |
-> install Mininet
|
17 |
|
18 |
test
|
19 |
-> sudo mn --test pingall
|
20 |
-> make test
|
21 |
|
22 |
release
|
23 |
-> shut down VM
|
24 |
-> shrink-wrap VM
|
25 |
-> upload to storage
|
26 |
|
27 |
"""
|
28 |
|
29 |
import os |
30 |
from os import stat, path |
31 |
from stat import ST_MODE, ST_SIZE |
32 |
from os.path import abspath |
33 |
from sys import exit, stdout, argv, modules |
34 |
import re |
35 |
from glob import glob |
36 |
from subprocess import check_output, call, Popen |
37 |
from tempfile import mkdtemp, NamedTemporaryFile |
38 |
from time import time, strftime, localtime |
39 |
import argparse |
40 |
from distutils.spawn import find_executable |
41 |
import inspect |
42 |
|
43 |
pexpect = None # For code check - imported dynamically |
44 |
|
45 |
# boot can be slooooow!!!! need to debug/optimize somehow
|
46 |
TIMEOUT=600
|
47 |
|
48 |
# Some configuration options
|
49 |
# Possibly change this to use the parsed arguments instead!
|
50 |
|
51 |
LogToConsole = False # VM output to console rather than log file |
52 |
SaveQCOW2 = False # Save QCOW2 image rather than deleting it |
53 |
NoKVM = False # Don't use kvm and use emulation instead |
54 |
Branch = None # Branch to update and check out before testing |
55 |
Zip = False # Archive .ovf and .vmdk into a .zip file |
56 |
Forward = [] # VM port forwarding options (-redir)
|
57 |
|
58 |
VMImageDir = os.environ[ 'HOME' ] + '/vm-images' |
59 |
|
60 |
Prompt = '\$ ' # Shell prompt that pexpect will wait for |
61 |
|
62 |
isoURLs = { |
63 |
'precise32server':
|
64 |
'http://mirrors.kernel.org/ubuntu-releases/12.04/'
|
65 |
'ubuntu-12.04.5-server-i386.iso',
|
66 |
'precise64server':
|
67 |
'http://mirrors.kernel.org/ubuntu-releases/12.04/'
|
68 |
'ubuntu-12.04.5-server-amd64.iso',
|
69 |
'quantal32server':
|
70 |
'http://mirrors.kernel.org/ubuntu-releases/12.10/'
|
71 |
'ubuntu-12.10-server-i386.iso',
|
72 |
'quantal64server':
|
73 |
'http://mirrors.kernel.org/ubuntu-releases/12.10/'
|
74 |
'ubuntu-12.10-server-amd64.iso',
|
75 |
'raring32server':
|
76 |
'http://mirrors.kernel.org/ubuntu-releases/13.04/'
|
77 |
'ubuntu-13.04-server-i386.iso',
|
78 |
'raring64server':
|
79 |
'http://mirrors.kernel.org/ubuntu-releases/13.04/'
|
80 |
'ubuntu-13.04-server-amd64.iso',
|
81 |
'saucy32server':
|
82 |
'http://mirrors.kernel.org/ubuntu-releases/13.10/'
|
83 |
'ubuntu-13.10-server-i386.iso',
|
84 |
'saucy64server':
|
85 |
'http://mirrors.kernel.org/ubuntu-releases/13.10/'
|
86 |
'ubuntu-13.10-server-amd64.iso',
|
87 |
'trusty32server':
|
88 |
'http://mirrors.kernel.org/ubuntu-releases/14.04/'
|
89 |
'ubuntu-14.04-server-i386.iso',
|
90 |
'trusty64server':
|
91 |
'http://mirrors.kernel.org/ubuntu-releases/14.04/'
|
92 |
'ubuntu-14.04-server-amd64.iso',
|
93 |
'utopic32server':
|
94 |
'http://mirrors.kernel.org/ubuntu-releases/14.10/'
|
95 |
'ubuntu-14.10-server-i386.iso',
|
96 |
'utopic64server':
|
97 |
'http://mirrors.kernel.org/ubuntu-releases/14.10/'
|
98 |
'ubuntu-14.10-server-amd64.iso',
|
99 |
} |
100 |
|
101 |
|
102 |
def OSVersion( flavor ): |
103 |
"Return full OS version string for build flavor"
|
104 |
urlbase = path.basename( isoURLs.get( flavor, 'unknown' ) )
|
105 |
return path.splitext( urlbase )[ 0 ] |
106 |
|
107 |
def OVFOSNameID( flavor ): |
108 |
"Return OVF-specified ( OS Name, ID ) for flavor"
|
109 |
version = OSVersion( flavor ) |
110 |
arch = archFor( flavor ) |
111 |
if 'ubuntu' in version: |
112 |
map = { 'i386': ( 'Ubuntu', 93 ), |
113 |
'x86_64': ( 'Ubuntu 64-bit', 94 ) } |
114 |
else:
|
115 |
map = { 'i386': ( 'Linux', 36 ), |
116 |
'x86_64': ( 'Linux 64-bit', 101 ) } |
117 |
osname, osid = map[ arch ]
|
118 |
return osname, osid
|
119 |
|
120 |
LogStartTime = time() |
121 |
LogFile = None
|
122 |
|
123 |
def log( *args, **kwargs ): |
124 |
"""Simple log function: log( message along with local and elapsed time
|
125 |
cr: False/0 for no CR"""
|
126 |
cr = kwargs.get( 'cr', True ) |
127 |
elapsed = time() - LogStartTime |
128 |
clocktime = strftime( '%H:%M:%S', localtime() )
|
129 |
msg = ' '.join( str( arg ) for arg in args ) |
130 |
output = '%s [ %.3f ] %s' % ( clocktime, elapsed, msg )
|
131 |
if cr:
|
132 |
print output
|
133 |
else:
|
134 |
print output,
|
135 |
# Optionally mirror to LogFile
|
136 |
if type( LogFile ) is file: |
137 |
if cr:
|
138 |
output += '\n'
|
139 |
LogFile.write( output ) |
140 |
LogFile.flush() |
141 |
|
142 |
|
143 |
def run( cmd, **kwargs ): |
144 |
"Convenient interface to check_output"
|
145 |
log( '-', cmd )
|
146 |
cmd = cmd.split() |
147 |
arg0 = cmd[ 0 ]
|
148 |
if not find_executable( arg0 ): |
149 |
raise Exception( 'Cannot find executable "%s";' % arg0 + |
150 |
'you might try %s --depend' % argv[ 0 ] ) |
151 |
return check_output( cmd, **kwargs )
|
152 |
|
153 |
|
154 |
def srun( cmd, **kwargs ): |
155 |
"Run + sudo"
|
156 |
return run( 'sudo ' + cmd, **kwargs ) |
157 |
|
158 |
|
159 |
# BL: we should probably have a "checkDepend()" which
|
160 |
# checks to make sure all dependencies are satisfied!
|
161 |
|
162 |
def depend(): |
163 |
"Install package dependencies"
|
164 |
log( '* Installing package dependencies' )
|
165 |
run( 'sudo apt-get -qy update' )
|
166 |
run( 'sudo install -qy'
|
167 |
' kvm cloud-utils genisoimage qemu-kvm qemu-utils'
|
168 |
' e2fsprogs dnsmasq curl'
|
169 |
' python-setuptools mtools zip' )
|
170 |
run( 'sudo easy_install pexpect' )
|
171 |
|
172 |
|
173 |
def popen( cmd ): |
174 |
"Convenient interface to popen"
|
175 |
log( cmd ) |
176 |
cmd = cmd.split() |
177 |
return Popen( cmd )
|
178 |
|
179 |
|
180 |
def remove( fname ): |
181 |
"Remove a file, ignoring errors"
|
182 |
try:
|
183 |
os.remove( fname ) |
184 |
except OSError: |
185 |
pass
|
186 |
|
187 |
|
188 |
def findiso( flavor ): |
189 |
"Find iso, fetching it if it's not there already"
|
190 |
url = isoURLs[ flavor ] |
191 |
name = path.basename( url ) |
192 |
iso = path.join( VMImageDir, name ) |
193 |
if not path.exists( iso ) or ( stat( iso )[ ST_MODE ] & 0777 != 0444 ): |
194 |
log( '* Retrieving', url )
|
195 |
run( 'curl -C - -o %s %s' % ( iso, url ) )
|
196 |
# Make sure the file header/type is something reasonable like
|
197 |
# 'ISO' or 'x86 boot sector', and not random html or text
|
198 |
result = run( 'file ' + iso )
|
199 |
if 'ISO' not in result and 'boot' not in result: |
200 |
os.remove( iso ) |
201 |
raise Exception( 'findiso: could not download iso from ' + url ) |
202 |
# Write-protect iso, signaling it is complete
|
203 |
log( '* Write-protecting iso', iso)
|
204 |
os.chmod( iso, 0444 )
|
205 |
log( '* Using iso', iso )
|
206 |
return iso
|
207 |
|
208 |
|
209 |
def attachNBD( cow, flags='' ): |
210 |
"""Attempt to attach a COW disk image and return its nbd device
|
211 |
flags: additional flags for qemu-nbd (e.g. -r for readonly)"""
|
212 |
# qemu-nbd requires an absolute path
|
213 |
cow = abspath( cow ) |
214 |
log( '* Checking for unused /dev/nbdX device ' )
|
215 |
for i in range ( 0, 63 ): |
216 |
nbd = '/dev/nbd%d' % i
|
217 |
# Check whether someone's already messing with that device
|
218 |
if call( [ 'pgrep', '-f', nbd ] ) == 0: |
219 |
continue
|
220 |
srun( 'modprobe nbd max-part=64' )
|
221 |
srun( 'qemu-nbd %s -c %s %s' % ( flags, nbd, cow ) )
|
222 |
print
|
223 |
return nbd
|
224 |
raise Exception( "Error: could not find unused /dev/nbdX device" ) |
225 |
|
226 |
|
227 |
def detachNBD( nbd ): |
228 |
"Detatch an nbd device"
|
229 |
srun( 'qemu-nbd -d ' + nbd )
|
230 |
|
231 |
|
232 |
def extractKernel( image, flavor, imageDir=VMImageDir ): |
233 |
"Extract kernel and initrd from base image"
|
234 |
kernel = path.join( imageDir, flavor + '-vmlinuz' )
|
235 |
initrd = path.join( imageDir, flavor + '-initrd' )
|
236 |
if path.exists( kernel ) and ( stat( image )[ ST_MODE ] & 0777 ) == 0444: |
237 |
# If kernel is there, then initrd should also be there
|
238 |
return kernel, initrd
|
239 |
log( '* Extracting kernel to', kernel )
|
240 |
nbd = attachNBD( image, flags='-r' )
|
241 |
print srun( 'partx ' + nbd ) |
242 |
# Assume kernel is in partition 1/boot/vmlinuz*generic for now
|
243 |
part = nbd + 'p1'
|
244 |
mnt = mkdtemp() |
245 |
srun( 'mount -o ro,noload %s %s' % ( part, mnt ) )
|
246 |
kernsrc = glob( '%s/boot/vmlinuz*generic' % mnt )[ 0 ] |
247 |
initrdsrc = glob( '%s/boot/initrd*generic' % mnt )[ 0 ] |
248 |
srun( 'cp %s %s' % ( initrdsrc, initrd ) )
|
249 |
srun( 'chmod 0444 ' + initrd )
|
250 |
srun( 'cp %s %s' % ( kernsrc, kernel ) )
|
251 |
srun( 'chmod 0444 ' + kernel )
|
252 |
srun( 'umount ' + mnt )
|
253 |
run( 'rmdir ' + mnt )
|
254 |
detachNBD( nbd ) |
255 |
return kernel, initrd
|
256 |
|
257 |
|
258 |
def findBaseImage( flavor, size='8G' ): |
259 |
"Return base VM image and kernel, creating them if needed"
|
260 |
image = path.join( VMImageDir, flavor + '-base.qcow2' )
|
261 |
if path.exists( image ):
|
262 |
# Detect race condition with multiple builds
|
263 |
perms = stat( image )[ ST_MODE ] & 0777
|
264 |
if perms != 0444: |
265 |
raise Exception( 'Error - %s is writable ' % image + |
266 |
'; are multiple builds running?' )
|
267 |
else:
|
268 |
# We create VMImageDir here since we are called first
|
269 |
run( 'mkdir -p %s' % VMImageDir )
|
270 |
iso = findiso( flavor ) |
271 |
log( '* Creating image file', image )
|
272 |
run( 'qemu-img create -f qcow2 %s %s' % ( image, size ) )
|
273 |
installUbuntu( iso, image ) |
274 |
# Write-protect image, also signaling it is complete
|
275 |
log( '* Write-protecting image', image)
|
276 |
os.chmod( image, 0444 )
|
277 |
kernel, initrd = extractKernel( image, flavor ) |
278 |
log( '* Using base image', image, 'and kernel', kernel ) |
279 |
return image, kernel, initrd
|
280 |
|
281 |
|
282 |
# Kickstart and Preseed files for Ubuntu/Debian installer
|
283 |
#
|
284 |
# Comments: this is really clunky and painful. If Ubuntu
|
285 |
# gets their act together and supports kickstart a bit better
|
286 |
# then we can get rid of preseed and even use this as a
|
287 |
# Fedora installer as well.
|
288 |
#
|
289 |
# Another annoying thing about Ubuntu is that it can't just
|
290 |
# install a normal system from the iso - it has to download
|
291 |
# junk from the internet, making this house of cards even
|
292 |
# more precarious.
|
293 |
|
294 |
KickstartText ="""
|
295 |
#Generated by Kickstart Configurator
|
296 |
#platform=x86
|
297 |
|
298 |
#System language
|
299 |
lang en_US
|
300 |
#Language modules to install
|
301 |
langsupport en_US
|
302 |
#System keyboard
|
303 |
keyboard us
|
304 |
#System mouse
|
305 |
mouse
|
306 |
#System timezone
|
307 |
timezone America/Los_Angeles
|
308 |
#Root password
|
309 |
rootpw --disabled
|
310 |
#Initial user
|
311 |
user mininet --fullname "mininet" --password "mininet"
|
312 |
#Use text mode install
|
313 |
text
|
314 |
#Install OS instead of upgrade
|
315 |
install
|
316 |
#Use CDROM installation media
|
317 |
cdrom
|
318 |
#System bootloader configuration
|
319 |
bootloader --location=mbr
|
320 |
#Clear the Master Boot Record
|
321 |
zerombr yes
|
322 |
#Partition clearing information
|
323 |
clearpart --all --initlabel
|
324 |
#Automatic partitioning
|
325 |
autopart
|
326 |
#System authorization infomation
|
327 |
auth --useshadow --enablemd5
|
328 |
#Firewall configuration
|
329 |
firewall --disabled
|
330 |
#Do not configure the X Window System
|
331 |
skipx
|
332 |
"""
|
333 |
|
334 |
# Tell the Ubuntu/Debian installer to stop asking stupid questions
|
335 |
|
336 |
PreseedText = ( """
|
337 |
"""
|
338 |
#d-i mirror/country string manual
|
339 |
#d-i mirror/http/hostname string mirrors.kernel.org
|
340 |
"""
|
341 |
d-i mirror/http/directory string /ubuntu
|
342 |
d-i mirror/http/proxy string
|
343 |
d-i partman/confirm_write_new_label boolean true
|
344 |
d-i partman/choose_partition select finish
|
345 |
d-i partman/confirm boolean true
|
346 |
d-i partman/confirm_nooverwrite boolean true
|
347 |
d-i user-setup/allow-password-weak boolean true
|
348 |
d-i finish-install/reboot_in_progress note
|
349 |
d-i debian-installer/exit/poweroff boolean true
|
350 |
""" )
|
351 |
|
352 |
def makeKickstartFloppy(): |
353 |
"Create and return kickstart floppy, kickstart, preseed"
|
354 |
kickstart = 'ks.cfg'
|
355 |
with open( kickstart, 'w' ) as f: |
356 |
f.write( KickstartText ) |
357 |
preseed = 'ks.preseed'
|
358 |
with open( preseed, 'w' ) as f: |
359 |
f.write( PreseedText ) |
360 |
# Create floppy and copy files to it
|
361 |
floppy = 'ksfloppy.img'
|
362 |
run( 'qemu-img create %s 1440k' % floppy )
|
363 |
run( 'mkfs -t msdos ' + floppy )
|
364 |
run( 'mcopy -i %s %s ::/' % ( floppy, kickstart ) )
|
365 |
run( 'mcopy -i %s %s ::/' % ( floppy, preseed ) )
|
366 |
return floppy, kickstart, preseed
|
367 |
|
368 |
|
369 |
def archFor( filepath ): |
370 |
"Guess architecture for file path"
|
371 |
name = path.basename( filepath ) |
372 |
if 'amd64' in name or 'x86_64' in name: |
373 |
arch = 'x86_64'
|
374 |
# Beware of version 64 of a 32-bit OS
|
375 |
elif 'i386' in name or '32' in name or 'x86' in name: |
376 |
arch = 'i386'
|
377 |
elif '64' in name: |
378 |
arch = 'x86_64'
|
379 |
else:
|
380 |
log( "Error: can't discern CPU for name", name )
|
381 |
exit( 1 ) |
382 |
return arch
|
383 |
|
384 |
|
385 |
def installUbuntu( iso, image, logfilename='install.log', memory=1024 ): |
386 |
"Install Ubuntu from iso onto image"
|
387 |
kvm = 'qemu-system-' + archFor( iso )
|
388 |
floppy, kickstart, preseed = makeKickstartFloppy() |
389 |
# Mount iso so we can use its kernel
|
390 |
mnt = mkdtemp() |
391 |
srun( 'mount %s %s' % ( iso, mnt ) )
|
392 |
kernel = path.join( mnt, 'install/vmlinuz' )
|
393 |
initrd = path.join( mnt, 'install/initrd.gz' )
|
394 |
if NoKVM:
|
395 |
accel = 'tcg'
|
396 |
else:
|
397 |
accel = 'kvm'
|
398 |
cmd = [ 'sudo', kvm,
|
399 |
'-machine', 'accel=%s' % accel, |
400 |
'-nographic',
|
401 |
'-netdev', 'user,id=mnbuild', |
402 |
'-device', 'virtio-net,netdev=mnbuild', |
403 |
'-m', str( memory ), |
404 |
'-k', 'en-us', |
405 |
'-fda', floppy,
|
406 |
'-drive', 'file=%s,if=virtio' % image, |
407 |
'-cdrom', iso,
|
408 |
'-kernel', kernel,
|
409 |
'-initrd', initrd,
|
410 |
'-append',
|
411 |
' ks=floppy:/' + kickstart +
|
412 |
' preseed/file=floppy://' + preseed +
|
413 |
' console=ttyS0' ]
|
414 |
ubuntuStart = time() |
415 |
log( '* INSTALLING UBUNTU FROM', iso, 'ONTO', image ) |
416 |
log( ' '.join( cmd ) )
|
417 |
log( '* logging to', abspath( logfilename ) )
|
418 |
params = {} |
419 |
if not LogToConsole: |
420 |
logfile = open( logfilename, 'w' ) |
421 |
params = { 'stdout': logfile, 'stderr': logfile } |
422 |
vm = Popen( cmd, **params ) |
423 |
log( '* Waiting for installation to complete')
|
424 |
vm.wait() |
425 |
if not LogToConsole: |
426 |
logfile.close() |
427 |
elapsed = time() - ubuntuStart |
428 |
# Unmount iso and clean up
|
429 |
srun( 'umount ' + mnt )
|
430 |
run( 'rmdir ' + mnt )
|
431 |
if vm.returncode != 0: |
432 |
raise Exception( 'Ubuntu installation returned error %d' % |
433 |
vm.returncode ) |
434 |
log( '* UBUNTU INSTALLATION COMPLETED FOR', image )
|
435 |
log( '* Ubuntu installation completed in %.2f seconds' % elapsed )
|
436 |
|
437 |
|
438 |
def boot( cow, kernel, initrd, logfile, memory=1024 ): |
439 |
"""Boot qemu/kvm with a COW disk and local/user data store
|
440 |
cow: COW disk path
|
441 |
kernel: kernel path
|
442 |
logfile: log file for pexpect object
|
443 |
memory: memory size in MB
|
444 |
returns: pexpect object to qemu process"""
|
445 |
# pexpect might not be installed until after depend() is called
|
446 |
global pexpect
|
447 |
if not pexpect: |
448 |
import pexpect |
449 |
class Spawn( pexpect.spawn ): |
450 |
"Subprocess is sudo, so we have to sudo kill it"
|
451 |
def close( self, force=False ): |
452 |
srun( 'kill %d' % self.pid ) |
453 |
arch = archFor( kernel ) |
454 |
log( '* Detected kernel architecture', arch )
|
455 |
if NoKVM:
|
456 |
accel = 'tcg'
|
457 |
else:
|
458 |
accel = 'kvm'
|
459 |
cmd = [ 'sudo', 'qemu-system-' + arch, |
460 |
'-machine accel=%s' % accel,
|
461 |
'-nographic',
|
462 |
'-netdev user,id=mnbuild',
|
463 |
'-device virtio-net,netdev=mnbuild',
|
464 |
'-m %s' % memory,
|
465 |
'-k en-us',
|
466 |
'-kernel', kernel,
|
467 |
'-initrd', initrd,
|
468 |
'-drive file=%s,if=virtio' % cow,
|
469 |
'-append "root=/dev/vda1 init=/sbin/init console=ttyS0" ' ]
|
470 |
if Forward:
|
471 |
cmd += sum( [ [ '-redir', f ] for f in Forward ], [] ) |
472 |
cmd = ' '.join( cmd )
|
473 |
log( '* BOOTING VM FROM', cow )
|
474 |
log( cmd ) |
475 |
vm = Spawn( cmd, timeout=TIMEOUT, logfile=logfile ) |
476 |
return vm
|
477 |
|
478 |
|
479 |
def login( vm, user='mininet', password='mininet' ): |
480 |
"Log in to vm (pexpect object)"
|
481 |
log( '* Waiting for login prompt' )
|
482 |
vm.expect( 'login: ' )
|
483 |
log( '* Logging in' )
|
484 |
vm.sendline( user ) |
485 |
log( '* Waiting for password prompt' )
|
486 |
vm.expect( 'Password: ' )
|
487 |
log( '* Sending password' )
|
488 |
vm.sendline( password ) |
489 |
log( '* Waiting for login...' )
|
490 |
|
491 |
|
492 |
def removeNtpd( vm, prompt=Prompt, ntpPackage='ntp' ): |
493 |
"Remove ntpd and set clock immediately"
|
494 |
log( '* Removing ntpd' )
|
495 |
vm.sendline( 'sudo -n apt-get -qy remove ' + ntpPackage )
|
496 |
vm.expect( prompt ) |
497 |
# Try to make sure that it isn't still running
|
498 |
vm.sendline( 'sudo -n pkill ntpd' )
|
499 |
vm.expect( prompt ) |
500 |
log( '* Getting seconds since epoch from this server' )
|
501 |
# Note r'date +%s' specifies a format for 'date', not python!
|
502 |
seconds = int( run( r'date +%s' ) ) |
503 |
log( '* Setting VM clock' )
|
504 |
vm.sendline( 'sudo -n date -s @%d' % seconds )
|
505 |
|
506 |
|
507 |
def sanityTest( vm ): |
508 |
"Run Mininet sanity test (pingall) in vm"
|
509 |
vm.sendline( 'sudo -n mn --test pingall' )
|
510 |
if vm.expect( [ ' 0% dropped', pexpect.TIMEOUT ], timeout=45 ) == 0: |
511 |
log( '* Sanity check OK' )
|
512 |
else:
|
513 |
log( '* Sanity check FAILED' )
|
514 |
log( '* Sanity check output:' )
|
515 |
log( vm.before ) |
516 |
|
517 |
|
518 |
def coreTest( vm, prompt=Prompt ): |
519 |
"Run core tests (make test) in VM"
|
520 |
log( '* Making sure cgroups are mounted' )
|
521 |
vm.sendline( 'sudo -n service cgroup-lite restart' )
|
522 |
vm.expect( prompt ) |
523 |
vm.sendline( 'sudo -n cgroups-mount' )
|
524 |
vm.expect( prompt ) |
525 |
log( '* Running make test' )
|
526 |
vm.sendline( 'cd ~/mininet; sudo make test' )
|
527 |
# We should change "make test" to report the number of
|
528 |
# successful and failed tests. For now, we have to
|
529 |
# know the time for each test, which means that this
|
530 |
# script will have to change as we add more tests.
|
531 |
for test in range( 0, 2 ): |
532 |
if vm.expect( [ 'OK.*\r\n', 'FAILED.*\r\n', pexpect.TIMEOUT ], timeout=180 ) == 0: |
533 |
log( '* Test', test, 'OK' ) |
534 |
else:
|
535 |
log( '* Test', test, 'FAILED' ) |
536 |
log( '* Test', test, 'output:' ) |
537 |
log( vm.before ) |
538 |
|
539 |
|
540 |
def installPexpect( vm, prompt=Prompt ): |
541 |
"install pexpect"
|
542 |
vm.sendline( 'sudo -n apt-get -qy install python-pexpect' )
|
543 |
vm.expect( prompt ) |
544 |
|
545 |
|
546 |
def noneTest( vm, prompt=Prompt ): |
547 |
"This test does nothing"
|
548 |
installPexpect( vm, prompt ) |
549 |
vm.sendline( 'echo' )
|
550 |
|
551 |
|
552 |
def examplesquickTest( vm, prompt=Prompt ): |
553 |
"Quick test of mininet examples"
|
554 |
installPexpect( vm, prompt ) |
555 |
vm.sendline( 'sudo -n python ~/mininet/examples/test/runner.py -v -quick' )
|
556 |
|
557 |
|
558 |
def examplesfullTest( vm, prompt=Prompt ): |
559 |
"Full (slow) test of mininet examples"
|
560 |
installPexpect( vm, prompt ) |
561 |
vm.sendline( 'sudo -n python ~/mininet/examples/test/runner.py -v' )
|
562 |
|
563 |
|
564 |
def walkthroughTest( vm, prompt=Prompt ): |
565 |
"Test mininet walkthrough"
|
566 |
installPexpect( vm, prompt ) |
567 |
vm.sendline( 'sudo -n python ~/mininet/mininet/test/test_walkthrough.py -v' )
|
568 |
|
569 |
|
570 |
def useTest( vm, prompt=Prompt ): |
571 |
"Use VM interactively - exit by pressing control-]"
|
572 |
old = vm.logfile |
573 |
if old == stdout:
|
574 |
# Avoid doubling every output character!
|
575 |
log( '* Temporarily disabling logging to stdout' )
|
576 |
vm.logfile = None
|
577 |
log( '* Switching to interactive use - press control-] to exit' )
|
578 |
vm.interact() |
579 |
if old == stdout:
|
580 |
log( '* Restoring logging to stdout' )
|
581 |
vm.logfile = stdout |
582 |
|
583 |
|
584 |
def checkOutBranch( vm, branch, prompt=Prompt ): |
585 |
# This is a bit subtle; it will check out an existing branch (e.g. master)
|
586 |
# if it exists; otherwise it will create a detached branch.
|
587 |
# The branch will be rebased to its parent on origin.
|
588 |
# This probably doesn't matter since we're running on a COW disk
|
589 |
# anyway.
|
590 |
vm.sendline( 'cd ~/mininet; git fetch --all; git checkout '
|
591 |
+ branch + '; git pull --rebase origin ' + branch )
|
592 |
vm.expect( prompt ) |
593 |
vm.sendline( 'sudo -n make install' )
|
594 |
|
595 |
|
596 |
def interact( vm, tests, pre='', post='', prompt=Prompt ): |
597 |
"Interact with vm, which is a pexpect object"
|
598 |
login( vm ) |
599 |
log( '* Waiting for login...' )
|
600 |
vm.expect( prompt ) |
601 |
log( '* Sending hostname command' )
|
602 |
vm.sendline( 'hostname' )
|
603 |
log( '* Waiting for output' )
|
604 |
vm.expect( prompt ) |
605 |
log( '* Fetching Mininet VM install script' )
|
606 |
branch = Branch if Branch else 'master' |
607 |
vm.sendline( 'wget '
|
608 |
'https://raw.github.com/mininet/mininet/%s/util/vm/'
|
609 |
'install-mininet-vm.sh' % branch )
|
610 |
vm.expect( prompt ) |
611 |
log( '* Running VM install script' )
|
612 |
installcmd = 'bash -v install-mininet-vm.sh'
|
613 |
if Branch:
|
614 |
installcmd += ' ' + Branch
|
615 |
vm.sendline( installcmd ) |
616 |
vm.expect ( 'password for mininet: ' )
|
617 |
vm.sendline( 'mininet' )
|
618 |
log( '* Waiting for script to complete... ' )
|
619 |
# Gigantic timeout for now ;-(
|
620 |
vm.expect( 'Done preparing Mininet', timeout=3600 ) |
621 |
log( '* Completed successfully' )
|
622 |
vm.expect( prompt ) |
623 |
version = getMininetVersion( vm ) |
624 |
vm.expect( prompt ) |
625 |
log( '* Mininet version: ', version )
|
626 |
log( '* Testing Mininet' )
|
627 |
runTests( vm, tests=tests, pre=pre, post=post ) |
628 |
# Ubuntu adds this because we install via a serial console,
|
629 |
# but we want the VM to boot via the VM console. Otherwise
|
630 |
# we get the message 'error: terminal "serial" not found'
|
631 |
log( '* Disabling serial console' )
|
632 |
vm.sendline( "sudo sed -i -e 's/^GRUB_TERMINAL=serial/#GRUB_TERMINAL=serial/' "
|
633 |
"/etc/default/grub; sudo update-grub" )
|
634 |
vm.expect( prompt ) |
635 |
log( '* Shutting down' )
|
636 |
vm.sendline( 'sync; sudo shutdown -h now' )
|
637 |
log( '* Waiting for EOF/shutdown' )
|
638 |
vm.read() |
639 |
log( '* Interaction complete' )
|
640 |
return version
|
641 |
|
642 |
|
643 |
def cleanup(): |
644 |
"Clean up leftover qemu-nbd processes and other junk"
|
645 |
call( [ 'sudo', 'pkill', '-9', 'qemu-nbd' ] ) |
646 |
|
647 |
|
648 |
def convert( cow, basename ): |
649 |
"""Convert a qcow2 disk to a vmdk and put it a new directory
|
650 |
basename: base name for output vmdk file"""
|
651 |
vmdk = basename + '.vmdk'
|
652 |
log( '* Converting qcow2 to vmdk' )
|
653 |
run( 'qemu-img convert -f qcow2 -O vmdk %s %s' % ( cow, vmdk ) )
|
654 |
return vmdk
|
655 |
|
656 |
|
657 |
# Template for OVF - a very verbose format!
|
658 |
# In the best of all possible worlds, we might use an XML
|
659 |
# library to generate this, but a template is easier and
|
660 |
# possibly more concise!
|
661 |
# Warning: XML file cannot begin with a newline!
|
662 |
|
663 |
OVFTemplate = """<?xml version="1.0"?>
|
664 |
<Envelope ovf:version="1.0" xml:lang="en-US"
|
665 |
xmlns="http://schemas.dmtf.org/ovf/envelope/1"
|
666 |
xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1"
|
667 |
xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData"
|
668 |
xmlns:vssd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData"
|
669 |
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
670 |
<References>
|
671 |
<File ovf:href="%(diskname)s" ovf:id="file1" ovf:size="%(filesize)d"/>
|
672 |
</References>
|
673 |
<DiskSection>
|
674 |
<Info>Virtual disk information</Info>
|
675 |
<Disk ovf:capacity="%(disksize)d" ovf:capacityAllocationUnits="byte"
|
676 |
ovf:diskId="vmdisk1" ovf:fileRef="file1"
|
677 |
ovf:format="http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized"/>
|
678 |
</DiskSection>
|
679 |
<NetworkSection>
|
680 |
<Info>The list of logical networks</Info>
|
681 |
<Network ovf:name="nat">
|
682 |
<Description>The nat network</Description>
|
683 |
</Network>
|
684 |
</NetworkSection>
|
685 |
<VirtualSystem ovf:id="%(vmname)s">
|
686 |
<Info>%(vminfo)s (%(name)s)</Info>
|
687 |
<Name>%(vmname)s</Name>
|
688 |
<OperatingSystemSection ovf:id="%(osid)d">
|
689 |
<Info>The kind of installed guest operating system</Info>
|
690 |
<Description>%(osname)s</Description>
|
691 |
</OperatingSystemSection>
|
692 |
<VirtualHardwareSection>
|
693 |
<Info>Virtual hardware requirements</Info>
|
694 |
<Item>
|
695 |
<rasd:AllocationUnits>hertz * 10^6</rasd:AllocationUnits>
|
696 |
<rasd:Description>Number of Virtual CPUs</rasd:Description>
|
697 |
<rasd:ElementName>%(cpus)s virtual CPU(s)</rasd:ElementName>
|
698 |
<rasd:InstanceID>1</rasd:InstanceID>
|
699 |
<rasd:ResourceType>3</rasd:ResourceType>
|
700 |
<rasd:VirtualQuantity>%(cpus)s</rasd:VirtualQuantity>
|
701 |
</Item>
|
702 |
<Item>
|
703 |
<rasd:AllocationUnits>byte * 2^20</rasd:AllocationUnits>
|
704 |
<rasd:Description>Memory Size</rasd:Description>
|
705 |
<rasd:ElementName>%(mem)dMB of memory</rasd:ElementName>
|
706 |
<rasd:InstanceID>2</rasd:InstanceID>
|
707 |
<rasd:ResourceType>4</rasd:ResourceType>
|
708 |
<rasd:VirtualQuantity>%(mem)d</rasd:VirtualQuantity>
|
709 |
</Item>
|
710 |
<Item>
|
711 |
<rasd:Address>0</rasd:Address>
|
712 |
<rasd:Caption>scsiController0</rasd:Caption>
|
713 |
<rasd:Description>SCSI Controller</rasd:Description>
|
714 |
<rasd:ElementName>scsiController0</rasd:ElementName>
|
715 |
<rasd:InstanceID>4</rasd:InstanceID>
|
716 |
<rasd:ResourceSubType>lsilogic</rasd:ResourceSubType>
|
717 |
<rasd:ResourceType>6</rasd:ResourceType>
|
718 |
</Item>
|
719 |
<Item>
|
720 |
<rasd:AddressOnParent>0</rasd:AddressOnParent>
|
721 |
<rasd:ElementName>disk1</rasd:ElementName>
|
722 |
<rasd:HostResource>ovf:/disk/vmdisk1</rasd:HostResource>
|
723 |
<rasd:InstanceID>11</rasd:InstanceID>
|
724 |
<rasd:Parent>4</rasd:Parent>
|
725 |
<rasd:ResourceType>17</rasd:ResourceType>
|
726 |
</Item>
|
727 |
<Item>
|
728 |
<rasd:AddressOnParent>2</rasd:AddressOnParent>
|
729 |
<rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
|
730 |
<rasd:Connection>nat</rasd:Connection>
|
731 |
<rasd:Description>E1000 ethernet adapter on nat</rasd:Description>
|
732 |
<rasd:ElementName>ethernet0</rasd:ElementName>
|
733 |
<rasd:InstanceID>12</rasd:InstanceID>
|
734 |
<rasd:ResourceSubType>E1000</rasd:ResourceSubType>
|
735 |
<rasd:ResourceType>10</rasd:ResourceType>
|
736 |
</Item>
|
737 |
<Item>
|
738 |
<rasd:Address>0</rasd:Address>
|
739 |
<rasd:Caption>usb</rasd:Caption>
|
740 |
<rasd:Description>USB Controller</rasd:Description>
|
741 |
<rasd:ElementName>usb</rasd:ElementName>
|
742 |
<rasd:InstanceID>9</rasd:InstanceID>
|
743 |
<rasd:ResourceType>23</rasd:ResourceType>
|
744 |
</Item>
|
745 |
</VirtualHardwareSection>
|
746 |
</VirtualSystem>
|
747 |
</Envelope>
|
748 |
"""
|
749 |
|
750 |
|
751 |
def generateOVF( name, osname, osid, diskname, disksize, mem=1024, cpus=1, |
752 |
vmname='Mininet-VM', vminfo='A Mininet Virtual Machine' ): |
753 |
"""Generate (and return) OVF file "name.ovf"
|
754 |
name: root name of OVF file to generate
|
755 |
osname: OS name for OVF (Ubuntu | Ubuntu 64-bit)
|
756 |
osid: OS ID for OVF (93 | 94 )
|
757 |
diskname: name of disk file
|
758 |
disksize: size of virtual disk in bytes
|
759 |
mem: VM memory size in MB
|
760 |
cpus: # of virtual CPUs
|
761 |
vmname: Name for VM (default name when importing)
|
762 |
vmimfo: Brief description of VM for OVF"""
|
763 |
ovf = name + '.ovf'
|
764 |
filesize = stat( diskname )[ ST_SIZE ] |
765 |
params = dict( osname=osname, osid=osid, diskname=diskname,
|
766 |
filesize=filesize, disksize=disksize, name=name, |
767 |
mem=mem, cpus=cpus, vmname=vmname, vminfo=vminfo ) |
768 |
xmltext = OVFTemplate % params |
769 |
with open( ovf, 'w+' ) as f: |
770 |
f.write( xmltext ) |
771 |
return ovf
|
772 |
|
773 |
|
774 |
def qcow2size( qcow2 ): |
775 |
"Return virtual disk size (in bytes) of qcow2 image"
|
776 |
output = check_output( [ 'qemu-img', 'info', qcow2 ] ) |
777 |
try:
|
778 |
assert 'format: qcow' in output |
779 |
bytes = int( re.findall( '(\d+) bytes', output )[ 0 ] ) |
780 |
except:
|
781 |
raise Exception( 'Could not determine size of %s' % qcow2 ) |
782 |
return bytes |
783 |
|
784 |
|
785 |
def build( flavor='raring32server', tests=None, pre='', post='', memory=1024 ): |
786 |
"""Build a Mininet VM; return vmdk and vdisk size
|
787 |
tests: tests to run
|
788 |
pre: command line to run in VM before tests
|
789 |
post: command line to run in VM after tests
|
790 |
prompt: shell prompt (default '$ ')
|
791 |
memory: memory size in MB"""
|
792 |
global LogFile, Zip
|
793 |
start = time() |
794 |
lstart = localtime() |
795 |
date = strftime( '%y%m%d-%H-%M-%S', lstart)
|
796 |
ovfdate = strftime( '%y%m%d', lstart )
|
797 |
dir = 'mn-%s-%s' % ( flavor, date )
|
798 |
if Branch:
|
799 |
dir = 'mn-%s-%s-%s' % ( Branch, flavor, date )
|
800 |
try:
|
801 |
os.mkdir( dir )
|
802 |
except:
|
803 |
raise Exception( "Failed to create build directory %s" % dir ) |
804 |
os.chdir( dir )
|
805 |
LogFile = open( 'build.log', 'w' ) |
806 |
log( '* Logging to', abspath( LogFile.name ) )
|
807 |
log( '* Created working directory', dir ) |
808 |
image, kernel, initrd = findBaseImage( flavor ) |
809 |
basename = 'mininet-' + flavor
|
810 |
volume = basename + '.qcow2'
|
811 |
run( 'qemu-img create -f qcow2 -b %s %s' % ( image, volume ) )
|
812 |
log( '* VM image for', flavor, 'created as', volume ) |
813 |
if LogToConsole:
|
814 |
logfile = stdout |
815 |
else:
|
816 |
logfile = open( flavor + '.log', 'w+' ) |
817 |
log( '* Logging results to', abspath( logfile.name ) )
|
818 |
vm = boot( volume, kernel, initrd, logfile, memory=memory ) |
819 |
version = interact( vm, tests=tests, pre=pre, post=post ) |
820 |
size = qcow2size( volume ) |
821 |
arch = archFor( flavor ) |
822 |
vmdk = convert( volume, basename='mininet-vm-' + arch )
|
823 |
if not SaveQCOW2: |
824 |
log( '* Removing qcow2 volume', volume )
|
825 |
os.remove( volume ) |
826 |
log( '* Converted VM image stored as', abspath( vmdk ) )
|
827 |
ovfname = 'mininet-%s-%s-%s' % ( version, ovfdate, OSVersion( flavor ) )
|
828 |
osname, osid = OVFOSNameID( flavor ) |
829 |
ovf = generateOVF( name=ovfname, osname=osname, osid=osid, |
830 |
diskname=vmdk, disksize=size ) |
831 |
log( '* Generated OVF descriptor file', ovf )
|
832 |
if Zip:
|
833 |
log( '* Generating .zip file' )
|
834 |
run( 'zip %s-ovf.zip %s %s' % ( ovfname, ovf, vmdk ) )
|
835 |
end = time() |
836 |
elapsed = end - start |
837 |
log( '* Results logged to', abspath( logfile.name ) )
|
838 |
log( '* Completed in %.2f seconds' % elapsed )
|
839 |
log( '* %s VM build DONE!!!!! :D' % flavor )
|
840 |
os.chdir( '..' )
|
841 |
|
842 |
|
843 |
def runTests( vm, tests=None, pre='', post='', prompt=Prompt ): |
844 |
"Run tests (list) in vm (pexpect object)"
|
845 |
# We disable ntpd and set the time so that ntpd won't be
|
846 |
# messing with the time during tests
|
847 |
removeNtpd( vm ) |
848 |
vm.expect( prompt ) |
849 |
if Branch:
|
850 |
checkOutBranch( vm, branch=Branch ) |
851 |
vm.expect( prompt ) |
852 |
if not tests: |
853 |
tests = [] |
854 |
if pre:
|
855 |
log( '* Running command', pre )
|
856 |
vm.sendline( pre ) |
857 |
vm.expect( prompt ) |
858 |
testfns = testDict() |
859 |
if tests:
|
860 |
log( '* Running tests' )
|
861 |
for test in tests: |
862 |
if test not in testfns: |
863 |
raise Exception( 'Unknown test: ' + test ) |
864 |
log( '* Running test', test )
|
865 |
fn = testfns[ test ] |
866 |
fn( vm ) |
867 |
vm.expect( prompt ) |
868 |
if post:
|
869 |
log( '* Running post-test command', post )
|
870 |
vm.sendline( post ) |
871 |
vm.expect( prompt ) |
872 |
|
873 |
def getMininetVersion( vm ): |
874 |
"Run mn to find Mininet version in VM"
|
875 |
vm.sendline( '~/mininet/bin/mn --version' )
|
876 |
# Eat command line echo, then read output line
|
877 |
vm.readline() |
878 |
version = vm.readline().strip() |
879 |
return version
|
880 |
|
881 |
|
882 |
def bootAndRun( image, prompt=Prompt, memory=1024, outputFile=None, |
883 |
runFunction=None, **runArgs ):
|
884 |
"""Boot and test VM
|
885 |
tests: list of tests to run
|
886 |
pre: command line to run in VM before tests
|
887 |
post: command line to run in VM after tests
|
888 |
prompt: shell prompt (default '$ ')
|
889 |
memory: VM memory size in MB"""
|
890 |
bootTestStart = time() |
891 |
basename = path.basename( image ) |
892 |
image = abspath( image ) |
893 |
tmpdir = mkdtemp( prefix='test-' + basename )
|
894 |
log( '* Using tmpdir', tmpdir )
|
895 |
cow = path.join( tmpdir, basename + '.qcow2' )
|
896 |
log( '* Creating COW disk', cow )
|
897 |
run( 'qemu-img create -f qcow2 -b %s %s' % ( image, cow ) )
|
898 |
log( '* Extracting kernel and initrd' )
|
899 |
kernel, initrd = extractKernel( image, flavor=basename, imageDir=tmpdir ) |
900 |
if LogToConsole:
|
901 |
logfile = stdout |
902 |
else:
|
903 |
logfile = NamedTemporaryFile( prefix=basename, |
904 |
suffix='.testlog', delete=False ) |
905 |
log( '* Logging VM output to', logfile.name )
|
906 |
vm = boot( cow=cow, kernel=kernel, initrd=initrd, logfile=logfile, |
907 |
memory=memory ) |
908 |
login( vm ) |
909 |
log( '* Waiting for prompt after login' )
|
910 |
vm.expect( prompt ) |
911 |
# runFunction should begin with sendline and should eat its last prompt
|
912 |
if runFunction:
|
913 |
runFunction( vm, **runArgs ) |
914 |
log( '* Shutting down' )
|
915 |
vm.sendline( 'sudo -n shutdown -h now ' )
|
916 |
log( '* Waiting for shutdown' )
|
917 |
vm.wait() |
918 |
if outputFile:
|
919 |
log( '* Saving temporary image to %s' % outputFile )
|
920 |
convert( cow, outputFile ) |
921 |
log( '* Removing temporary dir', tmpdir )
|
922 |
srun( 'rm -rf ' + tmpdir )
|
923 |
elapsed = time() - bootTestStart |
924 |
log( '* Boot and test completed in %.2f seconds' % elapsed )
|
925 |
|
926 |
|
927 |
def buildFlavorString(): |
928 |
"Return string listing valid build flavors"
|
929 |
return 'valid build flavors: ( %s )' % ' '.join( sorted( isoURLs ) ) |
930 |
|
931 |
|
932 |
def testDict(): |
933 |
"Return dict of tests in this module"
|
934 |
suffix = 'Test'
|
935 |
trim = len( suffix )
|
936 |
fdict = dict( [ ( fname[ : -trim ], f ) for fname, f in |
937 |
inspect.getmembers( modules[ __name__ ], |
938 |
inspect.isfunction ) |
939 |
if fname.endswith( suffix ) ] )
|
940 |
return fdict
|
941 |
|
942 |
|
943 |
def testString(): |
944 |
"Return string listing valid tests"
|
945 |
return 'valid tests: ( %s )' % ' '.join( testDict().keys() ) |
946 |
|
947 |
|
948 |
def parseArgs(): |
949 |
"Parse command line arguments and run"
|
950 |
global LogToConsole, NoKVM, Branch, Zip, TIMEOUT, Forward
|
951 |
parser = argparse.ArgumentParser( description='Mininet VM build script',
|
952 |
epilog=buildFlavorString() + ' ' +
|
953 |
testString() ) |
954 |
parser.add_argument( '-v', '--verbose', action='store_true', |
955 |
help='send VM output to console rather than log file' )
|
956 |
parser.add_argument( '-d', '--depend', action='store_true', |
957 |
help='install dependencies for this script' )
|
958 |
parser.add_argument( '-l', '--list', action='store_true', |
959 |
help='list valid build flavors and tests' )
|
960 |
parser.add_argument( '-c', '--clean', action='store_true', |
961 |
help='clean up leftover build junk (e.g. qemu-nbd)' )
|
962 |
parser.add_argument( '-q', '--qcow2', action='store_true', |
963 |
help='save qcow2 image rather than deleting it' )
|
964 |
parser.add_argument( '-n', '--nokvm', action='store_true', |
965 |
help="Don't use kvm - use tcg emulation instead" )
|
966 |
parser.add_argument( '-m', '--memory', metavar='MB', type=int, |
967 |
default=1024, help='VM memory size in MB' ) |
968 |
parser.add_argument( '-i', '--image', metavar='image', default=[], |
969 |
action='append',
|
970 |
help='Boot and test an existing VM image' )
|
971 |
parser.add_argument( '-t', '--test', metavar='test', default=[], |
972 |
action='append',
|
973 |
help='specify a test to run' )
|
974 |
parser.add_argument( '-w', '--timeout', metavar='timeout', type=int, |
975 |
default=0, help='set expect timeout' ) |
976 |
parser.add_argument( '-r', '--run', metavar='cmd', default='', |
977 |
help='specify a command line to run before tests' )
|
978 |
parser.add_argument( '-p', '--post', metavar='cmd', default='', |
979 |
help='specify a command line to run after tests' )
|
980 |
parser.add_argument( '-b', '--branch', metavar='branch', |
981 |
help='branch to install and/or check out and test' )
|
982 |
parser.add_argument( 'flavor', nargs='*', |
983 |
help='VM flavor(s) to build (e.g. raring32server)' )
|
984 |
parser.add_argument( '-z', '--zip', action='store_true', |
985 |
help='archive .ovf and .vmdk into .zip file' )
|
986 |
parser.add_argument( '-o', '--out', |
987 |
help='output file for test image (vmdk)' )
|
988 |
parser.add_argument( '-f', '--forward', default=[], action='append', |
989 |
help='forward VM ports to local server, e.g. tcp:5555::22' )
|
990 |
args = parser.parse_args() |
991 |
if args.depend:
|
992 |
depend() |
993 |
if args.list:
|
994 |
print buildFlavorString()
|
995 |
if args.clean:
|
996 |
cleanup() |
997 |
if args.verbose:
|
998 |
LogToConsole = True
|
999 |
if args.nokvm:
|
1000 |
NoKVM = True
|
1001 |
if args.branch:
|
1002 |
Branch = args.branch |
1003 |
if args.zip:
|
1004 |
Zip = True
|
1005 |
if args.timeout:
|
1006 |
TIMEOUT = args.timeout |
1007 |
if args.forward:
|
1008 |
Forward = args.forward |
1009 |
if not args.test and not args.run and not args.post: |
1010 |
args.test = [ 'sanity', 'core' ] |
1011 |
for flavor in args.flavor: |
1012 |
if flavor not in isoURLs: |
1013 |
print "Unknown build flavor:", flavor |
1014 |
print buildFlavorString()
|
1015 |
break
|
1016 |
try:
|
1017 |
build( flavor, tests=args.test, pre=args.run, post=args.post, |
1018 |
memory=args.memory ) |
1019 |
except Exception as e: |
1020 |
log( '* BUILD FAILED with exception: ', e )
|
1021 |
exit( 1 ) |
1022 |
for image in args.image: |
1023 |
bootAndRun( image, runFunction=runTests, tests=args.test, pre=args.run, |
1024 |
post=args.post, memory=args.memory, outputFile=args.out ) |
1025 |
if not ( args.depend or args.list or args.clean or args.flavor |
1026 |
or args.image ):
|
1027 |
parser.print_help() |
1028 |
|
1029 |
|
1030 |
if __name__ == '__main__': |
1031 |
parseArgs() |