Revision 0038720c util/vm/build.py

View differences:

util/vm/build.py
28 28

  
29 29
import os
30 30
from os import stat, path
31
from stat import ST_MODE
31
from stat import ST_MODE, ST_SIZE
32 32
from os.path import abspath
33
from sys import exit, stdout
33
from sys import exit, stdout, argv
34 34
import re
35 35
from glob import glob
36 36
from subprocess import check_output, call, Popen
37 37
from tempfile import mkdtemp
38 38
from time import time, strftime, localtime
39 39
import argparse
40
from distutils.spawn import find_executable
40 41

  
41 42
pexpect = None  # For code check - imported dynamically
42 43

  
......
46 47
# Some configuration
47 48
LogToConsole = False        # VM output to console rather than log file
48 49
SaveQCOW2 = False           # Save QCOW2 image rather than deleting it
50
NoKVM = False               # Don't use kvm and use emulation instead
49 51

  
50 52
VMImageDir = os.environ[ 'HOME' ] + '/vm-images'
51 53

  
......
104 106
    "Convenient interface to check_output"
105 107
    log( '-', cmd )
106 108
    cmd = cmd.split()
109
    arg0 = cmd[ 0 ]
110
    if not find_executable( arg0 ):
111
        raise Exception( 'Cannot find executable "%s";' % arg0 +
112
                         'you might try %s --depend' % argv[ 0 ] )
107 113
    return check_output( cmd, **kwargs )
108 114

  
109 115

  
......
112 118
    return run( 'sudo ' + cmd, **kwargs )
113 119

  
114 120

  
121
# BL: we should probably have a "checkDepend()" which
122
# checks to make sure all dependencies are satisfied!
123

  
115 124
def depend():
116 125
    "Install package dependencies"
117 126
    log( '* Installing package dependencies' )
......
334 343
    srun( 'mount %s %s' % ( iso, mnt ) )
335 344
    kernel = path.join( mnt, 'install/vmlinuz' )
336 345
    initrd = path.join( mnt, 'install/initrd.gz' )
346
    if NoKVM:
347
        accel = 'tcg'
348
    else:
349
        accel = 'kvm'
