#!/usr/bin/env python import argparse import math import os import random import libvirt templatedir = os.getenv('LIB', '/var/opt/opnfv/lib') + '/installer/' MAX_NUM_MACS = math.trunc(0xff/2) def generate_baremetal_macs(count=1): """Generate an Ethernet MAC address suitable for baremetal testing.""" # NOTE(dprince): We generate our own bare metal MAC address's here # instead of relying on libvirt so that we can ensure the # locally administered bit is set low. (The libvirt default is # to set the 2nd MSB high.) This effectively allows our # fake baremetal VMs to more accurately behave like real hardware # and fixes issues with bridge/DHCP configurations which rely # on the fact that bridges assume the MAC address of the lowest # attached NIC. # MACs generated for a given machine will also be in sequential # order, which matches how most BM machines are laid out as well. # Additionally we increment each MAC by two places. macs = [] if count > MAX_NUM_MACS: raise ValueError("The MAX num of MACS supported is %i." % MAX_NUM_MACS) base_nums = [0x00, random.randint(0x00, 0xff), random.randint(0x00, 0xff), random.randint(0x00, 0xff), random.randint(0x00, 0xff)] base_mac = ':'.join(map(lambda x: "%02x" % x, base_nums)) start = random.randint(0x00, 0xff) if (start + (count * 2)) > 0xff: # leave room to generate macs in sequence start = 0xff - count * 2 for num in range(0, count*2, 2): mac = start + num macs.append(base_mac + ":" + ("%02x" % mac)) return macs def main(): parser = argparse.ArgumentParser( description="Configure a kvm virtual machine for the seed image.") parser.add_argument('--name', default='seed', help='the name to give the machine in libvirt.') parser.add_argument('--image', help='Use a custom image file (must be qcow2).') parser.add_argument('--diskbus', default='sata', help='Choose an alternate bus type for the disk') parser.add_argument('--baremetal-interface', nargs='+', default=['brbm'], help='The interface which bare metal nodes will be connected to.') parser.add_argument('--engine', default='kvm', help='The virtualization engine to use') parser.add_argument('--arch', default='i686', help='The architecture to use') parser.add_argument('--memory', default='2097152', help="Maximum memory for the VM in KB.") parser.add_argument('--cpus', default='1', help="CPU count for the VM.") parser.add_argument('--bootdev', default='hd', help="What boot device to use (hd/network).") parser.add_argument('--seed', default=False, action='store_true', help='Create a seed vm with two interfaces.') parser.add_argument('--ovsbridge', default="", help='Place the seed public interface on this ovs bridge.') parser.add_argument('--libvirt-nic-driver', default='virtio', help='The libvirt network driver to use') parser.add_argument('--enable-serial-console', action="store_true", help='Enable a serial console') parser.add_argument('--direct-boot', help='Enable directboot to <value>.{vmlinux & initrd}') parser.add_argument('--kernel-arg', action="append", dest='kernel_args', help='Kernel arguments, use multiple time for multiple args.') parser.add_argument('--uri', default='qemu:///system', help='The server uri with which to connect.') args = parser.parse_args() with file(templatedir + '/domain.xml', 'rb') as f: source_template = f.read() imagefile = '/var/lib/libvirt/images/seed.qcow2' if args.image: imagefile = args.image imagefile = os.path.realpath(imagefile) params = { 'name': args.name, 'imagefile': imagefile, 'engine': args.engine, 'arch': args.arch, 'memory': args.memory, 'cpus': args.cpus, 'bootdev': args.bootdev, 'network': '', 'enable_serial_console': '', 'direct_boot': '', 'kernel_args': '', 'user_interface': '', } if args.image is not None: params['imagefile'] = args.image # Configure the bus type for the target disk device params['diskbus'] = args.diskbus nicparams = { 'nicdriver': args.libvirt_nic_driver, 'ovsbridge': args.ovsbridge, } if args.seed: if args.ovsbridge: params['network'] = """ <interface type='bridge'> <source bridge='%(ovsbridge)s'/> <virtualport type='openvswitch'/> <model type='%(nicdriver)s'/> </interface>""" % nicparams else: params['network'] = """ <!-- regular natted network, for access to the vm --> <interface type='network'> <source network='default'/> <model type='%(nicdriver)s'/> </interface>""" % nicparams macs = generate_baremetal_macs(len(args.baremetal_interface)) params['bm_network'] = "" for bm_interface, mac in zip(args.baremetal_interface, macs): bm_interface_params = { 'bminterface': bm_interface, 'bmmacaddress': mac, 'nicdriver': args.libvirt_nic_driver, } params['bm_network'] += """ <!-- bridged 'bare metal' network on %(bminterface)s --> <interface type='network'> <mac address='%(bmmacaddress)s'/> <source network='%(bminterface)s'/> <model type='%(nicdriver)s'/> </interface>""" % bm_interface_params if args.enable_serial_console: params['enable_serial_console'] = """ <serial type='pty'> <target port='0'/> </serial> <console type='pty'> <target type='serial' port='0'/> </console> """ if args.direct_boot: params['direct_boot'] = """ <kernel>/var/lib/libvirt/images/%(direct_boot)s.vmlinuz</kernel> <initrd>/var/lib/libvirt/images/%(direct_boot)s.initrd</initrd> """ % { 'direct_boot': args.direct_boot } if args.kernel_args: params['kernel_args'] = """ <cmdline>%s</cmdline> """ % ' '.join(args.kernel_args) if args.arch == 'aarch64': params['direct_boot'] += """ <loader readonly='yes' type='pflash'>/usr/share/AAVMF/AAVMF_CODE.fd</loader> <nvram>/var/lib/libvirt/qemu/nvram/centos7.0_VARS.fd</nvram> """ params['user_interface'] = """ <controller type='virtio-serial' index='0'> <address type='virtio-mmio'/> </controller> <serial type='pty'> <target port='0'/> </serial> <console type='pty'> <target type='serial' port='0'/> </console> <channel type='unix'> <target type='virtio' name='org.qemu.guest_agent.0'/> <address type='virtio-serial' controller='0' bus='0' port='1'/> </channel> """ else: params['user_interface'] = """ <input type='mouse' bus='ps2'/> <graphics type='vnc' port='-1' autoport='yes'/> <video> <model type='cirrus' vram='9216' heads='1'/> </video> """ libvirt_template = source_template % params conn=libvirt.open(args.uri) a = conn.defineXML(libvirt_template) print ("Created machine %s with UUID %s" % (args.name, a.UUIDString())) if __name__ == '__main__': main()