############################################################################## # Copyright (c) 2017 Tim Rozet (trozet@redhat.com) and others. # # All rights reserved. This program and the accompanying materials # are made available under the terms of the Apache License, Version 2.0 # which accompanies this distribution, and is available at # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## import libvirt import logging import math import os import platform import random 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 create_vm_storage(domain, vol_path='/var/lib/libvirt/images'): volume_name = domain + '.qcow2' stgvol_xml = """ {} 0 41 {} 107 107 0744 """.format(volume_name, os.path.join(vol_path, volume_name)) conn = libvirt.open('qemu:///system') pool = conn.storagePoolLookupByName('default') if pool is None: raise Exception("Default libvirt storage pool missing") # TODO(trozet) create default storage pool if pool.isActive() == 0: pool.create() try: vol = pool.storageVolLookupByName(volume_name) vol.wipe(0) vol.delete(0) except libvirt.libvirtError as e: if e.get_error_code() != libvirt.VIR_ERR_NO_STORAGE_VOL: raise new_vol = pool.createXML(stgvol_xml) if new_vol is None: raise Exception("Unable to create new volume") logging.debug("Created new storage volume: {}".format(volume_name)) def create_vm(name, image, diskbus='sata', baremetal_interfaces=['admin'], arch=platform.machine(), engine='kvm', memory=8192, bootdev='network', cpus=4, nic_driver='virtio', macs=[], direct_boot=None, kernel_args=None, default_network=False, template_dir='/usr/share/opnfv-apex'): # TODO(trozet): fix name here to be image since it is full path of qcow2 create_vm_storage(name) with open(os.path.join(template_dir, 'domain.xml'), 'r') as f: source_template = f.read() imagefile = os.path.realpath(image) memory = int(memory) * 1024 params = { 'name': name, 'imagefile': imagefile, 'engine': engine, 'arch': arch, 'memory': str(memory), 'cpus': str(cpus), 'bootdev': bootdev, 'network': '', 'enable_serial_console': '', 'direct_boot': '', 'kernel_args': '', 'user_interface': '', } # assign virtio as default for aarch64 if arch == 'aarch64' and diskbus == 'sata': diskbus = 'virtio' # Configure the bus type for the target disk device params['diskbus'] = diskbus nicparams = { 'nicdriver': nic_driver, } if default_network: params['network'] = """ """ % nicparams else: params['network'] = '' while len(macs) < len(baremetal_interfaces): macs += generate_baremetal_macs(1) params['bm_network'] = "" for bm_interface, mac in zip(baremetal_interfaces, macs): bm_interface_params = { 'bminterface': bm_interface, 'bmmacaddress': mac, 'nicdriver': nic_driver, } params['bm_network'] += """ """ % bm_interface_params if direct_boot: params['direct_boot'] = """ /var/lib/libvirt/images/%(direct_boot)s.vmlinuz /var/lib/libvirt/images/%(direct_boot)s.initrd """ % {'direct_boot': direct_boot} if kernel_args: params['kernel_args'] = """ %s """ % ' '.join(kernel_args) if arch == 'aarch64': params['direct_boot'] += """ /usr/share/AAVMF/AAVMF_CODE.fd /var/lib/libvirt/qemu/nvram/centos7.0_VARS.fd """ params['user_interface'] = """
""" else: params['enable_serial_console'] = """ """ params['user_interface'] = """ """ libvirt_template = source_template % params logging.debug("libvirt template is {}".format(libvirt_template)) conn = libvirt.open('qemu:///system') vm = conn.defineXML(libvirt_template) logging.info("Created machine %s with UUID %s" % (name, vm.UUIDString())) return vm