337 350
    cmd = [ 'sudo', kvm,
338
           '-machine', 'accel=kvm',
351
           '-machine', 'accel=%s' % accel,
339 352
           '-nographic',
340 353
           '-netdev', 'user,id=mnbuild',
341 354
           '-device', 'virtio-net,netdev=mnbuild',
......
384 397
    global pexpect
385 398
    import pexpect
386 399
    arch = archFor( kernel )
400
    if NoKVM:
401
        accel = 'tcg'
402
    else:
403
        accel = 'kvm'
387 404
    cmd = [ 'sudo', 'qemu-system-' + arch,
388
            '-machine accel=kvm',
405
            '-machine accel=%s' % accel,
389 406
            '-nographic',
390 407
            '-netdev user,id=mnbuild',
391 408
            '-device virtio-net,netdev=mnbuild',
......
478 495
    return vmdk
479 496

  
480 497

  
481
# Template for virt-image(5) file
482

  
483
VirtImageXML = """
484
<?xml version="1.0" encoding="UTF-8"?>
485
<image>
486
    <name>%s</name>
487
    <domain>
488
        <boot type="hvm">
489
            <guest>
490
                <arch>%s<arch>
491
            </guest>
492
            <os>
493
                <loader dev="hd"/>
494
            </os>
495
            <drive disk="root.raw" target="hda"/>
496
        </boot>
497
        <devices>
498
            <vcpu>1</vcpu>
499
            <memory>%s</memory>
500
            <interface/>
501
            <graphics/>
502
        </devices>
503
    </domain>
504
        <storage>
505
            <disk file="%s" size="%s" format="vmdk"/>
506
        </storage>
507
</image>
498
# Template for OVF - a very verbose format!
499
# In the best of all possible worlds, we might use an XML
500
# library to generate this, but a template is easier and
501
# possibly more concise!
502

  
503
OVFTemplate = """
504
<?xml version="1.0"?>
505
<Envelope ovf:version="1.0" xml:lang="en-US" 
506
    xmlns="http://schemas.dmtf.org/ovf/envelope/1"
507
    xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1"
508
    xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData"
509
    xmlns:vssd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData"
510
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
511
<References>
512
<File ovf:href="%s" ovf:id="file1" ovf:size="%d"/>
513
</References>
514
<DiskSection>
515
<Info>Virtual disk information</Info>
516
<Disk ovf:capacity="%d" ovf:capacityAllocationUnits="byte" 
517
    ovf:diskId="vmdisk1" ovf:fileRef="file1" 
518
    ovf:format="http://www.vmware.com/interfaces/specifications/vmdk.html"/>
519
</DiskSection>
520
<NetworkSection>
521
<Info>The list of logical networks</Info>
522
<Network ovf:name="nat">
523
<Description>The nat  network</Description>
524
</Network>
525
</NetworkSection>
526
<VirtualSystem ovf:id="Mininet-VM">
527
<Info>A Mininet Virtual Machine (%s)</Info>
528
<Name>mininet-vm</Name>
529
<VirtualHardwareSection>
530
<Info>Virtual hardware requirements</Info>
531
<Item>
532
<rasd:AllocationUnits>hertz * 10^6</rasd:AllocationUnits>
533
<rasd:Description>Number of Virtual CPUs</rasd:Description>
534
<rasd:ElementName>1 virtual CPU(s)</rasd:ElementName>
535
<rasd:InstanceID>1</rasd:InstanceID>
536
<rasd:ResourceType>3</rasd:ResourceType>
537
<rasd:VirtualQuantity>1</rasd:VirtualQuantity>
538
</Item>
539
<Item>
540
<rasd:AllocationUnits>byte * 2^20</rasd:AllocationUnits>
541
<rasd:Description>Memory Size</rasd:Description>
542
<rasd:ElementName>%dMB of memory</rasd:ElementName>
543
<rasd:InstanceID>2</rasd:InstanceID>
544
<rasd:ResourceType>4</rasd:ResourceType>
545
<rasd:VirtualQuantity>%d</rasd:VirtualQuantity>
546
</Item>
547
<Item>
548
<rasd:Address>0</rasd:Address>
549
<rasd:Caption>scsiController0</rasd:Caption>
550
<rasd:Description>SCSI Controller</rasd:Description>
551
<rasd:ElementName>scsiController0</rasd:ElementName>
552
<rasd:InstanceID>4</rasd:InstanceID>
553
<rasd:ResourceSubType>lsilogic</rasd:ResourceSubType>
554
<rasd:ResourceType>6</rasd:ResourceType>
555
</Item>
556
<Item>
557
<rasd:AddressOnParent>0</rasd:AddressOnParent>
558
<rasd:ElementName>disk1</rasd:ElementName>
559
<rasd:HostResource>ovf:/disk/vmdisk1</rasd:HostResource>
560
<rasd:InstanceID>11</rasd:InstanceID>
561
<rasd:Parent>4</rasd:Parent>
562
<rasd:ResourceType>17</rasd:ResourceType>
563
</Item>
564
<Item>
565
<rasd:AddressOnParent>2</rasd:AddressOnParent>
566
<rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
567
<rasd:Connection>nat</rasd:Connection>
568
<rasd:Description>E1000 ethernet adapter on nat</rasd:Description>
569
<rasd:ElementName>ethernet0</rasd:ElementName>
570
<rasd:InstanceID>12</rasd:InstanceID>
571
<rasd:ResourceSubType>E1000</rasd:ResourceSubType>
572
<rasd:ResourceType>10</rasd:ResourceType>
573
</Item>
574
<Item>
575
<rasd:Address>0</rasd:Address>
576
<rasd:Caption>usb</rasd:Caption>
577
<rasd:Description>USB Controller</rasd:Description>
578
<rasd:ElementName>usb</rasd:ElementName>
579
<rasd:InstanceID>9</rasd:InstanceID>
580
<rasd:ResourceType>23</rasd:ResourceType>
581
</Item>
582
</VirtualHardwareSection>
583
</VirtualSystem>
584
</Envelope>
508 585
"""
509 586

  
510 587

  
511
def genVirtImage( name, mem, diskname, disksize ):
512
    "Generate and return virt-image file name.xml"
513
    # Our strategy is going to be: create a
514
    # virt-image file and then use virt-convert to convert
515
    # it to an .ovf file
516
    xmlfile = name + '.xml'
517
    arch = archFor( name )
518
    xmltext = VirtImageXML % ( name, arch, mem, diskname, disksize )
519
    with open( xmlfile, 'w+' ) as f:
588
def generateOVF( name, diskname, disksize, mem=1024 ):
589
    """Generate (and return) OVF file "name.ovf"
590
       name: root name of OVF file to generate
591
       diskname: name of disk file
592
       disksize: size of virtual disk in bytes
593
       mem: VM memory size in MB"""
594
    ovf = name + '.ovf'
595
    filesize = stat( diskname )[ ST_SIZE ]
596
    # OVFTemplate uses the memory size twice in a row
597
    xmltext = OVFTemplate % ( diskname, filesize, disksize, name, mem, mem )
598
    with open( ovf, 'w+' ) as f:
520 599
        f.write( xmltext )
521
    return xmlfile
600
    return ovf
522 601

  
523 602

  
524 603
def qcow2size( qcow2 ):
......
560 639
        log( '* Removing qcow2 volume', volume )
561 640
        os.remove( volume )
562 641
    log( '* Converted VM image stored as', abspath( vmdk ) )
563
    vimage = genVirtImage( flavor, mem=512, diskname=vmdk, disksize=size )
564
    log( '* Generated virtimage file as', vimage )
642
    ovf = generateOVF( diskname=vmdk, disksize=size, name=flavor, mem=1024 )
643
    log( '* Generated OVF descriptor file', ovf )
565 644
    end = time()
566 645
    elapsed = end - start
567 646
    log( '* Results logged to', abspath( logfile.name ) )
......
576 655

  
577 656
def parseArgs():
578 657
    "Parse command line arguments and run"
579
    global LogToConsole
658
    global LogToConsole, NoKVM
580 659
    parser = argparse.ArgumentParser( description='Mininet VM build script',
581 660
                                      epilog=buildFlavorString() )
582 661
    parser.add_argument( '-v', '--verbose', action='store_true',
......
589 668
                         help='clean up leftover build junk (e.g. qemu-nbd)' )
590 669
    parser.add_argument( '-q', '--qcow2', action='store_true',
591 670
                         help='save qcow2 image rather than deleting it' )
671
    parser.add_argument( '-n', '--nokvm', action='store_true',
672
                         help="Don't use kvm - use tcg emulation instead" )
592 673
    parser.add_argument( 'flavor', nargs='*',
593 674
                         help='VM flavor(s) to build (e.g. raring32server)' )
594 675
    args = parser.parse_args()
......
600 681
        cleanup()
601 682
    if args.verbose:
602 683
        LogToConsole = True
684
    if args.nokvm:
685
        NoKVM = True
603 686
    for flavor in args.flavor:
604 687
        if flavor not in isoURLs:
605 688
            print "Unknown build flavor:", flavor

Also available in: Unified diff