def test_vf_hotplug(): """ Hot-plug VF to VM """ logging.info("Preparing a running guest...") libvirt_vmxml.remove_vm_devices_by_type(vm, 'interface') vm.start() vm_session = vm.wait_for_serial_login(timeout=180) logging.info("Attaching VF to the guest...") mac_addr = utils_net.generate_mac_address_simple() iface_dict = eval( params.get('iface_dict', '{"hostdev_addr": "%s"}') % utils_sriov.pci_to_addr(vf_pci)) iface = interface.Interface("hostdev") iface.xml = libvirt.modify_vm_iface(vm.name, "get_xml", iface_dict) virsh.attach_device(vm_name, iface.xml, debug=True, ignore_status=False) logging.info("Checking VF in the guest...") vm_iface_types = [ iface.get_type_name() for iface in vm_xml.VMXML.new_from_dumpxml( vm_name).devices.by_device_tag("interface") ] if 'hostdev' not in vm_iface_types: test.fail('Unable to get hostdev interface!') if cmd_in_vm: if not utils_misc.wait_for( lambda: not vm_session.cmd_status(cmd_in_vm), 30, 10): test.fail("Can not get the Virtual Function info on vm!") vm_session.close()
def create_iface(iface_model, iface_source, **kwargs): """ Create an interface to be attached to vm :param iface_model: model of the interface device :param iface_source: source of the interface device :param kwargs: other k-w args that needed to create device :return: the newly created interface object """ iface = Interface('network') iface.model = iface_model iface.source = eval(iface_source) if 'mac' in kwargs: iface.mac_address = kwargs['mac'] else: mac = utils_net.generate_mac_address_simple() iface.mac_address = mac if 'address' in kwargs: iface.address = iface.new_iface_address( attrs=eval(kwargs['address'])) logging.debug('iface: %s', iface) return iface
def create_interface(): """ Call different function to create interface according to the type """ new_iface = Interface('network') if vf_type == "vf": new_iface = create_hostdev_interface(vf_addr, managed, model) if vf_type == "vf_pool": netxml = create_hostdev_network() virsh.net_define(netxml.xml, ignore_status=True) if not inactive_pool: virsh.net_start(netxml.name) new_iface = create_network_interface(netxml.name) if vf_type == "macvtap": new_iface = Interface('direct') new_iface.source = {"dev": vf_name, "mode": "passthrough"} new_iface.mac_address = utils_net.generate_mac_address_simple() new_iface.model = "virtio" if vlan_id: new_iface.vlan = new_iface.new_vlan(**vlan_id) if vf_type == "macvtap_network": netxml = create_macvtap_network() result = virsh.net_define(netxml.xml, ignore_status=True) virsh.net_start(netxml.name) new_iface = create_network_interface(netxml.name) return new_iface
def create_network_interface(name): """ Create network type interface xml. """ new_iface = Interface('network') new_iface.source = {'network': name} new_iface.model = "virtio" new_iface.mac_address = utils_net.generate_mac_address_simple() return new_iface
def create_hostdev_interface(pci_id, managed, model): """ Create hostdev type interface xml. """ attrs = create_address_dict(pci_id) new_iface = Interface('hostdev') new_iface.managed = managed if model != "": new_iface.model = model new_iface.mac_address = utils_net.generate_mac_address_simple() new_iface.hostdev_address = new_iface.new_iface_address( **{"attrs": attrs}) return new_iface
def add_vdpa_dev(self, idx, pci_addr, extra=""): """ Add vDPA device :param idx: Index of dev :param pci_addr: PCI address :param extra: Extra parameters of vdpa tool """ mac_addr = utils_net.generate_mac_address_simple() cmd = "vdpa dev add name vdpa{} mgmtdev pci/{} mac {} {}".format( idx, pci_addr, mac_addr, extra) process.run(cmd, shell=True) self.vdpa_mac.update({'vdpa{}'.format(idx): mac_addr})
def add_iface(vm): """ Attach interface for the vm """ if vm.is_alive(): vm.destroy(gracefully=False) iface_source = params.get("iface_source", "default") iface_type = params.get("iface_type", "network") iface_model = params.get("iface_model", "virtio") iface_mac = utils_net.generate_mac_address_simple() at_options = (" --type %s --source %s --model %s --mac %s --config " % (iface_type, iface_source, iface_model, iface_mac)) ret = virsh.attach_interface(vm.name, at_options, ignore_status=True) libvirt.check_exit_status(ret) vm.start()
def create_hostdev_interface(pci_id, managed, model): """ Create hostdev type interface xml. """ attrs = create_address_dict(pci_id) new_iface = Interface('hostdev') new_iface.managed = managed if model != "": new_iface.model = model new_iface.mac_address = utils_net.generate_mac_address_simple() new_iface.hostdev_address = new_iface.new_iface_address(**{"attrs": attrs}) chars = string.ascii_letters + string.digits + '-_' alias_name = 'ua-' + ''.join(random.choice(chars) for _ in list(range(64))) new_iface.alias = {'name': alias_name} return new_iface
def create_iface(iface_type, **kwargs): """ Create a interface to be attached to vm """ m_iface = Interface(iface_type) m_iface.mac_address = utils_net.generate_mac_address_simple() if 'base_if' in kwargs: m_iface.source = {'dev': kwargs['base_if'], 'mode': 'vepa'} if 'source_net' in kwargs: m_iface.source = {'network': kwargs['source_net']} if 'mtu' in kwargs: m_iface.mtu = {'size': kwargs['mtu']} if 'model_net' in kwargs: m_iface.model = kwargs['model_net'] logging.debug(m_iface.get_xml()) logging.debug(m_iface) return m_iface
def clone_vm_filesystem(self, newname=None): """ Clone a new vm with only its filesystem disk. @param newname:if newname is None, create a new name with clone added. """ logging.info("Cloning...") # Init options for virt-clone options = {} autoclone = bool(self.params.get("autoclone", False)) new_filesystem_path = self.params.get("new_filesystem_path") cloned_files = [] if new_filesystem_path: self.outdisk = new_filesystem_path elif self.indisk is not None: self.outdisk = "%s-clone" % self.indisk cloned_files.append(self.outdisk) options['files'] = cloned_files # cloned_mac can be CREATED, RANDOM or a string. cloned_mac = self.params.get("cloned_mac", "CREATED") if cloned_mac == "CREATED": options['mac'] = utils_net.generate_mac_address_simple() else: options['mac'] = cloned_mac options['ignore_status'] = True options['debug'] = True options['timeout'] = int(self.params.get("timeout", 240)) if newname is None: newname = "%s-virtclone" % self.oldvm.name result = lgf.virt_clone_cmd(self.oldvm.name, newname, autoclone, **options) if result.exit_status: error_info = "Clone %s to %s failed." % (self.oldvm.name, newname) logging.error(error_info) return (False, result) else: self.newvm.name = newname cloned_mac = vm_xml.VMXML.get_first_mac_by_name(newname) if cloned_mac is not None: self.newvm.address_cache[cloned_mac] = None return (True, result)
def test_device_hotplug(): """ Hotplug/unplug VF with managed='no' 1) Prepare a running guest 2) Check the driver of vf on host 3) Prepare a xml with "managed=no"and attach to guest 4) Detach the device from host 5) Check the driver of vf on host 6) Attach the device to guest 7) Check the interface of the guest 8) Detach the device from guest and check the driver 9) Reattach the device to the host and check the driver """ libvirt_vmxml.remove_vm_devices_by_type(vm, 'interface') start_vm(vm) libvirt_vfio.check_vfio_pci(vf_pci, status_error=True) mac_addr = utils_net.generate_mac_address_simple() iface_dict = eval( params.get('iface_dict', '{"hostdev_addr": "%s"}') % utils_sriov.pci_to_addr(vf_pci)) iface = interface.Interface("hostdev") iface.xml = libvirt.modify_vm_iface(vm.name, "get_xml", iface_dict) res = virsh.attach_device(vm_name, iface.xml, debug=True) libvirt.check_exit_status(res, True) virsh.nodedev_detach(dev_name, debug=True, ignore_status=False) libvirt_vfio.check_vfio_pci(vf_pci) virsh.attach_device(vm_name, iface.xml, debug=True, ignore_status=False) check_vm_iface_managed(vm_name, iface_dict) vm.wait_for_serial_login().close() virsh.detach_device(vm_name, iface.xml, wait_remove_event=True, debug=True, ignore_status=False) libvirt_vfio.check_vfio_pci(vf_pci) virsh.nodedev_reattach(dev_name, debug=True, ignore_status=False) libvirt_vfio.check_vfio_pci(vf_pci, status_error=True)
def add_iface(vm, at_option=""): """ Attach interface for the vm """ if vm.is_alive() and "--inactive" not in additional_options: vm.destroy(gracefully=False) iface_source = params.get("iface_source", "default") iface_type = params.get("iface_type", "network") iface_model = params.get("iface_model", "virtio") iface_mac = utils_net.generate_mac_address_simple() at_options = (" --type %s --source %s --model %s --mac %s --config %s" % (iface_type, iface_source, iface_model, iface_mac, at_option)) ret = virsh.attach_interface(vm.name, at_options, ignore_status=True) libvirt.check_exit_status(ret) nic_params = {'mac': iface_mac, 'nettype': 'bridge'} vm.add_nic(**nic_params) if not vm.is_alive(): vm.start() # Return the new attached interface index return vm.get_nic_index_by_mac(iface_mac)
def add_iface(vm, at_option=""): """ Attach interface for the vm """ if vm.is_alive() and "--inactive" not in additional_options: vm.destroy(gracefully=False) iface_source = params.get("iface_source", "default") iface_type = params.get("iface_type", "network") iface_model = params.get("iface_model", "virtio") iface_mac = utils_net.generate_mac_address_simple() at_options = ( " --type %s --source %s --model %s --mac %s --config %s" % (iface_type, iface_source, iface_model, iface_mac, at_option)) ret = virsh.attach_interface(vm.name, at_options, ignore_status=True) libvirt.check_exit_status(ret) nic_params = {'mac': iface_mac, 'nettype': 'bridge'} vm.add_nic(**nic_params) if not vm.is_alive(): vm.start() # Return the new attached interface index return vm.get_nic_index_by_mac(iface_mac)
def create_interface(): """ Call different function to create interface according to the type """ new_iface = Interface('network') if vf_type == "vf": new_iface = create_hostdev_interface(vf_addr, managed, model) if vf_type == "vf_pool": netxml = create_hostdev_network() virsh.net_define(netxml.xml, ignore_status=True) if not inactive_pool: virsh.net_start(netxml.name) new_iface = create_network_interface(netxml.name) if vf_type == "macvtap": new_iface = Interface('direct') new_iface.source = {"dev": vf_name, "mode": "passthrough"} new_iface.mac_address = utils_net.generate_mac_address_simple() if vf_type == "macvtap_network": netxml = create_macvtap_network() result = virsh.net_define(netxml.xml, ignore_status=True) virsh.net_start(netxml.name) new_iface = create_network_interface(netxml.name) return new_iface
def generate_container_xml(): """ Generate container xml """ vmxml = vm_xml.VMXML(dom_type) vmxml.vm_name = vm_name vmxml.max_mem = max_mem vmxml.current_mem = current_mem vmxml.vcpu = vcpu # Generate os vm_os = vm_xml.VMOSXML() vm_os.type = os_type vm_os.arch = os_arch vm_os.init = os_init vmxml.os = vm_os # Generate emulator emulator = Emulator() emulator.path = emulator_path # Generate console console = Console() filesystem = Filesystem() filesystem.accessmode = fs_accessmode filesystem.source = {'dir': install_root} filesystem.target = {'dir': fs_target} # Add emulator and console in devices devices = vm_xml.VMXMLDevices() devices.append(emulator) devices.append(console) devices.append(filesystem) # Add network device network = Interface(type_name=interface_type) network.mac_address = utils_net.generate_mac_address_simple() network.source = {interface_type: net_name} devices.append(network) vmxml.set_devices(devices) return vmxml
def create_iface(iface_model, iface_source, **kwargs): """ Create an interface to be attached to vm :param iface_model: model of the interface device :param iface_source: source of the interface device :param kwargs: other k-w args that needed to create device :return: the newly created interface object """ iface = Interface('network') iface.model = iface_model iface.source = eval(iface_source) if 'mac' in kwargs: iface.mac_address = kwargs['mac'] else: mac = utils_net.generate_mac_address_simple() iface.mac_address = mac if 'address' in kwargs: iface.address = iface.new_iface_address(attrs=eval(kwargs['address'])) logging.debug('iface: %s', iface) return iface
def run(test, params, env): """ Test virsh {at|de}tach-interface command. 1) Prepare test environment and its parameters 2) Attach the required interface 3) Perform attach and detach operation 4) Check if attached interface is correct 5) Detach the attached interface """ def is_attached(vmxml_devices, iface_type, iface_source, iface_mac): """ Check attached interface exist or not. :param vmxml_devices: VMXMLDevices instance :param iface_type: interface device type :param iface_source : interface source :param iface_mac : interface MAC address :return: True/False if backing file and interface found """ ifaces = vmxml_devices.by_device_tag('interface') for iface in ifaces: if iface.type_name != iface_type: continue if iface.mac_address != iface_mac: continue if iface_source is not None: if iface.xmltreefile.find('source') is not None: if iface.source['network'] != iface_source: continue else: continue # All three conditions met logging.debug("Find %s in given iface XML", iface_mac) return True logging.debug("Not find %s in given iface XML", iface_mac) return False def check_result(vm_name, iface_source, iface_type, iface_mac, flags, vm_state, attach=True): """ Check the test result of attach/detach-device command. """ active_vmxml = vm_xml.VMXML.new_from_dumpxml(vm_name) if not attach: utils_misc.wait_for(lambda: not is_attached(active_vmxml.devices, iface_type, iface_source, iface_mac), 20) active_attached = is_attached(active_vmxml.devices, iface_type, iface_source, iface_mac) if vm_state != "transient": inactive_vmxml = vm_xml.VMXML.new_from_dumpxml( vm_name, options="--inactive") inactive_attached = is_attached(inactive_vmxml.devices, iface_type, iface_source, iface_mac) if flags.count("config"): if vm_state != "transient": if attach: if not inactive_attached: raise exceptions.TestFail("Inactive domain XML not" " updated when --config " "options used for attachment") else: if inactive_attached: raise exceptions.TestFail("Inactive domain XML not" " updated when --config " "options used for detachment") if flags.count("live"): if attach: if vm_state in ["paused", "running", "transient"]: if not active_attached: raise exceptions.TestFail("Active domain XML not updated" " when --live options used for" " attachment") else: if vm_state in ["paused", "running", "transient"]: if active_attached: raise exceptions.TestFail("Active domain XML not updated" " when --live options used for" " detachment") if flags.count("current") or flags == "": if attach: if vm_state in ["paused", "running", "transient"]: if not active_attached: raise exceptions.TestFail("Active domain XML not updated" " when --current options used " "for attachment") elif vm_state == "shutoff" and not inactive_attached: raise exceptions.TestFail("Inactive domain XML not updated" " when --current options used for" " attachment") else: if vm_state in ["paused", "running", "transient"]: if active_attached: raise exceptions.TestFail("Active domain XML not updated" " when --current options used " "for detachment") elif vm_state == "shutoff" and inactive_attached: raise exceptions.TestFail("Inactive domain XML not updated " "when --current options used for " "detachment") vm_name = params.get("main_vm") vm = env.get_vm(vm_name) # Test parameters uri = libvirt_vm.normalize_connect_uri(params.get("connect_uri", "default")) vm_ref = params.get("at_detach_iface_vm_ref", "domname") at_options_suffix = params.get("at_dt_iface_at_options", "") dt_options_suffix = params.get("at_dt_iface_dt_options", "") at_status_error = "yes" == params.get("at_status_error", "no") dt_status_error = "yes" == params.get("dt_status_error", "no") pre_vm_state = params.get("at_dt_iface_pre_vm_state") # Skip if libvirt doesn't support --live/--current. if (at_options_suffix.count("--live") or dt_options_suffix.count("--live")): if not libvirt_version.version_compare(1, 0, 5): raise exceptions.TestSkipError("update-device doesn't" " support --live") if (at_options_suffix.count("--current") or dt_options_suffix.count("--current")): if not libvirt_version.version_compare(1, 0, 5): raise exceptions.TestSkipError("virsh update-device " "doesn't support --current") # Interface specific attributes. iface_type = params.get("at_detach_iface_type", "network") if iface_type == "bridge": try: utils_misc.find_command("brctl") except ValueError: raise exceptions.TestSkipError("Command 'brctl' is missing." " You must install it.") iface_source = params.get("at_detach_iface_source", "default") iface_mac = params.get("at_detach_iface_mac", "created") virsh_dargs = {'ignore_status': True, 'uri': uri, 'debug': True} # Check host version. rhel6_host = False if not process.run("grep 'Red Hat Enterprise Linux Server " "release 6' /etc/redhat-release", ignore_status=True, shell=True).exit_status: rhel6_host = True # Back up xml file. if vm.is_alive(): vm.destroy(gracefully=False) backup_xml = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) # Get a bridge name for test if iface_type is bridge. # If there is no bridge other than virbr0, raise TestSkipError if iface_type == "bridge": host_bridge = utils_net.Bridge() bridge_list = host_bridge.list_br() try: bridge_list.remove("virbr0") except AttributeError: pass # If no virbr0, just pass is ok logging.debug("Useful bridges:%s", bridge_list) # just choosing one bridge on host. if len(bridge_list): iface_source = bridge_list[0] else: raise exceptions.TestSkipError("No useful bridge on host " "other than 'virbr0'.") # Turn VM into certain state. if pre_vm_state == "running": if (rhel6_host and at_options_suffix == "--config" and dt_options_suffix == ""): raise exceptions.TestSkipError("For bug921407, " "won't fix on rhel6 host") logging.info("Starting %s..." % vm_name) if vm.is_dead(): vm.start() vm.wait_for_login().close() elif pre_vm_state == "shutoff": logging.info("Shuting down %s..." % vm_name) if vm.is_alive(): vm.destroy(gracefully=False) elif pre_vm_state == "paused": if (rhel6_host and at_options_suffix == "--config" and dt_options_suffix == ""): raise exceptions.TestSkipError("For bug921407, " "won't fix on rhel6 host") logging.info("Pausing %s..." % vm_name) if vm.is_dead(): vm.start() vm.wait_for_login().close() if not vm.pause(): raise exceptions.TestSkipError("Cann't pause the domain") elif pre_vm_state == "transient": logging.info("Creating %s..." % vm_name) vm.undefine() if virsh.create(backup_xml.xml, **virsh_dargs).exit_status: backup_xml.define() raise exceptions.TestSkipError("Cann't create the domain") dom_uuid = vm.get_uuid() dom_id = vm.get_id() # Set attach-interface domain if vm_ref == "domname": vm_ref = vm_name elif vm_ref == "domid": vm_ref = dom_id elif vm_ref == "domuuid": vm_ref = dom_uuid elif vm_ref == "hexdomid" and dom_id is not None: vm_ref = hex(int(dom_id)) # Get a mac address if iface_mac is 'created'. if iface_mac == "created": iface_mac = utils_net.generate_mac_address_simple() try: # Set attach-interface options and # start attach-interface test options = set_options(iface_type, iface_source, iface_mac, at_options_suffix, "attach") ret = virsh.attach_interface(vm_name, options, **virsh_dargs) libvirt.check_exit_status(ret, at_status_error) # Check if the command take effect in vm # or config file. if vm.is_paused(): vm.resume() vm.wait_for_login().close() #Sleep a while for vm is stable time.sleep(3) if not ret.exit_status: check_result(vm_name, iface_source, iface_type, iface_mac, at_options_suffix, pre_vm_state) # Set detach-interface options options = set_options(iface_type, None, iface_mac, dt_options_suffix, "detach") # Sleep for a while time.sleep(10) # Start detach-interface test if pre_vm_state == "paused": if not vm.pause(): raise exceptions.TestFail("Cann't pause the domain") ret = virsh.detach_interface(vm_ref, options, **virsh_dargs) if rhel6_host and pre_vm_state in ['paused', 'running']: if (at_options_suffix == "--config" and dt_options_suffix == "--config"): dt_status_error = True libvirt.check_exit_status(ret, dt_status_error) # Check if the command take effect # in vm or config file. if vm.is_paused(): vm.resume() vm.wait_for_login().close() #Sleep a while for vm is stable time.sleep(10) if not ret.exit_status: check_result(vm_name, iface_source, iface_type, iface_mac, dt_options_suffix, pre_vm_state, False) finally: # Restore the vm if vm.is_alive(): vm.destroy(gracefully=False, free_mac_addresses=False) backup_xml.sync()
def run(test, params, env): """ Test virsh migrate command. """ def check_vm_network_accessed(session=None, ping_dest="www.baidu.com"): """ The operations to the VM need to be done before or after migration happens :param session: The session object to the host :param ping_dest: The destination to be ping :raise: test.fail when ping fails """ # Confirm local/remote VM can be accessed through network. logging.info("Check VM network connectivity") status, output = utils_test.ping(ping_dest, count=10, timeout=20, output_func=logging.debug, session=session) if status != 0: test.fail("Ping failed, status: %s," " output: %s" % (status, output)) def get_vm_ifaces(session=None): """ Get interfaces of vm :param session: The session object to the host :return: interfaces """ p_iface, v_iface = utils_net.get_remote_host_net_ifs(session) return p_iface def check_vm_iface_num(iface_list, exp_num=3): """ Check he number of interfaces :param iface_list: The interface list :param exp_num: The expected number :raise: test.fail when interfaces' number is not equal to exp_num """ if len(iface_list) != exp_num: test.fail("%d interfaces should be found on the vm, " "but find %s." % (exp_num, iface_list)) def create_or_del_networks(pf_name, params, remote_virsh_session=None, is_del=False): """ Create or delete network on local or remote :param params: Dictionary with the test parameters :param pf_name: The name of PF :param remote_virsh_session: The virsh session object to the remote host :param is_del: Whether the networks should be deleted :raise: test.fail when fails to define/start network """ net_hostdev_name = params.get("net_hostdev_name", "hostdev-net") net_hostdev_fwd = params.get("net_hostdev_fwd", '{"mode": "hostdev", "managed": "yes"}') net_bridge_name = params.get("net_bridge_name", "host-bridge") net_bridge_fwd = params.get("net_bridge_fwd", '{"mode": "bridge"}') bridge_name = params.get("bridge_name", "br0") net_dict = {"net_name": net_hostdev_name, "net_forward": net_hostdev_fwd, "net_forward_pf": '{"dev": "%s"}' % pf_name} bridge_dict = {"net_name": net_bridge_name, "net_forward": net_bridge_fwd, "net_bridge": '{"name": "%s"}' % bridge_name} if not is_del: for net_params in (net_dict, bridge_dict): net_dev = libvirt.create_net_xml(net_params.get("net_name"), net_params) if not remote_virsh_session: if net_dev.get_active(): net_dev.undefine() net_dev.define() net_dev.start() else: remote.scp_to_remote(server_ip, '22', server_user, server_pwd, net_dev.xml, net_dev.xml, limit="", log_filename=None, timeout=600, interface=None) remote_virsh_session.net_define(net_dev.xml, **virsh_args) remote_virsh_session.net_start(net_params.get("net_name"), **virsh_args) else: virsh_session = virsh if remote_virsh_session: virsh_session = remote_virsh_session for nname in (net_hostdev_name, net_bridge_name): if nname not in virsh_session.net_state_dict(): continue virsh_session.net_destroy(nname, debug=True, ignore_status=True) virsh_session.net_undefine(nname, debug=True, ignore_status=True) def check_vm_network_connection(net_name, expected_conn=0): """ Check network connections in network xml :param net_name: The network to be checked :param expected_conn: The expected value :raise: test.fail when fails """ output = virsh.net_dumpxml(net_name, debug=True).stdout_text if expected_conn == 0: reg_pattern = r"<network>" else: reg_pattern = r"<network connections='(\d)'>" res = re.findall(reg_pattern, output, re.I) if not res: test.fail("Unable to find expected connection in %s." % net_name) if expected_conn != 0: if expected_conn != int(res[0]): test.fail("Unable to get expected connection number." "Expected: %s, Actual %s" % (expected_conn, int(res[0]))) def get_hostdev_addr_from_xml(): """ Get VM hostdev address :return: pci driver id """ address_dict = {} for ifac in vm_xml.VMXML.new_from_dumpxml(vm_name).devices.by_device_tag("interface"): if ifac.type_name == "hostdev": address_dict = ifac.hostdev_address.attrs return libvirt.pci_info_from_address(address_dict, 16, "id") def check_vfio_pci(pci_path, status_error=False): """ Check if vf driver is vfio-pci :param pci_path: The absolute path of pci device :param status_error: Whether the driver should be vfio-pci """ cmd = "readlink %s/driver | awk -F '/' '{print $NF}'" % pci_path output = process.run(cmd, shell=True, verbose=True).stdout_text.strip() if (output == "vfio-pci") == status_error: test.fail("Get incorrect dirver %s, it should%s be vfio-pci." % (output, ' not' if status_error else '')) def update_iface_xml(vmxml): """ Update interfaces for guest :param vmxml: vm_xml.VMXML object """ vmxml.remove_all_device_by_type('interface') vmxml.sync() iface_dict = {"type": "network", "source": "{'network': 'host-bridge'}", "mac": mac_addr, "model": "virtio", "teaming": '{"type":"persistent"}', "alias": '{"name": "ua-backup0"}', "inbound": '{"average":"5"}', "outbound": '{"average":"5"}'} iface_dict2 = {"type": "network", "source": "{'network': 'hostdev-net'}", "mac": mac_addr, "model": "virtio", "teaming": '{"type":"transient", "persistent": "ua-backup0"}'} iface = interface.Interface('network') for ifc in (iface_dict, iface_dict2): iface.xml = libvirt.modify_vm_iface(vm.name, "get_xml", ifc) vmxml.add_device(iface) vmxml.sync() migration_test = migration.MigrationTest() migration_test.check_parameters(params) # Params for NFS shared storage shared_storage = params.get("migrate_shared_storage", "") if shared_storage == "": default_guest_asset = defaults.get_default_guest_os_info()['asset'] default_guest_asset = "%s.qcow2" % default_guest_asset shared_storage = os.path.join(params.get("nfs_mount_dir"), default_guest_asset) logging.debug("shared_storage:%s", shared_storage) # Params to update disk using shared storage params["disk_type"] = "file" params["disk_source_protocol"] = "netfs" params["mnt_path_name"] = params.get("nfs_mount_dir") # Local variables virsh_args = {"debug": True} virsh_options = params.get("virsh_options", "") server_ip = params.get("server_ip") server_user = params.get("server_user", "root") server_pwd = params.get("server_pwd") client_ip = params.get("client_ip") client_pwd = params.get("client_pwd") extra = params.get("virsh_migrate_extra") options = params.get("virsh_migrate_options") bridge_name = params.get("bridge_name", "br0") net_hostdev_name = params.get("net_hostdev_name", "hostdev-net") net_bridge_name = params.get("net_bridge_name", "host-bridge") driver = params.get("driver", "ixgbe") vm_tmp_file = params.get("vm_tmp_file", "/tmp/test.txt") cmd_during_mig = params.get("cmd_during_mig") net_failover_test = "yes" == params.get("net_failover_test", "no") cancel_migration = "yes" == params.get("cancel_migration", "no") try: vf_no = int(params.get("vf_no", "4")) except ValueError as e: test.error(e) migr_vm_back = "yes" == params.get("migrate_vm_back", "no") err_msg = params.get("err_msg") status_error = "yes" == params.get("status_error", "no") cmd_parms = {'server_ip': server_ip, 'server_user': server_user, 'server_pwd': server_pwd} remote_virsh_dargs = {'remote_ip': server_ip, 'remote_user': server_user, 'remote_pwd': server_pwd, 'unprivileged_user': None, 'ssh_remote_auth': True} destparams_dict = copy.deepcopy(params) remote_virsh_session = None vm_session = None vm = None mig_result = None func_name = None extra_args = {} default_src_vf = 0 default_dest_vf = 0 default_src_rp_filter = 1 default_dest_rp_filer = 1 if not libvirt_version.version_compare(6, 0, 0): test.cancel("This libvirt version doesn't support migration with " "net failover devices.") # params for migration connection params["virsh_migrate_desturi"] = libvirt_vm.complete_uri( params.get("migrate_dest_host")) params["virsh_migrate_connect_uri"] = libvirt_vm.complete_uri( params.get("migrate_source_host")) src_uri = params.get("virsh_migrate_connect_uri") dest_uri = params.get("virsh_migrate_desturi") vm_name = params.get("migrate_main_vm") vm = env.get_vm(vm_name) vm.verify_alive() # For safety reasons, we'd better back up xmlfile. new_xml = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) orig_config_xml = new_xml.copy() try: # Create a remote runner for later use runner_on_target = remote.RemoteRunner(host=server_ip, username=server_user, password=server_pwd) server_session = remote.wait_for_login('ssh', server_ip, '22', server_user, server_pwd, r"[\#\$]\s*$") if net_failover_test: src_pf, src_pf_pci = utils_sriov.find_pf(driver) logging.debug("src_pf is %s. src_pf_pci: %s", src_pf, src_pf_pci) params['pf_name'] = src_pf dest_pf, dest_pf_pci = utils_sriov.find_pf(driver, server_session) logging.debug("dest_pf is %s. dest_pf_pci: %s", dest_pf, dest_pf_pci) destparams_dict['pf_name'] = dest_pf src_pf_pci_path = utils_misc.get_pci_path(src_pf_pci) dest_pf_pci_path = utils_misc.get_pci_path(dest_pf_pci, server_session) cmd = "cat %s/sriov_numvfs" % (src_pf_pci_path) default_src_vf = process.run(cmd, shell=True, verbose=True).stdout_text cmd = "cat %s/sriov_numvfs" % (dest_pf_pci_path) status, default_dest_vf = utils_misc.cmd_status_output(cmd, shell=True, session=server_session) if status: test.error("Unable to get default sriov_numvfs on target!" "status: %s, output: %s" % (status, default_dest_vf)) if not utils_sriov.set_vf(src_pf_pci_path, vf_no): test.error("Failed to set vf on source.") if not utils_sriov.set_vf(dest_pf_pci_path, vf_no, session=server_session): test.error("Failed to set vf on target.") # Create PF and bridge connection on source and target host cmd = 'cat /proc/sys/net/ipv4/conf/all/rp_filter' default_src_rp_filter = process.run(cmd, shell=True, verbose=True).stdout_text status, default_dest_rp_filter = utils_misc.cmd_status_output(cmd, shell=True, session=server_session) if status: test.error("Unable to get default rp_filter on target!" "status: %s, output: %s" % (status, default_dest_rp_filter)) cmd = 'echo 0 >/proc/sys/net/ipv4/conf/all/rp_filter' process.run(cmd, shell=True, verbose=True) utils_misc.cmd_status_output(cmd, shell=True, session=server_session) utils_sriov.add_or_del_connection(params, is_del=False) utils_sriov.add_or_del_connection(destparams_dict, is_del=False, session=server_session) if not remote_virsh_session: remote_virsh_session = virsh.VirshPersistent(**remote_virsh_dargs) create_or_del_networks(dest_pf, params, remote_virsh_session=remote_virsh_session) remote_virsh_session.close_session() create_or_del_networks(src_pf, params) # Change network interface xml mac_addr = utils_net.generate_mac_address_simple() update_iface_xml(new_xml) # Change the disk of the vm libvirt.set_vm_disk(vm, params) if not vm.is_alive(): vm.start() # Check local guest network connection before migration if vm.serial_console is not None: vm.cleanup_serial_console() vm.create_serial_console() vm_session = vm.wait_for_serial_login(timeout=240) if net_failover_test: utils_net.restart_guest_network(vm_session) iface_list = get_vm_ifaces(vm_session) vm_ipv4, vm_ipv6 = utils_net.get_linux_ipaddr(vm_session, iface_list[0]) check_vm_network_accessed(ping_dest=vm_ipv4) if net_failover_test: check_vm_iface_num(iface_list) check_vm_network_connection(net_hostdev_name, 1) check_vm_network_connection(net_bridge_name, 1) hostdev_pci_id = get_hostdev_addr_from_xml() vf_path = utils_misc.get_pci_path(hostdev_pci_id) check_vfio_pci(vf_path) if cmd_during_mig: s, o = utils_misc.cmd_status_output(cmd_during_mig, shell=True, session=vm_session) if s: test.fail("Failed to run %s in vm." % cmd_during_mig) if extra.count("--postcopy"): func_name = virsh.migrate_postcopy extra_args.update({'func_params': params}) if cancel_migration: func_name = migration_test.do_cancel # Execute migration process vms = [vm] migration_test.do_migration(vms, None, dest_uri, 'orderly', options, thread_timeout=900, ignore_status=True, virsh_opt=virsh_options, func=func_name, extra_opts=extra, **extra_args) mig_result = migration_test.ret migration_test.check_result(mig_result, params) if int(mig_result.exit_status) == 0: server_session = remote.wait_for_login('ssh', server_ip, '22', server_user, server_pwd, r"[\#\$]\s*$") check_vm_network_accessed(server_session, vm_ipv4) server_session.close() if net_failover_test: # Check network connection check_vm_network_connection(net_hostdev_name) check_vm_network_connection(net_bridge_name) # VF driver should not be vfio-pci check_vfio_pci(vf_path, True) cmd_parms.update({'vm_ip': vm_ipv4, 'vm_pwd': params.get("password")}) vm_after_mig = remote.VMManager(cmd_parms) vm_after_mig.setup_ssh_auth() cmd = "ip link" cmd_result = vm_after_mig.run_command(cmd) libvirt.check_result(cmd_result) p_iface = re.findall(r"\d+:\s+(\w+):\s+.*", cmd_result.stdout_text) p_iface = [x for x in p_iface if x != 'lo'] check_vm_iface_num(p_iface) # Check the output of ping command cmd = 'cat %s' % vm_tmp_file cmd_result = vm_after_mig.run_command(cmd) libvirt.check_result(cmd_result) if re.findall('Destination Host Unreachable', cmd_result.stdout_text, re.M): test.fail("The network does not work well during " "the migration peirod. ping output: %s" % cmd_result.stdout_text) # Execute migration from remote if migr_vm_back: ssh_connection = utils_conn.SSHConnection(server_ip=client_ip, server_pwd=client_pwd, client_ip=server_ip, client_pwd=server_pwd) try: ssh_connection.conn_check() except utils_conn.ConnectionError: ssh_connection.conn_setup() ssh_connection.conn_check() # Pre migration setup for local machine migration_test.migrate_pre_setup(src_uri, params) cmd = "virsh migrate %s %s %s" % (vm_name, virsh_options, src_uri) logging.debug("Start migration: %s", cmd) cmd_result = remote.run_remote_cmd(cmd, params, runner_on_target) logging.info(cmd_result) if cmd_result.exit_status: test.fail("Failed to run '%s' on remote: %s" % (cmd, cmd_result)) logging.debug("migration back done") check_vm_network_accessed(ping_dest=vm_ipv4) if net_failover_test: if vm_session: vm_session.close() vm_session = vm.wait_for_login() iface_list = get_vm_ifaces(vm_session) check_vm_iface_num(iface_list) else: check_vm_network_accessed(ping_dest=vm_ipv4) if net_failover_test: iface_list = get_vm_ifaces(vm_session) check_vm_iface_num(iface_list) finally: logging.debug("Recover test environment") # Clean VM on destination migration_test.cleanup_dest_vm(vm, vm.connect_uri, dest_uri) if vm.is_alive(): vm.destroy(gracefully=False) logging.info("Recovery VM XML configration") orig_config_xml.sync() logging.debug("The current VM XML:\n%s", orig_config_xml.xmltreefile) server_session = remote.wait_for_login('ssh', server_ip, '22', server_user, server_pwd, r"[\#\$]\s*$") if 'src_pf' in locals(): cmd = 'echo %s >/proc/sys/net/ipv4/conf/all/rp_filter' % default_src_rp_filter process.run(cmd, shell=True, verbose=True) utils_sriov.add_or_del_connection(params, is_del=True) create_or_del_networks(src_pf, params, is_del=True) if 'dest_pf' in locals(): cmd = 'echo %s >/proc/sys/net/ipv4/conf/all/rp_filter' % default_dest_rp_filter utils_misc.cmd_status_output(cmd, shell=True, session=server_session) utils_sriov.add_or_del_connection(destparams_dict, session=server_session, is_del=True) remote_virsh_session = virsh.VirshPersistent(**remote_virsh_dargs) create_or_del_networks(dest_pf, params, remote_virsh_session, is_del=True) remote_virsh_session.close_session() if 'dest_pf_pci_path' in locals() and default_dest_vf != vf_no: utils_sriov.set_vf(dest_pf_pci_path, default_dest_vf, server_session) if 'src_pf_pci_path' in locals() and default_src_vf != vf_no: utils_sriov.set_vf(src_pf_pci_path, default_src_vf) # Clean up of pre migration setup for local machine if migr_vm_back: if 'ssh_connection' in locals(): ssh_connection.auto_recover = True migration_test.migrate_pre_setup(src_uri, params, cleanup=True) server_session.close() if remote_virsh_session: remote_virsh_session.close_session() logging.info("Remove local NFS image") source_file = params.get("source_file") if source_file: libvirt.delete_local_disk("file", path=source_file)
def run(test, params, env): """ Test hotplug of sr-iov devices. (Elements between [] are configurable test parameters) 1) Set up sr-iov test environment in host. 2) Start VM. 3) Disable the primary link(s) of guest. 4) PCI add one/multi sr-io deivce with (or without) repeat 5) Compare output of monitor command 'info pci'. 6) Compare output of guest command [reference_cmd]. 7) Verify whether pci_model is shown in [pci_find_cmd]. 8) Check whether the newly added PCI device works fine. 9) Delete the device, verify whether could remove the sr-iov device. 10) Re-enabling the primary link(s) of guest. :param test: QEMU test object. :param params: Dictionary with the test parameters. :param env: Dictionary with test environment. """ def get_active_network_device(session, nic_filter): devnames = [] cmd = "ifconfig -a" status, output = session.cmd_status_output(cmd) if status: msg = "Guest command '%s' fail with output: %s." % (cmd, output) raise error.TestError(msg) devnames = re.findall(nic_filter, output) return devnames def pci_add_iov(pci_num): pci_add_cmd = ("pci_add pci_addr=auto host host=%s,if=%s" % (pa_pci_ids[pci_num], pci_model)) if params.get("hotplug_params"): assign_param = params.get("hotplug_params").split() for param in assign_param: value = params.get(param) if value: pci_add_cmd += ",%s=%s" % (param, value) return pci_add(pci_add_cmd) def pci_add(pci_add_cmd): error.context("Adding pci device with command 'pci_add'") add_output = vm.monitor.send_args_cmd(pci_add_cmd, convert=False) pci_info.append(['', add_output]) if "OK domain" not in add_output: raise error.TestFail("Add PCI device failed. " "Monitor command is: %s, Output: %r" % (pci_add_cmd, add_output)) return vm.monitor.info("pci") def check_support_device(dev): if vm.monitor.protocol == 'qmp': devices_supported = vm.monitor.human_monitor_cmd("%s ?" % cmd_type) else: devices_supported = vm.monitor.send_args_cmd("%s ?" % cmd_type) # Check if the device is support in qemu is_support = utils_misc.find_substring(devices_supported, dev) if not is_support: raise error.TestError("%s doesn't support device: %s" % (cmd_type, dev)) def device_add_iov(pci_num): device_id = "%s" % pci_model + "-" + utils_misc.generate_random_id() pci_info.append([device_id]) driver = params.get("device_driver", "pci-assign") check_support_device(driver) pci_add_cmd = ("device_add id=%s,driver=%s,host=%s" % (pci_info[pci_num][0], driver, pa_pci_ids[pci_num])) if params.get("hotplug_params"): assign_param = params.get("hotplug_params").split() for param in assign_param: value = params.get(param) if value: pci_add_cmd += ",%s=%s" % (param, value) return device_add(pci_num, pci_add_cmd) def device_add(pci_num, pci_add_cmd): error.context("Adding pci device with command 'device_add'") if vm.monitor.protocol == 'qmp': add_output = vm.monitor.send_args_cmd(pci_add_cmd) else: add_output = vm.monitor.send_args_cmd(pci_add_cmd, convert=False) pci_info[pci_num].append(add_output) after_add = vm.monitor.info("pci") if pci_info[pci_num][0] not in str(after_add): logging.debug("Print info pci after add the block: %s" % after_add) raise error.TestFail("Add device failed. Monitor command is: %s" ". Output: %r" % (pci_add_cmd, add_output)) return after_add # Hot add a pci device def add_device(pci_num): reference_cmd = params["reference_cmd"] find_pci_cmd = params["find_pci_cmd"] info_pci_ref = vm.monitor.info("pci") reference = session.cmd_output(reference_cmd) active_nics = get_active_network_device(session, nic_filter) try: # get function for adding device. add_fuction = local_functions["%s_iov" % cmd_type] except Exception: raise error.TestError( "No function for adding sr-iov dev with '%s'" % cmd_type) after_add = None if add_fuction: # Do add pci device. after_add = add_fuction(pci_num) try: # Define a helper function to compare the output def _new_shown(): output = session.cmd_output(reference_cmd) return output != reference # Define a helper function to make sure new nic could get ip. def _check_ip(): post_nics = get_active_network_device(session, nic_filter) return (len(active_nics) <= len(post_nics) and active_nics != post_nics) # Define a helper function to catch PCI device string def _find_pci(): output = session.cmd_output(find_pci_cmd) if re.search(match_string, output, re.IGNORECASE): return True else: return False error.context("Start checking new added device") # Compare the output of 'info pci' if after_add == info_pci_ref: raise error.TestFail("No new PCI device shown after executing " "monitor command: 'info pci'") secs = int(params["wait_secs_for_hook_up"]) if not utils_misc.wait_for(_new_shown, test_timeout, secs, 3): raise error.TestFail( "No new device shown in output of command " "executed inside the guest: %s" % reference_cmd) if not utils_misc.wait_for(_find_pci, test_timeout, 3, 3): raise error.TestFail("New add device not found in guest. " "Command was: %s" % find_pci_cmd) # Test the newly added device if not utils_misc.wait_for(_check_ip, 120, 3, 3): ifconfig = session.cmd_output("ifconfig -a") raise error.TestFail("New hotpluged device could not get ip " "after 120s in guest. guest ifconfig " "output: \n%s" % ifconfig) try: session.cmd(params["pci_test_cmd"] % (pci_num + 1)) except aexpect.ShellError, e: raise error.TestFail("Check device failed after PCI " "hotplug. Output: %r" % e.output) except Exception: pci_del(pci_num, ignore_failure=True) raise # Hot delete a pci device def pci_del(pci_num, ignore_failure=False): def _device_removed(): after_del = vm.monitor.info("pci") return after_del != before_del before_del = vm.monitor.info("pci") if cmd_type == "pci_add": slot_id = "0" + pci_info[pci_num][1].split(",")[2].split()[1] cmd = "pci_del pci_addr=%s" % slot_id vm.monitor.send_args_cmd(cmd, convert=False) elif cmd_type == "device_add": cmd = "device_del id=%s" % pci_info[pci_num][0] vm.monitor.send_args_cmd(cmd) if (not utils_misc.wait_for(_device_removed, test_timeout, 0, 1) and not ignore_failure): raise error.TestFail("Failed to hot remove PCI device: %s. " "Monitor command: %s" % (pci_model, cmd)) vm = env.get_vm(params["main_vm"]) vm.verify_alive() timeout = int(params.get("login_timeout", 360)) session = vm.wait_for_serial_login(timeout=timeout) test_timeout = int(params.get("test_timeout", 360)) # Test if it is nic or block pci_num_range = int(params.get("pci_num", 1)) rp_times = int(params.get("repeat_times", 1)) pci_model = params.get("pci_model", "pci-assign") # Need udpate match_string if you use a card other than 82576 match_string = params.get("match_string", "82576") generate_mac = params.get("generate_mac", "yes") nic_filter = params["nic_interface_filter"] devices = [] device_type = params.get("hotplug_device_type", "vf") for i in xrange(pci_num_range): device = {} device["type"] = device_type if generate_mac == "yes": device['mac'] = utils_net.generate_mac_address_simple() if params.get("device_name"): device["name"] = params.get("device_name") devices.append(device) device_driver = params.get("device_driver", "pci-assign") if vm.pci_assignable is None: vm.pci_assignable = test_setup.PciAssignable( driver=params.get("driver"), driver_option=params.get("driver_option"), host_set_flag=params.get("host_setup_flag"), kvm_params=params.get("kvm_default"), vf_filter_re=params.get("vf_filter_re"), pf_filter_re=params.get("pf_filter_re"), device_driver=device_driver) pa_pci_ids = vm.pci_assignable.request_devs(devices) # Modprobe the module if specified in config file module = params.get("modprobe_module") if module: error.context("modprobe the module %s" % module, logging.info) session.cmd("modprobe %s" % module) # Probe qemu to verify what is the supported syntax for PCI hotplug if vm.monitor.protocol == 'qmp': cmd_o = vm.monitor.info("commands") else: cmd_o = vm.monitor.send_args_cmd("help") cmd_type = utils_misc.find_substring(str(cmd_o), "device_add", "pci_add") if not cmd_o: raise error.TestError("Unknown version of qemu") local_functions = locals() if params.get("enable_set_link" "yes") == "yes": error.context("Disable the primary link(s) of guest", logging.info) for nic in vm.virtnet: vm.set_link(nic.device_id, up=False) try: for j in range(rp_times): # pci_info is a list of list. # each element 'i' has 4 members: # pci_info[i][0] == device id, only used for device_add # pci_info[i][1] == output of device add command pci_info = [] for pci_num in xrange(pci_num_range): msg = "Start hot-adding %sth pci device," % (pci_num + 1) msg += " repeat %d" % (j + 1) error.context(msg, logging.info) add_device(pci_num) sub_type = params.get("sub_type_after_plug") if sub_type: error.context("Running sub test '%s' after hotplug" % sub_type, logging.info) utils_test.run_virt_sub_test(test, params, env, sub_type) if "guest_suspend" == sub_type: # Hotpluged device have been released after guest suspend, # so do not need unpluged step. break for pci_num in xrange(pci_num_range): msg = "start hot-deleting %sth pci device," % (pci_num + 1) msg += " repeat %d" % (j + 1) error.context(msg, logging.info) pci_del(-(pci_num + 1)) finally: if params.get("enable_set_link", "yes") == "yes": error.context("Re-enabling the primary link(s) of guest", logging.info) for nic in vm.virtnet: vm.set_link(nic.device_id, up=True)
def attach_nic(): # pylint: disable=W0611 """ Attach interface, by xml or cmd, for both hot and cold plug """ def create_iface_xml(mac): """ Create interface xml file :param mac: The mac address of nic device """ iface = Interface(type_name='network') iface.source = iface_source iface.model = iface_model iface.mac_address = mac logging.debug("Create new interface xml: %s", iface) return iface plug_method = params.get('plug_method', 'interface') cold_plug = params.get('cold_plug', 'no') mac = utils_net.generate_mac_address_simple() iface_source = {'network': 'default'} iface_model = params["virtio_model"] options = ("network %s --model %s --mac %s" % (iface_source['network'], iface_model, mac)) nic_params = { 'mac': mac, 'nettype': params['nettype'], 'ip_version': 'ipv4' } if cold_plug == "yes": options += ' --config' if plug_method == 'interface': # Hotplug nic vir attach-interface ret = virsh.attach_interface(vm_name, options, ignore_status=True) else: # Hotplug nic via attach-device nic_xml = create_iface_xml(mac) nic_xml.xmltreefile.write() xml_file = nic_xml.xml with open(xml_file) as nic_file: logging.debug("Attach device by XML: %s", nic_file.read()) ret = virsh.attach_device(domainarg=vm_name, filearg=xml_file, debug=True) libvirt.check_exit_status(ret) if cold_plug == "yes": reboot() # Reboot guest if it is cold plug test detect_new_nic(mac) if plug_method == 'interface' and cold_plug == 'no': check_plug_to_pci_bridge(vm_name, mac) session = vm.wait_for_login(serial=True) # Add nic to VM object for further check nic_name = vm.add_nic(**nic_params)["nic_name"] nic = vm.virtnet[nic_name] # Config ip inside guest for new added nic if not utils_misc.wait_for( lambda: get_hotplug_nic_ip(vm, nic, session, guest_os_type), timeout=30): test.fail("Does not find plugged nic %s in guest" % mac) options = ("network %s" % mac) if cold_plug == "yes": options += ' --config' # Detach nic device if plug_method == 'interface': ret = virsh.detach_interface(vm_name, options, ignore_status=True) else: with open(xml_file) as nic_file: logging.debug("Detach device by XML: %s", nic_file.read()) ret = virsh.detach_device(domainarg=vm_name, filearg=xml_file, debug=True) libvirt.check_exit_status(ret) if cold_plug == "yes": session = reboot() # Reboot guest if it is cold plug test # Check if nic is removed from guest if not utils_misc.wait_for(lambda: check_nic_removed(mac, session), timeout=30): test.fail("The nic %s still exist in guest after being unplugged" % nic_name)
def run(test, params, env): """ create/delete macvtap in host 1) Verify no other macvtap share the physical network device. 2) Create a macvtap device in host. 3) Check configuraton of macvtap device. 4) Ping out from host with the interface that create macvtap. 5) Delete the macvtap device create in step 2. 6) Ping out from host with the interface that create macvtap. :param test: QEMU test object. :param params: Dictionary with the test parameters. :param env: Dictionary with test environment. """ ifname = params.get("macvtap_base_interface") macvtap_mode = params.get("macvtap_mode", "passthru") dest_host = params.get("dest_host") set_mac = params.get("set_mac", "yes") == "yes" macvtaps = [] if not ifname: ifname = params.get("netdst") ifname = utils_net.get_macvtap_base_iface(ifname) error.context("Verify no other macvtap share the physical network device.", logging.info) macvtap_devices = get_macvtap_device_on_ifname(ifname) for device in macvtap_devices: utils.system_output("ip link delete %s" % device) for mode in macvtap_mode.split(): macvtap_name = "%s_01" % mode txt = "Create %s mode macvtap device %s on %s." % (mode, macvtap_name, ifname) error.context(txt, logging.info) cmd = " ip link add link %s name %s type macvtap mode %s" % ( ifname, macvtap_name, mode) utils.system(cmd, timeout=240) if set_mac: txt = "Determine and configure mac address of %s, " % macvtap_name txt += "Then link up it." error.context(txt, logging.info) mac = utils_net.generate_mac_address_simple() cmd = " ip link set %s address %s up" % (macvtap_name, mac) utils.system(cmd, timeout=240) error.context("Check configuraton of macvtap device", logging.info) check_cmd = " ip -d link show %s" % macvtap_name try: tap_info = utils.system_output(check_cmd, timeout=240) except error.CmdError: err = "Fail to create %s mode macvtap on %s" % (mode, ifname) raise error.TestFail(err) if set_mac: if mac not in tap_info: err = "Fail to set mac for %s" % macvtap_name raise error.TestFail(err) macvtaps.append(macvtap_name) if not dest_host: dest_host_get_cmd = "ip route | awk '/default/ { print $3 }'" dest_host_get_cmd = params.get("dest_host_get_cmd", dest_host_get_cmd) dest_host = utils.system_output(dest_host_get_cmd).split()[-1] txt = "Ping dest host %s from " % dest_host txt += "localhost with the interface %s" % ifname error.context(txt, logging.info) status, output = utils_test.ping(dest_host, 10, interface=ifname, timeout=20) ratio = utils_test.get_loss_ratio(output) if "passthru" in macvtap_mode: ifnames = utils_net.get_host_iface() ifnames.remove(ifname) logging.info("ifnames = %s", ifnames) ips = [] for name in ifnames: try: _ip = utils_net.get_ip_address_by_interface(name) if _ip != "127.0.0.1": ips.append(_ip) except Exception: pass logging.info("ips = %s", ips) if not ips: if ratio != 100: err = "%s did not lost network connection after " % ifname err += " creating %s mode macvtap on it." % macvtap_mode raise error.TestFail(err) else: err = "%s is not the only network device in host" % ifname logging.debug(err) else: if ratio != 0: err = "Package lost during ping %s from %s " % (dest_host, ifname) err += "after creating %s mode macvtap on it." % macvtap_mode raise error.TestFail(err) for name in macvtaps: txt = "Delete macvtap device %s on %s." % (name, ifname) error.context(txt, logging.info) del_cmd = "ip link delete %s" % name utils.system(del_cmd) devices = get_macvtap_device_on_ifname(ifname) if name in devices: err = "Fail to delete macvtap %s on %s" % (name, ifname) raise error.TestFail(err) logging.info("dest_host = %s", dest_host) txt = "Ping dest host %s from " % dest_host txt += "localhost with the interface %s" % ifname error.context(txt, logging.info) status, output = utils_test.ping(dest_host, 10, interface=ifname, timeout=20) if status != 0: raise error.TestFail("Ping failed, status: %s," " output: %s" % (status, output)) ratio = utils_test.get_loss_ratio(output) if ratio != 0: err = "Package lost during ping %s from %s " % (dest_host, ifname) raise error.TestFail(err)
def run(test, params, env): """ Test command: virsh net-dhcp-leases 1. Create a new network and run virsh command to check dhcp leases info. 2. Attach an interface before or after start the domain, then check the dhcp leases info. 3. Clean the environment. """ vm_name = params.get("main_vm") vm = env.get_vm(vm_name) net_name = params.get("net_name", "default") net_option = params.get("net_option", "") status_error = "yes" == params.get("status_error", "no") prepare_net = "yes" == params.get("prepare_net", "yes") hotplug_iface = "yes" == params.get("hotplug_interface", "no") filter_by_mac = "yes" == params.get("filter_by_mac", "no") invalid_mac = "yes" == params.get("invalid_mac", "no") expect_msg = params.get("leases_err_msg") # upstream expect msg may change on new libvirt new_expect_msg = params.get("new_leases_err_msg") range_lease = eval(params.get("range_lease", "None")) host_lease = eval(params.get("host_lease", "None")) host = eval(params.get("host", "None")) invalid_lease = "yes" == params.get("invalid_lease", "no") blank_lease = "yes" == params.get("blank_lease", "no") if (host_lease or range_lease) and not libvirt_version.version_compare(6, 2, 0): test.cancel( "Don't support: libvirt support lease setting since 6.2.0!") # Generate a random string as the MAC address nic_mac = None if invalid_mac: nic_mac = utils_misc.generate_random_string(17) # Command won't fail on old libvirt if not libvirt_version.version_compare(1, 3, 1) and invalid_mac: logging.debug("Reset case to positive as BZ#1261432") status_error = False def create_network(): """ Create a network """ net_ip_addr = params.get("net_ip_addr", "192.168.200.1") net_ip_netmask = params.get("net_ip_netmask", "255.255.255.0") net_dhcp_start = params.get("net_dhcp_start", "192.168.200.2") net_dhcp_end = params.get("net_dhcp_end", "192.168.200.254") netxml = network_xml.NetworkXML() netxml.name = net_name netxml.forward = {'mode': "nat"} range = network_xml.RangeXML() range.attrs = {'start': net_dhcp_start, "end": net_dhcp_end} ipxml = network_xml.IPXML() if range_lease: range.lease_attrs = range_lease ipxml.address = net_ip_addr ipxml.netmask = net_ip_netmask ipxml.dhcp_ranges = range if host: new_host = network_xml.DhcpHostXML() new_host.attrs = host new_host.lease_attrs = host_lease ipxml.hosts = [new_host] netxml.set_ip(ipxml) netxml.create() def get_net_dhcp_leases(output): """ Return the dhcp lease info in a list """ leases = [] lines = output.splitlines() if not lines: return leases try: pat = r"\S+\ ?\S+\ ?\S+\ ?\S+|\S+" keys = re.findall(pat, lines[0]) for line in lines[2:]: values = re.findall(pat, line) leases.append(dict(list(zip(keys, values)))) return leases except Exception: test.error("Fail to parse output: %s" % output) def check_lease_time(ex_time, duration): """ Compare the expiry time from the virsh cmd output and the setting :param ex_time: text, the expiry time get from the net-dhcp-lease output :param duration: dict, the configured expiry time """ now_time = datetime.now() # convert the lease time from str to the datetime structure # lease is in format like: 2021-01-18 02:15:35 get_ex_time = datetime.strptime(ex_time, '%Y-%m-%d %H:%M:%S') if duration['expiry'] == '0': if get_ex_time > now_time: test.fail("The expiry time is not correct!!") if 'unit' not in duration: duration['unit'] = 'minutes' else: if duration['unit'] == 'seconds': dur_sec = int(duration['expiry']) elif duration['unit'] == 'hours': dur_sec = int(duration['expiry']) * 3600 else: dur_sec = int(duration['expiry']) * 60 delta = get_ex_time - now_time logging.debug( "The get_ex_time is %s, the now_time is %s, " "duration is %s", get_ex_time, now_time, duration) if delta > timedelta(seconds=dur_sec): test.fail("Get expiry time %s longer than the setting %s!!" % (delta, timedelta(seconds=dur_sec))) elif delta < timedelta(seconds=(dur_sec - 30)): test.fail("Get expiry time %s shorter than the setting %s" % (delta, timedelta(seconds=dur_sec))) else: logging.info("Get expected lease info.") return None def get_ip_by_mac(mac_addr, try_dhclint=False, timeout=120): """ Get interface IP address by given MAC addrss. If try_dhclint is True, then try to allocate IP addrss for the interface. """ session = vm.wait_for_login(login_nic_index, timeout=timeout, serial=True) def f(): return utils_net.get_guest_ip_addr(session, mac_addr) try: ip_addr = utils_misc.wait_for(f, 10) if ip_addr is None: iface_name = utils_net.get_linux_ifname(session, mac_addr) if try_dhclint: session.cmd("dhclient %s" % iface_name) ip_addr = utils_misc.wait_for(f, 10) else: # No IP for the interface, just print the interface name logging.warn( "Find '%s' with MAC address '%s', " "but which has no IP address", iface_name, mac_addr) finally: session.close() return ip_addr def check_net_lease(net_leases, expected_find=True): """ Check the dhcp lease info. """ if not net_leases: if expected_find: test.fail("Lease info is empty") else: logging.debug("No dhcp lease info find as expected") else: if not expected_find: test.fail("Find unexpected dhcp lease info: %s" % net_leases) find_mac = False for net_lease in net_leases: net_mac = net_lease['MAC address'] net_ip = net_lease['IP address'][:-3] expiry_time = net_lease['Expiry Time'] if vm_xml.VMXML.get_iface_by_mac(vm_name, net_mac): find_mac = True logging.debug("Find '%s' in domain XML", net_mac) else: logging.debug("Not find '%s' in domain XML", net_mac) continue iface_ip = get_ip_by_mac(net_mac) if iface_ip and iface_ip != net_ip: test.fail("Address '%s' is not expected" % iface_ip) #check if lease time is correct if libvirt_version.version_compare(6, 2, 0): if host_lease and net_mac == host['mac']: check_lease_time(expiry_time, host_lease) elif range_lease: check_lease_time(expiry_time, range_lease) if expected_find and not find_mac: test.fail("No matched MAC address") vmxml = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) vmxml_backup = vmxml.copy() if vm.is_alive(): vm.destroy(gracefully=False) login_nic_index = 0 new_nic_index = 0 # Cleanup dirty dnsmaq, firstly get all network,and destroy all networks except # default net_state = virsh.net_state_dict(only_names=True) logging.debug( "current networks: %s, destroy and undefine networks " "except default!", net_state) for net in net_state: if net != "default": virsh.net_destroy(net) virsh.net_undefine(net) cmd = "ps aux|grep dnsmasq|grep -v grep | grep -v default | awk '{print $2}'" pid_list = process.run(cmd, shell=True).stdout_text.strip().splitlines() logging.debug(pid_list) for pid in pid_list: utils_misc.safe_kill(pid, signal.SIGKILL) try: # Create new network if prepare_net: nets_old = virsh.net_state_dict() if net_name in list(nets_old.keys()): virsh.net_destroy(net_name) virsh.net_undefine(net_name) create_network() nets = virsh.net_state_dict() if net_name not in list(nets.keys()) and not status_error: test.error("Not find network '%s'" % net_name) expected_find = False result = virsh.net_dhcp_leases(net_name, mac=nic_mac, options=net_option, debug=True, ignore_status=True) utlv.check_exit_status(result, status_error) lease = get_net_dhcp_leases(result.stdout.strip()) check_net_lease(lease, expected_find) if not status_error: if host: iface_mac = host['mac'] else: iface_mac = utils_net.generate_mac_address_simple() if filter_by_mac: nic_mac = iface_mac op = "--type network --model virtio --source %s --mac %s" \ % (net_name, iface_mac) nic_params = { 'mac': iface_mac, 'nettype': 'bridge', 'ip_version': 'ipv4' } login_timeout = 120 if not hotplug_iface: op += " --config" virsh.attach_interface(vm_name, option=op, debug=True, ignore_status=False) vm.add_nic(**nic_params) vm.start() new_nic_index = vm.get_nic_index_by_mac(iface_mac) if new_nic_index > 0: login_nic_index = new_nic_index else: vm.start() # wait for VM start before hotplug interface vm.wait_for_serial_login() virsh.attach_interface(vm_name, option=op, debug=True, ignore_status=False) vm.add_nic(**nic_params) # As VM already started, so the login timeout could be shortened login_timeout = 10 new_interface_ip = get_ip_by_mac(iface_mac, try_dhclint=True, timeout=login_timeout) if new_interface_ip: expected_find = True result = virsh.net_dhcp_leases(net_name, mac=nic_mac, debug=False, ignore_status=True) utlv.check_exit_status(result, status_error) lease = get_net_dhcp_leases(result.stdout.strip()) check_net_lease(lease, expected_find) else: if expect_msg: utlv.check_result(result, expect_msg.split(';')) except LibvirtXMLError as e: if status_error and invalid_lease: if blank_lease and libvirt_version.version_compare(7, 1, 0): expect_msg = new_expect_msg if expect_msg not in e.details: test.fail("Network create fail unexpected: %s" % e.details) else: logging.debug("Network create fail expected: %s", e.details) finally: # Delete the new attached interface if new_nic_index > 0: vm.del_nic(new_nic_index) if vm.is_alive(): vm.destroy(gracefully=False) vmxml_backup.sync() if prepare_net: virsh.net_destroy(net_name)
def run(test, params, env): """ Test bridge support from network 1) create a linux bridge and connect a physical interface to it 2) define nwfilter with "vdsm-no-mac-spoofing" 3) redefine the vm with the new create bridge and filter 4) check if guest can get public ip after vm start 5) check if guest and host can ping each other 6) check if guest and host can ping outside 7) start another vm connected to the same bridge 8) check if the 2 guests can ping each other """ def create_bridge(br_name, iface_name): """ Create a linux bridge by virsh cmd: 1. Stop NetworkManager and Start network service 2. virsh iface-bridge <iface> <name> [--no-stp] :param br_name: bridge name :param iface_name: physical interface name :return: bridge created or raise exception """ # Make sure the bridge not exist if libvirt.check_iface(br_name, "exists", "--all"): test.cancel("The bridge %s already exist" % br_name) # Create bridge utils_package.package_install('tmux') cmd = 'tmux -c "ip link add name {0} type bridge; ip link set {1} up;' \ ' ip link set {1} master {0}; ip link set {0} up;' \ ' pkill dhclient; sleep 6; dhclient {0}; ifconfig {1} 0"'.format(br_name, iface_name) process.run(cmd, shell=True, verbose=True) def create_bridge_network(br_name, net_name): """ Define and start the bridge type network """ # check if network with the same name already exists output_all = virsh.net_list("--all").stdout.strip() if re.search(net_name, output_all): test.cancel("Network with the same name already exists!") test_xml = network_xml.NetworkXML(network_name="%s" % net_name) test_xml.forward = {"mode": "bridge"} test_xml.bridge = {"name": br_name} test_xml.create() def define_nwfilter(filter_name): """ Define nwfilter vdsm-no-mac-spoofing with content like: <filter name='vdsm-no-mac-spoofing' chain='root'> <filterref filter='no-mac-spoofing'/> <filterref filter='no-arp-mac-spoofing'/> </filter> :param filter_name: the name of nwfilter :return: filter created or raise exception """ filter_uuid = params.get("filter_uuid", "11111111-b071-6127-b4ec-111111111111") filter_params = { "filter_name": "vdsm-no-mac-spoofing", "filter_chain": "root", "filter_uuid": filter_uuid, "filterref_name_1": "no-mac-spoofing", "filterref_name_2": "no-arp-mac-spoofing" } filter_xml = libvirt.create_nwfilter_xml(filter_params).xml # Run command result = virsh.nwfilter_define(filter_xml, ignore_status=True, debug=True) if result.exit_status: test.fail("Failed to define nwfilter with %s" % filter_xml) def ping(src_ip, dest_ip, ping_count, timeout, session=None): """ Wrap of ping :param src_ip: source address :param dest_ip: destination address :param ping_count: count of icmp packet :param timeout: timeout for the ping command :param session: local execution or session to execute the ping command :return: ping succeed or raise exception """ status, output = utils_net.ping(dest=dest_ip, count=ping_count, interface=src_ip, timeout=timeout, session=session, force_ipv4=True) if status: test.fail("Fail to ping %s from %s" % (dest_ip, src_ip)) def check_net_functions(guest_ip, ping_count, ping_timeout, guest_session, host_ip, remote_url, endpoint_ip): # make sure host network works well # host ping remote url ping(host_ip, remote_url, ping_count, ping_timeout) # host ping guest ping(host_ip, guest_ip, ping_count, ping_timeout) # guest ping host ping(guest_ip, host_ip, ping_count, ping_timeout, session=guest_session) # guest ping remote url ping(guest_ip, remote_url, ping_count, ping_timeout, session=guest_session) # guest ping endpoint ping(guest_ip, endpoint_ip, ping_count, ping_timeout, session=guest_session) # Get test params bridge_name = params.get("bridge_name", "test_br0") filter_name = params.get("filter_name", "vdsm-no-mac-spoofing") ping_count = params.get("ping_count", "5") ping_timeout = float(params.get("ping_timeout", "10")) iface_name = utils_net.get_net_if(state="UP")[0] bridge_script = NETWORK_SCRIPT + bridge_name iface_script = NETWORK_SCRIPT + iface_name iface_script_bk = os.path.join(data_dir.get_tmp_dir(), "iface-%s.bk" % iface_name) attach_interface = "yes" == params.get("attach_interface", "no") iface_model = params.get("iface_model", "virtio") iface_source = eval(params.get("iface_source", "{'bridge':'test_br0'}")) iface_type = params.get("iface_type", None) iface_target = params.get("iface_target", "br_target") iface_alias = params.get("iface_alias", None) hotplug = "yes" == params.get("hotplug", "no") iface_driver = params.get("iface_driver", None) start_vm2 = "yes" == params.get("start_vm2", "no") create_network = "yes" == params.get("create_network", "no") update_device = "yes" == params.get("update_with_diff_type", "no") vms = params.get("vms").split() if len(vms) <= 1: test.cancel("Need two VMs to test") else: vm1_name = vms[0] vm2_name = vms[1] vm1 = env.get_vm(vm1_name) vm2 = env.get_vm(vm2_name) # Back up the interface script process.run("cp %s %s" % (iface_script, iface_script_bk), shell=True, verbose=True) # Back up vm xml vm1_xml_bak = vm_xml.VMXML.new_from_dumpxml(vm1_name) vm2_xml_bak = vm_xml.VMXML.new_from_dumpxml(vm2_name) # Stop NetworkManager service NM_service = service.Factory.create_service("NetworkManager") NM_status = NM_service.status() if not NM_status: NM_service.start() mac = utils_net.generate_mac_address_simple() try: create_bridge(bridge_name, iface_name) define_nwfilter(filter_name) if hotplug: err_msgs = ("No more available PCI slots", "No more available PCI addresses") # delete the original interface on the vm before hot-plug if vm1.is_alive(): vm1.destroy() vmxml = vm_xml.VMXML.new_from_dumpxml(vm1_name) iface_xml = vmxml.get_devices('interface')[0] logging.debug("Delete the original interface") vmxml.del_device(iface_xml) vmxml.sync() vm1.start() # do hot-plug if attach_interface: logging.info("Try to hot-plug interface") options = ( "%s %s --model %s --mac %s" % (iface_type, iface_source['bridge'], iface_model, mac)) ret = virsh.attach_interface(vm1_name, options, ignore_status=True) else: logging.info("Try to hot-plug device") if create_network: create_bridge_network(bridge_name, iface_source["network"]) target = str({'dev': iface_target}) iface_alias = str({'name': iface_alias}) vm_iface_source = str(iface_source) iface_params = { "type": iface_type, "source": vm_iface_source, "filter": filter_name, "mac": mac, 'alias': iface_alias, 'target': target, 'model': iface_model, 'driver': iface_driver } attach_xml = interface.Interface(iface_params['type']) attach_xml.xml = libvirt.modify_vm_iface( vm1_name, 'get_xml', iface_params) ret = virsh.attach_device(vm1_name, attach_xml.xml, ignore_status=True, debug=True) if ret.exit_status: if any([msg in ret.stderr for msg in err_msgs]): test.error("No more pci slots, can't attach more devices") else: test.fail("Failed to attach-interface: %s" % ret.stderr.strip()) else: logging.debug("Hot-plug interface or device pass") if update_device: # As the interface type will change to actual type "bridge" in live xml, we need to ensure # the update with original "network" type will not fail. # Try to delete the nwfilter with original type in iface_params update_xml = interface.Interface(iface_type) iface_params_update = { "del_filter": "yes", "type": "network", "source": vm_iface_source } update_xml.xml = libvirt.modify_vm_iface( vm1_name, 'get_xml', iface_params_update) ret = virsh.update_device(vm1_name, update_xml.xml, ignore_status=True, debug=True) libvirt.check_exit_status(ret) else: vm_iface_source = str(iface_source) vm1_iface_params = { "type": "bridge", "source": vm_iface_source, "filter": filter_name, "mac": mac, 'driver': iface_driver, "iface_model": iface_model } libvirt.modify_vm_iface(vm1_name, "update_iface", vm1_iface_params) if vm1.is_alive(): vm1.destroy() vm1.start() # apply ip address as it may not be initialized session1 = session2 = None session1 = vm1.wait_for_serial_login() utils_net.restart_guest_network(session1) output = session1.cmd_output("ifconfig || ip a") logging.debug("guest1 ip info %s" % output) # Check guest's network function host_ip = utils_net.get_ip_address_by_interface(bridge_name) remote_url = params.get("remote_ip", "www.google.com") try: vm1_ip = utils_net.get_guest_ip_addr(session1, mac) except Exception as errs: test.fail("vm1 can't get IP with the new create bridge: %s" % errs) if hotplug: # reboot vm1 then check network function to ensure the interface still there and works fine logging.info("reboot the vm") virsh.reboot(vm1) if session1 is None: session1 = vm1.wait_for_serial_login() ping(vm1_ip, remote_url, ping_count, ping_timeout, session=session1) # restart libvirtd service then check the interface still works fine libvirtd = utils_libvirtd.Libvirtd() libvirtd.restart() vm1.cleanup_serial_console() vm1.create_serial_console() session1 = vm1.wait_for_serial_login() ping(vm1_ip, remote_url, ping_count, ping_timeout, session=session1) logging.info( "after reboot and restart libvirtd, the network works fine") if iface_driver: try: driver_dict = eval(iface_driver) if session1 is None: session1 = vm1.wait_for_serial_login() guest_iface_info = session1.cmd_output("ip l").strip() guest_iface_name = re.findall( r"^\d+: (\S+?)[@:].*state UP.*$", guest_iface_info, re.MULTILINE)[0] comb_size = driver_dict.get('queues') rx_size = driver_dict.get('rx_queue_size') session1.cmd_status("ethtool -L %s combined %s" % (guest_iface_name, comb_size)) ret, outp = session1.cmd_status_output("ethtool -l %s" % guest_iface_name) logging.debug("ethtool cmd output:%s" % outp) if not ret: pre_comb = re.search( "Pre-set maximums:[\s\S]*?Combined:.*?(\d+)", outp).group(1) cur_comb = re.search( "Current hardware settings:[\s\S]*?Combined:.*?(\d+)", outp).group(1) if int(pre_comb) != int(comb_size) or int( cur_comb) != int(comb_size): test.fail( "Fail to check the combined size: setting: %s," "Pre-set: %s, Current-set: %s" % (comb_size, pre_comb, cur_comb)) else: logging.info( "Getting correct Pre-set and Current set value" ) else: test.error("ethtool list fail: %s" % outp) # as tx_queue size is only supported for vhost-user interface, only check rx_queue size ret1, outp1 = session1.cmd_status_output("ethtool -g %s" % guest_iface_name) logging.debug("guest queue size setting is %s" % outp1) if not ret1: pre_set = re.search(r"Pre-set maximums:\s*RX:\s*(\d+)", outp1).group(1) cur_set = re.search( r"Current hardware settings:\s*RX:\s*(\d+)", outp1).group(1) if int(pre_set) != int(rx_size) or int(cur_set) != int( rx_size): test.fail("Fail to check the rx_queue_size!") except Exception as errs: test.fail("fail to get driver info") # hot-unplug interface/device if attach_interface: ret = virsh.detach_interface(vm1_name, "bridge", ignore_status=True) else: ret = virsh.detach_device(vm1_name, attach_xml.xml, ignore_status=True, debug=True) if ret.exit_status: test.fail("Hot-unplug interface/device fail") else: logging.info("hot-unplug interface/device succeed") else: if start_vm2: # Start vm2 connect to the same bridge mac2 = utils_net.generate_mac_address_simple() vm2_iface_params = { "type": "bridge", "source": vm_iface_source, "filter": filter_name, "mac": mac2 } libvirt.modify_vm_iface(vm2_name, "update_iface", vm2_iface_params) if vm2.is_alive(): vm2.destroy() vm2.start() # Check if vm1 and vm2 can ping each other try: utils_net.update_mac_ip_address(vm2, timeout=120) vm2_ip = vm2.get_address() except Exception as errs: test.fail( "vm2 can't get IP with the new create bridge: %s" % errs) session2 = vm2.wait_for_login() # make sure guest has got ip address utils_net.restart_guest_network(session2) output2 = session2.cmd_output("ifconfig || ip a") logging.debug("guest ip info %s" % output2) # check 2 guests' network functions check_net_functions(vm1_ip, ping_count, ping_timeout, session1, host_ip, remote_url, vm2_ip) check_net_functions(vm2_ip, ping_count, ping_timeout, session2, host_ip, remote_url, vm1_ip) finally: logging.debug("Start to restore") vm1_xml_bak.sync() vm2_xml_bak.sync() virsh.nwfilter_undefine(filter_name, ignore_status=True) if libvirt.check_iface(bridge_name, "exists", "--all"): virsh.iface_unbridge(bridge_name, timeout=60, debug=True) if os.path.exists(iface_script_bk): process.run("mv %s %s" % (iface_script_bk, iface_script), shell=True, verbose=True) if os.path.exists(bridge_script): process.run("rm -rf %s" % bridge_script, shell=True, verbose=True) cmd = 'tmux -c "ip link set {1} nomaster; ip link delete {0};' \ 'pkill dhclient; sleep 6; dhclient {1}"'.format(bridge_name, iface_name) process.run(cmd, shell=True, verbose=True) # reload network configuration NM_service.restart() # recover NetworkManager if NM_status is True: NM_service.start() if 'network' in iface_source and iface_source[ "network"] in virsh.net_state_dict(): virsh.net_destroy(iface_source["network"], ignore_status=False)
def run(test, params, env): """ Test interafce xml options. 1.Prepare test environment,destroy or suspend a VM. 2.Edit xml and start the domain. 3.Perform test operation. 4.Recover test environment. 5.Confirm the test result. """ vm_name = params.get("main_vm") vm = env.get_vm(vm_name) def prepare_pxe_boot(): """ Prepare tftp server and pxe boot files """ pkg_list = ["syslinux", "tftp-server", "tftp", "ipxe-roms-qemu", "wget"] # Try to install required packages if not utils_package.package_install(pkg_list): test.error("Failed ot install required packages") boot_initrd = params.get("boot_initrd", "EXAMPLE_INITRD") boot_vmlinuz = params.get("boot_vmlinuz", "EXAMPLE_VMLINUZ") if boot_initrd.count("EXAMPLE") or boot_vmlinuz.count("EXAMPLE"): test.cancel("Please provide initrd/vmlinuz URL") # Download pxe boot images process.system("wget %s -O %s/initrd.img" % (boot_initrd, tftp_root)) process.system("wget %s -O %s/vmlinuz" % (boot_vmlinuz, tftp_root)) process.system("cp -f /usr/share/syslinux/pxelinux.0 {0};" " mkdir -m 777 -p {0}/pxelinux.cfg".format(tftp_root), shell=True) pxe_file = "%s/pxelinux.cfg/default" % tftp_root boot_txt = """ DISPLAY boot.txt DEFAULT rhel LABEL rhel kernel vmlinuz append initrd=initrd.img PROMPT 1 TIMEOUT 3""" with open(pxe_file, 'w') as p_file: p_file.write(boot_txt) def modify_iface_xml(): """ Modify interface xml options """ vmxml = vm_xml.VMXML.new_from_dumpxml(vm_name) if pxe_boot: # Config boot console for pxe boot osxml = vm_xml.VMOSXML() osxml.type = vmxml.os.type osxml.arch = vmxml.os.arch osxml.machine = vmxml.os.machine osxml.loader = "/usr/share/seabios/bios.bin" osxml.bios_useserial = "yes" osxml.bios_reboot_timeout = "-1" osxml.boots = ['network'] del vmxml.os vmxml.os = osxml xml_devices = vmxml.devices iface_index = xml_devices.index( xml_devices.by_device_tag("interface")[0]) iface = xml_devices[iface_index] iface_bandwidth = {} iface_inbound = ast.literal_eval(iface_bandwidth_inbound) iface_outbound = ast.literal_eval(iface_bandwidth_outbound) if iface_inbound: iface_bandwidth["inbound"] = iface_inbound if iface_outbound: iface_bandwidth["outbound"] = iface_outbound if iface_bandwidth: bandwidth = iface.new_bandwidth(**iface_bandwidth) iface.bandwidth = bandwidth iface_type = params.get("iface_type", "network") iface.type_name = iface_type source = ast.literal_eval(iface_source) if not source: source = {"network": "default"} net_ifs = utils_net.get_net_if(state="UP") # Check source device is valid or not, # if it's not in host interface list, try to set # source device to first active interface of host if (iface.type_name == "direct" and 'dev' in source and source['dev'] not in net_ifs): logging.warn("Source device %s is not a interface" " of host, reset to %s", source['dev'], net_ifs[0]) source['dev'] = net_ifs[0] del iface.source iface.source = source if iface_model: iface.model = iface_model if iface_rom: iface.rom = eval(iface_rom) if iface_boot: vmxml.remove_all_boots() iface.boot = iface_boot logging.debug("New interface xml file: %s", iface) vmxml.devices = xml_devices vmxml.xmltreefile.write() vmxml.sync() def run_dnsmasq_default_test(key, value=None, exists=True, name="default"): """ Test dnsmasq configuration. :param key: key in conf file to check :param value: value in conf file to check :param exists: check the key:value exist or not :param name: The name of conf file """ conf_file = "/var/lib/libvirt/dnsmasq/%s.conf" % name if not os.path.exists(conf_file): test.cancel("Can't find %s.conf file" % name) configs = "" with open(conf_file, 'r') as f: configs = f.read() logging.debug("configs in file %s: %s", conf_file, configs) if value: config = "%s=%s" % (key, value) else: config = key if not configs.count(config): if exists: test.fail("Can't find %s=%s in configuration file" % (key, value)) else: if not exists: test.fail("Found %s=%s in configuration file" % (key, value)) def run_dnsmasq_addnhosts_test(hostip, hostnames): """ Test host ip and names configuration """ conf_file = "/var/lib/libvirt/dnsmasq/default.addnhosts" hosts_re = ".*".join(hostnames) configs = "" with open(conf_file, 'r') as f: configs = f.read() logging.debug("configs in file %s: %s", conf_file, configs) if not re.search(r"%s.*%s" % (hostip, hosts_re), configs, re.M): test.fail("Can't find '%s' in configuration file" % hostip) def run_dnsmasq_host_test(iface_mac, guest_ip, guest_name): """ Test host name and ip configuration for dnsmasq """ conf_file = "/var/lib/libvirt/dnsmasq/default.hostsfile" config = "%s,%s,%s" % (iface_mac, guest_ip, guest_name) configs = "" with open(conf_file, 'r') as f: configs = f.read() logging.debug("configs in file %s: %s", conf_file, configs) if not configs.count(config): test.fail("Can't find host configuration in file %s" % conf_file) def check_class_rules(ifname, rule_id, bandwidth): """ Check bandwidth settings via 'tc class' output """ cmd = "tc class show dev %s" % ifname class_output = to_text(process.system_output(cmd)) logging.debug("Bandwidth class output: %s", class_output) class_pattern = (r"class htb %s.*rate (\d+)(K?M?)bit ceil" " (\d+)(K?M?)bit burst (\d+)(K?M?)b.*" % rule_id) se = re.search(class_pattern, class_output, re.M) if not se: test.fail("Can't find outbound setting for htb %s" % rule_id) logging.debug("bandwidth from tc output:%s" % str(se.groups())) rate = None if "floor" in bandwidth: rate = int(bandwidth["floor"]) * 8 elif "average" in bandwidth: rate = int(bandwidth["average"]) * 8 if rate: if se.group(2) == 'M': rate_check = int(se.group(1)) * 1000 else: rate_check = int(se.group(1)) assert rate_check == rate if "peak" in bandwidth: if se.group(4) == 'M': ceil_check = int(se.group(3)) * 1000 else: ceil_check = int(se.group(3)) assert ceil_check == int(bandwidth["peak"]) * 8 if "burst" in bandwidth: if se.group(6) == 'M': tc_burst = int(se.group(5)) * 1024 else: tc_burst = int(se.group(5)) assert tc_burst == int(bandwidth["burst"]) def check_filter_rules(ifname, bandwidth): """ Check bandwidth settings via 'tc filter' output """ cmd = "tc -d filter show dev %s parent ffff:" % ifname filter_output = to_text(process.system_output(cmd)) logging.debug("Bandwidth filter output: %s", filter_output) if not filter_output.count("filter protocol all pref"): test.fail("Can't find 'protocol all' settings in filter rules") filter_pattern = ".*police.*rate (\d+)(K?M?)bit burst (\d+)(K?M?)b.*" se = re.search(r"%s" % filter_pattern, filter_output, re.M) if not se: test.fail("Can't find any filter policy") logging.debug("bandwidth from tc output:%s" % str(se.groups())) logging.debug("bandwidth from setting:%s" % str(bandwidth)) if "average" in bandwidth: if se.group(2) == 'M': tc_average = int(se.group(1)) * 1000 else: tc_average = int(se.group(1)) assert tc_average == int(bandwidth["average"]) * 8 if "burst" in bandwidth: if se.group(4) == 'M': tc_burst = int(se.group(3)) * 1024 else: tc_burst = int(se.group(3)) assert tc_burst == int(bandwidth["burst"]) def check_host_routes(): """ Check network routes on host """ for rt in routes: try: route = ast.literal_eval(rt) addr = "%s/%s" % (route["address"], route["prefix"]) cmd = "ip route list %s" % addr if "family" in route and route["family"] == "ipv6": cmd = "ip -6 route list %s" % addr output = to_text(process.system_output(cmd)) match_obj = re.search(r"via (\S+).*metric (\d+)", output) if match_obj: via_addr = match_obj.group(1) metric = match_obj.group(2) logging.debug("via address %s for %s, matric is %s" % (via_addr, addr, metric)) assert via_addr == route["gateway"] if "metric" in route: assert metric == route["metric"] except KeyError: pass def run_bandwidth_test(check_net=False, check_iface=False): """ Test bandwidth option for network or interface by tc command. """ iface_inbound = ast.literal_eval(iface_bandwidth_inbound) iface_outbound = ast.literal_eval(iface_bandwidth_outbound) net_inbound = ast.literal_eval(net_bandwidth_inbound) net_outbound = ast.literal_eval(net_bandwidth_outbound) net_bridge_name = ast.literal_eval(net_bridge)["name"] iface_name = libvirt.get_ifname_host(vm_name, iface_mac) try: if check_net and net_inbound: # Check qdisc rules cmd = "tc -d qdisc show dev %s" % net_bridge_name qdisc_output = to_text(process.system_output(cmd)) logging.debug("Bandwidth qdisc output: %s", qdisc_output) if not qdisc_output.count("qdisc ingress ffff:"): test.fail("Can't find ingress setting") check_class_rules(net_bridge_name, "1:1", {"average": net_inbound["average"], "peak": net_inbound["peak"]}) check_class_rules(net_bridge_name, "1:2", net_inbound) # Check filter rules on bridge interface if check_net and net_outbound: check_filter_rules(net_bridge_name, net_outbound) # Check class rules on interface inbound settings if check_iface and iface_inbound: check_class_rules(iface_name, "1:1", {'average': iface_inbound['average'], 'peak': iface_inbound['peak'], 'burst': iface_inbound['burst']}) if "floor" in iface_inbound: if not libvirt_version.version_compare(1, 0, 1): test.cancel("Not supported Qos options 'floor'") check_class_rules(net_bridge_name, "1:3", {'floor': iface_inbound["floor"]}) # Check filter rules on interface outbound settings if check_iface and iface_outbound: check_filter_rules(iface_name, iface_outbound) except AssertionError: stacktrace.log_exc_info(sys.exc_info()) test.fail("Failed to check network bandwidth") def check_name_ip(session): """ Check dns resolving on guest """ # Check if bind-utils is installed if "ubuntu" in vm.get_distro().lower(): pkg = "bind9" else: pkg = "bind-utils" if not utils_package.package_install(pkg, session): test.error("Failed to install bind-utils on guest") # Run host command to check if hostname can be resolved if not guest_ipv4 and not guest_ipv6: test.fail("No ip address found from parameters") guest_ip = guest_ipv4 if guest_ipv4 else guest_ipv6 cmd = "host %s | grep %s" % (guest_name, guest_ip) if session.cmd_status(cmd): test.fail("Can't resolve name %s on guest" % guest_name) def check_ipt_rules(check_ipv4=True, check_ipv6=False): """ Check iptables for network/interface """ br_name = ast.literal_eval(net_bridge)["name"] net_forward = ast.literal_eval(params.get("net_forward", "{}")) net_ipv4 = params.get("net_ipv4") net_ipv6 = params.get("net_ipv6") net_dev_in = "" net_dev_out = "" if "dev" in net_forward: net_dev_in = " -i %s" % net_forward["dev"] net_dev_out = " -o %s" % net_forward["dev"] ipt_rules = ( "INPUT -i %s -p udp -m udp --dport 53 -j ACCEPT" % br_name, "INPUT -i %s -p tcp -m tcp --dport 53 -j ACCEPT" % br_name, "FORWARD -i {0} -o {0} -j ACCEPT".format(br_name), "FORWARD -o %s -j REJECT --reject-with icmp" % br_name, "FORWARD -i %s -j REJECT --reject-with icmp" % br_name) if check_ipv4: ipv4_rules = list(ipt_rules) ipv4_rules.extend( ["INPUT -i %s -p udp -m udp --dport 67 -j ACCEPT" % br_name, "INPUT -i %s -p tcp -m tcp --dport 67 -j ACCEPT" % br_name, "OUTPUT -o %s -p udp -m udp --dport 68 -j ACCEPT" % br_name, "POSTROUTING -o %s -p udp -m udp --dport 68 " "-j CHECKSUM --checksum-fill" % br_name]) ctr_rule = "" nat_rules = [] if "mode" in net_forward and net_forward["mode"] == "nat": nat_port = ast.literal_eval(params.get("nat_port")) p_start = nat_port["start"] p_end = nat_port["end"] ctr_rule = " -m .* RELATED,ESTABLISHED" nat_rules = [("POSTROUTING -s {0} ! -d {0} -p tcp -j MASQUERADE" " --to-ports {1}-{2}".format(net_ipv4, p_start, p_end)), ("POSTROUTING -s {0} ! -d {0} -p udp -j MASQUERADE" " --to-ports {1}-{2}".format(net_ipv4, p_start, p_end)), ("POSTROUTING -s {0} ! -d {0}" " -j MASQUERADE".format(net_ipv4))] if nat_rules: ipv4_rules.extend(nat_rules) if (net_ipv4 and "mode" in net_forward and net_forward["mode"] in ["nat", "route"]): rules = [("FORWARD -d %s%s -o %s%s -j ACCEPT" % (net_ipv4, net_dev_in, br_name, ctr_rule)), ("FORWARD -s %s -i %s%s -j ACCEPT" % (net_ipv4, br_name, net_dev_out))] ipv4_rules.extend(rules) output = to_text(process.system_output('iptables-save')) logging.debug("iptables: %s", output) if "mode" in net_forward and net_forward["mode"] == "open": if re.search(r"%s|%s" % (net_ipv4, br_name), output, re.M): test.fail("Find iptable rule for open mode") utils_libvirtd.libvirtd_restart() output_again = to_text(process.system_output('iptables-save')) if re.search(r"%s|%s" % (net_ipv4, br_name), output_again, re.M): test.fail("Find iptable rule for open mode after restart " "libvirtd") else: logging.info("Can't find iptable rule for open mode as expected") else: for ipt in ipv4_rules: if not re.search(r"%s" % ipt, output, re.M): test.fail("Can't find iptable rule:\n%s" % ipt) return ipv4_rules if check_ipv6: ipv6_rules = list(ipt_rules) ipt6_rules.extend([ ("INPUT -i %s -p udp -m udp --dport 547 -j ACCEPT" % br_name)]) if (net_ipv6 and "mode" in net_forward and net_forward["mode"] in ["nat", "route"]): rules = [("FORWARD -d %s%s -o %s -j ACCEPT" % (net_ipv6, net_dev_in, br_name)), ("FORWARD -s %s -i %s%s -j ACCEPT" % (net_ipv6, br_name, net_dev_out))] ipv6_rules.extend(rules) output = to_text(process.system_output("ip6tables-save")) logging.debug("ip6tables: %s", output) if "mode" in net_forward and net_forward["mode"] == "open": if re.search(r"%s|%s" % (net_ipv6, br_name), output, re.M): test.fail("Find ip6table rule for open mode") utils_libvirtd.libvirtd_restart() output_again = to_text(process.system_output('ip6tables-save')) if re.search(r"%s|%s" % (net_ipv6, br_name), output_again, re.M): test.fail("Find ip6table rule for open mode after restart " "libvirtd") else: for ipt in ipv6_rules: if not re.search(r"%s" % ipt, output, re.M): test.fail("Can't find ip6table rule:\n%s" % ipt) return ipv6_rules def run_ip_test(session, ip_ver): """ Check iptables on host and ipv6 address on guest """ if ip_ver == "ipv6": # Clean up iptables rules for guest to get ipv6 address session.cmd_status("ip6tables -F") # It may take some time to get the ip address def get_ip_func(): return utils_net.get_guest_ip_addr(session, iface_mac, ip_version=ip_ver) utils_misc.wait_for(get_ip_func, 5) if not get_ip_func(): utils_net.restart_guest_network(session, iface_mac, ip_version=ip_ver) utils_misc.wait_for(get_ip_func, 5) vm_ip = get_ip_func() logging.debug("Guest has ip: %s", vm_ip) if not vm_ip: test.fail("Can't find ip address on guest") ip_gateway = net_ip_address if ip_ver == "ipv6": ip_gateway = net_ipv6_address # Cleanup ip6talbes on host for ping6 test process.system("ip6tables -F") if ip_gateway and not routes: ping_s, _ = ping(dest=ip_gateway, count=5, timeout=10, session=session) if ping_s: test.fail("Failed to ping gateway address: %s" % ip_gateway) def run_guest_libvirt(session): """ Check guest libvirt network """ # Try to install required packages if "ubuntu" in vm.get_distro().lower(): pkg = "libvirt-bin" else: pkg = "libvirt" if not utils_package.package_install(pkg, session): test.error("Failed to install libvirt package on guest") # Try to load tun module first session.cmd("lsmod | grep tun || modprobe tun") # Check network state on guest cmd = ("service libvirtd restart; virsh net-info default" " | grep 'Active:.*yes'") if session.cmd_status(cmd): test.fail("'default' network isn't in active state") # Try to destroy&start default network on guest for opt in ['net-destroy', 'net-start']: cmd = "virsh %s default" % opt status, output = session.cmd_status_output(cmd) logging.debug("Run %s on guest exit %s, output %s" % (cmd, status, output)) if status: test.fail(output) if not utils_package.package_remove("libvirt*", session): test.error("Failed to remove libvirt packages on guest") start_error = "yes" == params.get("start_error", "no") define_error = "yes" == params.get("define_error", "no") restart_error = "yes" == params.get("restart_error", "no") # network specific attributes. net_name = params.get("net_name", "default") net_bridge = params.get("net_bridge", "{'name':'virbr0'}") net_domain = params.get("net_domain") net_ip_address = params.get("net_ip_address") net_ipv6_address = params.get("net_ipv6_address") net_dns_forward = params.get("net_dns_forward") net_dns_txt = params.get("net_dns_txt") net_dns_srv = params.get("net_dns_srv") net_dns_hostip = params.get("net_dns_hostip") net_dns_hostnames = params.get("net_dns_hostnames", "").split() dhcp_start_ipv4 = params.get("dhcp_start_ipv4") dhcp_end_ipv4 = params.get("dhcp_end_ipv4") dhcp_start_ipv6 = params.get("dhcp_start_ipv6") dhcp_end_ipv6 = params.get("dhcp_end_ipv6") guest_name = params.get("guest_name") guest_ipv4 = params.get("guest_ipv4") guest_ipv6 = params.get("guest_ipv6") tftp_root = params.get("tftp_root") pxe_boot = "yes" == params.get("pxe_boot", "no") routes = params.get("routes", "").split() net_bandwidth_inbound = params.get("net_bandwidth_inbound", "{}") net_bandwidth_outbound = params.get("net_bandwidth_outbound", "{}") iface_bandwidth_inbound = params.get("iface_bandwidth_inbound", "{}") iface_bandwidth_outbound = params.get("iface_bandwidth_outbound", "{}") iface_num = params.get("iface_num", "1") iface_source = params.get("iface_source", "{}") iface_rom = params.get("iface_rom") iface_boot = params.get("iface_boot") iface_model = params.get("iface_model") multiple_guests = params.get("multiple_guests") create_network = "yes" == params.get("create_network", "no") attach_iface = "yes" == params.get("attach_iface", "no") serial_login = "******" == params.get("serial_login", "no") change_iface_option = "yes" == params.get("change_iface_option", "no") test_bridge = "yes" == params.get("test_bridge", "no") test_dnsmasq = "yes" == params.get("test_dnsmasq", "no") test_dhcp_range = "yes" == params.get("test_dhcp_range", "no") test_dns_host = "yes" == params.get("test_dns_host", "no") test_qos_bandwidth = "yes" == params.get("test_qos_bandwidth", "no") test_pg_bandwidth = "yes" == params.get("test_portgroup_bandwidth", "no") test_qos_remove = "yes" == params.get("test_qos_remove", "no") test_ipv4_address = "yes" == params.get("test_ipv4_address", "no") test_ipv6_address = "yes" == params.get("test_ipv6_address", "no") test_guest_libvirt = "yes" == params.get("test_guest_libvirt", "no") net_no_bridge = "yes" == params.get("no_bridge", "no") net_no_mac = "yes" == params.get("no_mac", "no") net_no_ip = "yes" == params.get("no_ip", "no") net_with_dev = "yes" == params.get("with_dev", "no") username = params.get("username") password = params.get("password") forward = ast.literal_eval(params.get("net_forward", "{}")) boot_failure = "yes" == params.get("boot_failure", "no") ipt_rules = [] ipt6_rules = [] # Destroy VM first if vm.is_alive(): vm.destroy(gracefully=False) # Back up xml file. netxml_backup = NetworkXML.new_from_net_dumpxml("default") iface_mac = vm_xml.VMXML.get_first_mac_by_name(vm_name) params["guest_mac"] = iface_mac vmxml_backup = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) vms_list = [] if "floor" in ast.literal_eval(iface_bandwidth_inbound): if not libvirt_version.version_compare(1, 0, 1): test.cancel("Not supported Qos options 'floor'") # Enabling IPv6 forwarding with RA routes without accept_ra set to 2 # is likely to cause routes loss sysctl_cmd = 'sysctl net.ipv6.conf.all.accept_ra' original_accept_ra = to_text(process.system_output(sysctl_cmd + ' -n')) if test_ipv6_address and original_accept_ra != '2': process.system(sysctl_cmd + '=2') # Build the xml and run test. try: if test_dnsmasq: # Check the settings before modifying network xml if net_dns_forward == "no": run_dnsmasq_default_test("domain-needed", exists=False) run_dnsmasq_default_test("local", "//", exists=False) if net_domain: run_dnsmasq_default_test("domain", net_domain, exists=False) run_dnsmasq_default_test("expand-hosts", exists=False) # Prepare pxe boot directory if pxe_boot: prepare_pxe_boot() # Edit the network xml or create a new one. if create_network: net_ifs = utils_net.get_net_if(state="UP") # Check forward device is valid or not, # if it's not in host interface list, try to set # forward device to first active interface of host if ('mode' in forward and forward['mode'] in ['passthrough', 'private', 'bridge', 'macvtap'] and 'dev' in forward and forward['dev'] not in net_ifs): logging.warn("Forward device %s is not a interface" " of host, reset to %s", forward['dev'], net_ifs[0]) forward['dev'] = net_ifs[0] params["net_forward"] = str(forward) forward_iface = params.get("forward_iface") if forward_iface: interface = [x for x in forward_iface.split()] # The guest will use first interface of the list, # check if it's valid or not, if it's not in host # interface list, try to set forward interface to # first active interface of host. if interface[0] not in net_ifs: logging.warn("Forward interface %s is not a " " interface of host, reset to %s", interface[0], net_ifs[0]) interface[0] = net_ifs[0] params["forward_iface"] = " ".join(interface) netxml = libvirt.create_net_xml(net_name, params) if "mode" in forward and forward["mode"] == "open": netxml.mac = utils_net.generate_mac_address_simple() try: if net_no_bridge: netxml.del_bridge() if net_no_ip: netxml.del_ip() netxml.del_ip() if net_no_mac: netxml.del_mac() except xcepts.LibvirtXMLNotFoundError: pass if net_with_dev: net_forward = netxml.forward net_forward.update({"dev": net_ifs[0]}) netxml.forward = net_forward logging.info("netxml before define is %s", netxml) try: netxml.sync() except xcepts.LibvirtXMLError as details: logging.info(str(details)) if define_error: return else: test.fail("Failed to define network") # Check open mode network xml if "mode" in forward and forward["mode"] == "open": netxml_new = NetworkXML.new_from_net_dumpxml(net_name) logging.info("netxml after define is %s", netxml_new) try: if net_no_bridge: net_bridge = str(netxml_new.bridge) if net_no_mac: netxml_new.mac except xcepts.LibvirtXMLNotFoundError as details: test.fail("Failed to check %s xml: %s" % (net_name, details)) logging.info("mac/bridge still exist even if removed before define") # Edit the interface xml. if change_iface_option: try: modify_iface_xml() except xcepts.LibvirtXMLError as details: logging.info(str(details)) if define_error: if not str(details).count("Failed to define"): test.fail("VM sync failed msg not expected") return else: test.fail("Failed to sync VM") # Attach interface if needed if attach_iface: iface_type = params.get("iface_type", "network") for i in range(int(iface_num)): logging.info("Try to attach interface loop %s" % i) options = ("%s %s --model %s --config" % (iface_type, net_name, iface_model)) ret = virsh.attach_interface(vm_name, options, ignore_status=True) if ret.exit_status: logging.error("Command output %s" % ret.stdout.strip()) test.fail("Failed to attach-interface") if multiple_guests: # Clone more vms for testing for i in range(int(multiple_guests)): guest_name = "%s_%s" % (vm_name, i) timeout = params.get("clone_timeout", 360) utils_libguestfs.virt_clone_cmd(vm_name, guest_name, True, timeout=timeout) vms_list.append(vm.clone(guest_name)) if test_bridge: bridge = ast.literal_eval(net_bridge) br_if = utils_net.Interface(bridge['name']) if not br_if.is_up(): test.fail("Bridge interface isn't up") if test_dnsmasq: # Check dnsmasq process dnsmasq_cmd = to_text(process.system_output("ps -aux|grep dnsmasq", shell=True)) logging.debug(dnsmasq_cmd) if not re.search("dnsmasq --conf-file=/var/lib/libvirt/dnsmasq/%s.conf" % net_name, dnsmasq_cmd): test.fail("Can not find dnsmasq process or the process is not correct") # Check the settings in dnsmasq config file if net_dns_forward == "no": run_dnsmasq_default_test("domain-needed") run_dnsmasq_default_test("local", "//") if net_domain: run_dnsmasq_default_test("domain", net_domain) run_dnsmasq_default_test("expand-hosts") if net_bridge: bridge = ast.literal_eval(net_bridge) run_dnsmasq_default_test("interface", bridge['name'], name=net_name) if 'stp' in bridge and bridge['stp'] == 'on': if 'delay' in bridge and bridge['delay'] != '0': # network xml forward delay value in seconds, while on # host, check by ip command, the value is in second*100 br_delay = int(bridge['delay'])*100 logging.debug("Expect forward_delay is %s ms" % br_delay) cmd = ("ip -d link sh %s | grep 'bridge forward_delay'" % bridge['name']) out = to_text(process.system_output( cmd, shell=True, ignore_status=False)) logging.debug("bridge statistics output: %s", out) pattern = (r"\s*bridge forward_delay\s+(\d+)") match_obj = re.search(pattern, out, re.M) if not match_obj: test.fail("Can't see forward delay messages from command") elif int(match_obj.group(1)) != br_delay: test.fail("Foward delay setting can't take effect") else: logging.debug("Foward delay set successfully!") if dhcp_start_ipv4 and dhcp_end_ipv4: run_dnsmasq_default_test("dhcp-range", "%s,%s" % (dhcp_start_ipv4, dhcp_end_ipv4), name=net_name) if dhcp_start_ipv6 and dhcp_end_ipv6: run_dnsmasq_default_test("dhcp-range", "%s,%s,64" % (dhcp_start_ipv6, dhcp_end_ipv6), name=net_name) if guest_name and guest_ipv4: run_dnsmasq_host_test(iface_mac, guest_ipv4, guest_name) # check the left part in dnsmasq conf run_dnsmasq_default_test("strict-order", name=net_name) run_dnsmasq_default_test("pid-file", "/var/run/libvirt/network/%s.pid" % net_name, name=net_name) run_dnsmasq_default_test("except-interface", "lo", name=net_name) run_dnsmasq_default_test("bind-dynamic", name=net_name) run_dnsmasq_default_test("dhcp-no-override", name=net_name) if dhcp_start_ipv6 and dhcp_start_ipv4: run_dnsmasq_default_test("dhcp-lease-max", "493", name=net_name) else: range_num = int(params.get("dhcp_range", "252")) run_dnsmasq_default_test("dhcp-lease-max", str(range_num+1), name=net_name) run_dnsmasq_default_test("dhcp-hostsfile", "/var/lib/libvirt/dnsmasq/%s.hostsfile" % net_name, name=net_name) run_dnsmasq_default_test("addn-hosts", "/var/lib/libvirt/dnsmasq/%s.addnhosts" % net_name, name=net_name) if dhcp_start_ipv6: run_dnsmasq_default_test("enable-ra", name=net_name) if test_dns_host: if net_dns_txt: dns_txt = ast.literal_eval(net_dns_txt) run_dnsmasq_default_test("txt-record", "%s,%s" % (dns_txt["name"], dns_txt["value"])) if net_dns_srv: dns_srv = ast.literal_eval(net_dns_srv) run_dnsmasq_default_test("srv-host", "_%s._%s.%s,%s,%s,%s,%s" % (dns_srv["service"], dns_srv["protocol"], dns_srv["domain"], dns_srv["target"], dns_srv["port"], dns_srv["priority"], dns_srv["weight"])) if net_dns_hostip and net_dns_hostnames: run_dnsmasq_addnhosts_test(net_dns_hostip, net_dns_hostnames) # Run bandwidth test for network if test_qos_bandwidth: run_bandwidth_test(check_net=True) # Check routes if needed if routes: check_host_routes() try: # Start the VM. vm.start() if start_error: test.fail("VM started unexpectedly") if pxe_boot: # Just check network boot messages here try: vm.serial_console.read_until_output_matches( ["Loading vmlinuz", "Loading initrd.img"], utils_misc.strip_console_codes) output = vm.serial_console.get_stripped_output() logging.debug("Boot messages: %s", output) except ExpectTimeoutError as details: if boot_failure: logging.info("Fail to boot from pxe as expected") else: test.fail("Fail to boot from pxe") else: if serial_login: session = vm.wait_for_serial_login(username=username, password=password) else: session = vm.wait_for_login() if test_dhcp_range: dhcp_range = int(params.get("dhcp_range", "252")) utils_net.restart_guest_network(session, iface_mac) vm_ip = utils_net.get_guest_ip_addr(session, iface_mac) logging.debug("Guest has ip: %s", vm_ip) if not vm_ip and dhcp_range: test.fail("Guest has invalid ip address") elif vm_ip and not dhcp_range: test.fail("Guest has ip address: %s" % vm_ip) dhcp_range = dhcp_range - 1 for vms in vms_list: # Start other VMs. vms.start() sess = vms.wait_for_serial_login() vms_mac = vms.get_virsh_mac_address() # restart guest network to get ip addr utils_net.restart_guest_network(sess, vms_mac) vms_ip = utils_net.get_guest_ip_addr(sess, vms_mac) if not vms_ip and dhcp_range: test.fail("Guest has invalid ip address") elif vms_ip and not dhcp_range: # Get IP address on guest should return Null # if it exceeds the dhcp range test.fail("Guest has ip address: %s" % vms_ip) dhcp_range = dhcp_range - 1 if vms_ip: ping_s, _ = ping(dest=vm_ip, count=5, timeout=10, session=sess) if ping_s: test.fail("Failed to ping, src: %s, " "dst: %s" % (vms_ip, vm_ip)) sess.close() # Check dnsmasq settings if take affect in guest if guest_ipv4: check_name_ip(session) # Run bandwidth test for interface if test_qos_bandwidth: run_bandwidth_test(check_iface=True) # Run bandwidth test for portgroup if test_pg_bandwidth: pg_bandwidth_inbound = params.get( "portgroup_bandwidth_inbound", "").split() pg_bandwidth_outbound = params.get( "portgroup_bandwidth_outbound", "").split() pg_name = params.get("portgroup_name", "").split() pg_default = params.get("portgroup_default", "").split() iface_inbound = ast.literal_eval(iface_bandwidth_inbound) iface_outbound = ast.literal_eval(iface_bandwidth_outbound) iface_name = libvirt.get_ifname_host(vm_name, iface_mac) if_source = ast.literal_eval(iface_source) if "portgroup" in if_source: pg = if_source["portgroup"] else: pg = "default" for (name, df, bw_ib, bw_ob) in zip(pg_name, pg_default, pg_bandwidth_inbound, pg_bandwidth_outbound): if pg == name: inbound = ast.literal_eval(bw_ib) outbound = ast.literal_eval(bw_ob) elif pg == "default" and df == "yes": inbound = ast.literal_eval(bw_ib) outbound = ast.literal_eval(bw_ob) else: continue # Interface bandwidth settings will # overwriting portgroup settings if iface_inbound: inbound = iface_inbound if iface_outbound: outbound = iface_outbound check_class_rules(iface_name, "1:1", inbound) check_filter_rules(iface_name, outbound) if test_qos_remove: # Remove the bandwidth settings in network xml logging.debug("Removing network bandwidth settings...") netxml_backup.sync() vm.destroy(gracefully=False) # Should fail to start vm vm.start() if restart_error: test.fail("VM started unexpectedly") if test_ipv6_address: ipt6_rules = check_ipt_rules(check_ipv4=False, check_ipv6=True) if not ("mode" in forward and forward["mode"] == "open"): run_ip_test(session, "ipv6") if test_ipv4_address: ipt_rules = check_ipt_rules(check_ipv4=True) if not ("mode" in forward and forward["mode"] == "open"): run_ip_test(session, "ipv4") if test_guest_libvirt: run_guest_libvirt(session) session.close() except virt_vm.VMStartError as details: logging.info(str(details)) if not (start_error or restart_error): test.fail('VM failed to start:\n%s' % details) # Destroy created network and check iptable rules if net_name != "default": virsh.net_destroy(net_name) if ipt_rules: output_des = to_text(process.system_output('iptables-save')) for ipt in ipt_rules: if re.search(r"%s" % ipt, output_des, re.M): test.fail("Find iptable rule %s after net destroyed" % ipt) if ipt6_rules: output_des = to_text(process.system_output('ip6tables-save')) for ipt in ipt6_rules: if re.search(r"%s" % ipt, output_des, re.M): test.fail("Find ip6table rule %s after net destroyed" % ipt) finally: # Recover VM. if vm.is_alive(): vm.destroy(gracefully=False) for vms in vms_list: virsh.remove_domain(vms.name, "--remove-all-storage") logging.info("Restoring network...") if net_name == "default": netxml_backup.sync() else: # Destroy and undefine new created network virsh.net_destroy(net_name) virsh.net_undefine(net_name) vmxml_backup.sync() if test_ipv6_address and original_accept_ra != '2': process.system(sysctl_cmd + "=%s" % original_accept_ra)
def run(test, params, env): """ cpu, memory, network, disk limit test: 1) prepare the guest with given topology, memory and if any devices 2) Start and login to the guest 3) Check if the guest functional 4) if given run some stress test :param test: QEMU test object :param params: Dictionary with the test parameters :param env: Dictionary with test environment. TODO: 1. Add multiple pci-bridge and pci devices respectively. 2. Get no. VM interfaces at test start and use for validation. """ failures = {"Failed to allocate KVM HPT of order": "Host does no have enough cma to boot, try increasing \ kvm_cma_resv_ratio in host boot", "unable to map backing store for guest RAM": "Not enough memory in the host to boot the guest"} vm_name = params.get("main_vm") max_vcpu = current_vcpu = int(params.get("max_vcpu", 240)) vm_cores = int(params.get("limit_vcpu_cores", 240)) vm_threads = int(params.get("limit_vcpu_threads", 1)) vm_sockets = int(params.get("limit_vcpu_sockets", 1)) usermaxmem = params.get("usermaxmem", '') default_mem = int(params.get("default_mem", 8)) maxmem = params.get("maxmem", "no") == "yes" swap_setup = params.get("swap_setup", "yes") == "yes" blk_partition = params.get("blk_part", '') graphic = params.get("graphics", "no") == "yes" vm = env.get_vm(vm_name) guestmemory = None max_network = params.get("max_network", "no") == "yes" max_disk = params.get("max_disk", "no") == "yes" num_network = int(params.get("num_network", 16)) num_disk = int(params.get("num_disk", 16)) drive_format = params.get("drive_format", "scsi") disk_format = params.get("disk_format", "qcow2") netdst = params.get("netdst", "virbr0") memunit = params.get("memunit", 'G') failed = False vmxml = libvirt_xml.VMXML.new_from_inactive_dumpxml(vm_name) org_xml = vmxml.copy() # Destroy the vm vm.destroy() try: # Setup swap if swap_setup: if not blk_partition: test.cancel("block partition is not given") # Check if valid partition or raise cmd = "blkid /dev/%s|awk -F" " '{print $2}'" % blk_partition output = process.system_output(cmd, shell=True) if "UUID" not in output: test.cancel("Not a valid partition given for swap creation") # Create swap partition cmd = "mkswap /dev/%s" % blk_partition process.system(cmd, shell=True) cmd = "swapon /dev/%s" % blk_partition process.system(cmd, shell=True) # Check for host memory and cpu levels and validate against # requested limits, allow to max of 10X for CPU and 2.5X for memory host_memory = int(memory.rounded_memtotal()) if maxmem: if usermaxmem: guestmemory = usermaxmem else: # Normalize to GB guestmemory = int(2.5 * host_memory/(1024 * 1024)) else: pass if not guestmemory: # assign default memory guestmemory = default_mem # Set the current and max memory params vmxml.current_mem_unit = memunit vmxml.max_mem_unit = memunit vmxml.current_mem = int(guestmemory) vmxml.max_mem = int(guestmemory) vmxml.sync() # Set vcpu and topology libvirt_xml.VMXML.set_vm_vcpus(vm_name, max_vcpu, current_vcpu, vm_sockets, vm_cores, vm_threads) vmxml = libvirt_xml.VMXML.new_from_inactive_dumpxml(vm_name) # Set vnc display as needed graphics = vmxml.get_device_class('graphics')() if graphic: if not vmxml.get_graphics_devices("vnc"): graphics.add_graphic(vm_name, graphic="vnc") else: if vmxml.get_graphics_devices("vnc"): graphics.del_graphic(vm_name) vmxml = libvirt_xml.VMXML.new_from_inactive_dumpxml(vm_name) network_str = None disk_str = None # Set network devices if max_network: network_str = "ip link|grep ^[1-9]|wc -l" for idx in range(num_network): network = Interface(type_name="bridge") network.mac_address = utils_net.generate_mac_address_simple() network.source = {"bridge": netdst} vmxml.add_device(network) # Set disk devices if max_disk: for idx in range(num_disk): disk_str = "lsblk|grep ^[s,v]|grep 1G|wc -l" disk = Disk() disk_path = os.path.join(data_dir.get_data_dir(), "images", "%s.qcow2" % idx) if "scsi" in drive_format: drive_format = "scsi" disk_target = "sd%s" % letters[(idx % 51)+1] else: drive_format = "virtio" disk_target = "vd%s" % letters[(idx % 51)+1] disk_source = libvirt.create_local_disk("file", disk_path, '1', "qcow2") disk.device = "disk" disk.source = disk.new_disk_source(**{"attrs": {'file': disk_source}}) disk.target = {"dev": disk_target, "bus": drive_format} disk.driver = {"name": "qemu", 'type': disk_format} vmxml.add_device(disk) vmxml.sync() # Start VM logging.debug("VM XML: \n%s", vmxml) try: vm.start() except virt_vm.VMStartError, detail: for msg in failures.items(): if msg[0] in detail: test.cancel("%s", msg[1]) test.fail("%s" % detail) # Check the memory and vcpu inside guest memtotal = vm.get_totalmem_sys() cpucount = vm.get_cpu_count() session = vm.wait_for_login() if network_str: guestnetworks = int(session.cmd_output(network_str)) logging.debug("guestnet: %d", guestnetworks) if (guestnetworks - 2) != num_network: failed = True logging.error("mismatch in guest network devices: \n" "Expected: %d\nActual: %d", num_network, guestnetworks) if disk_str: guestdisks = int(session.cmd_output(disk_str)) logging.debug("guestdisk: %d", guestdisks) if guestdisks != num_disk: failed = True logging.error("mismatch in guest disk devices: \n" "Expected: %d\nActual: %s", num_disk, guestdisks) session.close() guestmem = utils_misc.normalize_data_size("%s G" % guestmemory) # TODO:512 MB threshold deviation value, need to normalize if int(float(guestmem) - memtotal) > 512: failed = True logging.error("mismatch in guest memory: \nExpected: " "%s\nActual: %s", float(guestmem), memtotal) if cpucount != current_vcpu: failed = True logging.error("mismatch in guest vcpu:\nExpected: %d\nActual: " "%d", current_vcpu, cpucount) if failed: test.fail("Consult previous failures")
def run_virsh_attach_detach_interface(test, params, env): """ Test virsh {at|de}tach-interface command. 1) Prepare test environment and its parameters 2) Attach the required interface 3) According test type(only attach or both attach and detach): a.Go on to test detach(if attaching is correct) b.Return GOOD or raise TestFail(if attaching is wrong) 4) Check if attached interface is correct: a.Try to catch it in vm's XML file b.Try to catch it in vm 5) Detach the attached interface 6) Check result """ vm_name = params.get("main_vm") vm = env.get_vm(vm_name) # Test parameters uri = libvirt_vm.normalize_connect_uri(params.get("connect_uri", "default")) vm_ref = params.get("at_detach_iface_vm_ref", "domname") options_suffix = params.get("at_detach_iface_options_suffix", "") status_error = "yes" == params.get("status_error", "no") start_vm = params.get("start_vm") # Should attach must be pass for detach test. correct_attach = "yes" == params.get("correct_attach", "no") # Interface specific attributes. iface_type = params.get("at_detach_iface_type", "network") iface_source = params.get("at_detach_iface_source", "default") iface_mac = params.get("at_detach_iface_mac", "created") virsh_dargs = {'ignore_status': True, 'uri': uri} # Get a bridge name for test if iface_type is bridge. # If there is no bridge other than virbr0, raise TestNAError if iface_type == "bridge": host_bridge = utils_net.Bridge() bridge_list = host_bridge.list_br() try: bridge_list.remove("virbr0") except AttributeError: pass # If no virbr0, just pass is ok logging.debug("Useful bridges:%s", bridge_list) # just choosing one bridge on host. if len(bridge_list): iface_source = bridge_list[0] else: raise error.TestNAError("No useful bridge on host " "other than 'virbr0'.") dom_uuid = vm.get_uuid() dom_id = vm.get_id() # To confirm vm's state if start_vm == "no" and vm.is_alive(): vm.destroy() # Test both detach and attach, So collect info # both of them for result check. # When something wrong with interface, set it to 1 fail_flag = 0 result_info = [] # Set attach-interface domain if vm_ref == "domname": vm_ref = vm_name elif vm_ref == "domid": vm_ref = dom_id elif vm_ref == "domuuid": vm_ref = dom_uuid elif vm_ref == "hexdomid" and dom_id is not None: vm_ref = hex(int(dom_id)) # Get a mac address if iface_mac is 'created'. if iface_mac == "created" or correct_attach: iface_mac = utils_net.generate_mac_address_simple() # Set attach-interface options and Start attach-interface test if correct_attach: options = set_options("network", "default", iface_mac, "", "attach") attach_result = virsh.attach_interface(vm_name, options, **virsh_dargs) else: options = set_options(iface_type, iface_source, iface_mac, options_suffix, "attach") attach_result = virsh.attach_interface(vm_ref, options, **virsh_dargs) attach_status = attach_result.exit_status logging.debug(attach_result) # If attach interface failed. if attach_status: if not status_error: fail_flag = 1 result_info.append("Attach Failed: %s" % attach_result) elif status_error: # Here we just use it to exit, do not mean test failed fail_flag = 1 # If attach interface succeeded. else: if status_error and not correct_attach: fail_flag = 1 result_info.append("Attach Success with wrong command.") if fail_flag and start_vm == "yes": vm.destroy() if len(result_info): raise error.TestFail(result_info) else: # Exit because it is error_test for attach-interface. return # Check dumpxml file whether the interface is added successfully. status, ret = check_dumpxml_iface( vm_name, iface_mac, iface_type, iface_source) if status: fail_flag = 1 result_info.append(ret) # Login to domain to check new interface. if not vm.is_alive(): vm.start() elif vm.state() == "paused": vm.resume() status, ret = login_to_check(vm, iface_mac) if status: fail_flag = 1 result_info.append(ret) # Set detach-interface options options = set_options(iface_type, None, iface_mac, options_suffix, "detach") # Start detach-interface test detach_result = virsh.detach_interface(vm_ref, options, **virsh_dargs) detach_status = detach_result.exit_status logging.debug(detach_result) # Clean up. if check_dumpxml_iface(vm_name, iface_mac) is not None: cleanup_options = "--type %s --mac %s" % (iface_type, iface_mac) virsh.detach_interface(vm_ref, cleanup_options, **virsh_dargs) # Shutdown vm to be afraid of cleaning up failed if vm.is_alive(): vm.destroy() # Check results. if status_error: if detach_status == 0: raise error.TestFail("Detach Success with wrong command.") else: if detach_status != 0: raise error.TestFail("Detach Failed.") else: if fail_flag: raise error.TestFail("Attach-Detach Success but " "something wrong with its " "functional use:%s" % result_info)
def run(test, params, env): """ Test interafce xml options. 1.Prepare test environment,destroy or suspend a VM. 2.Edit xml and start the domain. 3.Perform test operation. 4.Recover test environment. 5.Confirm the test result. """ vm_name = params.get("main_vm") vm = env.get_vm(vm_name) virsh_dargs = {'debug': True, 'ignore_status': False} def create_iface_xml(iface_mac): """ Create interface xml file """ iface = Interface(type_name=iface_type) source = ast.literal_eval(iface_source) if source: iface.source = source iface.model = iface_model if iface_model else "virtio" iface.mac_address = iface_mac driver_dict = {} driver_host = {} driver_guest = {} if iface_driver: driver_dict = ast.literal_eval(iface_driver) if iface_driver_host: driver_host = ast.literal_eval(iface_driver_host) if iface_driver_guest: driver_guest = ast.literal_eval(iface_driver_guest) iface.driver = iface.new_driver(driver_attr=driver_dict, driver_host=driver_host, driver_guest=driver_guest) logging.debug("Create new interface xml: %s", iface) return iface def modify_iface_xml(update, status_error=False): """ Modify interface xml options """ vmxml = vm_xml.VMXML.new_from_dumpxml(vm_name) xml_devices = vmxml.devices iface_index = xml_devices.index( xml_devices.by_device_tag("interface")[0]) iface = xml_devices[iface_index] if iface_model: iface.model = iface_model else: del iface.model if iface_type: iface.type_name = iface_type del iface.source source = ast.literal_eval(iface_source) if source: net_ifs = utils_net.get_net_if(state="UP") # Check source device is valid or not, # if it's not in host interface list, try to set # source device to first active interface of host if (iface.type_name == "direct" and source.has_key('dev') and source['dev'] not in net_ifs): logging.warn( "Source device %s is not a interface" " of host, reset to %s", source['dev'], net_ifs[0]) source['dev'] = net_ifs[0] iface.source = source backend = ast.literal_eval(iface_backend) if backend: iface.backend = backend driver_dict = {} driver_host = {} driver_guest = {} if iface_driver: driver_dict = ast.literal_eval(iface_driver) if iface_driver_host: driver_host = ast.literal_eval(iface_driver_host) if iface_driver_guest: driver_guest = ast.literal_eval(iface_driver_guest) iface.driver = iface.new_driver(driver_attr=driver_dict, driver_host=driver_host, driver_guest=driver_guest) if iface.address: del iface.address logging.debug("New interface xml file: %s", iface) if unprivileged_user: # Create disk image for unprivileged user disk_index = xml_devices.index( xml_devices.by_device_tag("disk")[0]) disk_xml = xml_devices[disk_index] logging.debug("source: %s", disk_xml.source) disk_source = disk_xml.source.attrs["file"] cmd = ("cp -fZ {0} {1} && chown {2}:{2} {1}" "".format(disk_source, dst_disk, unprivileged_user)) utils.run(cmd) disk_xml.source = disk_xml.new_disk_source( attrs={"file": dst_disk}) vmxml.devices = xml_devices # Remove all channels to avoid of permission problem channels = vmxml.get_devices(device_type="channel") for channel in channels: vmxml.del_device(channel) vmxml.xmltreefile.write() logging.debug("New VM xml: %s", vmxml) utils.run("chmod a+rw %s" % vmxml.xml) virsh.define(vmxml.xml, **virsh_dargs) # Try to modify interface xml by update-device or edit xml elif update: iface.xmltreefile.write() ret = virsh.update_device(vm_name, iface.xml, ignore_status=True) libvirt.check_exit_status(ret, status_error) else: vmxml.devices = xml_devices vmxml.xmltreefile.write() vmxml.sync() def check_offloads_option(if_name, driver_options, session=None): """ Check interface offloads by ethtool output """ offloads = { "csum": "tx-checksumming", "gso": "generic-segmentation-offload", "tso4": "tcp-segmentation-offload", "tso6": "tx-tcp6-segmentation", "ecn": "tx-tcp-ecn-segmentation", "ufo": "udp-fragmentation-offload" } if session: ret, output = session.cmd_status_output("ethtool -k %s | head" " -18" % if_name) else: out = utils.run("ethtool -k %s | head -18" % if_name) ret, output = out.exit_status, out.stdout if ret: raise error.TestFail("ethtool return error code") logging.debug("ethtool output: %s", output) for offload in driver_options.keys(): if offloads.has_key(offload): if (output.count(offloads[offload]) and not output.count( "%s: %s" % (offloads[offload], driver_options[offload]))): raise error.TestFail( "offloads option %s: %s isn't" " correct in ethtool output" % (offloads[offload], driver_options[offload])) def run_xml_test(iface_mac): """ Test for interface options in vm xml """ # Get the interface object according the mac address vmxml = vm_xml.VMXML.new_from_dumpxml(vm_name) iface_devices = vmxml.get_devices(device_type="interface") iface = None for iface_dev in iface_devices: if iface_dev.mac_address == iface_mac: iface = iface_dev if not iface: raise error.TestFail("Can't find interface with mac" " '%s' in vm xml" % iface_mac) driver_dict = {} if iface_driver: driver_dict = ast.literal_eval(iface_driver) for driver_opt in driver_dict.keys(): if not driver_dict[driver_opt] == iface.driver.driver_attr[ driver_opt]: raise error.TestFail( "Can't see driver option %s=%s in vm xml" % (driver_opt, driver_dict[driver_opt])) if iface_target: if (not iface.target.has_key("dev") or not iface.target["dev"].startswith(iface_target)): raise error.TestFail("Can't see device target dev in vm xml") # Check macvtap mode by ip link command if iface_target == "macvtap" and iface.source.has_key("mode"): cmd = "ip -d link show %s" % iface.target["dev"] output = utils.run(cmd).stdout logging.debug("ip link output: %s", output) mode = iface.source["mode"] if mode == "passthrough": mode = "passthru" if not output.count("macvtap mode %s" % mode): raise error.TestFail("Failed to verify macvtap mode") def run_cmdline_test(iface_mac): """ Test for qemu-kvm command line options """ cmd = ("ps -ef | grep %s | grep -v grep " % vm_name) ret = utils.run(cmd) logging.debug("Command line %s", ret.stdout) if test_vhost_net: if not ret.stdout.count("vhost=on") and not rm_vhost_driver: raise error.TestFail("Can't see vhost options in" " qemu-kvm command line") if iface_model == "virtio": model_option = "device virtio-net-pci" else: model_option = "device rtl8139" iface_cmdline = re.findall( r"%s,(.+),mac=%s" % (model_option, iface_mac), ret.stdout) if not iface_cmdline: raise error.TestFail("Can't see %s with mac %s in command" " line" % (model_option, iface_mac)) cmd_opt = {} for opt in iface_cmdline[0].split(','): tmp = opt.rsplit("=") cmd_opt[tmp[0]] = tmp[1] logging.debug("Command line options %s", cmd_opt) driver_dict = {} # Test <driver> xml options. if iface_driver: iface_driver_dict = ast.literal_eval(iface_driver) for driver_opt in iface_driver_dict.keys(): if driver_opt == "name": continue elif driver_opt == "txmode": if iface_driver_dict["txmode"] == "iothread": driver_dict["tx"] = "bh" else: driver_dict["tx"] = iface_driver_dict["txmode"] elif driver_opt == "queues": driver_dict["mq"] = "on" driver_dict["vectors"] = str( int(iface_driver_dict["queues"]) * 2 + 2) else: driver_dict[driver_opt] = iface_driver_dict[driver_opt] # Test <driver><host/><driver> xml options. if iface_driver_host: driver_dict.update(ast.literal_eval(iface_driver_host)) # Test <driver><guest/><driver> xml options. if iface_driver_guest: driver_dict.update(ast.literal_eval(iface_driver_guest)) for driver_opt in driver_dict.keys(): if (not cmd_opt.has_key(driver_opt) or not cmd_opt[driver_opt] == driver_dict[driver_opt]): raise error.TestFail("Can't see option '%s=%s' in qemu-kvm " " command line" % (driver_opt, driver_dict[driver_opt])) if test_backend: guest_pid = ret.stdout.rsplit()[1] cmd = "lsof %s | grep %s" % (backend["tap"], guest_pid) if utils.system(cmd, ignore_status=True): raise error.TestFail("Guest process didn't open backend file" " %s" % backend["tap"]) cmd = "lsof %s | grep %s" % (backend["vhost"], guest_pid) if utils.system(cmd, ignore_status=True): raise error.TestFail("Guest process didn't open backend file" " %s" % backend["tap"]) def get_guest_ip(session, mac): """ Wrapper function to get guest ip address """ utils_net.restart_guest_network(session, mac) # Wait for IP address is ready utils_misc.wait_for(lambda: utils_net.get_guest_ip_addr(session, mac), 10) return utils_net.get_guest_ip_addr(session, mac) def check_user_network(session): """ Check user network ip address on guest """ vm_ips = [] vm_ips.append(get_guest_ip(session, iface_mac_old)) if attach_device: vm_ips.append(get_guest_ip(session, iface_mac)) logging.debug("IP address on guest: %s", vm_ips) if len(vm_ips) != len(set(vm_ips)): raise error.TestFail("Duplicated IP address on guest. " "Check bug: https://bugzilla.redhat." "com/show_bug.cgi?id=1147238") for vm_ip in vm_ips: if vm_ip is None or not vm_ip.startswith("10.0.2."): raise error.TestFail("Found wrong IP address" " on guest") # Check gateway address gateway = utils_net.get_net_gateway(session.cmd_output) if gateway != "10.0.2.2": raise error.TestFail("The gateway on guest is not" " right") # Check dns server address ns_list = utils_net.get_net_nameserver(session.cmd_output) if "10.0.2.3" not in ns_list: raise error.TestFail("The dns server can't be found" " on guest") def check_mcast_network(session): """ Check multicast ip address on guests """ username = params.get("username") password = params.get("password") src_addr = ast.literal_eval(iface_source)['address'] add_session = additional_vm.wait_for_serial_login(username=username, password=password) vms_sess_dict = {vm_name: session, additional_vm.name: add_session} # Check mcast address on host cmd = "netstat -g | grep %s" % src_addr if utils.run(cmd, ignore_status=True).exit_status: raise error.TestFail("Can't find multicast ip address" " on host") vms_ip_dict = {} # Get ip address on each guest for vms in vms_sess_dict.keys(): vm_mac = vm_xml.VMXML.get_first_mac_by_name(vms) vm_ip = get_guest_ip(vms_sess_dict[vms], vm_mac) if not vm_ip: raise error.TestFail("Can't get multicast ip" " address on guest") vms_ip_dict.update({vms: vm_ip}) if len(set(vms_ip_dict.values())) != len(vms_sess_dict): raise error.TestFail("Got duplicated multicast ip address") logging.debug("Found ips on guest: %s", vms_ip_dict) # Run omping server on host if not utils_package.package_install(["omping"]): raise error.TestError("Failed to install omping" " on host") cmd = ("iptables -F;omping -m %s %s" % (src_addr, "192.168.122.1 %s" % ' '.join(vms_ip_dict.values()))) # Run a backgroup job waiting for connection of client bgjob = utils.AsyncJob(cmd) # Run omping client on guests for vms in vms_sess_dict.keys(): # omping should be installed first if not utils_package.package_install(["omping"], vms_sess_dict[vms]): raise error.TestError("Failed to install omping" " on guest") cmd = ("iptables -F; omping -c 5 -T 5 -m %s %s" % (src_addr, "192.168.122.1 %s" % vms_ip_dict[vms])) ret, output = vms_sess_dict[vms].cmd_status_output(cmd) logging.debug("omping ret: %s, output: %s", ret, output) if (not output.count('multicast, xmt/rcv/%loss = 5/5/0%') or not output.count('unicast, xmt/rcv/%loss = 5/5/0%')): raise error.TestFail("omping failed on guest") # Kill the backgroup job bgjob.kill_func() status_error = "yes" == params.get("status_error", "no") start_error = "yes" == params.get("start_error", "no") unprivileged_user = params.get("unprivileged_user") # Interface specific attributes. iface_type = params.get("iface_type", "network") iface_source = params.get("iface_source", "{}") iface_driver = params.get("iface_driver") iface_model = params.get("iface_model", "virtio") iface_target = params.get("iface_target") iface_backend = params.get("iface_backend", "{}") iface_driver_host = params.get("iface_driver_host") iface_driver_guest = params.get("iface_driver_guest") attach_device = params.get("attach_iface_device") change_option = "yes" == params.get("change_iface_options", "no") update_device = "yes" == params.get("update_iface_device", "no") additional_guest = "yes" == params.get("additional_guest", "no") serial_login = "******" == params.get("serial_login", "no") rm_vhost_driver = "yes" == params.get("rm_vhost_driver", "no") test_option_cmd = "yes" == params.get("test_iface_option_cmd", "no") test_option_xml = "yes" == params.get("test_iface_option_xml", "no") test_vhost_net = "yes" == params.get("test_vhost_net", "no") test_option_offloads = "yes" == params.get("test_option_offloads", "no") test_iface_user = "******" == params.get("test_iface_user", "no") test_iface_mcast = "yes" == params.get("test_iface_mcast", "no") test_libvirtd = "yes" == params.get("test_libvirtd", "no") test_guest_ip = "yes" == params.get("test_guest_ip", "no") test_backend = "yes" == params.get("test_backend", "no") if iface_driver_host or iface_driver_guest or test_backend: if not libvirt_version.version_compare(1, 2, 8): raise error.TestNAError("Offloading/backend options not " "supported in this libvirt version") if iface_driver and "queues" in ast.literal_eval(iface_driver): if not libvirt_version.version_compare(1, 0, 6): raise error.TestNAError("Queues options not supported" " in this libvirt version") if unprivileged_user: if not libvirt_version.version_compare(1, 1, 1): raise error.TestNAError("qemu-bridge-helper not supported" " on this host") virsh_dargs["unprivileged_user"] = unprivileged_user # Create unprivileged user if needed cmd = ("grep {0} /etc/passwd || " "useradd {0}".format(unprivileged_user)) utils.run(cmd) # Need another disk image for unprivileged user to access dst_disk = "/tmp/%s.img" % unprivileged_user # Destroy VM first if vm.is_alive(): vm.destroy(gracefully=False) # Back up xml file. vmxml_backup = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) iface_mac_old = vm_xml.VMXML.get_first_mac_by_name(vm_name) # iface_mac will update if attach a new interface iface_mac = iface_mac_old # Additional vm for test additional_vm = None libvirtd = utils_libvirtd.Libvirtd() try: # Build the xml and run test. try: # Prepare interface backend files if test_backend: if not os.path.exists("/dev/vhost-net"): utils.run("modprobe vhost-net") backend = ast.literal_eval(iface_backend) backend_tap = "/dev/net/tun" backend_vhost = "/dev/vhost-net" if not backend: backend["tap"] = backend_tap backend["vhost"] = backend_vhost if not start_error: # Create backend files for normal test if not os.path.exists(backend["tap"]): os.rename(backend_tap, backend["tap"]) if not os.path.exists(backend["vhost"]): os.rename(backend_vhost, backend["vhost"]) # Edit the interface xml. if change_option: modify_iface_xml(update=False) if rm_vhost_driver: # Check vhost driver. kvm_version = os.uname()[2] driver_path = ("/lib/modules/%s/kernel/drivers/vhost/" "vhost_net.ko" % kvm_version) driver_backup = driver_path + ".bak" cmd = ("modprobe -r {0}; lsmod | " "grep {0}".format("vhost_net")) if not utils.system(cmd, ignore_status=True): raise error.TestError("Failed to remove vhost_net driver") # Move the vhost_net driver if os.path.exists(driver_path): os.rename(driver_path, driver_backup) else: # Load vhost_net driver by default cmd = "modprobe vhost_net" utils.system(cmd) # Attach a interface when vm is shutoff if attach_device == 'config': iface_mac = utils_net.generate_mac_address_simple() iface_xml_obj = create_iface_xml(iface_mac) iface_xml_obj.xmltreefile.write() ret = virsh.attach_device(vm_name, iface_xml_obj.xml, flagstr="--config", ignore_status=True) libvirt.check_exit_status(ret) # Clone additional vm if additional_guest: guest_name = "%s_%s" % (vm_name, '1') # Clone additional guest timeout = params.get("clone_timeout", 360) utils_libguestfs.virt_clone_cmd(vm_name, guest_name, True, timeout=timeout) additional_vm = vm.clone(guest_name) additional_vm.start() # additional_vm.wait_for_login() # Start the VM. if unprivileged_user: virsh.start(vm_name, **virsh_dargs) cmd = ("su - %s -c 'virsh console %s'" % (unprivileged_user, vm_name)) session = aexpect.ShellSession(cmd) session.sendline() remote.handle_prompts(session, params.get("username"), params.get("password"), "[\#\$]", 30) # Get ip address on guest if not get_guest_ip(session, iface_mac): raise error.TestError("Can't get ip address on guest") else: # Will raise VMStartError exception if start fails vm.start() if serial_login: session = vm.wait_for_serial_login() else: session = vm.wait_for_login() if start_error: raise error.TestFail("VM started unexpectedly") # Attach a interface when vm is running if attach_device == 'live': iface_mac = utils_net.generate_mac_address_simple() iface_xml_obj = create_iface_xml(iface_mac) iface_xml_obj.xmltreefile.write() ret = virsh.attach_device(vm_name, iface_xml_obj.xml, flagstr="--live", ignore_status=True) libvirt.check_exit_status(ret) # Need sleep here for attachment take effect time.sleep(5) # Update a interface options if update_device: modify_iface_xml(update=True, status_error=status_error) # Run tests for qemu-kvm command line options if test_option_cmd: run_cmdline_test(iface_mac) # Run tests for vm xml if test_option_xml: run_xml_test(iface_mac) # Run tests for offloads options if test_option_offloads: if iface_driver_host: ifname_guest = utils_net.get_linux_ifname( session, iface_mac) check_offloads_option(ifname_guest, ast.literal_eval(iface_driver_host), session) if iface_driver_guest: ifname_host = libvirt.get_ifname_host(vm_name, iface_mac) check_offloads_option(ifname_host, ast.literal_eval(iface_driver_guest)) if test_iface_user: # Test user type network check_user_network(session) if test_iface_mcast: # Test mcast type network check_mcast_network(session) # Check guest ip address if test_guest_ip: if not get_guest_ip(session, iface_mac): raise error.TestFail("Guest can't get a" " valid ip address") session.close() # Restart libvirtd and guest, then test again if test_libvirtd: libvirtd.restart() vm.destroy() vm.start() if test_option_xml: run_xml_test(iface_mac) # Detach hot/cold-plugged interface at last if attach_device: ret = virsh.detach_device(vm_name, iface_xml_obj.xml, flagstr="", ignore_status=True) libvirt.check_exit_status(ret) except virt_vm.VMStartError as e: logging.info(str(e)) if not start_error: raise error.TestFail('VM failed to start\n%s' % e) finally: # Recover VM. logging.info("Restoring vm...") # Restore interface backend files if test_backend: if not os.path.exists(backend_tap): os.rename(backend["tap"], backend_tap) if not os.path.exists(backend_vhost): os.rename(backend["vhost"], backend_vhost) if rm_vhost_driver: # Restore vhost_net driver if os.path.exists(driver_backup): os.rename(driver_backup, driver_path) if unprivileged_user: virsh.remove_domain(vm_name, "--remove-all-storage", **virsh_dargs) if additional_vm: virsh.remove_domain(additional_vm.name, "--remove-all-storage") # Kill all omping server process on host utils.system("pidof omping && killall omping", ignore_status=True) if vm.is_alive(): vm.destroy(gracefully=False) vmxml_backup.sync()
def run(test, params, env): """ Test interafce xml options. 1.Prepare test environment,destroy or suspend a VM. 2.Edit xml and start the domain. 3.Perform test operation. 4.Recover test environment. 5.Confirm the test result. """ vm_name = params.get("main_vm") vm = env.get_vm(vm_name) if vm.is_alive(): vm.wait_for_login() def create_iface_xml(mac): """ Create interface xml file """ iface = Interface(type_name=iface_type) iface.source = iface_source iface.model = iface_model if iface_model else "virtio" if iface_target: iface.target = {'dev': iface_target} iface.mac_address = mac logging.debug("Create new interface xml: %s", iface) return iface status_error = "yes" == params.get("status_error", "no") # Interface specific attributes. iface_num = params.get("iface_num", '1') iface_type = params.get("iface_type", "network") iface_source = eval(params.get("iface_source", "{'network':'default'}")) iface_model = params.get("iface_model") iface_target = params.get("iface_target") iface_mac = params.get("iface_mac") attach_device = "yes" == params.get("attach_device", "no") attach_iface = "yes" == params.get("attach_iface", "no") attach_option = params.get("attach_option", "") detach_device = "yes" == params.get("detach_device") stress_test = "yes" == params.get("stress_test") restart_libvirtd = "yes" == params.get("restart_libvirtd", "no") username = params.get("username") password = params.get("password") poll_timeout = int(params.get("poll_timeout", 10)) # stree_test require detach operation stress_test_detach_device = False stress_test_detach_interface = False if stress_test: if attach_device: stress_test_detach_device = True if attach_iface: stress_test_detach_interface = True # The following detach-device step also using attach option detach_option = attach_option # Back up xml file. vmxml_backup = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) #iface_mac = vm_xml.VMXML.get_first_mac_by_name(vm_name) libvirtd = utils_libvirtd.Libvirtd() # Check virsh command option check_cmds = [] if not status_error: if attach_device and attach_option: check_cmds.append(('attach-device', attach_option)) if attach_iface and attach_option: check_cmds.append(('attach-interface', attach_option)) if (detach_device or stress_test_detach_device) and detach_option: check_cmds.append(('detach-device', detach_option)) if stress_test_detach_interface and detach_option: check_cmds.append(('detach-device', detach_option)) for cmd, option in check_cmds: libvirt.virsh_cmd_has_option(cmd, option) try: try: # Attach an interface when vm is running iface_list = [] err_msgs = ("No more available PCI slots", "No more available PCI addresses") if attach_device: for i in range(int(iface_num)): logging.info("Try to attach interface loop %s" % i) if iface_mac: mac = iface_mac else: mac = utils_net.generate_mac_address_simple() iface_xml_obj = create_iface_xml(mac) iface_xml_obj.xmltreefile.write() ret = virsh.attach_device(vm_name, iface_xml_obj.xml, flagstr=attach_option, ignore_status=True) if ret.exit_status: if any([msg in ret.stderr for msg in err_msgs]): logging.debug("No more pci slots, can't" " attach more devices") break elif (ret.stderr.count("doesn't support option %s" % attach_option)): raise error.TestNAError(ret.stderr) elif status_error: continue else: logging.error("Command output %s" % ret.stdout.strip()) raise error.TestFail("Failed to attach-device") elif stress_test: # Detach the device immediately for stress test ret = virsh.detach_device(vm_name, iface_xml_obj.xml, flagstr=detach_option, ignore_status=True) libvirt.check_exit_status(ret) else: iface_list.append({'mac': mac, 'iface_xml': iface_xml_obj}) # Need sleep here for attachment take effect time.sleep(5) elif attach_iface: for i in range(int(iface_num)): logging.info("Try to attach interface loop %s" % i) mac = utils_net.generate_mac_address_simple() options = ("%s %s --model %s --mac %s %s" % (iface_type, iface_source['network'], iface_model, mac, attach_option)) ret = virsh.attach_interface(vm_name, options, ignore_status=True) if ret.exit_status: if any([msg in ret.stderr for msg in err_msgs]): logging.debug("No more pci slots, can't" " attach more devices") break elif (ret.stderr.count("doesn't support option %s" % attach_option)): raise error.TestNAError(ret.stderr) elif status_error: continue else: logging.error("Command output %s" % ret.stdout.strip()) raise error.TestFail("Failed to attach-interface") elif stress_test: # Detach the device immediately for stress test options = ("--type %s --mac %s %s" % (iface_type, mac, detach_option)) ret = virsh.detach_interface(vm_name, options, ignore_status=True) libvirt.check_exit_status(ret) else: iface_list.append({'mac': mac}) # Need sleep here for attachment take effect time.sleep(5) # Restart libvirtd service if restart_libvirtd: libvirtd.restart() vm.cleanup_serial_console() # Start the domain if needed if vm.is_dead(): vm.start() session = vm.wait_for_serial_login(username=username, password=password) # Check if interface was attached for iface in iface_list: if iface.has_key('mac'): # Check interface in dumpxml output if_attr = vm_xml.VMXML.get_iface_by_mac(vm_name, iface['mac']) if not if_attr: raise error.TestFail("Can't see interface " " in dumpxml") if (if_attr['type'] != iface_type or if_attr['source'] != iface_source['network']): raise error.TestFail("Interface attribute doesn't" " match attachment opitons") # Check interface on guest if not utils_net.get_linux_ifname(session, iface['mac']): raise error.TestFail("Can't see interface" " on guest") # Detach hot/cold-plugged interface at last if detach_device: for iface in iface_list: if iface.has_key('iface_xml'): ret = virsh.detach_device(vm_name, iface['iface_xml'].xml, flagstr="", ignore_status=True) libvirt.check_exit_status(ret) else: options = ("%s --mac %s" % (iface_type, iface['mac'])) ret = virsh.detach_interface(vm_name, options, ignore_status=True) libvirt.check_exit_status(ret) # Check if interface was detached for iface in iface_list: if iface.has_key('mac'): polltime = time.time() + poll_timeout while True: # Check interface in dumpxml output if not vm_xml.VMXML.get_iface_by_mac(vm_name, iface['mac']): break else: time.sleep(2) if time.time() > polltime: test.fail("Interface still " "exists after detachment") session.close() except virt_vm.VMStartError as e: logging.info(str(e)) raise error.TestFail('VM failed to start:\n%s' % e) finally: # Recover VM. logging.info("Restoring vm...") if vm.is_alive(): vm.destroy(gracefully=False) vmxml_backup.sync()
def run(test, params, env): """ SR-IOV devices sanity test: 1) Bring up VFs by following instructions How To in Setup. 2) Configure all VFs in host. 3) Check whether all VFs get ip in host. 4) Unbind PFs/VFs from host kernel driver to sr-iov driver. 5) Bind PFs/VFs back to host kernel driver. 6) Repeat step 4, 5. 7) Try to boot up guest(s) with VF(s). :param test: QEMU test object :param params: Dictionary with the test parameters :param env: Dictionary with test environment. """ device_driver = params.get("device_driver", "pci-assign") repeat_time = int(params.get("bind_repeat_time", 1)) pci_assignable = test_setup.PciAssignable( driver=params.get("driver"), driver_option=params.get("driver_option"), host_set_flag=1, kvm_params=params.get("kvm_default"), vf_filter_re=params.get("vf_filter_re"), pf_filter_re=params.get("pf_filter_re"), device_driver=device_driver) devices = [] device_type = params.get("device_type", "vf") if device_type == "vf": device_num = pci_assignable.get_vfs_count() if device_num == 0: msg = " No VF device found even after running SR-IOV setup" raise error.TestFail(msg) elif device_type == "pf": device_num = len(pci_assignable.get_pf_vf_info()) else: msg = "Unsupport device type '%s'." % device_type msg += " Please set device_type to 'vf' or 'pf'." raise error.TestError(msg) for i in xrange(device_num): device = {} device["type"] = device_type if device_type == "vf": device['mac'] = utils_net.generate_mac_address_simple() if params.get("device_name"): device["name"] = params.get("device_name") devices.append(device) pci_assignable.devices = devices vf_pci_id = [] pf_vf_dict = pci_assignable.get_pf_vf_info() for pf_dict in pf_vf_dict: vf_pci_id.extend(pf_dict["vf_ids"]) ethname_dict = [] ips = {} msg = "Configure all VFs in host." error.context(msg, logging.info) for pci_id in vf_pci_id: cmd = "ls /sys/bus/pci/devices/%s/net/" % pci_id ethname = utils.system_output(cmd).strip() ethname_dict.append(ethname) network_script = os.path.join("/etc/sysconfig/network-scripts", "ifcfg-%s" % ethname) if not os.path.exists(network_script): error.context("Create %s file." % network_script, logging.info) txt = "DEVICE=%s\nONBOOT=yes\nBOOTPROTO=dhcp\n" % ethname file(network_script, "w").write(txt) msg = "Check whether VFs could get ip in host." error.context(msg, logging.info) for ethname in ethname_dict: ifup_down_interface(ethname) _ip = check_network_interface_ip(ethname) if not _ip: msg = "Interface '%s' could not get IP." % ethname logging.error(msg) else: ips[ethname] = _ip logging.info("Interface '%s' get IP '%s'", ethname, _ip) for i in xrange(repeat_time): msg = "Bind/unbind device from host. Repeat %s/%s" % (i + 1, repeat_time) error.context(msg, logging.info) bind_device_num = random.randint(1, device_num) pci_assignable.request_devs(devices[:bind_device_num]) logging.info("Sleep 3s before releasing vf to host.") time.sleep(3) pci_assignable.release_devs() logging.info("Sleep 3s after releasing vf to host.") time.sleep(3) if device_type == "vf": post_device_num = pci_assignable.get_vfs_count() else: post_device_num = len(pci_assignable.get_pf_vf_info()) if post_device_num != device_num: msg = "lspci cannot report the correct PF/VF number." msg += " Correct number is '%s'" % device_num msg += " lspci report '%s'" % post_device_num raise error.TestFail(msg) dmesg = utils.system_output("dmesg") file_name = "host_dmesg_after_unbind_device.txt" logging.info("Log dmesg after bind/unbing device to '%s'.", file_name) utils_misc.log_line(file_name, dmesg) msg = "Check whether VFs still get ip in host." error.context(msg, logging.info) for ethname in ips: ifup_down_interface(ethname, action="up") _ip = check_network_interface_ip(ethname) if not _ip: msg = "Interface '%s' could not get IP." % ethname msg += "Before bind/unbind it have IP '%s'." % ips[ethname] logging.error(msg) else: logging.info("Interface '%s' get IP '%s'", ethname, _ip) msg = "Try to boot up guest(s) with VF(s)." error.context(msg, logging.info) for vm_name in params["vms"].split(" "): params["start_vm"] = "yes" env_process.preprocess_vm(test, params, env, vm_name) vm = env.get_vm(vm_name) vm.verify_alive() vm.wait_for_login(timeout=int(params.get("login_timeout", 360)))
def run(test, params, env): """ create/delete macvtap in host 1) Verify no other macvtap share the physical network device. 2) Create a macvtap device in host. 3) Check configuraton of macvtap device. 4) Ping out from host with the interface that create macvtap. 5) Delete the macvtap device create in step 2. 6) Ping out from host with the interface that create macvtap. :param test: QEMU test object. :param params: Dictionary with the test parameters. :param env: Dictionary with test environment. """ ifname = params.get("macvtap_base_interface") macvtap_mode = params.get("macvtap_mode", "passthru") dest_host = params.get("dest_host") set_mac = params.get("set_mac", "yes") == "yes" macvtaps = [] if not ifname: ifname = params.get("netdst") ifname = utils_net.get_macvtap_base_iface(ifname) error.context("Verify no other macvtap share the physical network device.", logging.info) macvtap_devices = get_macvtap_device_on_ifname(ifname) for device in macvtap_devices: utils.system_output("ip link delete %s" % device) for mode in macvtap_mode.split(): macvtap_name = "%s_01" % mode txt = "Create %s mode macvtap device %s on %s." % (mode, macvtap_name, ifname) error.context(txt, logging.info) cmd = " ip link add link %s name %s type macvtap mode %s" % (ifname, macvtap_name, mode) utils.system(cmd, timeout=240) if set_mac: txt = "Determine and configure mac address of %s, " % macvtap_name txt += "Then link up it." error.context(txt, logging.info) mac = utils_net.generate_mac_address_simple() cmd = " ip link set %s address %s up" % (macvtap_name, mac) utils.system(cmd, timeout=240) error.context("Check configuraton of macvtap device", logging.info) check_cmd = " ip -d link show %s" % macvtap_name try: tap_info = utils.system_output(check_cmd, timeout=240) except error.CmdError: err = "Fail to create %s mode macvtap on %s" % (mode, ifname) raise error.TestFail(err) if set_mac: if mac not in tap_info: err = "Fail to set mac for %s" % macvtap_name raise error.TestFail(err) macvtaps.append(macvtap_name) if not dest_host: dest_host_get_cmd = "ip route | awk '/default/ { print $3 }'" dest_host_get_cmd = params.get("dest_host_get_cmd", dest_host_get_cmd) dest_host = utils.system_output(dest_host_get_cmd).split()[-1] txt = "Ping dest host %s from " % dest_host txt += "localhost with the interface %s" % ifname error.context(txt, logging.info) status, output = utils_test.ping(dest_host, 10, interface=ifname, timeout=20) ratio = utils_test.get_loss_ratio(output) if "passthru" in macvtap_mode: ifnames = utils_net.get_host_iface() ifnames.remove(ifname) logging.info("ifnames = %s", ifnames) ips = [] for name in ifnames: try: _ip = utils_net.get_ip_address_by_interface(name) if _ip != "127.0.0.1": ips.append(_ip) except Exception: pass logging.info("ips = %s", ips) if not ips: if ratio != 100: err = "%s did not lost network connection after " % ifname err += " creating %s mode macvtap on it." % macvtap_mode raise error.TestFail(err) else: err = "%s is not the only network device in host" % ifname logging.debug(err) else: if ratio != 0: err = "Package lost during ping %s from %s " % (dest_host, ifname) err += "after creating %s mode macvtap on it." % macvtap_mode raise error.TestFail(err) for name in macvtaps: txt = "Delete macvtap device %s on %s." % (name, ifname) error.context(txt, logging.info) del_cmd = "ip link delete %s" % name utils.system(del_cmd) devices = get_macvtap_device_on_ifname(ifname) if name in devices: err = "Fail to delete macvtap %s on %s" % (name, ifname) raise error.TestFail(err) logging.info("dest_host = %s", dest_host) txt = "Ping dest host %s from " % dest_host txt += "localhost with the interface %s" % ifname error.context(txt, logging.info) status, output = utils_test.ping(dest_host, 10, interface=ifname, timeout=20) if status != 0: raise error.TestFail("Ping failed, status: %s," " output: %s" % (status, output)) ratio = utils_test.get_loss_ratio(output) if ratio != 0: err = "Package lost during ping %s from %s " % (dest_host, ifname) raise error.TestFail(err)
def run(test, params, env): """ Test hotplug of sr-iov devices. (Elements between [] are configurable test parameters) 1) Set up sr-iov test environment in host. 2) Start VM. 3) Disable the primary link(s) of guest. 4) PCI add one/multi sr-io deivce with (or without) repeat 5) Compare output of monitor command 'info pci'. 6) Compare output of guest command [reference_cmd]. 7) Verify whether pci_model is shown in [pci_find_cmd]. 8) Check whether the newly added PCI device works fine. 9) Delete the device, verify whether could remove the sr-iov device. 10) Re-enabling the primary link(s) of guest. :param test: QEMU test object. :param params: Dictionary with the test parameters. :param env: Dictionary with test environment. """ def get_active_network_device(session, nic_filter): devnames = [] cmd = "ifconfig -a" status, output = session.cmd_status_output(cmd) if status: msg = "Guest command '%s' fail with output: %s." % (cmd, output) raise error.TestError(msg) devnames = re.findall(nic_filter, output) return devnames def pci_add_iov(pci_num): pci_add_cmd = ("pci_add pci_addr=auto host host=%s,if=%s" % (pa_pci_ids[pci_num], pci_model)) if params.get("hotplug_params"): assign_param = params.get("hotplug_params").split() for param in assign_param: value = params.get(param) if value: pci_add_cmd += ",%s=%s" % (param, value) return pci_add(pci_add_cmd) def pci_add(pci_add_cmd): error.context("Adding pci device with command 'pci_add'") add_output = vm.monitor.send_args_cmd(pci_add_cmd, convert=False) pci_info.append(['', add_output]) if not "OK domain" in add_output: raise error.TestFail("Add PCI device failed. " "Monitor command is: %s, Output: %r" % (pci_add_cmd, add_output)) return vm.monitor.info("pci") def check_support_device(dev): if vm.monitor.protocol == 'qmp': devices_supported = vm.monitor.human_monitor_cmd("%s ?" % cmd_type) else: devices_supported = vm.monitor.send_args_cmd("%s ?" % cmd_type) # Check if the device is support in qemu is_support = utils_misc.find_substring(devices_supported, dev) if not is_support: raise error.TestError("%s doesn't support device: %s" % (cmd_type, dev)) def device_add_iov(pci_num): device_id = "%s" % pci_model + "-" + utils_misc.generate_random_id() pci_info.append([device_id]) driver = params.get("device_driver", "pci-assign") check_support_device(driver) pci_add_cmd = ("device_add id=%s,driver=%s,host=%s" % (pci_info[pci_num][0], driver, pa_pci_ids[pci_num])) if params.get("hotplug_params"): assign_param = params.get("hotplug_params").split() for param in assign_param: value = params.get(param) if value: pci_add_cmd += ",%s=%s" % (param, value) return device_add(pci_num, pci_add_cmd) def device_add(pci_num, pci_add_cmd): error.context("Adding pci device with command 'device_add'") if vm.monitor.protocol == 'qmp': add_output = vm.monitor.send_args_cmd(pci_add_cmd) else: add_output = vm.monitor.send_args_cmd(pci_add_cmd, convert=False) pci_info[pci_num].append(add_output) after_add = vm.monitor.info("pci") if pci_info[pci_num][0] not in after_add: logging.debug("Print info pci after add the block: %s" % after_add) raise error.TestFail("Add device failed. Monitor command is: %s" ". Output: %r" % (pci_add_cmd, add_output)) return after_add # Hot add a pci device def add_device(pci_num): reference_cmd = params["reference_cmd"] find_pci_cmd = params["find_pci_cmd"] info_pci_ref = vm.monitor.info("pci") reference = session.cmd_output(reference_cmd) active_nics = get_active_network_device(session, nic_filter) try: # get function for adding device. add_fuction = local_functions["%s_iov" % cmd_type] except Exception: raise error.TestError( "No function for adding sr-iov dev with '%s'" % cmd_type) after_add = None if add_fuction: # Do add pci device. after_add = add_fuction(pci_num) try: # Define a helper function to compare the output def _new_shown(): output = session.cmd_output(reference_cmd) return output != reference # Define a helper function to make sure new nic could get ip. def _check_ip(): post_nics = get_active_network_device(session, nic_filter) return (len(active_nics) <= len(post_nics) and active_nics != post_nics) # Define a helper function to catch PCI device string def _find_pci(): output = session.cmd_output(find_pci_cmd) if re.search(match_string, output, re.IGNORECASE): return True else: return False error.context("Start checking new added device") # Compare the output of 'info pci' if after_add == info_pci_ref: raise error.TestFail("No new PCI device shown after executing " "monitor command: 'info pci'") secs = int(params["wait_secs_for_hook_up"]) if not utils_misc.wait_for(_new_shown, test_timeout, secs, 3): raise error.TestFail("No new device shown in output of command " "executed inside the guest: %s" % reference_cmd) if not utils_misc.wait_for(_find_pci, test_timeout, 3, 3): raise error.TestFail("New add device not found in guest. " "Command was: %s" % find_pci_cmd) # Test the newly added device if not utils_misc.wait_for(_check_ip, 30, 3, 3): ifconfig = session.cmd_output("ifconfig -a") raise error.TestFail("New hotpluged device could not get ip " "after 30s in guest. guest ifconfig " "output: \n%s" % ifconfig) try: session.cmd(params["pci_test_cmd"] % (pci_num + 1)) except aexpect.ShellError, e: raise error.TestFail("Check device failed after PCI " "hotplug. Output: %r" % e.output) except Exception: pci_del(pci_num, ignore_failure=True) raise # Hot delete a pci device def pci_del(pci_num, ignore_failure=False): def _device_removed(): after_del = vm.monitor.info("pci") return after_del != before_del before_del = vm.monitor.info("pci") if cmd_type == "pci_add": slot_id = "0" + pci_info[pci_num][1].split(",")[2].split()[1] cmd = "pci_del pci_addr=%s" % slot_id vm.monitor.send_args_cmd(cmd, convert=False) elif cmd_type == "device_add": cmd = "device_del id=%s" % pci_info[pci_num][0] vm.monitor.send_args_cmd(cmd) if (not utils_misc.wait_for(_device_removed, test_timeout, 0, 1) and not ignore_failure): raise error.TestFail("Failed to hot remove PCI device: %s. " "Monitor command: %s" % (pci_model, cmd)) vm = env.get_vm(params["main_vm"]) vm.verify_alive() timeout = int(params.get("login_timeout", 360)) session = vm.wait_for_serial_login(timeout=timeout) test_timeout = int(params.get("test_timeout", 360)) # Test if it is nic or block pci_num_range = int(params.get("pci_num", 1)) rp_times = int(params.get("repeat_times", 1)) pci_model = params.get("pci_model", "pci-assign") # Need udpate match_string if you use a card other than 82576 match_string = params.get("match_string", "82576") nic_filter = params["nic_interface_filter"] devices = [] device_type = params.get("hotplug_device_type", "vf") for i in xrange(pci_num_range): device = {} device["type"] = device_type device['mac'] = utils_net.generate_mac_address_simple() if params.get("device_name"): device["name"] = params.get("device_name") devices.append(device) device_driver = params.get("device_driver", "pci-assign") if vm.pci_assignable is None: vm.pci_assignable = test_setup.PciAssignable( driver=params.get("driver"), driver_option=params.get("driver_option"), host_set_flag=params.get("host_setup_flag"), kvm_params=params.get("kvm_default"), vf_filter_re=params.get("vf_filter_re"), pf_filter_re=params.get("pf_filter_re"), device_driver=device_driver) pa_pci_ids = vm.pci_assignable.request_devs(devices) # Modprobe the module if specified in config file module = params.get("modprobe_module") if module: error.context("modprobe the module %s" % module, logging.info) session.cmd("modprobe %s" % module) # Probe qemu to verify what is the supported syntax for PCI hotplug if vm.monitor.protocol == 'qmp': cmd_o = vm.monitor.info("commands") else: cmd_o = vm.monitor.send_args_cmd("help") cmd_type = utils_misc.find_substring(str(cmd_o), "device_add", "pci_add") if not cmd_o: raise error.TestError("Unknow version of qemu") local_functions = locals() error.context("Disable the primary link(s) of guest", logging.info) for nic in vm.virtnet: vm.set_link(nic.device_id, up=False) try: for j in range(rp_times): # pci_info is a list of list. # each element 'i' has 4 members: # pci_info[i][0] == device id, only used for device_add # pci_info[i][1] == output of device add command pci_info = [] for pci_num in xrange(pci_num_range): msg = "Start hot-adding %sth pci device," % (pci_num + 1) msg += " repeat %d" % (j + 1) error.context(msg, logging.info) add_device(pci_num) sub_type = params.get("sub_type_after_plug") if sub_type: error.context("Running sub test '%s' after hotplug" % sub_type, logging.info) utils_test.run_virt_sub_test(test, params, env, sub_type) if "guest_suspend" == sub_type: # Hotpluged device have been released after guest suspend, # so do not need unpluged step. break for pci_num in xrange(pci_num_range): msg = "start hot-deleting %sth pci device," % (pci_num + 1) msg += " repeat %d" % (j + 1) error.context(msg, logging.info) pci_del(-(pci_num + 1)) finally: error.context("Re-enabling the primary link(s) of guest", logging.info) for nic in vm.virtnet: vm.set_link(nic.device_id, up=True)
def run(test, params, env): """ Test hotplug of sr-iov devices. (Elements between [] are configurable test parameters) 1) Set up sr-iov test environment in host. 2) Start VM. 3) Disable the primary link(s) of guest. 4) PCI add one/multi sr-io deivce with (or without) repeat 5) Compare output of monitor command 'info pci'. 6) Compare output of guest command [reference_cmd]. 7) Verify whether pci_model is shown in [pci_find_cmd]. 8) Check whether the newly added PCI device works fine. 9) Delete the device, verify whether could remove the sr-iov device. 10) Re-enabling the primary link(s) of guest. :param test: QEMU test object. :param params: Dictionary with the test parameters. :param env: Dictionary with test environment. """ def check_interface(iface, nic_filter): cmd = "ifconfig %s" % str(iface) session = vm.wait_for_serial_login(timeout=timeout) status, output = session.cmd_status_output(cmd) if status: test.error("Guest command '%s' fail with output: %s." % (cmd, output)) if re.findall(nic_filter, output, re.MULTILINE | re.DOTALL): return True return False def get_active_network_device(session, nic_filter): devnames = [] cmd = "ifconfig -a" nic_reg = r"\w+(?=: flags)|\w+(?=\s*Link)" status, output = session.cmd_status_output(cmd) if status: test.error("Guest command '%s' fail with output: %s." % (cmd, output)) ifaces = re.findall(nic_reg, output) for iface in ifaces: if check_interface(str(iface), nic_filter): devnames.append(iface) return devnames def pci_add_iov(pci_num): pci_add_cmd = ("pci_add pci_addr=auto host host=%s,if=%s" % (pa_pci_ids[pci_num], pci_model)) if params.get("hotplug_params"): assign_param = params.get("hotplug_params").split() for param in assign_param: value = params.get(param) if value: pci_add_cmd += ",%s=%s" % (param, value) return pci_add(pci_add_cmd) def pci_add(pci_add_cmd): error_context.context("Adding pci device with command 'pci_add'") add_output = vm.monitor.send_args_cmd(pci_add_cmd, convert=False) pci_info.append(['', add_output]) if "OK domain" not in add_output: test.fail("Add PCI device failed. Monitor command is: %s, " "Output: %r" % (pci_add_cmd, add_output)) return vm.monitor.info("pci") def check_support_device(dev): if vm.monitor.protocol == 'qmp': devices_supported = vm.monitor.human_monitor_cmd("%s ?" % cmd_type) else: devices_supported = vm.monitor.send_args_cmd("%s ?" % cmd_type) # Check if the device is support in qemu is_support = utils_misc.find_substring(devices_supported, dev) if not is_support: test.error("%s doesn't support device: %s" % (cmd_type, dev)) def device_add_iov(pci_num): device_id = "%s" % pci_model + "-" + utils_misc.generate_random_id() pci_info.append([device_id]) driver = params.get("device_driver", "pci-assign") check_support_device(driver) pci_add_cmd = ("device_add id=%s,driver=%s,host=%s" % (pci_info[pci_num][0], driver, pa_pci_ids[pci_num])) if params.get("hotplug_params"): assign_param = params.get("hotplug_params").split() for param in assign_param: value = params.get(param) if value: pci_add_cmd += ",%s=%s" % (param, value) return device_add(pci_num, pci_add_cmd) def device_add(pci_num, pci_add_cmd): error_context.context("Adding pci device with command 'device_add'") if vm.monitor.protocol == 'qmp': add_output = vm.monitor.send_args_cmd(pci_add_cmd) else: add_output = vm.monitor.send_args_cmd(pci_add_cmd, convert=False) pci_info[pci_num].append(add_output) after_add = vm.monitor.info("pci") if pci_info[pci_num][0] not in str(after_add): logging.debug("Print info pci after add the block: %s" % after_add) test.fail("Add device failed. Monitor command is: %s" ". Output: %r" % (pci_add_cmd, add_output)) return after_add def clean_network_scripts(): logging.debug("Clean up network scripts in guest") session = vm.wait_for_serial_login(timeout=timeout) if "ubuntu" in vm.get_distro().lower(): iface_script = "/etc/network/interfaces" cmd = "cat %s.BACKUP" % iface_script if not session.cmd_status(cmd): cmd = "mv %s.BACKUP %s" % (iface_script, iface_script) status, output = session.cmd_status_output(cmd) if status: test.error("Failed to cleanup network script in guest: " "%s" % output) else: global iface_scripts for iface_script in iface_scripts: cmd = "rm -f %s" % iface_script status, output = session.cmd_status_output(cmd) if status: test.error("Failed to delete iface_script") iface_scripts.remove(iface_script) # Hot add a pci device def add_device(pci_num): global iface_scripts reference_cmd = params["reference_cmd"] info_pci_ref = vm.monitor.info("pci") session = vm.wait_for_serial_login(timeout=timeout) reference = session.cmd_output(reference_cmd) active_nics = get_active_network_device(session, nic_filter) logging.debug("Active nics before hotplug - %s", active_nics) # Stop the VM monitor and try hot adding SRIOV dev if params.get("vm_stop", "no") == "yes": logging.debug("stop the monitor of the VM before hotplug") vm.pause() try: # get function for adding device. add_function = local_functions["%s_iov" % cmd_type] except Exception: test.error("No function for adding sr-iov dev with '%s'" % cmd_type) after_add = None if add_function: # Do add pci device. after_add = add_function(pci_num) try: # Define a helper function to compare the output def _new_shown(): output = session.cmd_output(reference_cmd) return output != reference # Define a helper function to make sure new nic could get ip. def _check_ip(): post_nics = get_active_network_device(session, nic_filter) logging.debug("Active nics after hotplug - %s", post_nics) return (len(active_nics) <= len(post_nics) and active_nics != post_nics) # Define a helper function to catch PCI device string def _find_pci(): output = session.cmd_output("lspci -nn") if re.search(vf_filter, output, re.IGNORECASE): return True else: return False # Resume the VM if params.get("vm_resume", "no") == "yes": logging.debug("resuming the VM after hotplug") vm.resume() # Reboot the VM if params.get("vm_reboot", "no") == "yes": logging.debug("Rebooting the VM after hotplug") vm.reboot() session = vm.wait_for_serial_login(timeout=timeout) error_context.context("Start checking new added device") # Compare the output of 'info pci' if after_add == info_pci_ref: test.fail("No new PCI device shown after executing " "monitor command: 'info pci'") secs = int(params["wait_secs_for_hook_up"]) if not utils_misc.wait_for(_new_shown, test_timeout, secs, 3): test.fail("No new device shown in output of command " "executed inside the guest: %s" % reference_cmd) if not utils_misc.wait_for(_find_pci, test_timeout, 3, 3): test.fail("New add device not found in guest. " "Command was: lspci -nn") # Assign static IP to the hotplugged interface if params.get("assign_static_ip", "no") == "yes": cmd = "service networking restart" static_ip = next(ip_gen) net_mask = params.get("static_net_mask", "255.255.255.0") broadcast = params.get("static_broadcast", "10.10.10.255") pci_id = utils_misc.get_pci_id_using_filter(vf_filter, session) logging.debug("PCIs associated with %s - %s", vf_filter, ', '.join(map(str, pci_id))) for each_pci in pci_id: iface_name = utils_misc.get_interface_from_pci_id(each_pci, session) logging.debug("Interface associated with PCI %s - %s", each_pci, iface_name) mac = session.cmd_output("ethtool -P %s" % iface_name) mac = mac.split("Permanent address:")[-1].strip() logging.debug("mac address of %s: %s", iface_name, mac) # backup the network script for other distros if "ubuntu" not in vm.get_distro().lower(): cmd = "service network restart" iface_scripts.append(utils_net.get_network_cfg_file(iface_name)) if not check_interface(str(iface_name), nic_filter): utils_net.create_network_script(iface_name, mac, boot_proto="static", net_mask=net_mask, vm=vm, ip_addr=static_ip) status, output = session.cmd_status_output(cmd) if status: test.error("Failed to set static ip in guest: " "%s" % output) # Test the newly added device if not utils_misc.wait_for(_check_ip, 120, 3, 3): ifconfig = session.cmd_output("ifconfig -a") test.fail("New hotpluged device could not get ip " "after 120s in guest. guest ifconfig " "output: \n%s" % ifconfig) try: session.cmd(params["pci_test_cmd"] % (pci_num + 1)) except aexpect.ShellError as e: test.fail("Check device failed after PCI " "hotplug. Output: %r" % e.output) except Exception: pci_del(pci_num, ignore_failure=True) raise # Hot delete a pci device def pci_del(pci_num, ignore_failure=False): def _device_removed(): after_del = vm.monitor.info("pci") return after_del != before_del before_del = vm.monitor.info("pci") if cmd_type == "pci_add": slot_id = "0" + pci_info[pci_num][1].split(",")[2].split()[1] cmd = "pci_del pci_addr=%s" % slot_id vm.monitor.send_args_cmd(cmd, convert=False) elif cmd_type == "device_add": cmd = "device_del id=%s" % pci_info[pci_num][0] vm.monitor.send_args_cmd(cmd) if (not utils_misc.wait_for(_device_removed, test_timeout, 0, 1) and not ignore_failure): test.fail("Failed to hot remove PCI device: %s. " "Monitor command: %s" % (pci_model, cmd)) vm = env.get_vm(params["main_vm"]) vm.verify_alive() timeout = int(params.get("login_timeout", 360)) session = vm.wait_for_serial_login(timeout=timeout) test_timeout = int(params.get("test_timeout", 360)) # Test if it is nic or block pci_num_range = int(params.get("pci_num", 1)) rp_times = int(params.get("repeat_times", 1)) pci_model = params.get("pci_model", "pci-assign") vf_filter = params.get("vf_filter_re") generate_mac = params.get("generate_mac", "yes") nic_filter = params["nic_interface_filter"] devices = [] device_type = params.get("hotplug_device_type", "vf") for i in range(pci_num_range): device = {} device["type"] = device_type if generate_mac == "yes": device['mac'] = utils_net.generate_mac_address_simple() if params.get("device_name"): device["name"] = params.get("device_name") devices.append(device) device_driver = params.get("device_driver", "pci-assign") if vm.pci_assignable is None: vm.pci_assignable = test_setup.PciAssignable( driver=params.get("driver"), driver_option=params.get("driver_option"), host_set_flag=params.get("host_setup_flag"), kvm_params=params.get("kvm_default"), vf_filter_re=vf_filter, pf_filter_re=params.get("pf_filter_re"), device_driver=device_driver, pa_type=params.get("pci_assignable")) pa_pci_ids = vm.pci_assignable.request_devs(devices) # Modprobe the module if specified in config file module = params.get("modprobe_module") if module: error_context.context("modprobe the module %s" % module, logging.info) session.cmd("modprobe %s" % module) # Probe qemu to verify what is the supported syntax for PCI hotplug if vm.monitor.protocol == 'qmp': cmd_o = vm.monitor.info("commands") else: cmd_o = vm.monitor.send_args_cmd("help") cmd_type = utils_misc.find_substring(str(cmd_o), "device_add", "pci_add") if not cmd_o: test.error("Unknown version of qemu") local_functions = locals() if params.get("enable_set_link" "yes") == "yes": error_context.context("Disable the primary link(s) of guest", logging.info) for nic in vm.virtnet: vm.set_link(nic.device_id, up=False) try: for j in range(rp_times): # pci_info is a list of list. # each element 'i' has 4 members: # pci_info[i][0] == device id, only used for device_add # pci_info[i][1] == output of device add command pci_info = [] if params.get("assign_static_ip", "no") == "yes": ip_gen = utils_net.gen_ipv4_addr(exclude_ips=[]) # backup the network script file if it is ubuntu if "ubuntu" in vm.get_distro().lower(): session = vm.wait_for_serial_login(timeout=timeout) iface_script = "/etc/network/interfaces" cmd = "cat %s" % iface_script if not session.cmd_status(cmd): logging.debug("Backup network script in guest - %s", iface_script) cmd = "cp %s %s.BACKUP" % (iface_script, iface_script) status, output = session.cmd_status_output(cmd) if status: test.error("Failed to backup in guest: %s" % output) for pci_num in range(pci_num_range): msg = "Start hot-adding %sth pci device," % (pci_num + 1) msg += " repeat %d" % (j + 1) error_context.context(msg, logging.info) add_device(pci_num) sub_type = params.get("sub_type_after_plug") if sub_type: error_context.context("Running sub test '%s' after hotplug" % sub_type, logging.info) utils_test.run_virt_sub_test(test, params, env, sub_type) if "guest_suspend" == sub_type: # Hotpluged device have been released after guest suspend, # so do not need unpluged step. break for pci_num in range(pci_num_range): msg = "start hot-deleting %sth pci device," % (pci_num + 1) msg += " repeat %d" % (j + 1) error_context.context(msg, logging.info) pci_del(-(pci_num + 1)) # cleanup network script after hot deleting pci device clean_network_scripts() finally: # clean network scripts on error clean_network_scripts() if params.get("enable_set_link", "yes") == "yes": error_context.context("Re-enabling the primary link(s) of guest", logging.info) for nic in vm.virtnet: vm.set_link(nic.device_id, up=True) if session: session.close()
def run(test, params, env): """ KVM sr-iov hotplug negatvie test: 1) Boot up VM. 2) Try to remove sr-iov device driver module (optional) 3) Hotplug sr-iov device to VM with negative parameters 4) Verify that qemu could handle the negative parameters check hotplug error message (optional) :param test: qemu test object :param params: Dictionary with the test parameters :param env: Dictionary with test environment. """ def make_pci_add_cmd(pa_pci_id, pci_addr="auto"): pci_add_cmd = ("pci_add pci_addr=%s host host=%s,if=%s" % (pci_addr, pa_pci_id, pci_model)) if params.get("hotplug_params"): assign_param = params.get("hotplug_params").split() for param in assign_param: value = params.get(param) if value: pci_add_cmd += ",%s=%s" % (param, value) return pci_add_cmd def make_device_add_cmd(pa_pci_id, pci_addr=None): device_id = "%s" % pci_model + "-" + utils_misc.generate_random_id() pci_add_cmd = ("device_add id=%s,driver=pci-assign,host=%s" % (device_id, pa_pci_id)) if pci_addr is not None: pci_add_cmd += ",addr=%s" % pci_addr if params.get("hotplug_params"): assign_param = params.get("hotplug_params").split() for param in assign_param: value = params.get(param) if value: pci_add_cmd += ",%s=%s" % (param, value) return pci_add_cmd neg_msg = params.get("negative_msg") vm = env.get_vm(params["main_vm"]) vm.verify_alive() rp_times = int(params.get("repeat_times", 1)) pci_model = params.get("pci_model", "pci-assign") pci_invaild_addr = params.get("pci_invaild_addr") modprobe_cmd = params.get("modprobe_cmd") device = {} device["type"] = params.get("hotplug_device_type", "vf") device['mac'] = utils_net.generate_mac_address_simple() if params.get("device_name"): device["name"] = params.get("device_name") if vm.pci_assignable is not None: pa_pci_ids = vm.pci_assignable.request_devs(device) # Probe qemu to verify what is the supported syntax for PCI hotplug if vm.monitor.protocol == 'qmp': cmd_output = vm.monitor.info("commands") else: cmd_output = vm.monitor.send_args_cmd("help") if not cmd_output: raise error.TestError("Unknown version of qemu") cmd_type = utils_misc.find_substring(str(cmd_output), "pci_add", "device_add") for j in range(rp_times): if cmd_type == "pci_add": pci_add_cmd = make_pci_add_cmd(pa_pci_ids[0], pci_invaild_addr) elif cmd_type == "device_add": pci_add_cmd = make_device_add_cmd(pa_pci_ids[0], pci_invaild_addr) try: msg = "Adding pci device with command '%s'" % pci_add_cmd error.context(msg, logging.info) case_fail = False add_output = vm.monitor.send_args_cmd(pci_add_cmd, convert=False) case_fail = True except Exception, err: if neg_msg: msg = "Check negative hotplug error message" error.context(msg, logging.info) if neg_msg not in str(err): msg = "Could not find '%s' in" % neg_msg msg += " command output '%s'" % add_output raise error.TestFail(msg) logging.debug("Could not boot up vm, %s" % err) if case_fail: if neg_msg: msg = "Check negative hotplug error message" error.context(msg, logging.info) if neg_msg not in str(add_output): msg = "Could not find '%s' in" % neg_msg msg += " command output '%s'" % add_output raise error.TestFail(msg) logging.debug("Could not boot up vm, %s" % add_output)
def run(test, params, env): """ Test interafce xml options. 1.Prepare test environment,destroy or suspend a VM. 2.Edit xml and start the domain. 3.Perform test operation. 4.Recover test environment. 5.Confirm the test result. """ vm_name = params.get("main_vm") vm = env.get_vm(vm_name) if vm.is_alive(): vm.wait_for_login() def create_iface_xml(mac): """ Create interface xml file """ iface = Interface(type_name=iface_type) iface.source = iface_source iface.model = iface_model if iface_model else "virtio" if iface_target: iface.target = {'dev': iface_target} iface.mac_address = mac if iface_rom: iface.rom = eval(iface_rom) logging.debug("Create new interface xml: %s", iface) return iface # Interface specific attributes. iface_num = params.get("iface_num", '1') iface_type = params.get("iface_type", "network") iface_source = eval(params.get("iface_source", "{'network':'default'}")) iface_model = params.get("iface_model") iface_target = params.get("iface_target") iface_mac = params.get("iface_mac") iface_rom = params.get("iface_rom") attach_device = "yes" == params.get("attach_device", "no") attach_iface = "yes" == params.get("attach_iface", "no") attach_option = params.get("attach_option", "") detach_device = "yes" == params.get("detach_device") stress_test = "yes" == params.get("stress_test") restart_libvirtd = "yes" == params.get("restart_libvirtd", "no") start_vm = "yes" == params.get("start_vm", "yes") options_test = "yes" == params.get("options_test", "no") username = params.get("username") password = params.get("password") poll_timeout = int(params.get("poll_timeout", 10)) err_msgs1 = params.get("err_msgs1") err_msgs2 = params.get("err_msgs2") err_msg_rom = params.get("err_msg_rom") # stree_test require detach operation stress_test_detach_device = False stress_test_detach_interface = False if stress_test: if attach_device: stress_test_detach_device = True if attach_iface: stress_test_detach_interface = True # The following detach-device step also using attach option detach_option = attach_option # Back up xml file. vmxml_backup = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) #iface_mac = vm_xml.VMXML.get_first_mac_by_name(vm_name) libvirtd = utils_libvirtd.Libvirtd() # Check virsh command option check_cmds = [] sep_options = attach_option.split() logging.debug("sep_options: %s" % sep_options) for sep_option in sep_options: if attach_device and sep_option: check_cmds.append(('attach-device', sep_option)) if attach_iface and sep_option: check_cmds.append(('attach-interface', sep_option)) if (detach_device or stress_test_detach_device) and sep_option: check_cmds.append(('detach-device', sep_option)) if stress_test_detach_interface and sep_option: check_cmds.append(('detach-device', sep_option)) for cmd, option in check_cmds: libvirt.virsh_cmd_has_option(cmd, option) try: try: # Attach an interface when vm is running iface_list = [] err_msgs = ("No more available PCI slots", "No more available PCI addresses") if not start_vm: virsh.destroy(vm_name) for i in range(int(iface_num)): if attach_device: logging.info("Try to attach device loop %s" % i) if iface_mac: mac = iface_mac else: mac = utils_net.generate_mac_address_simple() iface_xml_obj = create_iface_xml(mac) iface_xml_obj.xmltreefile.write() ret = virsh.attach_device(vm_name, iface_xml_obj.xml, flagstr=attach_option, ignore_status=True, debug=True) elif attach_iface: logging.info("Try to attach interface loop %s" % i) mac = utils_net.generate_mac_address_simple() options = ("%s %s --model %s --mac %s %s" % (iface_type, iface_source['network'], iface_model, mac, attach_option)) ret = virsh.attach_interface(vm_name, options, ignore_status=True) if ret.exit_status: if any([msg in ret.stderr for msg in err_msgs]): logging.debug("No more pci slots, can't attach more devices") break elif (ret.stderr.count("doesn't support option %s" % attach_option)): test.cancel(ret.stderr) elif err_msgs1 in ret.stderr: logging.debug("option %s is not supported when domain running is %s" % (attach_option, vm.is_alive())) if start_vm or ("--live" not in sep_options and attach_option): test.fail("return not supported, but it is unexpected") elif err_msgs2 in ret.stderr: logging.debug("options %s are mutually exclusive" % attach_option) if not ("--current" in sep_options and len(sep_options) > 1): test.fail("return mutualy exclusive, but it is unexpected") elif err_msg_rom in ret.stderr: logging.debug("Attach failed with expect err msg: %s" % err_msg_rom) else: test.fail("Failed to attach-interface: %s" % ret.stderr.strip()) elif stress_test: if attach_device: # Detach the device immediately for stress test ret = virsh.detach_device(vm_name, iface_xml_obj.xml, flagstr=detach_option, ignore_status=True) elif attach_iface: # Detach the device immediately for stress test options = ("--type %s --mac %s %s" % (iface_type, mac, detach_option)) ret = virsh.detach_interface(vm_name, options, ignore_status=True) libvirt.check_exit_status(ret) else: if attach_device: iface_list.append({'mac': mac, 'iface_xml': iface_xml_obj}) elif attach_iface: iface_list.append({'mac': mac}) # Restart libvirtd service if restart_libvirtd: libvirtd.restart() # After restart libvirtd, the old console was invalidated, # so we need create a new serial console vm.cleanup_serial_console() vm.create_serial_console() # in options test, check if the interface is attached # in current state when attach return true def check_iface_exist(): try: session = vm.wait_for_serial_login(username=username, password=password) if utils_net.get_linux_ifname(session, iface['mac']): return True else: logging.debug("can not find interface in vm") except Exception: return False if options_test: for iface in iface_list: if 'mac' in iface: # Check interface in dumpxml output if_attr = vm_xml.VMXML.get_iface_by_mac(vm_name, iface['mac']) if vm.is_alive() and attach_option == "--config": if if_attr: test.fail("interface should not exists " "in current live vm while " "attached by --config") else: if if_attr: logging.debug("interface %s found current " "state in xml" % if_attr['mac']) else: test.fail("no interface found in " "current state in xml") if if_attr: if if_attr['type'] != iface_type or \ if_attr['source'] != \ iface_source['network']: test.fail("Interface attribute doesn't " "match attachment options") # check interface in vm only when vm is active if vm.is_alive(): logging.debug("check interface in current state " "in vm") if not utils_misc.wait_for(check_iface_exist, timeout=20): if not attach_option == "--config": test.fail("Can't see interface " "in current state in vm") else: logging.debug("find interface in " "current state in vm") else: logging.debug("find interface in " "current state in vm") # in options test, if the attach is performed # when the vm is running # need to destroy and start to check again vm.destroy() # Start the domain if needed if vm.is_dead(): vm.start() session = vm.wait_for_serial_login(username=username, password=password) # check if interface is attached for iface in iface_list: if 'mac' in iface: logging.debug("check interface in xml") # Check interface in dumpxml output if_attr = vm_xml.VMXML.get_iface_by_mac(vm_name, iface['mac']) logging.debug(if_attr) if if_attr: logging.debug("interface {} is found in xml". format(if_attr['mac'])) if (if_attr['type'] != iface_type or if_attr['source'] != iface_source['network']): test.fail("Interface attribute doesn't " "match attachment options") if options_test and start_vm and attach_option \ in ("--current", "--live", ""): test.fail("interface should not exists when " "restart vm in options_test") else: logging.debug("no interface found in xml") if options_test and start_vm and attach_option in \ ("--current", "--live", ""): logging.debug("interface not exists next state " "in xml with %s" % attach_option) else: test.fail("Can't see interface in dumpxml") # Check interface on guest if not utils_misc.wait_for(check_iface_exist, timeout=20): logging.debug("can't see interface next state in vm") if start_vm and attach_option in \ ("--current", "--live", ""): logging.debug("it is expected") else: test.fail("should find interface " "but no seen in next state in vm") # Detach hot/cold-plugged interface at last if detach_device: for iface in iface_list: if 'iface_xml' in iface: ret = virsh.detach_device(vm_name, iface['iface_xml'].xml, flagstr="", ignore_status=True) libvirt.check_exit_status(ret) else: options = ("%s --mac %s" % (iface_type, iface['mac'])) ret = virsh.detach_interface(vm_name, options, ignore_status=True) libvirt.check_exit_status(ret) # Check if interface was detached for iface in iface_list: if 'mac' in iface: polltime = time.time() + poll_timeout while True: # Check interface in dumpxml output if not vm_xml.VMXML.get_iface_by_mac(vm_name, iface['mac']): break else: time.sleep(2) if time.time() > polltime: test.fail("Interface still " "exists after detachment") session.close() except virt_vm.VMStartError as e: logging.info(str(e)) test.fail('VM failed to start:\n%s' % e) finally: # Recover VM. logging.info("Restoring vm...") if vm.is_alive(): vm.destroy(gracefully=False) vmxml_backup.sync()
def vm_stress_events(self, event, vm): """ Stress events :param event: event name :param vm: vm object """ dargs = {'ignore_status': True, 'debug': True} for itr in range(self.iterations): if "vcpupin" in event: for vcpu in range(int(self.current_vcpu)): result = virsh.vcpupin(vm.name, vcpu, random.choice(self.host_cpu_list), **dargs) if not self.ignore_status: libvirt.check_exit_status(result) elif "emulatorpin" in event: for vcpu in range(int(self.current_vcpu)): result = virsh.emulatorpin(vm.name, random.choice( self.host_cpu_list), **dargs) if not self.ignore_status: libvirt.check_exit_status(result) elif "suspend" in event: result = virsh.suspend(vm.name, **dargs) if not self.ignore_status: libvirt.check_exit_status(result) time.sleep(self.event_sleep_time) result = virsh.resume(vm.name, **dargs) if not self.ignore_status: libvirt.check_exit_status(result) elif "cpuhotplug" in event: result = virsh.setvcpus(vm.name, self.max_vcpu, "--live", **dargs) if not self.ignore_status: libvirt.check_exit_status(result) exp_vcpu = {'max_config': self.max_vcpu, 'max_live': self.max_vcpu, 'cur_config': self.current_vcpu, 'cur_live': self.max_vcpu, 'guest_live': self.max_vcpu} utils_hotplug.check_vcpu_value( vm, exp_vcpu, option="--live") time.sleep(self.event_sleep_time) result = virsh.setvcpus(vm.name, self.current_vcpu, "--live", **dargs) if not self.ignore_status: libvirt.check_exit_status(result) exp_vcpu = {'max_config': self.max_vcpu, 'max_live': self.max_vcpu, 'cur_config': self.current_vcpu, 'cur_live': self.current_vcpu, 'guest_live': self.current_vcpu} utils_hotplug.check_vcpu_value( vm, exp_vcpu, option="--live") elif "reboot" in event: vm.reboot() elif "nethotplug" in event: for iface_num in range(int(self.iface_num)): logging.debug("Try to attach interface %d" % iface_num) mac = utils_net.generate_mac_address_simple() options = ("%s %s --model %s --mac %s %s" % (self.iface_type, self.iface_source['network'], self.iface_model, mac, self.attach_option)) logging.debug("VM name: %s , Options for Network attach: %s", vm.name, options) ret = virsh.attach_interface(vm.name, options, ignore_status=True) time.sleep(self.event_sleep_time) if not self.ignore_status: libvirt.check_exit_status(ret) if self.detach_option: options = ("--type %s --mac %s %s" % (self.iface_type, mac, self.detach_option)) logging.debug("VM name: %s , Options for Network detach: %s", vm.name, options) ret = virsh.detach_interface(vm.name, options, ignore_status=True) if not self.ignore_status: libvirt.check_exit_status(ret) elif "diskhotplug" in event: for disk_num in range(len(self.device_source_names)): disk = {} disk_attach_error = False disk_name = os.path.join(self.path, vm.name, self.device_source_names[disk_num]) device_source = libvirt.create_local_disk( self.disk_type, disk_name, self.disk_size, disk_format=self.disk_format) disk.update({"format": self.disk_format, "source": device_source}) disk_xml = Disk(self.disk_type) disk_xml.device = self.disk_device disk_xml.driver = {"name": self.disk_driver, "type": self.disk_format} ret = virsh.attach_disk(vm.name, disk["source"], self.device_target[disk_num], self.attach_option, debug=True) if not self.ignore_status: libvirt.check_exit_status(ret, disk_attach_error) if self.detach_option: ret = virsh.detach_disk(vm.name, self.device_target[disk_num], extra=self.detach_option) if not self.ignore_status: libvirt.check_exit_status(ret) libvirt.delete_local_disk(self.disk_type, disk_name) else: raise NotImplementedError
def run(test, params, env): """ Test interafce xml options. 1.Prepare test environment,destroy or suspend a VM. 2.Edit xml and start the domain. 3.Perform test operation. 4.Recover test environment. 5.Confirm the test result. """ vm_name = params.get("main_vm") vm = env.get_vm(vm_name) def create_iface_xml(mac): """ Create interface xml file """ iface = Interface(type_name=iface_type) iface.source = iface_source iface.model = iface_model if iface_model else "virtio" if iface_target: iface.target = {'dev': iface_target} iface.mac_address = mac logging.debug("Create new interface xml: %s", iface) return iface status_error = "yes" == params.get("status_error", "no") # Interface specific attributes. iface_num = params.get("iface_num", '1') iface_type = params.get("iface_type", "network") iface_source = eval(params.get("iface_source", "{'network':'default'}")) iface_model = params.get("iface_model") iface_target = params.get("iface_target") iface_mac = params.get("iface_mac") attach_device = "yes" == params.get("attach_device", "no") attach_iface = "yes" == params.get("attach_iface", "no") attach_option = params.get("attach_option", "") detach_device = "yes" == params.get("detach_device") stress_test = "yes" == params.get("stress_test") restart_libvirtd = "yes" == params.get("restart_libvirtd", "no") # Back up xml file. vmxml_backup = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) #iface_mac = vm_xml.VMXML.get_first_mac_by_name(vm_name) libvirtd = utils_libvirtd.Libvirtd() if vm.is_alive(): time.sleep(20) try: try: # Attach an interface when vm is running iface_list = [] err_msgs = ("No more available PCI slots", "No more available PCI addresses") if attach_device: for i in range(int(iface_num)): logging.info("Try to attach interface loop %s" % i) if iface_mac: mac = iface_mac else: mac = utils_net.generate_mac_address_simple() iface_xml_obj = create_iface_xml(mac) iface_xml_obj.xmltreefile.write() ret = virsh.attach_device(vm_name, iface_xml_obj.xml, flagstr=attach_option, ignore_status=True) if ret.exit_status: if any([msg in ret.stderr for msg in err_msgs]): logging.debug("No more pci slots, can't" " attach more devices") break elif (ret.stderr.count("doesn't support option %s" % attach_option)): raise error.TestNAError(ret.stderr) elif status_error: continue else: logging.error("Command output %s" % ret.stdout.strip()) raise error.TestFail("Failed to attach-device") elif stress_test: # Detach the device immediately for stress test ret = virsh.detach_device(vm_name, iface_xml_obj.xml, flagstr=attach_option, ignore_status=True) libvirt.check_exit_status(ret) else: iface_list.append({ 'mac': mac, 'iface_xml': iface_xml_obj }) # Need sleep here for attachment take effect time.sleep(5) elif attach_iface: for i in range(int(iface_num)): logging.info("Try to attach interface loop %s" % i) mac = utils_net.generate_mac_address_simple() options = ("%s %s --model %s --mac %s %s" % (iface_type, iface_source['network'], iface_model, mac, attach_option)) ret = virsh.attach_interface(vm_name, options, ignore_status=True) if ret.exit_status: if any([msg in ret.stderr for msg in err_msgs]): logging.debug("No more pci slots, can't" " attach more devices") break elif (ret.stderr.count("doesn't support option %s" % attach_option)): raise error.TestNAError(ret.stderr) elif status_error: continue else: logging.error("Command output %s" % ret.stdout.strip()) raise error.TestFail("Failed to attach-interface") elif stress_test: # Detach the device immediately for stress test ret = virsh.attach_interface(vm_name, options, ignore_status=True) libvirt.check_exit_status(ret) else: iface_list.append({'mac': mac}) # Need sleep here for attachment take effect time.sleep(5) # Restart libvirtd service if restart_libvirtd: libvirtd.restart() vm.cleanup_serial_console() # Start the domain if needed if vm.is_dead(): vm.start() session = vm.wait_for_serial_login() # Check if interface was attached for iface in iface_list: if iface.has_key('mac'): # Check interface in dumpxml output if_attr = vm_xml.VMXML.get_iface_by_mac( vm_name, iface['mac']) if not if_attr: raise error.TestFail("Can't see interface " " in dumpxml") if (if_attr['type'] != iface_type or if_attr['source'] != iface_source['network']): raise error.TestFail("Interface attribute doesn't" " match attachment opitons") # Check interface on guest if not utils_net.get_linux_ifname(session, iface['mac']): raise error.TestFail("Can't see interface" " on guest") # Detach hot/cold-plugged interface at last if detach_device: for iface in iface_list: if iface.has_key('iface_xml'): ret = virsh.detach_device(vm_name, iface['iface_xml'].xml, flagstr="", ignore_status=True) libvirt.check_exit_status(ret) else: options = ("%s --mac %s" % (iface_type, iface['mac'])) ret = virsh.detach_interface(vm_name, options, ignore_status=True) libvirt.check_exit_status(ret) # Check if interface was detached for iface in iface_list: if iface.has_key('mac'): # Check interface in dumpxml output if vm_xml.VMXML.get_iface_by_mac( vm_name, iface['mac']): raise error.TestFail("Interface still exist" " after detachment") session.close() except virt_vm.VMStartError, e: logging.info(str(e)) raise error.TestFail('VM Failed to start for some reason!') finally: # Recover VM. logging.info("Restoring vm...") if vm.is_alive(): vm.destroy(gracefully=False) vmxml_backup.sync()
def run(test, params, env): """ Test interface xml options. 1.Prepare test environment,destroy or suspend a VM. 2.Edit xml and start the domain. 3.Perform test operation. 4.Recover test environment. 5.Confirm the test result. """ vm_name = params.get("main_vm") vm = env.get_vm(vm_name) if vm.is_alive(): vm.wait_for_login() def create_iface_xml(mac=None): """ Create interface xml file """ iface = Interface(type_name=iface_type) iface.source = iface_source iface.model = iface_model if iface_model else "virtio" if iface_target: iface.target = {'dev': iface_target} if mac: iface.mac_address = mac if iface_rom: iface.rom = eval(iface_rom) logging.debug("Create new interface xml: %s", iface) return iface def get_all_mac_in_vm(): """ get the mac address list of all the interfaces from a running vm os return: a list of the mac addresses """ mac_list = [] interface_list = vm.get_interfaces() for iface_ in interface_list: mac_ = vm.get_interface_mac(iface_) mac_list.append(mac_) return mac_list # Interface specific attributes. iface_num = params.get("iface_num", '1') iface_type = params.get("iface_type", "network") iface_source = eval(params.get("iface_source", "{'network':'default'}")) iface_model = params.get("iface_model") iface_target = params.get("iface_target") iface_mac = params.get("iface_mac") iface_rom = params.get("iface_rom") attach_device = "yes" == params.get("attach_device", "no") attach_iface = "yes" == params.get("attach_iface", "no") attach_option = params.get("attach_option", "") detach_device = "yes" == params.get("detach_device") stress_test = "yes" == params.get("stress_test") restart_libvirtd = "yes" == params.get("restart_libvirtd", "no") start_vm = "yes" == params.get("start_vm", "yes") options_test = "yes" == params.get("options_test", "no") username = params.get("username") password = params.get("password") poll_timeout = int(params.get("poll_timeout", 10)) err_msgs1 = params.get("err_msgs1") err_msgs2 = params.get("err_msgs2") err_msg_rom = params.get("err_msg_rom") del_pci = "yes" == params.get("del_pci", "no") del_mac = "yes" == params.get("del_mac", "no") del_alias = "yes" == params.get("del_alias", "no") set_pci = "yes" == params.get("set_pci", "no") set_mac = "yes" == params.get("set_mac", "no") set_alias = "yes" == params.get("set_alias", "no") status_error = "yes" == params.get("status_error", "no") pci_addr = params.get("pci") check_mac = "yes" == params.get("check_mac", "no") vnet_mac = params.get("vnet_mac", None) customer_alias = "yes" == params.get("customer_alias", "no") detach_error = params.get("detach_error", None) libvirt_version.is_libvirt_feature_supported(params) # stree_test require detach operation stress_test_detach_device = False stress_test_detach_interface = False if stress_test: if attach_device: stress_test_detach_device = True if attach_iface: stress_test_detach_interface = True # The following detach-device step also using attach option detach_option = attach_option # Back up xml file. vmxml_backup = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) #iface_mac = vm_xml.VMXML.get_first_mac_by_name(vm_name) libvirtd = utils_libvirtd.Libvirtd() # Check virsh command option check_cmds = [] sep_options = attach_option.split() logging.debug("sep_options: %s" % sep_options) for sep_option in sep_options: if attach_device and sep_option: check_cmds.append(('attach-device', sep_option)) if attach_iface and sep_option: check_cmds.append(('attach-interface', sep_option)) if (detach_device or stress_test_detach_device) and sep_option: check_cmds.append(('detach-device', sep_option)) if stress_test_detach_interface and sep_option: check_cmds.append(('detach-device', sep_option)) for cmd, option in check_cmds: libvirt.virsh_cmd_has_option(cmd, option) try: try: # Attach an interface when vm is running iface_list = [] err_msgs = ("No more available PCI slots", "No more available PCI addresses") if not start_vm: virsh.destroy(vm_name) for i in range(int(iface_num)): if attach_device: logging.info("Try to attach device loop %s" % i) if iface_mac: mac = iface_mac iface_xml_obj = create_iface_xml(mac) elif check_mac: iface_xml_obj = create_iface_xml() else: mac = utils_net.generate_mac_address_simple() iface_xml_obj = create_iface_xml(mac) if customer_alias: random_id = process.run( "uuidgen", ignore_status=True, shell=True).stdout_text.strip() alias_str = "ua-" + random_id iface_xml_obj.alias = {"name": alias_str} logging.debug("Update number %s interface xml: %s", i, iface_xml_obj) iface_xml_obj.xmltreefile.write() if check_mac: mac_bef = get_all_mac_in_vm() ret = virsh.attach_device(vm_name, iface_xml_obj.xml, flagstr=attach_option, ignore_status=True, debug=True) elif attach_iface: logging.info("Try to attach interface loop %s" % i) if iface_mac: mac = iface_mac else: mac = utils_net.generate_mac_address_simple() options = ("%s %s --model %s --mac %s %s" % (iface_type, iface_source['network'], iface_model, mac, attach_option)) ret = virsh.attach_interface(vm_name, options, ignore_status=True) if ret.exit_status: if any([msg in ret.stderr for msg in err_msgs]): logging.debug( "No more pci slots, can't attach more devices") break elif ret.stderr.count("doesn't support option %s" % attach_option): test.cancel(ret.stderr) elif err_msgs1 in ret.stderr: logging.debug( "option %s is not supported when domain running is %s" % (attach_option, vm.is_alive())) if start_vm or ("--live" not in sep_options and attach_option): test.fail( "return not supported, but it is unexpected") elif err_msgs2 in ret.stderr: logging.debug("options %s are mutually exclusive" % attach_option) if not ("--current" in sep_options and len(sep_options) > 1): test.fail( "return mutualy exclusive, but it is unexpected" ) elif err_msg_rom and err_msg_rom in ret.stderr: logging.debug("Attach failed with expect err msg: %s" % err_msg_rom) else: test.fail("Failed to attach-interface: %s" % ret.stderr.strip()) elif stress_test: if attach_device: # Detach the device immediately for stress test ret = virsh.detach_device(vm_name, iface_xml_obj.xml, flagstr=detach_option, ignore_status=True) elif attach_iface: # Detach the device immediately for stress test options = ("--type %s --mac %s %s" % (iface_type, mac, detach_option)) ret = virsh.detach_interface(vm_name, options, ignore_status=True) libvirt.check_exit_status(ret) else: if attach_device: if check_mac: mac_aft = get_all_mac_in_vm() add_mac = list( set(mac_aft).difference(set(mac_bef))) try: mac = add_mac[0] logging.debug( "The mac address of the attached interface is %s" % mac) except IndexError: test.fail( "Can not find the new added interface in the guest os!" ) iface_list.append({ 'mac': mac, 'iface_xml': iface_xml_obj }) elif attach_iface: iface_list.append({'mac': mac}) # Restart libvirtd service if restart_libvirtd: libvirtd.restart() # After restart libvirtd, the old console was invalidated, # so we need create a new serial console vm.cleanup_serial_console() vm.create_serial_console() # in options test, check if the interface is attached # in current state when attach return true def check_iface_exist(): try: session = vm.wait_for_serial_login(username=username, password=password) if utils_net.get_linux_ifname(session, iface['mac']): return True else: logging.debug("can not find interface in vm") except Exception: return False if options_test: for iface in iface_list: if 'mac' in iface: # Check interface in dumpxml output if_attr = vm_xml.VMXML.get_iface_by_mac( vm_name, iface['mac']) if vm.is_alive() and attach_option == "--config": if if_attr: test.fail("interface should not exists " "in current live vm while " "attached by --config") else: if if_attr: logging.debug("interface %s found current " "state in xml" % if_attr['mac']) else: test.fail("no interface found in " "current state in xml") if if_attr: if if_attr['type'] != iface_type or \ if_attr['source'] != \ iface_source['network']: test.fail("Interface attribute doesn't " "match attachment options") # check interface in vm only when vm is active if vm.is_alive(): logging.debug("check interface in current state " "in vm") if not utils_misc.wait_for(check_iface_exist, timeout=20): if not attach_option == "--config": test.fail("Can't see interface " "in current state in vm") else: logging.debug("find interface in " "current state in vm") else: logging.debug("find interface in " "current state in vm") # in options test, if the attach is performed # when the vm is running # need to destroy and start to check again vm.destroy() # Start the domain if needed if vm.is_dead(): vm.start() session = vm.wait_for_serial_login(username=username, password=password) # check if interface is attached for iface in iface_list: if 'mac' in iface: logging.debug("check interface in xml") # Check interface in dumpxml output if_attr = vm_xml.VMXML.get_iface_by_mac( vm_name, iface['mac']) logging.debug(if_attr) if if_attr: logging.debug("interface {} is found in xml".format( if_attr['mac'])) if (if_attr['type'] != iface_type or if_attr['source'] != iface_source['network']): test.fail("Interface attribute doesn't " "match attachment options") if options_test and start_vm and attach_option \ in ("--current", "--live", ""): test.fail("interface should not exists when " "restart vm in options_test") else: logging.debug("no interface found in xml") if options_test and start_vm and attach_option in \ ("--current", "--live", ""): logging.debug("interface not exists next state " "in xml with %s" % attach_option) else: test.fail("Can't see interface in dumpxml") # Check interface on guest if not utils_misc.wait_for(check_iface_exist, timeout=20): logging.debug("can't see interface next state in vm") if start_vm and attach_option in \ ("--current", "--live", ""): logging.debug("it is expected") else: test.fail("should find interface " "but no seen in next state in vm") if vnet_mac: # get the name of the backend tap device iface_params = vm_xml.VMXML.get_iface_by_mac( vm_name, mac) target_name = iface_params['target']['dev'] # check the tap device mac on host tap_info = process.run("ip l show %s" % target_name, shell=True, ignore_status=True).stdout_text logging.debug("vnet_mac should be %s" % vnet_mac) logging.debug( "check on host for the details of tap device %s: %s" % (target_name, tap_info)) if vnet_mac not in tap_info: test.fail( "The mac address of the tap device do not match!" ) # Detach hot/cold-plugged interface at last if detach_device: logging.debug("detach interface here:") if attach_device: vmxml = vm_xml.VMXML.new_from_dumpxml(vm_name) iface_xml_ls = vmxml.get_devices("interface") iface_xml_ls_det = iface_xml_ls[1:] for iface_xml_det in iface_xml_ls_det: if del_mac: iface_xml_det.del_mac_address() if del_pci: iface_xml_det.del_address() if del_alias: iface_xml_det.del_alias() if set_mac: mac = utils_net.generate_mac_address_simple() iface_xml_det.set_mac_address(mac) if set_pci: pci_dict = ast.literal_eval(pci_addr) addr = iface_xml_det.new_iface_address( **{"attrs": pci_dict}) iface_xml_det.set_address(addr) if set_alias: random_id = process.run( "uuidgen", ignore_status=True, shell=True).stdout_text.strip() alias_str = "ua-" + random_id iface_xml_det.set_alias({"name": alias_str}) ori_pid_libvirtd = process.getoutput("pidof libvirtd") logging.debug( "The xml of the interface to detach is %s", iface_xml_det) ret = virsh.detach_device(vm_name, iface_xml_det.xml, flagstr="", ignore_status=True) libvirt.check_exit_status(ret, status_error) if detach_device and status_error and detach_error: if not ret.stderr.count(detach_error): test.error( "Detach fail as expected, but the error msg %s can not match!" % ret.stderr) aft_pid_libvirtd = process.getoutput("pidof libvirtd") if not utils_libvirtd.Libvirtd.is_running or ori_pid_libvirtd != aft_pid_libvirtd: test.fail( "Libvirtd crash after detach non-exists interface" ) else: for iface in iface_list: options = ("%s --mac %s" % (iface_type, iface['mac'])) ret = virsh.detach_interface(vm_name, options, ignore_status=True) libvirt.check_exit_status(ret) # Check if interface was detached if not status_error: for iface in iface_list: if 'mac' in iface: polltime = time.time() + poll_timeout while True: # Check interface in dumpxml output if not vm_xml.VMXML.get_iface_by_mac( vm_name, iface['mac']): break else: time.sleep(2) if time.time() > polltime: test.fail("Interface still " "exists after detachment") session.close() except virt_vm.VMStartError as e: logging.info(str(e)) test.fail('VM failed to start:\n%s' % e) finally: # Recover VM. logging.info("Restoring vm...") if vm.is_alive(): vm.destroy(gracefully=False) vmxml_backup.sync()
def run(test, params, env): """ SR-IOV devices sanity test: 1) Bring up VFs by following instructions How To in Setup. 2) Configure all VFs in host. 3) Check whether all VFs get ip in host. 4) Unbind PFs/VFs from host kernel driver to sr-iov driver. 5) Bind PFs/VFs back to host kernel driver. 6) Repeat step 4, 5. 7) Try to boot up guest(s) with VF(s). :param test: QEMU test object :param params: Dictionary with the test parameters :param env: Dictionary with test environment. """ device_driver = params.get("device_driver", "pci-assign") repeat_time = int(params.get("bind_repeat_time", 1)) configure_on_host = int(params.get("configure_on_host", 0)) static_ip = int(params.get("static_ip", 1)) serial_login = params.get("serial_login", "no") pci_assignable = test_setup.PciAssignable( driver=params.get("driver"), driver_option=params.get("driver_option"), host_set_flag=params.get("host_set_flag", 1), kvm_params=params.get("kvm_default"), vf_filter_re=params.get("vf_filter_re"), pf_filter_re=params.get("pf_filter_re"), device_driver=device_driver, pa_type=params.get("pci_assignable"), static_ip=static_ip, net_mask=params.get("net_mask"), start_addr_PF=params.get("start_addr_PF")) devices = [] device_type = params.get("device_type", "vf") if device_type == "vf": device_num = pci_assignable.get_vfs_count() if device_num == 0: msg = " No VF device found even after running SR-IOV setup" test.cancel(msg) elif device_type == "pf": device_num = len(pci_assignable.get_pf_vf_info()) else: msg = "Unsupport device type '%s'." % device_type msg += " Please set device_type to 'vf' or 'pf'." test.error(msg) for i in range(device_num): device = {} device["type"] = device_type if device_type == "vf": device['mac'] = utils_net.generate_mac_address_simple() if params.get("device_name"): device["name"] = params.get("device_name") devices.append(device) pci_assignable.devices = devices vf_pci_id = [] pf_vf_dict = pci_assignable.get_pf_vf_info() for pf_dict in pf_vf_dict: vf_pci_id.extend(pf_dict["vf_ids"]) ethname_dict = [] ips = {} # Not all test environments would have a dhcp server to serve IP for # all mac addresses. So configure_on_host param has been # introduced to choose whether configure VFs on host or not if configure_on_host: msg = "Configure all VFs in host." error_context.context(msg, logging.info) for pci_id in vf_pci_id: ethname = utils_misc.get_interface_from_pci_id(pci_id) mac = utils_net.generate_mac_address_simple() ethname_dict.append(ethname) # TODO:cleanup of the network scripts try: utils_net.create_network_script(ethname, mac, "dhcp", "255.255.255.0", on_boot="yes") except Exception as info: test.error("Network script creation failed - %s" % info) msg = "Check whether VFs could get ip in host." error_context.context(msg, logging.info) for ethname in ethname_dict: utils_net.bring_down_ifname(ethname) _ip = check_network_interface_ip(ethname) if not _ip: msg = "Interface '%s' could not get IP." % ethname logging.error(msg) else: ips[ethname] = _ip logging.info("Interface '%s' get IP '%s'", ethname, _ip) for i in range(repeat_time): msg = "Bind/unbind device from host. Repeat %s/%s" % (i + 1, repeat_time) error_context.context(msg, logging.info) bind_device_num = random.randint(1, device_num) pci_assignable.request_devs(devices[:bind_device_num]) logging.info("Sleep 3s before releasing vf to host.") time.sleep(3) pci_assignable.release_devs() logging.info("Sleep 3s after releasing vf to host.") time.sleep(3) if device_type == "vf": post_device_num = pci_assignable.get_vfs_count() else: post_device_num = len(pci_assignable.get_pf_vf_info()) if post_device_num != device_num: msg = "lspci cannot report the correct PF/VF number." msg += " Correct number is '%s'" % device_num msg += " lspci report '%s'" % post_device_num test.fail(msg) dmesg = process.system_output("dmesg") file_name = "host_dmesg_after_unbind_device.txt" logging.info("Log dmesg after bind/unbing device to '%s'.", file_name) if configure_on_host: msg = "Check whether VFs still get ip in host." error_context.context(msg, logging.info) for ethname in ips: utils_net.bring_up_ifname(ethname, action="up") _ip = utils_net.get_ip_address_by_interface(ethname, ip_ver="ipv4") if not _ip: msg = "Interface '%s' could not get IP." % ethname msg += "Before bind/unbind it have IP '%s'." % ips[ethname] logging.error(msg) else: logging.info("Interface '%s' get IP '%s'", ethname, _ip) msg = "Try to boot up guest(s) with VF(s)." error_context.context(msg, logging.info) regain_ip_cmd = params.get("regain_ip_cmd", None) timeout = int(params.get("login_timeout", 30)) for vm_name in params["vms"].split(" "): params["start_vm"] = "yes" vm = env.get_vm(vm_name) # User can opt for dhcp IP or a static IP configuration for probed # interfaces inside guest. Added option for static IP configuration # below if static_ip: if 'IP_addr_VF' not in locals(): IP_addr_VF = netaddr.IPAddress(params.get("start_addr_VF")) net_mask = params.get("net_mask") if not IP_addr_VF: test.fail("No IP address found, please" "populate starting IP address in " "configuration file") session = vm.wait_for_serial_login( timeout=int(params.get("login_timeout", 720))) rc, output = session.cmd_status_output( "ip li| grep -i 'BROADCAST'|awk '{print $2}'| sed 's/://'") if not rc: iface_probed = output.splitlines() logging.info("probed VF Interface(s) in guest: %s", iface_probed) for iface in iface_probed: mac = utils_net.get_linux_mac(session, iface) utils_net.set_guest_ip_addr(session, mac, IP_addr_VF) rc, output = utils_test.ping( str(IP_addr_VF), 30, timeout=60) if rc != 0: test.fail("New nic failed ping test" "with output:\n %s" % output) IP_addr_VF = IP_addr_VF + 1 else: test.fail("Fail to locate probed interfaces" "for VFs, please check on respective" "drivers in guest image") else: # User has opted for DHCP IP inside guest vm.verify_alive() vm.wait_for_login(timeout=int(params.get("login_timeout", 360)))
def run(test, params, env): """ Test command: virsh net-dhcp-leases 1. Create a new network and run virsh command to check dhcp leases info. 2. Attach an interface before or after start the domain, then check the dhcp leases info. 3. Clean the environment. """ vm_name = params.get("main_vm") vm = env.get_vm(vm_name) net_name = params.get("net_name", "default") net_option = params.get("net_option", "") status_error = "yes" == params.get("status_error", "no") prepare_net = "yes" == params.get("prepare_net", "yes") hotplug_iface = "yes" == params.get("hotplug_interface", "no") filter_by_mac = "yes" == params.get("filter_by_mac", "no") invalid_mac = "yes" == params.get("invalid_mac", "no") expect_msg = params.get("leases_err_msg") # Generate a random string as the MAC address nic_mac = None if invalid_mac: nic_mac = utils_misc.generate_random_string(17) # Command won't fail on old libvirt if not libvirt_version.version_compare(1, 3, 1) and invalid_mac: logging.debug("Reset case to positive as BZ#1261432") status_error = False def create_network(): """ Create a network """ net_ip_addr = params.get("net_ip_addr", "192.168.200.1") net_ip_netmask = params.get("net_ip_netmask", "255.255.255.0") net_dhcp_start = params.get("net_dhcp_start", "192.168.200.2") net_dhcp_end = params.get("net_dhcp_end", "192.168.200.254") netxml = network_xml.NetworkXML() netxml.name = net_name netxml.forward = {'mode': "nat"} ipxml = network_xml.IPXML() ipxml.address = net_ip_addr ipxml.netmask = net_ip_netmask ipxml.dhcp_ranges = {'start': net_dhcp_start, "end": net_dhcp_end} netxml.set_ip(ipxml) netxml.create() def get_net_dhcp_leases(output): """ Return the dhcp lease info in a list """ leases = [] lines = output.splitlines() if not lines: return leases try: pat = r"\S+\ ?\S+\ ?\S+\ ?\S+|\S+" keys = re.findall(pat, lines[0]) for line in lines[2:]: values = re.findall(pat, line) leases.append(dict(list(zip(keys, values)))) return leases except Exception: test.error("Fail to parse output: %s" % output) def get_ip_by_mac(mac_addr, try_dhclint=False, timeout=120): """ Get interface IP address by given MAC addrss. If try_dhclint is True, then try to allocate IP addrss for the interface. """ session = vm.wait_for_login(login_nic_index, timeout=timeout, serial=True) def f(): return utils_net.get_guest_ip_addr(session, mac_addr) try: ip_addr = utils_misc.wait_for(f, 10) if ip_addr is None: iface_name = utils_net.get_linux_ifname(session, mac_addr) if try_dhclint: session.cmd("dhclient %s" % iface_name) ip_addr = utils_misc.wait_for(f, 10) else: # No IP for the interface, just print the interface name logging.warn("Find '%s' with MAC address '%s', " "but which has no IP address", iface_name, mac_addr) finally: session.close() return ip_addr def check_net_lease(net_leases, expected_find=True): """ Check the dhcp lease info. """ if not net_leases: if expected_find: test.fail("Lease info is empty") else: logging.debug("No dhcp lease info find as expected") else: if not expected_find: test.fail("Find unexpected dhcp lease info: %s" % net_leases) find_mac = False for net_lease in net_leases: net_mac = net_lease['MAC address'] net_ip = net_lease['IP address'][:-3] if vm_xml.VMXML.get_iface_by_mac(vm_name, net_mac): find_mac = True logging.debug("Find '%s' in domain XML", net_mac) else: logging.debug("Not find '%s' in domain XML", net_mac) continue iface_ip = get_ip_by_mac(net_mac) if iface_ip and iface_ip != net_ip: test.fail("Address '%s' is not expected" % iface_ip) if expected_find and not find_mac: test.fail("No matched MAC address") vmxml = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) vmxml_backup = vmxml.copy() if vm.is_alive(): vm.destroy(gracefully=False) login_nic_index = 0 new_nic_index = 0 # Cleanup dirty dnsmaq, firstly get all network,and destroy all networks except # default net_state = virsh.net_state_dict(only_names=True) logging.debug("current networks: %s, destroy and undefine networks " "except default!", net_state) for net in net_state: if net != "default": virsh.net_destroy(net) virsh.net_undefine(net) cmd = "ps aux|grep dnsmasq|grep -v grep | grep -v default | awk '{print $2}'" pid_list = results_stdout_52lts(process.run(cmd, shell=True)).strip().splitlines() logging.debug(pid_list) for pid in pid_list: utils_misc.safe_kill(pid, signal.SIGKILL) # Create new network if prepare_net: create_network() nets = virsh.net_state_dict() if net_name not in list(nets.keys()) and not status_error: test.error("Not find network '%s'" % net_name) expected_find = False try: result = virsh.net_dhcp_leases(net_name, mac=nic_mac, options=net_option, debug=True, ignore_status=True) utlv.check_exit_status(result, status_error) lease = get_net_dhcp_leases(result.stdout.strip()) check_net_lease(lease, expected_find) if not status_error: iface_mac = utils_net.generate_mac_address_simple() if filter_by_mac: nic_mac = iface_mac op = "--type network --model virtio --source %s --mac %s" \ % (net_name, iface_mac) nic_params = {'mac': iface_mac, 'nettype': 'bridge', 'ip_version': 'ipv4'} login_timeout = 120 if not hotplug_iface: op += " --config" virsh.attach_interface(vm_name, option=op, debug=True, ignore_status=False) vm.add_nic(**nic_params) vm.start() new_nic_index = vm.get_nic_index_by_mac(iface_mac) if new_nic_index > 0: login_nic_index = new_nic_index else: vm.start() # wait for VM start before hotplug interface vm.wait_for_serial_login() virsh.attach_interface(vm_name, option=op, debug=True, ignore_status=False) vm.add_nic(**nic_params) # As VM already started, so the login timeout could be shortened login_timeout = 10 new_interface_ip = get_ip_by_mac(iface_mac, try_dhclint=True, timeout=login_timeout) # Allocate IP address for the new interface may fail, so only # check the result if get new IP address if new_interface_ip: expected_find = True result = virsh.net_dhcp_leases(net_name, mac=nic_mac, debug=False, ignore_status=True) utlv.check_exit_status(result, status_error) lease = get_net_dhcp_leases(result.stdout.strip()) check_net_lease(lease, expected_find) else: if expect_msg: utlv.check_result(result, expect_msg.split(';')) finally: # Delete the new attached interface if new_nic_index > 0: vm.del_nic(new_nic_index) if vm.is_alive(): vm.destroy(gracefully=False) vmxml_backup.sync() if prepare_net: virsh.net_destroy(net_name)
def run_sr_iov_hotplug(test, params, env): """ Test hotplug of sr-iov devices. (Elements between [] are configurable test parameters) 1) Set up sr-iov test environment in host. 2) Start VM. 3) PCI add one/multi sr-io deivce with (or without) repeat 4) Compare output of monitor command 'info pci'. 5) Compare output of guest command [reference_cmd]. 6) Verify whether pci_model is shown in [pci_find_cmd]. 7) Check whether the newly added PCI device works fine. 8) Delete the device, verify whether could remove the sr-iov device. :param test: QEMU test object. :param params: Dictionary with the test parameters. :param env: Dictionary with test environment. """ def pci_add_iov(pci_num): pci_add_cmd = ("pci_add pci_addr=auto host host=%s,if=%s" % (pa_pci_ids[pci_num], pci_model)) if params.get("hotplug_params"): assign_param = params.get("hotplug_params").split() for param in assign_param: value = params.get(param) if value: pci_add_cmd += ",%s=%s" % (param, value) return pci_add(pci_add_cmd) def pci_add(pci_add_cmd): error.context("Adding pci device with command 'pci_add'") add_output = vm.monitor.send_args_cmd(pci_add_cmd, convert=False) pci_info.append(['', add_output]) if not "OK domain" in add_output: raise error.TestFail("Add PCI device failed. " "Monitor command is: %s, Output: %r" % (pci_add_cmd, add_output)) return vm.monitor.info("pci") def check_support_device(dev): if vm.monitor.protocol == 'qmp': devices_supported = vm.monitor.human_monitor_cmd("%s ?" % cmd_type) else: devices_supported = vm.monitor.send_args_cmd("%s ?" % cmd_type) # Check if the device is support in qemu is_support = utils_test.find_substring(devices_supported, dev) if not is_support: raise error.TestError("%s doesn't support device: %s" % (cmd_type, dev)) def device_add_iov(pci_num): device_id = "%s" % pci_model + "-" + utils_misc.generate_random_id() pci_info.append([device_id]) check_support_device("pci-assign") pci_add_cmd = ("device_add id=%s,driver=pci-assign,host=%s" % (pci_info[pci_num][0], pa_pci_ids[pci_num])) if params.get("hotplug_params"): assign_param = params.get("hotplug_params").split() for param in assign_param: value = params.get(param) if value: pci_add_cmd += ",%s=%s" % (param, value) return device_add(pci_num, pci_add_cmd) def device_add(pci_num, pci_add_cmd): error.context("Adding pci device with command 'device_add'") if vm.monitor.protocol == 'qmp': add_output = vm.monitor.send_args_cmd(pci_add_cmd) else: add_output = vm.monitor.send_args_cmd(pci_add_cmd, convert=False) pci_info[pci_num].append(add_output) after_add = vm.monitor.info("pci") if pci_info[pci_num][0] not in after_add: logging.debug("Print info pci after add the block: %s" % after_add) raise error.TestFail("Add device failed. Monitor command is: %s" ". Output: %r" % (pci_add_cmd, add_output)) return after_add # Hot add a pci device def add_device(pci_num): reference_cmd = params["reference_cmd"] find_pci_cmd = params["find_pci_cmd"] info_pci_ref = vm.monitor.info("pci") reference = session.cmd_output(reference_cmd) try: # get function for adding device. add_fuction = local_functions["%s_iov" % cmd_type] except Exception: raise error.TestError( "No function for adding sr-iov dev with '%s'" % cmd_type) after_add = None if add_fuction: # Do add pci device. after_add = add_fuction(pci_num) try: # Define a helper function to compare the output def _new_shown(): o = session.cmd_output(reference_cmd) return o != reference # Define a helper function to catch PCI device string def _find_pci(): o = session.cmd_output(find_pci_cmd) if re.search(match_string, o, re.IGNORECASE): return True else: return False error.context("Start checking new added device") # Compare the output of 'info pci' if after_add == info_pci_ref: raise error.TestFail("No new PCI device shown after executing " "monitor command: 'info pci'") secs = int(params["wait_secs_for_hook_up"]) if not utils_misc.wait_for(_new_shown, test_timeout, secs, 3): raise error.TestFail("No new device shown in output of command " "executed inside the guest: %s" % reference_cmd) if not utils_misc.wait_for(_find_pci, test_timeout, 3, 3): raise error.TestFail("New add sr-iov device not found in guest. " "Command was: %s" % find_pci_cmd) # Test the newly added device try: session.cmd(params["pci_test_cmd"] % (pci_num + 1)) except aexpect.ShellError, e: raise error.TestFail("Check for sr-iov device failed after PCI " "hotplug. Output: %r" % e.output) except Exception: pci_del(pci_num, ignore_failure=True) raise # Hot delete a pci device def pci_del(pci_num, ignore_failure=False): def _device_removed(): after_del = vm.monitor.info("pci") return after_del != before_del before_del = vm.monitor.info("pci") if cmd_type == "pci_add": slot_id = "0" + pci_info[pci_num][1].split(",")[2].split()[1] cmd = "pci_del pci_addr=%s" % slot_id vm.monitor.send_args_cmd(cmd, convert=False) elif cmd_type == "device_add": cmd = "device_del id=%s" % pci_info[pci_num][0] vm.monitor.send_args_cmd(cmd) if (not utils_misc.wait_for(_device_removed, test_timeout, 0, 1) and not ignore_failure): raise error.TestFail("Failed to hot remove PCI device: %s. " "Monitor command: %s" % (pci_model, cmd)) vm = env.get_vm(params["main_vm"]) vm.verify_alive() timeout = int(params.get("login_timeout", 360)) session = vm.wait_for_login(timeout=timeout) test_timeout = int(params.get("test_timeout", 360)) # Test if it is nic or block pci_num_range = int(params.get("pci_num", 1)) rp_times = int(params.get("repeat_times", 1)) pci_model = params.get("pci_model", "pci-assign") # Need udpate match_string if you use a card other than 82576 match_string = params.get("match_string", "82576") devices = [] device_type = params.get("hotplug_device_type", "vf") for i in xrange(pci_num_range): device = {} device["type"] = device_type device['mac'] = utils_net.generate_mac_address_simple() if params.get("device_name"): device["name"] = params.get("device_name") devices.append(device) if vm.pci_assignable is not None: pa_pci_ids = vm.pci_assignable.request_devs(devices) # Modprobe the module if specified in config file module = params.get("modprobe_module") if module: error.context("modprobe the module %s" % module, logging.info) session.cmd("modprobe %s" % module) # Probe qemu to verify what is the supported syntax for PCI hotplug if vm.monitor.protocol == 'qmp': cmd_o = vm.monitor.info("commands") else: cmd_o = vm.monitor.send_args_cmd("help") cmd_type = utils_test.find_substring(str(cmd_o), "device_add", "pci_add") if not cmd_o: raise error.TestError("Unknow version of qemu") local_functions = locals() for j in range(rp_times): # pci_info is a list of list. # each element 'i' has 4 members: # pci_info[i][0] == device id, only used for device_add # pci_info[i][1] == output of device add command pci_info = [] for pci_num in xrange(pci_num_range): msg = "Start hot-adding %sth pci device, repeat %d" % (pci_num + 1, j + 1) error.context(msg, logging.info) add_device(pci_num) for pci_num in xrange(pci_num_range): msg = "start hot-deleting %sth pci device repeat %d" % (pci_num + 1, j + 1) error.context(msg, logging.info) pci_del(-(pci_num + 1))
def run(test, params, env): """ Test autogenerated tap device name. 1.Prepare test environment, restart libvirtd to flush the counter. 2.start the domain. 3.check the tap device name, then destroy the vm, restart libvirtd if needed, then start the vm again; 4.hotplug an interface and check the tap device name; 5.detach one interface, restart libvirtd if needed, then hotplug again to check the tap device name; 6.hotplug interface of another type, check tap device name; 7.recover the env. """ def prepare_vmxml(vm, vm_name, direct=False): """ Ensure there is only 1 requested interface in the vmxml param vm: the test vm param vm_name: the vm'name param direct: True or False, if True, prepare vm xml with a direct type interface(the tap device will be named as macvtap*); if False , prepare vm xml with a network type interface connected to default network(the tap device will be named as vnet* automatically instead) :return: None """ libvirt_vmxml.remove_vm_devices_by_type(vm, 'interface') iface_dict = prepare_iface_dict(direct) libvirt.modify_vm_iface(vm_name, 'update_iface', iface_dict) def prepare_iface_dict(direct=False): """ Prepare the iface_dict for the function 'libvirt.modify_vm_iface()' to use :param direct: True or False :return: a dictionary """ if direct: iface_name = utils_net.get_net_if(state="UP")[0] source = "{'dev': '%s', 'mode': 'bridge'}" % iface_name iface_dict = { "model": "virtio", 'type': 'direct', 'source': source, 'del_addr': 'yes', 'del_alias': 'yes', 'del_target': 'yes' } else: iface_dict = { "model": "virtio", 'type': 'network', 'source': "{'network': 'default'}", 'del_addr': 'yes', 'del_alias': 'yes', 'del_target': 'yes' } return iface_dict def check_target_name(index, direct=False): """ Check the auto generated tap device name on the host param index: an integer range in {-1,}, the max index occupied, if there is no occupied, initial index is -1 param direct: True or False. True: the expected auto-generated tap device name should be 'macvtap${index+1}' False: the expected auto-generated tap device name should be "vnet${index+1}" :return: None """ cmd_output = process.run("ls /sys/class/net", shell=True, ignore_status=True).stdout_text logging.debug("Current network interfaces on the host includes: %s", cmd_output) index_ = int(index) if direct: expected_name = 'macvtap' + str(index_ + 1) else: expected_name = "vnet" + str(index_ + 1) if expected_name not in cmd_output: test.fail("Can not get the expected tap: %s" % expected_name) else: logging.debug("Get the expected tap: %s" % expected_name) return libvirt_version.is_libvirt_feature_supported(params) vm_name = params.get("main_vm") vm = env.get_vm(vm_name) # Destroy VM first if vm.is_alive(): vm.destroy(gracefully=False) test_macvtap = "yes" == params.get("test_macvtap", "no") flush_with_occupation = "yes" == params.get("flush_with_occupation", "no") flush_after_detach = "yes" == params.get("flush_after_detach", "no") # if there is existing vnet* or macvtap* even the test vm is destroyed, # the env is not clean, cancel the test cmd_output = process.run("ls /sys/class/net", shell=True, ignore_status=True).stdout_text if ('vnet' in cmd_output and not test_macvtap) or \ ('macvtap' in cmd_output and test_macvtap): test.cancel("The env is not clean, there is existing tap device!") # Back up xml file. vmxml_backup = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) libvirtd = utils_libvirtd.Libvirtd() try: prepare_vmxml(vm, vm_name, test_macvtap) libvirt_network.ensure_default_network() libvirtd.restart() # if there is no vm running, restart libvirtd will flush the counter # to the initial value: -1 counter = -1 vm.start() # check the auto generated tap device name after fresh initialized logging.debug("1. Check the tap device name after initialized:") check_target_name(counter, test_macvtap) # if the vm start successfully and tap device create successfully, # current counter should increase by 1 counter = counter + 1 # destroy and start the vm again vm.destroy() time.sleep(2) # flush when vm down, if flushed, the counter is initialized to -1 if flush_with_occupation: libvirtd.restart() counter = -1 time.sleep(2) vm.start() logging.debug( "2. Check tap name after destroy and start vm again with " "libvirtd restart: %s:", flush_with_occupation) check_target_name(counter, test_macvtap) # new tap created after vm start, counter increase by 1 counter = counter + 1 # add another interface with the same interface type if_dict = prepare_iface_dict(test_macvtap) mac_addr = utils_net.generate_mac_address_simple() if_dict['mac'] = mac_addr iface_add_xml = libvirt.modify_vm_iface(vm_name, 'get_xml', if_dict) virsh.attach_device(vm_name, iface_add_xml, debug=True, ignore_status=False) time.sleep(2) logging.debug("3. Check tap name after hotplug an interface:") check_target_name(counter, test_macvtap) # one interface with same iface type attached, counter increase by 1 counter = counter + 1 # Make sure the guest boots up. Otherwise detach_device won't complete vm.wait_for_serial_login(timeout=180).close() # detach the new attached interface virsh.detach_device(vm_name, iface_add_xml, wait_for_event=True, debug=True, ignore_status=False) if flush_after_detach: libvirtd.restart() # the latest occupied name is recycled after restart # the counter is not initialized to -1 as the vm is running and # first tap name is occupied counter = counter - 1 virsh.attach_device(vm_name, iface_add_xml, debug=True, ignore_status=False) logging.debug( "4 Check tap name after detach and reattach with " "flushed: %s:", flush_after_detach) check_target_name(counter, test_macvtap) finally: if vm.is_alive(): vm.destroy(gracefully=False) vmxml_backup.sync()
def run(test, params, env): """ Test command: virsh net-dhcp-leases 1. Create a new network and run virsh command to check dhcp leases info. 2. Attach an interface before or after start the domain, then check the dhcp leases info. 3. Clean the environment. """ vm_name = params.get("main_vm") vm = env.get_vm(vm_name) net_name = params.get("net_name", "default") nic_mac = params.get("nic_mac", "") net_option = params.get("net_option", "") status_error = "yes" == params.get("status_error", "no") prepare_net = "yes" == params.get("prepare_net", "yes") hotplug_iface = "yes" == params.get("hotplug_interface", "no") filter_by_mac = "yes" == params.get("filter_by_mac", "no") exist_bug = params.get("exist_bug") def create_network(): """ Create a network """ net_ip_addr = params.get("net_ip_addr", "192.168.200.1") net_ip_netmask = params.get("net_ip_netmask", "255.255.255.0") net_dhcp_start = params.get("net_dhcp_start", "192.168.200.2") net_dhcp_end = params.get("net_dhcp_end", "192.168.200.254") netxml = network_xml.NetworkXML() netxml.name = net_name netxml.forward = {'mode': "nat"} ipxml = network_xml.IPXML() ipxml.address = net_ip_addr ipxml.netmask = net_ip_netmask ipxml.dhcp_ranges = {'start': net_dhcp_start, "end": net_dhcp_end} netxml.set_ip(ipxml) netxml.create() def get_net_dhcp_leases(output): """ Return the dhcp lease info in a list """ leases = [] lines = output.splitlines() if not lines: return leases try: pat = r"\S+\ ?\S+\ ?\S+\ ?\S+|\S+" keys = re.findall(pat, lines[0]) for line in lines[2:]: values = re.findall(pat, line) leases.append(dict(zip(keys, values))) return leases except: raise error.TestError("Fail to parse output: %s" % output) def get_ip_by_mac(mac_addr, try_dhclint=False): """ Get interface IP address by given MAC addrss. If try_dhclint is True, then try to allocate IP addrss for the interface. """ session = vm.wait_for_login() def f(): return utils_net.get_guest_ip_addr(session, mac_addr) try: ip_addr = utils_misc.wait_for(f, 10) if ip_addr is None: iface_name = utils_net.get_linux_ifname(session, mac_addr) if try_dhclint: session.cmd("dhclient %s" % iface_name) ip_addr = utils_misc.wait_for(f, 10) else: # No IP for the interface, just print the interface name logging.warn("Find '%s' with MAC address '%s', " "but which has no IP address", iface_name, mac_addr) finally: session.close() return ip_addr def check_net_lease(net_leases, expected_find=True): """ Check the dhcp lease info. """ if not net_leases: if expected_find: raise error.TestFail("Lease info is empty") else: logging.debug("No dhcp lease info find as expected") else: if not expected_find: raise error.TestFail("Find unexpected dhcp lease info: %s" % net_leases) find_mac = False for net_lease in net_leases: net_mac = net_lease['MAC address'] net_ip = net_lease['IP address'][:-3] if vm_xml.VMXML.get_iface_by_mac(vm_name, net_mac): find_mac = True logging.debug("Find '%s' in domain XML", net_mac) else: logging.debug("Not find '%s' in domain XML", net_mac) continue iface_ip = get_ip_by_mac(net_mac) if iface_ip and iface_ip != net_ip: raise error.TestFail("Address '%s' is not expected" % iface_ip) if expected_find and not find_mac: raise error.TestFail("No matched MAC address") vmxml = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) vmxml_backup = vmxml.copy() # Remove all interfaces of the VM if vm.is_alive(): vm.destroy(gracefully=False) vm.free_mac_address(0) vmxml.remove_all_device_by_type("interface") # Create new network if prepare_net: create_network() nets = virsh.net_state_dict() if net_name not in nets.keys() and not status_error: raise error.TestError("Not find network '%s'" % net_name) expected_find = False try: result = virsh.net_dhcp_leases(net_name, mac=nic_mac, options=net_option, debug=True, ignore_status=True) utlv.check_exit_status(result, status_error) lease = get_net_dhcp_leases(result.stdout.strip()) check_net_lease(lease, expected_find) if not status_error: iface_mac = utils_net.generate_mac_address_simple() if filter_by_mac: nic_mac = iface_mac op = "--type network --source %s --mac %s" % (net_name, iface_mac) if not hotplug_iface: op += " --config" virsh.attach_interface(vm_name, option=op, debug=True, ignore_status=False) vm.start() else: vm.start() virsh.attach_interface(vm_name, option=op, debug=True, ignore_status=False) new_interface_ip = get_ip_by_mac(iface_mac, try_dhclint=True) # Allocate IP address for the new interface may fail, so only # check the result if get new IP address if new_interface_ip: expected_find = True result = virsh.net_dhcp_leases(net_name, mac=nic_mac, debug=False, ignore_status=True) utlv.check_exit_status(result, status_error) lease = get_net_dhcp_leases(result.stdout.strip()) check_net_lease(lease, expected_find) finally: if exist_bug: logging.warn("Case may failed as bug: %s", BUG_URL % exist_bug) if vm.is_alive(): vm.destroy(gracefully=False) vmxml_backup.sync() if prepare_net: virsh.net_destroy(net_name)
def run(test, params, env): """ QEMU 'multi macvtap devices' test 1) Create and up multi macvtap devices in setting mode. 2) change the limitations of fd to 10240 in host. 3) Boot multi guest with macvtap and at least one guest use fd whick bigger than 1024. 4) Ping from guests to an external host for 100 counts. 5) Shutdown all guests. 6) Delete all macvtap interfaces. :param test: QEMU test object. :param params: Dictionary with the test parameters. :param env: Dictionary with test environment. """ macvtap_num = int(params.get("macvtap_num", "5000")) macvtap_mode = params.get("macvtap_mode", "vepa") timeout = int(params.get("login_timeout", 360)) ping_count = int(params.get("ping_count", 100)) netdst = params.get("netdst") macvtap_ifnames = [] default_host = params.get("default_ext_host") ext_host_get_cmd = params.get("ext_host_get_cmd") try: ext_host = utils.system_output(ext_host_get_cmd) except error.CmdError: logging.warn("Can't get specified host with cmd '%s'," " Fallback to default host '%s'", ext_host_get_cmd, default_host) ext_host = default_host try: txt = "Create and up %s macvtap devices in setting mode." % macvtap_num error.context(txt, logging.info) for num in xrange(macvtap_num): mac = utils_net.generate_mac_address_simple() ifname = "%s_%s" % (macvtap_mode, num) tapfd = utils_net.create_and_open_macvtap(ifname, macvtap_mode, 1, netdst, mac) check_cmd = "ip -d link show %s" % ifname output = utils.system_output(check_cmd) logging.debug(output) macvtap_ifnames.append(ifname) vms = params.get("vms").split() params["start_vm"] = "yes" error.context("Boot multi guest with macvtap", logging.info) for vm_name in vms: env_process.preprocess_vm(test, params, env, vm_name) for vm_name in vms: vm = env.get_vm(vm_name) vm.verify_alive() session = vm.wait_for_serial_login(timeout=timeout) if wait_guest_network_up(session, ext_host, timeout=timeout): txt = " Ping from guests to %s for %s counts." % (ext_host, ping_count) error.context(txt, logging.info) guest_ping(session, ext_host, 100) else: ipconfig_cmd = params.get("ipconfig_cmd", "ifconfig -a") out = session.cmd(ipconfig_cmd) msg = "Could not ping %s successful after %ss." msg += "Guest network status (%s): %s" % (ipconfig_cmd, out) raise error.TestFail(msg) finally: error.context("Delete all macvtap interfaces.", logging.info) for ifname in macvtap_ifnames: del_cmd = "ip link delete %s" % ifname utils.system(del_cmd, ignore_status=True)
def run(test, params, env): """ Test interafce xml options. 1.Prepare test environment,destroy or suspend a VM. 2.Edit xml and start the domain. 3.Perform test operation. 4.Recover test environment. 5.Confirm the test result. """ vm_name = params.get("main_vm") vm = env.get_vm(vm_name) host_arch = platform.machine() virsh_dargs = {'debug': True, 'ignore_status': False} if not utils_package.package_install(["lsof"]): test.cancel("Failed to install dependency package lsof" " on host") def create_iface_xml(iface_mac): """ Create interface xml file """ iface = Interface(type_name=iface_type) source = ast.literal_eval(iface_source) if source: iface.source = source iface.model = iface_model if iface_model else "virtio" iface.mac_address = iface_mac driver_dict = {} driver_host = {} driver_guest = {} if iface_driver: driver_dict = ast.literal_eval(iface_driver) if iface_driver_host: driver_host = ast.literal_eval(iface_driver_host) if iface_driver_guest: driver_guest = ast.literal_eval(iface_driver_guest) iface.driver = iface.new_driver(driver_attr=driver_dict, driver_host=driver_host, driver_guest=driver_guest) if test_target: iface.target = {"dev": target_dev} logging.debug("Create new interface xml: %s", iface) return iface def modify_iface_xml(update, status_error=False): """ Modify interface xml options """ vmxml = vm_xml.VMXML.new_from_dumpxml(vm_name) xml_devices = vmxml.devices iface_index = xml_devices.index( xml_devices.by_device_tag("interface")[0]) iface = xml_devices[iface_index] if iface_model: iface.model = iface_model else: del iface.model if iface_type: iface.type_name = iface_type del iface.source source = ast.literal_eval(iface_source) if source: net_ifs = utils_net.get_net_if(state="UP") # Check source device is valid or not, # if it's not in host interface list, try to set # source device to first active interface of host if (iface.type_name == "direct" and 'dev' in source and source['dev'] not in net_ifs): logging.warn( "Source device %s is not a interface" " of host, reset to %s", source['dev'], net_ifs[0]) source['dev'] = net_ifs[0] iface.source = source backend = ast.literal_eval(iface_backend) if backend: iface.backend = backend driver_dict = {} driver_host = {} driver_guest = {} if iface_driver: driver_dict = ast.literal_eval(iface_driver) if iface_driver_host: driver_host = ast.literal_eval(iface_driver_host) if iface_driver_guest: driver_guest = ast.literal_eval(iface_driver_guest) iface.driver = iface.new_driver(driver_attr=driver_dict, driver_host=driver_host, driver_guest=driver_guest) if test_target: logging.debug("iface.target is %s" % target_dev) iface.target = {"dev": target_dev} if iface.address: del iface.address if set_ip: iface.ips = [ast.literal_eval(x) for x in set_ips] logging.debug("New interface xml file: %s", iface) if unprivileged_user: # Create disk image for unprivileged user disk_index = xml_devices.index( xml_devices.by_device_tag("disk")[0]) disk_xml = xml_devices[disk_index] logging.debug("source: %s", disk_xml.source) disk_source = disk_xml.source.attrs["file"] cmd = ("cp -fZ {0} {1} && chown {2}:{2} {1}" "".format(disk_source, dst_disk, unprivileged_user)) process.run(cmd, shell=True) disk_xml.source = disk_xml.new_disk_source( attrs={"file": dst_disk}) vmxml.devices = xml_devices # Remove all channels to avoid of permission problem channels = vmxml.get_devices(device_type="channel") for channel in channels: vmxml.del_device(channel) logging.info("Unprivileged users can't use 'dac' security driver," " removing from domain xml if present...") vmxml.del_seclabel([('model', 'dac')]) # Set vm memory to 2G if it's larger than 2G if vmxml.memory > 2097152: vmxml.memory = vmxml.current_mem = 2097152 vmxml.xmltreefile.write() logging.debug("New VM xml: %s", vmxml) process.run("chmod a+rw %s" % vmxml.xml, shell=True) virsh.define(vmxml.xml, **virsh_dargs) # Try to modify interface xml by update-device or edit xml elif update: iface.xmltreefile.write() ret = virsh.update_device(vm_name, iface.xml, ignore_status=True) libvirt.check_exit_status(ret, status_error) else: vmxml.devices = xml_devices vmxml.xmltreefile.write() try: vmxml.sync() if define_error: test.fail("Define VM succeed, but it should fail") except xcepts.LibvirtXMLError as e: if not define_error: test.fail("Define VM fail: %s" % e) def check_offloads_option(if_name, driver_options, session=None): """ Check interface offloads by ethtool output """ offloads = { "csum": "tx-checksumming", "tso4": "tcp-segmentation-offload", "tso6": "tx-tcp6-segmentation", "ecn": "tx-tcp-ecn-segmentation", "ufo": "udp-fragmentation-offload" } if session: ret, output = session.cmd_status_output("ethtool -k %s | head" " -18" % if_name) else: out = process.run("ethtool -k %s | head -18" % if_name, shell=True) ret, output = out.exit_status, out.stdout_text if ret: test.fail("ethtool return error code") logging.debug("ethtool output: %s", output) for offload in list(driver_options.keys()): if offload in offloads: if (output.count(offloads[offload]) and not output.count( "%s: %s" % (offloads[offload], driver_options[offload]))): test.fail("offloads option %s: %s isn't" " correct in ethtool output" % (offloads[offload], driver_options[offload])) def run_xml_test(iface_mac): """ Test for interface options in vm xml """ # Get the interface object according the mac address vmxml = vm_xml.VMXML.new_from_dumpxml(vm_name) iface_devices = vmxml.get_devices(device_type="interface") iface = None for iface_dev in iface_devices: if iface_dev.mac_address == iface_mac: iface = iface_dev if not iface: test.fail("Can't find interface with mac" " '%s' in vm xml" % iface_mac) driver_dict = {} if iface_driver: driver_dict = ast.literal_eval(iface_driver) for driver_opt in list(driver_dict.keys()): if not driver_dict[driver_opt] == iface.driver.driver_attr[ driver_opt]: test.fail("Can't see driver option %s=%s in vm xml" % (driver_opt, driver_dict[driver_opt])) else: logging.info("Find %s=%s in vm xml" % (driver_opt, driver_dict[driver_opt])) if iface_target: if ("dev" not in iface.target or not iface.target["dev"].startswith(iface_target)): test.fail("Can't see device target dev in vm xml") # Check macvtap mode by ip link command if iface_target == "macvtap" and "mode" in iface.source: cmd = "ip -d link show %s" % iface.target["dev"] output = process.run(cmd, shell=True).stdout_text logging.debug("ip link output: %s", output) mode = iface.source["mode"] if mode == "passthrough": mode = "passthru" if not re.search(r"macvtap\s+mode %s" % mode, output): test.fail("Failed to verify macvtap mode") # Check if the "target dev" is set successfully # 1. Target dev name with prefix as "vnet" will always be override; # 2. Target dev name with prefix as "macvtap" or "macvlan" with direct # type interface will be override; # 3. Other scenarios, the target dev should be set successfully. if test_target: if target_dev != iface.target["dev"]: if target_dev.startswith("vnet") or \ (iface_type == "direct" and (target_dev.startswith("macvtap") or target_dev.startswith("macvlan"))): logging.debug("target dev %s is override" % target_dev) else: test.fail("Failed to set target dev to %s", target_dev) else: logging.debug("target dev set successfully to %s", iface.target["dev"]) def run_cmdline_test(iface_mac, host_arch): """ Test qemu command line :param iface_mac: expected MAC :param host_arch: host architecture, e.g. x86_64 :raise avocado.core.exceptions.TestError: if preconditions are not met :raise avocado.core.exceptions.TestFail: if commandline doesn't match :return: None """ cmd = ("ps -ef | grep %s | grep -v grep " % vm_name) ret = process.run(cmd, shell=True) logging.debug("Command line %s", ret.stdout_text) if test_vhost_net: if not ret.stdout_text.count("vhost=on") and not rm_vhost_driver: test.fail("Can't see vhost options in" " qemu-kvm command line") if iface_model == "virtio": if host_arch == 's390x': model_option = "device virtio-net-ccw" else: model_option = "device virtio-net-pci" elif iface_model == 'rtl8139': model_option = "device rtl8139" else: test.error( "Don't know which device driver to expect on qemu cmdline" " for iface_model %s" % iface_model) iface_cmdline = re.findall( r"%s,(.+),mac=%s" % (model_option, iface_mac), ret.stdout_text) if not iface_cmdline: test.fail("Can't see %s with mac %s in command" " line" % (model_option, iface_mac)) cmd_opt = {} for opt in iface_cmdline[0].split(','): tmp = opt.rsplit("=") cmd_opt[tmp[0]] = tmp[1] logging.debug("Command line options %s", cmd_opt) driver_dict = {} # Test <driver> xml options. if iface_driver: iface_driver_dict = ast.literal_eval(iface_driver) for driver_opt in list(iface_driver_dict.keys()): if driver_opt == "name": continue elif driver_opt == "txmode": if iface_driver_dict["txmode"] == "iothread": driver_dict["tx"] = "bh" else: driver_dict["tx"] = iface_driver_dict["txmode"] elif driver_opt == "queues": driver_dict["mq"] = "on" if "pci" in model_option: driver_dict["vectors"] = str( int(iface_driver_dict["queues"]) * 2 + 2) else: driver_dict[driver_opt] = iface_driver_dict[driver_opt] # Test <driver><host/><driver> xml options. if iface_driver_host: driver_dict.update(ast.literal_eval(iface_driver_host)) # Test <driver><guest/><driver> xml options. if iface_driver_guest: driver_dict.update(ast.literal_eval(iface_driver_guest)) for driver_opt in list(driver_dict.keys()): if (driver_opt not in cmd_opt or not cmd_opt[driver_opt] == driver_dict[driver_opt]): test.fail("Can't see option '%s=%s' in qemu-kvm " " command line" % (driver_opt, driver_dict[driver_opt])) logging.info("Find %s=%s in qemu-kvm command line" % (driver_opt, driver_dict[driver_opt])) if test_backend: guest_pid = ret.stdout_text.rsplit()[1] cmd = "lsof %s | grep %s" % (backend["tap"], guest_pid) if process.system(cmd, ignore_status=True, shell=True): test.fail("Guest process didn't open backend file" " %s" % backend["tap"]) cmd = "lsof %s | grep %s" % (backend["vhost"], guest_pid) if process.system(cmd, ignore_status=True, shell=True): test.fail("Guest process didn't open backend file" " %s" % backend["vhost"]) def get_guest_ip(session, mac): """ Wrapper function to get guest ip address """ utils_net.restart_guest_network(session, mac) # Wait for IP address is ready utils_misc.wait_for(lambda: utils_net.get_guest_ip_addr(session, mac), 10) return utils_net.get_guest_ip_addr(session, mac) def check_user_network(session): """ Check user network ip address on guest """ vm_ips = [] vm_ips.append(get_guest_ip(session, iface_mac_old)) if attach_device: vm_ips.append(get_guest_ip(session, iface_mac)) logging.debug("IP address on guest: %s", vm_ips) if len(vm_ips) != len(set(vm_ips)): logging.debug( "Duplicated IP address on guest. Check bug: " "https://bugzilla.redhat.com/show_bug.cgi?id=1147238") for vm_ip in vm_ips: if not vm_ip or vm_ip != expect_ip: logging.debug("vm_ip is %s, expect_ip is %s", vm_ip, expect_ip) test.fail("Found wrong IP address" " on guest") # Check gateway address gateway = str(utils_net.get_default_gateway(False, session)) if expect_gw not in gateway: test.fail("The gateway on guest is %s, while expect is %s" % (gateway, expect_gw)) # Check dns server address ns_list = utils_net.get_guest_nameserver(session) if expect_ns not in ns_list: test.fail("The dns found is %s, which expect is %s" % (ns_list, expect_ns)) def check_mcast_network(session, add_session): """ Check multicast ip address on guests :param session: vm session :param add_session: additional vm session """ src_addr = ast.literal_eval(iface_source)['address'] vms_sess_dict = {vm_name: session, additional_vm.name: add_session} # Check mcast address on host cmd = "netstat -g | grep %s" % src_addr if process.run(cmd, ignore_status=True, shell=True).exit_status: test.fail("Can't find multicast ip address" " on host") vms_ip_dict = {} # Get ip address on each guest for vms in list(vms_sess_dict.keys()): vm_mac = vm_xml.VMXML.get_first_mac_by_name(vms) vm_ip = get_guest_ip(vms_sess_dict[vms], vm_mac) if not vm_ip: test.fail("Can't get multicast ip" " address on guest") vms_ip_dict.update({vms: vm_ip}) if len(set(vms_ip_dict.values())) != len(vms_sess_dict): test.fail("Got duplicated multicast ip address") logging.debug("Found ips on guest: %s", vms_ip_dict) # Run omping server on host if not utils_package.package_install(["omping"]): test.error("Failed to install omping" " on host") cmd = ("iptables -F;omping -m %s %s" % (src_addr, "192.168.122.1 %s" % ' '.join(list(vms_ip_dict.values())))) # Run a backgroup job waiting for connection of client bgjob = utils_misc.AsyncJob(cmd) # Run omping client on guests for vms in list(vms_sess_dict.keys()): # omping should be installed first if not utils_package.package_install(["omping"], vms_sess_dict[vms]): test.error("Failed to install omping" " on guest") cmd = ("iptables -F; omping -c 5 -T 5 -m %s %s" % (src_addr, "192.168.122.1 %s" % vms_ip_dict[vms])) ret, output = vms_sess_dict[vms].cmd_status_output(cmd) logging.debug("omping ret: %s, output: %s", ret, output) if (not output.count('multicast, xmt/rcv/%loss = 5/5/0%') or not output.count('unicast, xmt/rcv/%loss = 5/5/0%')): test.fail("omping failed on guest") # Kill the backgroup job bgjob.kill_func() def get_iface_model(iface_model, host_arch): """ Get iface_model. On s390x use default model 'virtio' if non-virtio given :param iface_model: value as by test configuration or default :param host_arch: host architecture, e.g. x86_64 :return: iface_model """ if 's390x' == host_arch and 'virtio' not in iface_model: return "virtio" else: return iface_model def check_vhostuser_guests(session1, session2): """ Check the vhostuser interface in guests param session1: Session of original guest param session2: Session of original additional guest """ logging.debug("iface details is %s" % libvirt.get_interface_details(vm_name)) vm1_mac = str(libvirt.get_interface_details(vm_name)[0]['mac']) vm2_mac = str(libvirt.get_interface_details(add_vm_name)[0]['mac']) utils_net.set_guest_ip_addr(session1, vm1_mac, guest1_ip) utils_net.set_guest_ip_addr(session2, vm2_mac, guest2_ip) ping_status, ping_output = utils_net.ping(dest=guest2_ip, count='3', timeout=5, session=session1) logging.info("output:%s" % ping_output) if ping_status != 0: if ping_expect_fail: logging.info("Can not ping guest2 as expected") else: test.fail("Can not ping guest2 from guest1") else: if ping_expect_fail: test.fail("Ping guest2 successfully not expected") else: logging.info("Can ping guest2 from guest1") def get_ovs_statis(ovs): """ Get ovs-vsctl interface statistics and format in dict param ovs: openvswitch instance """ ovs_statis_dict = {} ovs_iface_info = ovs.ovs_vsctl(["list", "interface"]).stdout_text.strip() ovs_iface_list = re.findall( 'name\s+: (\S+)\n.*?statistics\s+: {(.*?)}\n', ovs_iface_info, re.S) logging.info("ovs iface list is %s", ovs_iface_list) # Dict of iface name and statistics for iface_name in vhostuser_names.split(): for ovs_iface in ovs_iface_list: if iface_name == eval(ovs_iface[0]): format_statis = dict( re.findall(r'(\S*?)=(\d*?),', ovs_iface[1])) ovs_statis_dict[iface_name] = format_statis break return ovs_statis_dict status_error = "yes" == params.get("status_error", "no") start_error = "yes" == params.get("start_error", "no") define_error = "yes" == params.get("define_error", "no") unprivileged_user = params.get("unprivileged_user") # Interface specific attributes. iface_type = params.get("iface_type", "network") iface_source = params.get("iface_source", "{}") iface_driver = params.get("iface_driver") iface_model = get_iface_model(params.get("iface_model", "virtio"), host_arch) iface_target = params.get("iface_target") iface_backend = params.get("iface_backend", "{}") iface_driver_host = params.get("iface_driver_host") iface_driver_guest = params.get("iface_driver_guest") ovs_br_name = params.get("ovs_br_name") vhostuser_names = params.get("vhostuser_names") attach_device = params.get("attach_iface_device") expect_tx_size = params.get("expect_tx_size") guest1_ip = params.get("vhostuser_guest1_ip", "192.168.100.1") guest2_ip = params.get("vhostuser_guest2_ip", "192.168.100.2") change_option = "yes" == params.get("change_iface_options", "no") update_device = "yes" == params.get("update_iface_device", "no") additional_guest = "yes" == params.get("additional_guest", "no") serial_login = "******" == params.get("serial_login", "no") rm_vhost_driver = "yes" == params.get("rm_vhost_driver", "no") test_option_cmd = "yes" == params.get("test_iface_option_cmd", "no") test_option_xml = "yes" == params.get("test_iface_option_xml", "no") test_vhost_net = "yes" == params.get("test_vhost_net", "no") test_option_offloads = "yes" == params.get("test_option_offloads", "no") test_iface_user = "******" == params.get("test_iface_user", "no") test_iface_mcast = "yes" == params.get("test_iface_mcast", "no") test_libvirtd = "yes" == params.get("test_libvirtd", "no") restart_libvirtd = "yes" == params.get("restart_libvirtd", "no") restart_vm = "yes" == params.get("restart_vm", "no") test_guest_ip = "yes" == params.get("test_guest_ip", "no") test_backend = "yes" == params.get("test_backend", "no") check_guest_trans = "yes" == params.get("check_guest_trans", "no") set_ip = "yes" == params.get("set_user_ip", "no") set_ips = params.get("set_ips", "").split() expect_ip = params.get("expect_ip") expect_gw = params.get("expect_gw") expect_ns = params.get("expect_ns") test_target = "yes" == params.get("test_target", "no") target_dev = params.get("target_dev", None) # test params for vhostuser test huge_page = ast.literal_eval(params.get("huge_page", "{}")) numa_cell = ast.literal_eval(params.get("numa_cell", "{}")) additional_iface_source = ast.literal_eval( params.get("additional_iface_source", "{}")) vcpu_num = params.get("vcpu_num") cpu_mode = params.get("cpu_mode") hugepage_num = params.get("hugepage_num") log_pattern = params.get("log_pattern") # judgement params for vhostuer test need_vhostuser_env = "yes" == params.get("need_vhostuser_env", "no") ping_expect_fail = "yes" == params.get("ping_expect_fail", "no") check_libvirtd_log = "yes" == params.get("check_libvirtd_log", "no") check_statistics = "yes" == params.get("check_statistics", "no") enable_multiqueue = "yes" == params.get("enable_multiqueue", "no") queue_size = None if iface_driver: driver_dict = ast.literal_eval(iface_driver) if "queues" in driver_dict: queue_size = int(driver_dict.get("queues")) if iface_driver_host or iface_driver_guest or test_backend: if not libvirt_version.version_compare(1, 2, 8): test.cancel("Offloading/backend options not " "supported in this libvirt version") if iface_driver and "queues" in ast.literal_eval(iface_driver): if not libvirt_version.version_compare(1, 0, 6): test.cancel("Queues options not supported" " in this libvirt version") if unprivileged_user: if not libvirt_version.version_compare(1, 1, 1): test.cancel("qemu-bridge-helper not supported" " on this host") virsh_dargs["unprivileged_user"] = unprivileged_user # Create unprivileged user if needed cmd = ("grep {0} /etc/passwd || " "useradd {0}".format(unprivileged_user)) process.run(cmd, shell=True) # Need another disk image for unprivileged user to access dst_disk = "/tmp/%s.img" % unprivileged_user # Destroy VM first if vm.is_alive(): vm.destroy(gracefully=False) # Back up xml file. vmxml_backup = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) iface_mac_old = vm_xml.VMXML.get_first_mac_by_name(vm_name) # iface_mac will update if attach a new interface iface_mac = iface_mac_old # Additional vm for test additional_vm = None libvirtd = utils_libvirtd.Libvirtd() libvirtd_log_path = None libvirtd_conf = None if check_libvirtd_log: libvirtd_log_path = os.path.join(test.tmpdir, "libvirtd.log") libvirtd_conf = utils_config.LibvirtdConfig() libvirtd_conf["log_outputs"] = '"1:file:%s"' % libvirtd_log_path libvirtd.restart() # Prepare vhostuser ovs = None if need_vhostuser_env: # Reserve selinux status selinux_mode = utils_selinux.get_status() # Reserve orig page size orig_size = utils_memory.get_num_huge_pages() ovs_dir = data_dir.get_tmp_dir() ovs = utils_net.setup_ovs_vhostuser(hugepage_num, ovs_dir, ovs_br_name, vhostuser_names, queue_size) try: # Build the xml and run test. try: # Prepare interface backend files if test_backend: if not os.path.exists("/dev/vhost-net"): process.run("modprobe vhost-net", shell=True) backend = ast.literal_eval(iface_backend) backend_tap = "/dev/net/tun" backend_vhost = "/dev/vhost-net" if not backend: backend["tap"] = backend_tap backend["vhost"] = backend_vhost if not start_error: # Create backend files for normal test if not os.path.exists(backend["tap"]): os.rename(backend_tap, backend["tap"]) if not os.path.exists(backend["vhost"]): os.rename(backend_vhost, backend["vhost"]) # Edit the interface xml. if change_option: modify_iface_xml(update=False) if define_error: return if test_target: logging.debug("Setting target device name to %s", target_dev) modify_iface_xml(update=False) if rm_vhost_driver: # remove vhost driver on host and # the character file /dev/vhost-net cmd = ("modprobe -r {0}; " "rm -f /dev/vhost-net".format("vhost_net")) if process.system(cmd, ignore_status=True, shell=True): test.error("Failed to remove vhost_net driver") else: # Load vhost_net driver by default cmd = "modprobe vhost_net" process.system(cmd, shell=True) # Attach a interface when vm is shutoff if attach_device == 'config': iface_mac = utils_net.generate_mac_address_simple() iface_xml_obj = create_iface_xml(iface_mac) iface_xml_obj.xmltreefile.write() ret = virsh.attach_device(vm_name, iface_xml_obj.xml, flagstr="--config", ignore_status=True) libvirt.check_exit_status(ret) # Add hugepage and update cpu for vhostuser testing if huge_page: vmxml = vm_xml.VMXML.new_from_dumpxml(vm_name) membacking = vm_xml.VMMemBackingXML() hugepages = vm_xml.VMHugepagesXML() pagexml = hugepages.PageXML() pagexml.update(huge_page) hugepages.pages = [pagexml] membacking.hugepages = hugepages vmxml.mb = membacking vmxml.vcpu = int(vcpu_num) cpu_xml = vm_xml.VMCPUXML() cpu_xml.xml = "<cpu><numa/></cpu>" cpu_xml.numa_cell = cpu_xml.dicts_to_cells([numa_cell]) cpu_xml.mode = cpu_mode if cpu_mode == "custom": vm_capability = capability_xml.CapabilityXML() cpu_xml.model = vm_capability.model vmxml.cpu = cpu_xml vmxml.sync() logging.debug("xmltreefile:%s", vmxml.xmltreefile) # Clone additional vm if additional_guest: add_vm_name = "%s_%s" % (vm_name, '1') # Clone additional guest timeout = params.get("clone_timeout", 360) utils_libguestfs.virt_clone_cmd(vm_name, add_vm_name, True, timeout=timeout) additional_vm = vm.clone(add_vm_name) # Update iface source if needed if additional_iface_source: add_vmxml = vm_xml.VMXML.new_from_dumpxml(add_vm_name) add_xml_devices = add_vmxml.devices add_iface_index = add_xml_devices.index( add_xml_devices.by_device_tag("interface")[0]) add_iface = add_xml_devices[add_iface_index] add_iface.source = additional_iface_source add_vmxml.devices = add_xml_devices add_vmxml.xmltreefile.write() add_vmxml.sync() logging.debug("add vm xmltreefile:%s", add_vmxml.xmltreefile) additional_vm.start() # additional_vm.wait_for_login() username = params.get("username") password = params.get("password") add_session = additional_vm.wait_for_serial_login( username=username, password=password) # Start the VM. if unprivileged_user: virsh.start(vm_name, **virsh_dargs) cmd = ("su - %s -c 'virsh console %s'" % (unprivileged_user, vm_name)) session = aexpect.ShellSession(cmd) session.sendline() remote.handle_prompts(session, params.get("username"), params.get("password"), r"[\#\$]\s*$", 60) # Get ip address on guest if not get_guest_ip(session, iface_mac): test.error("Can't get ip address on guest") else: # Will raise VMStartError exception if start fails vm.start() if serial_login: session = vm.wait_for_serial_login() else: session = vm.wait_for_login() if start_error: test.fail("VM started unexpectedly") # Attach a interface when vm is running if attach_device == 'live': iface_mac = utils_net.generate_mac_address_simple() iface_xml_obj = create_iface_xml(iface_mac) iface_xml_obj.xmltreefile.write() ret = virsh.attach_device(vm_name, iface_xml_obj.xml, flagstr="--live", ignore_status=True, debug=True) libvirt.check_exit_status(ret, status_error) # Need sleep here for attachment take effect time.sleep(5) # Update a interface options if update_device: modify_iface_xml(update=True, status_error=status_error) # Run tests for qemu-kvm command line options if test_option_cmd: run_cmdline_test(iface_mac, host_arch) # Run tests for vm xml if test_option_xml: run_xml_test(iface_mac) # Run tests for offloads options if test_option_offloads: if iface_driver_host: ifname_guest = utils_net.get_linux_ifname( session, iface_mac) check_offloads_option(ifname_guest, ast.literal_eval(iface_driver_host), session) if iface_driver_guest: ifname_host = libvirt.get_ifname_host(vm_name, iface_mac) check_offloads_option(ifname_host, ast.literal_eval(iface_driver_guest)) if test_iface_user: # Test user type network check_user_network(session) if test_iface_mcast: # Test mcast type network check_mcast_network(session, add_session) # Check guest ip address if test_guest_ip: if not get_guest_ip(session, iface_mac): test.fail("Guest can't get a" " valid ip address") # Check guest RX/TX ring if check_guest_trans: ifname_guest = utils_net.get_linux_ifname(session, iface_mac) ret, outp = session.cmd_status_output("ethtool -g %s" % ifname_guest) if ret: test.fail("ethtool return error code") logging.info("ethtool output is %s", outp) driver_dict = ast.literal_eval(iface_driver) if expect_tx_size: driver_dict['tx_queue_size'] = expect_tx_size for outp_p in outp.split("Current hardware"): if 'rx_queue_size' in driver_dict: if re.search( r"RX:\s*%s" % driver_dict['rx_queue_size'], outp_p): logging.info("Find RX setting RX:%s by ethtool", driver_dict['rx_queue_size']) else: test.fail("Cannot find matching rx setting") if 'tx_queue_size' in driver_dict: if re.search( r"TX:\s*%s" % driver_dict['tx_queue_size'], outp_p): logging.info("Find TX settint TX:%s by ethtool", driver_dict['tx_queue_size']) else: test.fail("Cannot find matching tx setting") if test_target: logging.debug("Check if the target dev is set") run_xml_test(iface_mac) # Check vhostuser guest if additional_iface_source: check_vhostuser_guests(session, add_session) # Check libvirtd log if check_libvirtd_log: find = 0 with open(libvirtd_log_path) as f: lines = "".join(f.readlines()) if log_pattern in lines: logging.info("Finding msg<%s> in libvirtd log", log_pattern) else: test.fail("Can not find msg:<%s> in libvirtd.log" % log_pattern) # Check statistics if check_statistics: session.sendline("ping %s" % guest2_ip) add_session.sendline("ping %s" % guest1_ip) time.sleep(5) vhost_name = vhostuser_names.split()[0] ovs_statis_dict = get_ovs_statis(ovs)[vhost_name] domif_info = {} domif_info = libvirt.get_interface_details(vm_name) virsh.domiflist(vm_name, debug=True) domif_stat_result = virsh.domifstat(vm_name, vhost_name) if domif_stat_result.exit_status != 0: test.fail("domifstat cmd fail with msg:%s" % domif_stat_result.stderr) else: domif_stat = domif_stat_result.stdout.strip() logging.debug("vhost_name is %s, domif_stat is %s", vhost_name, domif_stat) domif_stat_dict = dict( re.findall("%s (\S*) (\d*)" % vhost_name, domif_stat)) logging.debug("ovs_statis is %s, domif_stat is %s", ovs_statis_dict, domif_stat_dict) ovs_cmp_dict = { 'tx_bytes': ovs_statis_dict['rx_bytes'], 'tx_drop': ovs_statis_dict['rx_dropped'], 'tx_errs': ovs_statis_dict['rx_errors'], 'tx_packets': ovs_statis_dict['rx_packets'], 'rx_bytes': ovs_statis_dict['tx_bytes'], 'rx_drop': ovs_statis_dict['tx_dropped'] } logging.debug("ovs_cmp_dict is %s", ovs_cmp_dict) for dict_key in ovs_cmp_dict.keys(): if domif_stat_dict[dict_key] != ovs_cmp_dict[dict_key]: test.fail( "Find ovs %s result (%s) different with domifstate result (%s)" % (dict_key, ovs_cmp_dict[dict_key], domif_stat_dict[dict_key])) else: logging.info("ovs %s value %s is same with domifstate", dict_key, domif_stat_dict[dict_key]) # Check multi_queue if enable_multiqueue: ifname_guest = utils_net.get_linux_ifname(session, iface_mac) for comb_size in (queue_size, queue_size - 1): logging.info("Setting multiqueue size to %s" % comb_size) session.cmd_status("ethtool -L %s combined %s" % (ifname_guest, comb_size)) ret, outp = session.cmd_status_output("ethtool -l %s" % ifname_guest) logging.debug("ethtool cmd output:%s" % outp) if not ret: pre_comb = re.search( "Pre-set maximums:[\s\S]*?Combined:.*?(\d+)", outp).group(1) cur_comb = re.search( "Current hardware settings:[\s\S]*?Combined:.*?(\d+)", outp).group(1) if int(pre_comb) != queue_size or int(cur_comb) != int( comb_size): test.fail( "Fail to check the combined size: setting: %s," "Pre-set: %s, Current-set: %s, queue_size: %s" % (comb_size, pre_comb, cur_comb, queue_size)) else: logging.info( "Getting correct Pre-set and Current set value" ) else: test.error("ethtool list fail: %s" % outp) session.close() if additional_guest: add_session.close() # Restart libvirtd and guest, then test again if restart_libvirtd: libvirtd.restart() if restart_vm: vm.destroy(gracefully=True) vm.start() if test_option_xml: run_xml_test(iface_mac) # Detach hot/cold-plugged interface at last if attach_device and not status_error: ret = virsh.detach_device(vm_name, iface_xml_obj.xml, flagstr="", ignore_status=True, debug=True) libvirt.check_exit_status(ret) except virt_vm.VMStartError as e: logging.info(str(e)) if not start_error: test.fail('VM failed to start\n%s' % e) finally: # Recover VM. logging.info("Restoring vm...") # Restore interface backend files if test_backend: if not os.path.exists(backend_tap): os.rename(backend["tap"], backend_tap) if not os.path.exists(backend_vhost): os.rename(backend["vhost"], backend_vhost) if rm_vhost_driver: # Restore vhost_net driver process.system("modprobe vhost_net", shell=True) if unprivileged_user: virsh.remove_domain(vm_name, **virsh_dargs) process.run('rm -f %s' % dst_disk, shell=True) if additional_vm: virsh.remove_domain(additional_vm.name, "--remove-all-storage") # Kill all omping server process on host process.system("pidof omping && killall omping", ignore_status=True, shell=True) if vm.is_alive(): vm.destroy(gracefully=True) vmxml_backup.sync() if need_vhostuser_env: utils_net.clean_ovs_env(selinux_mode=selinux_mode, page_size=orig_size, clean_ovs=True) if libvirtd_conf: libvirtd_conf.restore() libvirtd.restart() if libvirtd_log_path and os.path.exists(libvirtd_log_path): os.unlink(libvirtd_log_path)
def run(test, params, env): """ Test interafce xml options. 1.Prepare test environment,destroy or suspend a VM. 2.Edit xml and start the domain. 3.Perform test operation. 4.Recover test environment. 5.Confirm the test result. """ vm_name = params.get("main_vm") vm = env.get_vm(vm_name) virsh_dargs = {'debug': True, 'ignore_status': False} def create_iface_xml(iface_mac): """ Create interface xml file """ iface = Interface(type_name=iface_type) source = ast.literal_eval(iface_source) if source: iface.source = source iface.model = iface_model if iface_model else "virtio" iface.mac_address = iface_mac driver_dict = {} driver_host = {} driver_guest = {} if iface_driver: driver_dict = ast.literal_eval(iface_driver) if iface_driver_host: driver_host = ast.literal_eval(iface_driver_host) if iface_driver_guest: driver_guest = ast.literal_eval(iface_driver_guest) iface.driver = iface.new_driver(driver_attr=driver_dict, driver_host=driver_host, driver_guest=driver_guest) logging.debug("Create new interface xml: %s", iface) return iface def modify_iface_xml(update, status_error=False): """ Modify interface xml options """ vmxml = vm_xml.VMXML.new_from_dumpxml(vm_name) xml_devices = vmxml.devices iface_index = xml_devices.index( xml_devices.by_device_tag("interface")[0]) iface = xml_devices[iface_index] if iface_model: iface.model = iface_model else: del iface.model if iface_type: iface.type_name = iface_type del iface.source source = ast.literal_eval(iface_source) if source: net_ifs = utils_net.get_net_if(state="UP") # Check source device is valid or not, # if it's not in host interface list, try to set # source device to first active interface of host if (iface.type_name == "direct" and source.has_key('dev') and source['dev'] not in net_ifs): logging.warn("Source device %s is not a interface" " of host, reset to %s", source['dev'], net_ifs[0]) source['dev'] = net_ifs[0] iface.source = source backend = ast.literal_eval(iface_backend) if backend: iface.backend = backend driver_dict = {} driver_host = {} driver_guest = {} if iface_driver: driver_dict = ast.literal_eval(iface_driver) if iface_driver_host: driver_host = ast.literal_eval(iface_driver_host) if iface_driver_guest: driver_guest = ast.literal_eval(iface_driver_guest) iface.driver = iface.new_driver(driver_attr=driver_dict, driver_host=driver_host, driver_guest=driver_guest) if iface.address: del iface.address logging.debug("New interface xml file: %s", iface) if unprivileged_user: # Create disk image for unprivileged user disk_index = xml_devices.index( xml_devices.by_device_tag("disk")[0]) disk_xml = xml_devices[disk_index] logging.debug("source: %s", disk_xml.source) disk_source = disk_xml.source.attrs["file"] cmd = ("cp -fZ {0} {1} && chown {2}:{2} {1}" "".format(disk_source, dst_disk, unprivileged_user)) utils.run(cmd) disk_xml.source = disk_xml.new_disk_source( attrs={"file": dst_disk}) vmxml.devices = xml_devices # Remove all channels to avoid of permission problem channels = vmxml.get_devices(device_type="channel") for channel in channels: vmxml.del_device(channel) vmxml.xmltreefile.write() logging.debug("New VM xml: %s", vmxml) utils.run("chmod a+rw %s" % vmxml.xml) virsh.define(vmxml.xml, **virsh_dargs) # Try to modify interface xml by update-device or edit xml elif update: iface.xmltreefile.write() ret = virsh.update_device(vm_name, iface.xml, ignore_status=True) libvirt.check_exit_status(ret, status_error) else: vmxml.devices = xml_devices vmxml.xmltreefile.write() vmxml.sync() def check_offloads_option(if_name, driver_options, session=None): """ Check interface offloads by ethtool output """ offloads = {"csum": "tx-checksumming", "gso": "generic-segmentation-offload", "tso4": "tcp-segmentation-offload", "tso6": "tx-tcp6-segmentation", "ecn": "tx-tcp-ecn-segmentation", "ufo": "udp-fragmentation-offload"} if session: ret, output = session.cmd_status_output("ethtool -k %s | head" " -18" % if_name) else: out = utils.run("ethtool -k %s | head -18" % if_name) ret, output = out.exit_status, out.stdout if ret: raise error.TestFail("ethtool return error code") logging.debug("ethtool output: %s", output) for offload in driver_options.keys(): if offloads.has_key(offload): if (output.count(offloads[offload]) and not output.count("%s: %s" % ( offloads[offload], driver_options[offload]))): raise error.TestFail("offloads option %s: %s isn't" " correct in ethtool output" % (offloads[offload], driver_options[offload])) def run_xml_test(iface_mac): """ Test for interface options in vm xml """ # Get the interface object according the mac address vmxml = vm_xml.VMXML.new_from_dumpxml(vm_name) iface_devices = vmxml.get_devices(device_type="interface") iface = None for iface_dev in iface_devices: if iface_dev.mac_address == iface_mac: iface = iface_dev if not iface: raise error.TestFail("Can't find interface with mac" " '%s' in vm xml" % iface_mac) driver_dict = {} if iface_driver: driver_dict = ast.literal_eval(iface_driver) for driver_opt in driver_dict.keys(): if not driver_dict[driver_opt] == iface.driver.driver_attr[driver_opt]: raise error.TestFail("Can't see driver option %s=%s in vm xml" % (driver_opt, driver_dict[driver_opt])) if iface_target: if (not iface.target.has_key("dev") or not iface.target["dev"].startswith(iface_target)): raise error.TestFail("Can't see device target dev in vm xml") # Check macvtap mode by ip link command if iface_target == "macvtap" and iface.source.has_key("mode"): cmd = "ip -d link show %s" % iface.target["dev"] output = utils.run(cmd).stdout logging.debug("ip link output: %s", output) mode = iface.source["mode"] if mode == "passthrough": mode = "passthru" if not output.count("macvtap mode %s" % mode): raise error.TestFail("Failed to verify macvtap mode") def run_cmdline_test(iface_mac): """ Test for qemu-kvm command line options """ cmd = ("ps -ef | grep %s | grep -v grep " % vm_name) if test_vhost_net: cmd += " | grep 'vhost=on'" ret = utils.run(cmd) if ret.exit_status: raise error.TestFail("Can't parse qemu-kvm command line") logging.debug("Command line %s", ret.stdout) if iface_model == "virtio": model_option = "device virtio-net-pci" else: model_option = "device rtl8139" iface_cmdline = re.findall(r"%s,(.+),mac=%s" % (model_option, iface_mac), ret.stdout) if not iface_cmdline: raise error.TestFail("Can't see %s with mac %s in command" " line" % (model_option, iface_mac)) cmd_opt = {} for opt in iface_cmdline[0].split(','): tmp = opt.rsplit("=") cmd_opt[tmp[0]] = tmp[1] logging.debug("Command line options %s", cmd_opt) driver_dict = {} # Test <driver> xml options. if iface_driver: iface_driver_dict = ast.literal_eval(iface_driver) for driver_opt in iface_driver_dict.keys(): if driver_opt == "name": continue elif driver_opt == "txmode": if iface_driver_dict["txmode"] == "iothread": driver_dict["tx"] = "bh" else: driver_dict["tx"] = iface_driver_dict["txmode"] elif driver_opt == "queues": driver_dict["mq"] = "on" driver_dict["vectors"] = str(int( iface_driver_dict["queues"]) * 2 + 2) else: driver_dict[driver_opt] = iface_driver_dict[driver_opt] # Test <driver><host/><driver> xml options. if iface_driver_host: driver_dict.update(ast.literal_eval(iface_driver_host)) # Test <driver><guest/><driver> xml options. if iface_driver_guest: driver_dict.update(ast.literal_eval(iface_driver_guest)) for driver_opt in driver_dict.keys(): if (not cmd_opt.has_key(driver_opt) or not cmd_opt[driver_opt] == driver_dict[driver_opt]): raise error.TestFail("Can't see option '%s=%s' in qemu-kvm " " command line" % (driver_opt, driver_dict[driver_opt])) if test_backend: guest_pid = ret.stdout.rsplit()[1] cmd = "lsof %s | grep %s" % (backend["tap"], guest_pid) if utils.system(cmd, ignore_status=True): raise error.TestFail("Guest process didn't open backend file" % backend["tap"]) cmd = "lsof %s | grep %s" % (backend["vhost"], guest_pid) if utils.system(cmd, ignore_status=True): raise error.TestFail("Guest process didn't open backend file" % backend["tap"]) def get_guest_ip(session, mac): """ Wrapper function to get guest ip address """ utils_net.restart_guest_network(session, mac) # Wait for IP address is ready utils_misc.wait_for( lambda: utils_net.get_guest_ip_addr(session, mac), 10) return utils_net.get_guest_ip_addr(session, mac) def check_user_network(session): """ Check user network ip address on guest """ vm_ips = [] vm_ips.append(get_guest_ip(session, iface_mac_old)) if attach_device: vm_ips.append(get_guest_ip(session, iface_mac)) logging.debug("IP address on guest: %s", vm_ips) if len(vm_ips) != len(set(vm_ips)): raise error.TestFail("Duplicated IP address on guest. " "Check bug: https://bugzilla.redhat." "com/show_bug.cgi?id=1147238") for vm_ip in vm_ips: if vm_ip is None or not vm_ip.startswith("10.0.2."): raise error.TestFail("Found wrong IP address" " on guest") # Check gateway address gateway = utils_net.get_net_gateway(session.cmd_output) if gateway != "10.0.2.2": raise error.TestFail("The gateway on guest is not" " right") # Check dns server address ns_list = utils_net.get_net_nameserver(session.cmd_output) if "10.0.2.3" not in ns_list: raise error.TestFail("The dns server can't be found" " on guest") def check_mcast_network(session): """ Check multicast ip address on guests """ src_addr = ast.literal_eval(iface_source)['address'] add_session = additional_vm.wait_for_serial_login() vms_sess_dict = {vm_name: session, additional_vm.name: add_session} # Check mcast address on host cmd = "netstat -g | grep %s" % src_addr if utils.run(cmd, ignore_status=True).exit_status: raise error.TestFail("Can't find multicast ip address" " on host") vms_ip_dict = {} # Get ip address on each guest for vms in vms_sess_dict.keys(): vm_mac = vm_xml.VMXML.get_first_mac_by_name(vms) vm_ip = get_guest_ip(vms_sess_dict[vms], vm_mac) if not vm_ip: raise error.TestFail("Can't get multicast ip" " address on guest") vms_ip_dict.update({vms: vm_ip}) if len(set(vms_ip_dict.values())) != len(vms_sess_dict): raise error.TestFail("Got duplicated multicast ip address") logging.debug("Found ips on guest: %s", vms_ip_dict) # Run omping server on host if not utils_misc.yum_install(["omping"]): raise error.TestError("Failed to install omping" " on host") cmd = ("iptables -F;omping -m %s %s" % (src_addr, "192.168.122.1 %s" % ' '.join(vms_ip_dict.values()))) # Run a backgroup job waiting for connection of client bgjob = utils.AsyncJob(cmd) # Run omping client on guests for vms in vms_sess_dict.keys(): # omping should be installed first if not utils_misc.yum_install(["omping"], vms_sess_dict[vms]): raise error.TestError("Failed to install omping" " on guest") cmd = ("iptables -F; omping -c 5 -T 5 -m %s %s" % (src_addr, "192.168.122.1 %s" % vms_ip_dict[vms])) ret, output = vms_sess_dict[vms].cmd_status_output(cmd) logging.debug("omping ret: %s, output: %s", ret, output) if (not output.count('multicast, xmt/rcv/%loss = 5/5/0%') or not output.count('unicast, xmt/rcv/%loss = 5/5/0%')): raise error.TestFail("omping failed on guest") # Kill the backgroup job bgjob.kill_func() status_error = "yes" == params.get("status_error", "no") start_error = "yes" == params.get("start_error", "no") unprivileged_user = params.get("unprivileged_user") # Interface specific attributes. iface_type = params.get("iface_type", "network") iface_source = params.get("iface_source", "{}") iface_driver = params.get("iface_driver") iface_model = params.get("iface_model") iface_target = params.get("iface_target") iface_backend = params.get("iface_backend", "{}") iface_driver_host = params.get("iface_driver_host") iface_driver_guest = params.get("iface_driver_guest") attach_device = params.get("attach_iface_device") change_option = "yes" == params.get("change_iface_options", "no") update_device = "yes" == params.get("update_iface_device", "no") additional_guest = "yes" == params.get("additional_guest", "no") serial_login = "******" == params.get("serial_login", "no") test_option_cmd = "yes" == params.get( "test_iface_option_cmd", "no") test_option_xml = "yes" == params.get( "test_iface_option_xml", "no") test_vhost_net = "yes" == params.get( "test_vhost_net", "no") test_option_offloads = "yes" == params.get( "test_option_offloads", "no") test_iface_user = "******" == params.get( "test_iface_user", "no") test_iface_mcast = "yes" == params.get( "test_iface_mcast", "no") test_libvirtd = "yes" == params.get("test_libvirtd", "no") test_guest_ip = "yes" == params.get("test_guest_ip", "no") test_backend = "yes" == params.get("test_backend", "no") if iface_driver_host or iface_driver_guest or test_backend: if not libvirt_version.version_compare(1, 2, 8): raise error.TestNAError("Offloading/backend options not " "supported in this libvirt version") if iface_driver and "queues" in ast.literal_eval(iface_driver): if not libvirt_version.version_compare(1, 0, 6): raise error.TestNAError("Queues options not supported" " in this libvirt version") if unprivileged_user: virsh_dargs["unprivileged_user"] = unprivileged_user # Create unprivileged user if needed cmd = ("grep {0} /etc/passwd || " "useradd {0}".format(unprivileged_user)) utils.run(cmd) # Need another disk image for unprivileged user to access dst_disk = "/tmp/%s.img" % unprivileged_user # Destroy VM first if vm.is_alive(): vm.destroy(gracefully=False) # Back up xml file. vmxml_backup = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) iface_mac_old = vm_xml.VMXML.get_first_mac_by_name(vm_name) # iface_mac will update if attach a new interface iface_mac = iface_mac_old # Additional vm for test additional_vm = None libvirtd = utils_libvirtd.Libvirtd() try: # Build the xml and run test. try: # Prepare interface backend files if test_backend: if not os.path.exists("/dev/vhost-net"): utils.run("modprobe vhost-net") backend = ast.literal_eval(iface_backend) backend_tap = "/dev/net/tun" backend_vhost = "/dev/vhost-net" if not backend: backend["tap"] = backend_tap backend["vhost"] = backend_vhost if not start_error: # Create backend files for normal test if not os.path.exists(backend["tap"]): os.rename(backend_tap, backend["tap"]) if not os.path.exists(backend["vhost"]): os.rename(backend_vhost, backend["vhost"]) # Edit the interface xml. if change_option: modify_iface_xml(update=False) # Check vhost driver. if test_vhost_net: if os.path.exists("/dev/vhost-net"): cmd = ("modprobe -r {0}; lsmod | " "grep {0}".format("vhost_net")) if not utils.system(cmd, ignore_status=True): raise error.TestError("Can't remove " "vhost_net driver") # Attach a interface when vm is shutoff if attach_device == 'config': iface_mac = utils_net.generate_mac_address_simple() iface_xml_obj = create_iface_xml(iface_mac) iface_xml_obj.xmltreefile.write() ret = virsh.attach_device(vm_name, iface_xml_obj.xml, flagstr="--config", ignore_status=True) libvirt.check_exit_status(ret) # Clone additional vm if additional_guest: guest_name = "%s_%s" % (vm_name, '1') # Clone additional guest timeout = params.get("clone_timeout", 360) utils_libguestfs.virt_clone_cmd(vm_name, guest_name, True, timeout=timeout) additional_vm = vm.clone(guest_name) additional_vm.start() #additional_vm.wait_for_login() # Start the VM. if unprivileged_user: virsh.start(vm_name, **virsh_dargs) cmd = ("su - %s -c 'virsh console %s'" % (unprivileged_user, vm_name)) session = aexpect.ShellSession(cmd) session.sendline() remote.handle_prompts(session, params.get("username"), params.get("password"), "[\#\$]", 30) # Get ip address on guest if not get_guest_ip(session, iface_mac): raise error.TestError("Can't get ip address on guest") else: # Will raise VMStartError exception if start fails vm.start() if serial_login: session = vm.wait_for_serial_login() else: session = vm.wait_for_login() if start_error: raise error.TestFail("VM started unexpectedly") if test_vhost_net: if utils.system("lsmod | grep vhost_net", ignore_status=True): raise error.TestFail("vhost_net module can't be" " loaded automatically") # Attach a interface when vm is running if attach_device == 'live': iface_mac = utils_net.generate_mac_address_simple() iface_xml_obj = create_iface_xml(iface_mac) iface_xml_obj.xmltreefile.write() ret = virsh.attach_device(vm_name, iface_xml_obj.xml, flagstr="--live", ignore_status=True) libvirt.check_exit_status(ret) # Need sleep here for attachment take effect time.sleep(5) # Update a interface options if update_device: modify_iface_xml(update=True, status_error=status_error) # Run tests for qemu-kvm command line options if test_option_cmd: run_cmdline_test(iface_mac) # Run tests for vm xml if test_option_xml: run_xml_test(iface_mac) # Run tests for offloads options if test_option_offloads: if iface_driver_host: ifname_guest = utils_net.get_linux_ifname( session, iface_mac) check_offloads_option( ifname_guest, ast.literal_eval( iface_driver_host), session) if iface_driver_guest: ifname_host = libvirt.get_ifname_host(vm_name, iface_mac) check_offloads_option( ifname_host, ast.literal_eval(iface_driver_guest)) if test_iface_user: # Test user type network check_user_network(session) if test_iface_mcast: # Test mcast type network check_mcast_network(session) # Check guest ip address if test_guest_ip: if not get_guest_ip(session, iface_mac): raise error.TestFail("Guest can't get a" " valid ip address") session.close() # Restart libvirtd and guest, then test again if test_libvirtd: libvirtd.restart() vm.destroy() vm.start() if test_option_xml: run_xml_test(iface_mac) # Detach hot/cold-plugged interface at last if attach_device: ret = virsh.detach_device(vm_name, iface_xml_obj.xml, flagstr="", ignore_status=True) libvirt.check_exit_status(ret) except virt_vm.VMStartError, e: logging.info(str(e)) if start_error: pass else: raise error.TestFail('VM Failed to start for some reason!') finally: # Recover VM. logging.info("Restoring vm...") # Restore interface backend files if test_backend: if not os.path.exists(backend_tap): os.rename(backend["tap"], backend_tap) if not os.path.exists(backend_vhost): os.rename(backend["vhost"], backend_vhost) if unprivileged_user: virsh.remove_domain(vm_name, "--remove-all-storage", **virsh_dargs) if additional_vm: virsh.remove_domain(additional_vm.name, "--remove-all-storage") # Kill all omping server process on host utils.system("pidof omping && killall omping", ignore_status=True) if vm.is_alive(): vm.destroy(gracefully=False) vmxml_backup.sync()
def run(test, params, env): """ Test interface with unprivileged user """ def create_bridge(br_name, iface_name): """ Create bridge attached to physical interface """ # Make sure the bridge not exist if libvirt.check_iface(br_name, "exists", "--all"): test.cancel("The bridge %s already exist" % br_name) # Create bridge utils_package.package_install('tmux') cmd = 'tmux -c "ip link add name {0} type bridge; ip link set {1} up;' \ ' ip link set {1} master {0}; ip link set {0} up; pkill dhclient; ' \ 'sleep 6; dhclient {0}; ifconfig {1} 0"'.format(br_name, iface_name) process.run(cmd, shell=True, verbose=True) def check_ping(dest_ip, ping_count, timeout, src_ip=None, session=None, expect_success=True): """ Check if ping result meets expectation """ status, output = utils_net.ping(dest=dest_ip, count=ping_count, interface=src_ip, timeout=timeout, session=session, force_ipv4=True) success = True if status == 0 else False if success != expect_success: test.fail('Ping result not met expectation, ' 'actual result is {}'.format(success)) if not libvirt_version.version_compare(5, 6, 0): test.cancel('Libvirt version is too low for this test.') vm_name = params.get('main_vm') rand_id = '_' + utils_misc.generate_random_string(3) upu_vm_name = 'upu_vm' + rand_id user_vm_name = params.get('user_vm_name', 'non_root_vm') bridge_name = params.get('bridge_name', 'test_br0') + rand_id device_type = params.get('device_type', '') iface_name = utils_net.get_net_if(state="UP")[0] tap_name = params.get('tap_name', 'mytap0') + rand_id macvtap_name = params.get('macvtap_name', 'mymacvtap0') + rand_id remote_ip = params.get('remote_ip') up_user = params.get('up_user', 'test_upu') + rand_id case = params.get('case', '') # Create unprivileged user logging.info('Create unprivileged user %s', up_user) process.run('useradd %s' % up_user, shell=True, verbose=True) root_vmxml = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) upu_args = { 'unprivileged_user': up_user, 'ignore_status': False, 'debug': True, } try: # Create vm as unprivileged user logging.info('Create vm as unprivileged user') upu_vmxml = root_vmxml.copy() # Prepare vm for unprivileged user xml_devices = upu_vmxml.devices disks = xml_devices.by_device_tag("disk") for disk in disks: ori_path = disk.source['attrs'].get('file') if not ori_path: continue file_name = ori_path.split('/')[-1] new_disk_path = '/home/{}/{}'.format(up_user, file_name) logging.debug('New disk path:{}'.format(new_disk_path)) # Copy disk image file and chown to make sure that # unprivileged user has access shutil.copyfile(ori_path, new_disk_path) shutil.chown(new_disk_path, up_user, up_user) # Modify xml to set new path of disk disk_index = xml_devices.index(disk) source = xml_devices[disk_index].source new_attrs = source.attrs new_attrs['file'] = new_disk_path source.attrs = new_attrs xml_devices[disk_index].source = source logging.debug(xml_devices[disk_index].source) upu_vmxml.devices = xml_devices new_xml_path = '/home/{}/upu.xml'.format(up_user) shutil.copyfile(upu_vmxml.xml, new_xml_path) # Define vm for unprivileged user virsh.define(new_xml_path, **upu_args) virsh.domrename(vm_name, upu_vm_name, **upu_args) logging.debug(virsh.dumpxml(upu_vm_name, **upu_args)) upu_vmxml = vm_xml.VMXML() upu_vmxml.xml = virsh.dumpxml(upu_vm_name, **upu_args).stdout_text if case == 'precreated': if device_type == 'tap': # Create bridge create_bridge(bridge_name, iface_name) # Create tap device tap_cmd = 'ip tuntap add mode tap user {user} group {user} ' \ 'name {tap};ip link set {tap} up;ip link set {tap} ' \ 'master {br}'.format(tap=tap_name, user=up_user, br=bridge_name) # Execute command as root process.run(tap_cmd, shell=True, verbose=True) if device_type == 'macvtap': # Create macvtap device mac_addr = utils_net.generate_mac_address_simple() macvtap_cmd = 'ip link add link {iface} name {macvtap} address' \ ' {mac} type macvtap mode bridge;' \ 'ip link set {macvtap} up'.format( iface=iface_name, macvtap=macvtap_name, mac=mac_addr) process.run(macvtap_cmd, shell=True, verbose=True) cmd_get_tap = 'ip link show {} | head -1 | cut -d: -f1'.format(macvtap_name) tap_index = process.run(cmd_get_tap, shell=True, verbose=True).stdout_text.strip() device_path = '/dev/tap{}'.format(tap_index) logging.debug('device_path: {}'.format(device_path)) # Change owner and group for device process.run('chown {user} {path};chgrp {user} {path}'.format( user=up_user, path=device_path), shell=True, verbose=True) # Check if device owner is changed to unprivileged user process.run('ls -l %s' % device_path, shell=True, verbose=True) # Modify interface all_devices = upu_vmxml.devices iface_list = all_devices.by_device_tag('interface') if not iface_list: test.error('No iface to modify') iface = iface_list[0] # Remove other interfaces for ifc in iface_list[1:]: all_devices.remove(ifc) if device_type == 'tap': dev_name = tap_name elif device_type == 'macvtap': dev_name = macvtap_name else: test.error('Invalid device type: {}'.format(device_type)) if_index = all_devices.index(iface) iface = all_devices[if_index] iface.type_name = 'ethernet' iface.target = { 'dev': dev_name, 'managed': 'no' } if device_type == 'macvtap': iface.mac_address = mac_addr logging.debug(iface) upu_vmxml.devices = all_devices logging.debug(upu_vmxml) # Define updated xml shutil.copyfile(upu_vmxml.xml, new_xml_path) upu_vmxml.xml = new_xml_path virsh.define(new_xml_path, **upu_args) # Switch to unprivileged user and modify vm's interface # Start vm as unprivileged user and test network virsh.start(upu_vm_name, debug=True, ignore_status=False, unprivileged_user=up_user) cmd = ("su - %s -c 'virsh console %s'" % (up_user, upu_vm_name)) session = aexpect.ShellSession(cmd) session.sendline() remote.handle_prompts(session, params.get("username"), params.get("password"), r"[\#\$]\s*$", 60) logging.debug(session.cmd_output('ifconfig')) check_ping(remote_ip, 5, 10, session=session) session.close() finally: if 'upu_virsh' in locals(): virsh.destroy(upu_vm_name, unprivileged_user=up_user) virsh.undefine(upu_vm_name, unprivileged_user=up_user) if case == 'precreated': try: if device_type == 'tap': process.run('ip tuntap del mode tap {}'.format(tap_name), shell=True, verbose=True) elif device_type == 'macvtap': process.run('ip l del {}'.format(macvtap_name), shell=True, verbose=True) except Exception: pass finally: cmd = 'tmux -c "ip link set {1} nomaster; ip link delete {0};' \ 'pkill dhclient; sleep 6; dhclient {1}"'.format(bridge_name, iface_name) process.run(cmd, shell=True, verbose=True, ignore_status=True) process.run('pkill -u {0};userdel -f -r {0}'.format(up_user), shell=True, verbose=True, ignore_status=True)
def run(test, params, env): """ Test interafce xml options. 1.Prepare test environment,destroy or suspend a VM. 2.Edit xml and start the domain. 3.Perform test operation. 4.Recover test environment. 5.Confirm the test result. """ vm_name = params.get("main_vm") vm = env.get_vm(vm_name) def prepare_pxe_boot(): """ Prepare tftp server and pxe boot files """ pkg_list = ["syslinux", "tftp-server", "tftp", "ipxe-roms-qemu", "wget"] # Try to install required packages if not utils_package.package_install(pkg_list): test.error("Failed ot install required packages") boot_initrd = params.get("boot_initrd", "EXAMPLE_INITRD") boot_vmlinuz = params.get("boot_vmlinuz", "EXAMPLE_VMLINUZ") if boot_initrd.count("EXAMPLE") or boot_vmlinuz.count("EXAMPLE"): test.cancel("Please provide initrd/vmlinuz URL") # Download pxe boot images process.system("wget %s -O %s/initrd.img" % (boot_initrd, tftp_root)) process.system("wget %s -O %s/vmlinuz" % (boot_vmlinuz, tftp_root)) process.system("cp -f /usr/share/syslinux/pxelinux.0 {0};" " mkdir -m 777 -p {0}/pxelinux.cfg".format(tftp_root), shell=True) pxe_file = "%s/pxelinux.cfg/default" % tftp_root boot_txt = """ DISPLAY boot.txt DEFAULT rhel LABEL rhel kernel vmlinuz append initrd=initrd.img PROMPT 1 TIMEOUT 3""" with open(pxe_file, 'w') as p_file: p_file.write(boot_txt) def modify_iface_xml(): """ Modify interface xml options """ vmxml = vm_xml.VMXML.new_from_dumpxml(vm_name) if pxe_boot: # Config boot console for pxe boot osxml = vm_xml.VMOSXML() osxml.type = vmxml.os.type osxml.arch = vmxml.os.arch osxml.machine = vmxml.os.machine osxml.loader = "/usr/share/seabios/bios.bin" osxml.bios_useserial = "yes" osxml.bios_reboot_timeout = "-1" osxml.boots = ['network'] del vmxml.os vmxml.os = osxml xml_devices = vmxml.devices iface_index = xml_devices.index( xml_devices.by_device_tag("interface")[0]) iface = xml_devices[iface_index] iface_bandwidth = {} iface_inbound = ast.literal_eval(iface_bandwidth_inbound) iface_outbound = ast.literal_eval(iface_bandwidth_outbound) if iface_inbound: iface_bandwidth["inbound"] = iface_inbound if iface_outbound: iface_bandwidth["outbound"] = iface_outbound if iface_bandwidth: bandwidth = iface.new_bandwidth(**iface_bandwidth) iface.bandwidth = bandwidth iface_type = params.get("iface_type", "network") iface.type_name = iface_type source = ast.literal_eval(iface_source) if not source: source = {"network": "default"} net_ifs = utils_net.get_net_if(state="UP") # Check source device is valid or not, # if it's not in host interface list, try to set # source device to first active interface of host if (iface.type_name == "direct" and 'dev' in source and source['dev'] not in net_ifs): logging.warn("Source device %s is not a interface" " of host, reset to %s", source['dev'], net_ifs[0]) source['dev'] = net_ifs[0] del iface.source iface.source = source iface_model = params.get("iface_model", "virtio") iface.model = iface_model logging.debug("New interface xml file: %s", iface) vmxml.devices = xml_devices vmxml.xmltreefile.write() vmxml.sync() def run_dnsmasq_default_test(key, value=None, exists=True, name="default"): """ Test dnsmasq configuration. :param key: key in conf file to check :param value: value in conf file to check :param exists: check the key:value exist or not :param name: The name of conf file """ conf_file = "/var/lib/libvirt/dnsmasq/%s.conf" % name if not os.path.exists(conf_file): test.cancel("Can't find %s.conf file" % name) configs = "" with open(conf_file, 'r') as f: configs = f.read() logging.debug("configs in file %s: %s", conf_file, configs) if value: config = "%s=%s" % (key, value) else: config = key if not configs.count(config): if exists: test.fail("Can't find %s=%s in configuration file" % (key, value)) else: if not exists: test.fail("Found %s=%s in configuration file" % (key, value)) def run_dnsmasq_addnhosts_test(hostip, hostnames): """ Test host ip and names configuration """ conf_file = "/var/lib/libvirt/dnsmasq/default.addnhosts" hosts_re = ".*".join(hostnames) configs = "" with open(conf_file, 'r') as f: configs = f.read() logging.debug("configs in file %s: %s", conf_file, configs) if not re.search(r"%s.*%s" % (hostip, hosts_re), configs, re.M): test.fail("Can't find '%s' in configuration file" % hostip) def run_dnsmasq_host_test(iface_mac, guest_ip, guest_name): """ Test host name and ip configuration for dnsmasq """ conf_file = "/var/lib/libvirt/dnsmasq/default.hostsfile" config = "%s,%s,%s" % (iface_mac, guest_ip, guest_name) configs = "" with open(conf_file, 'r') as f: configs = f.read() logging.debug("configs in file %s: %s", conf_file, configs) if not configs.count(config): test.fail("Can't find host configuration in file %s" % conf_file) def check_class_rules(ifname, rule_id, bandwidth): """ Check bandwidth settings via 'tc class' output """ cmd = "tc class show dev %s" % ifname class_output = to_text(process.system_output(cmd)) logging.debug("Bandwidth class output: %s", class_output) class_pattern = (r"class htb %s.*rate (\d+)(K?M?)bit ceil" " (\d+)(K?M?)bit burst (\d+)(K?M?)b.*" % rule_id) se = re.search(class_pattern, class_output, re.M) if not se: test.fail("Can't find outbound setting for htb %s" % rule_id) logging.debug("bandwidth from tc output:%s" % str(se.groups())) rate = None if "floor" in bandwidth: rate = int(bandwidth["floor"]) * 8 elif "average" in bandwidth: rate = int(bandwidth["average"]) * 8 if rate: if se.group(2) == 'M': rate_check = int(se.group(1)) * 1000 else: rate_check = int(se.group(1)) assert rate_check == rate if "peak" in bandwidth: if se.group(4) == 'M': ceil_check = int(se.group(3)) * 1000 else: ceil_check = int(se.group(3)) assert ceil_check == int(bandwidth["peak"]) * 8 if "burst" in bandwidth: if se.group(6) == 'M': tc_burst = int(se.group(5)) * 1024 else: tc_burst = int(se.group(5)) assert tc_burst == int(bandwidth["burst"]) def check_filter_rules(ifname, bandwidth): """ Check bandwidth settings via 'tc filter' output """ cmd = "tc -d filter show dev %s parent ffff:" % ifname filter_output = to_text(process.system_output(cmd)) logging.debug("Bandwidth filter output: %s", filter_output) if not filter_output.count("filter protocol all pref"): test.fail("Can't find 'protocol all' settings in filter rules") filter_pattern = ".*police.*rate (\d+)(K?M?)bit burst (\d+)(K?M?)b.*" se = re.search(r"%s" % filter_pattern, filter_output, re.M) if not se: test.fail("Can't find any filter policy") logging.debug("bandwidth from tc output:%s" % str(se.groups())) logging.debug("bandwidth from setting:%s" % str(bandwidth)) if "average" in bandwidth: if se.group(2) == 'M': tc_average = int(se.group(1)) * 1000 else: tc_average = int(se.group(1)) assert tc_average == int(bandwidth["average"]) * 8 if "burst" in bandwidth: if se.group(4) == 'M': tc_burst = int(se.group(3)) * 1024 else: tc_burst = int(se.group(3)) assert tc_burst == int(bandwidth["burst"]) def check_host_routes(): """ Check network routes on host """ for rt in routes: try: route = ast.literal_eval(rt) addr = "%s/%s" % (route["address"], route["prefix"]) cmd = "ip route list %s" % addr if "family" in route and route["family"] == "ipv6": cmd = "ip -6 route list %s" % addr output = to_text(process.system_output(cmd)) match_obj = re.search(r"via (\S+).*metric (\d+)", output) if match_obj: via_addr = match_obj.group(1) metric = match_obj.group(2) logging.debug("via address %s for %s, matric is %s" % (via_addr, addr, metric)) assert via_addr == route["gateway"] if "metric" in route: assert metric == route["metric"] except KeyError: pass def run_bandwidth_test(check_net=False, check_iface=False): """ Test bandwidth option for network or interface by tc command. """ iface_inbound = ast.literal_eval(iface_bandwidth_inbound) iface_outbound = ast.literal_eval(iface_bandwidth_outbound) net_inbound = ast.literal_eval(net_bandwidth_inbound) net_outbound = ast.literal_eval(net_bandwidth_outbound) net_bridge_name = ast.literal_eval(net_bridge)["name"] iface_name = libvirt.get_ifname_host(vm_name, iface_mac) try: if check_net and net_inbound: # Check qdisc rules cmd = "tc -d qdisc show dev %s" % net_bridge_name qdisc_output = to_text(process.system_output(cmd)) logging.debug("Bandwidth qdisc output: %s", qdisc_output) if not qdisc_output.count("qdisc ingress ffff:"): test.fail("Can't find ingress setting") check_class_rules(net_bridge_name, "1:1", {"average": net_inbound["average"], "peak": net_inbound["peak"]}) check_class_rules(net_bridge_name, "1:2", net_inbound) # Check filter rules on bridge interface if check_net and net_outbound: check_filter_rules(net_bridge_name, net_outbound) # Check class rules on interface inbound settings if check_iface and iface_inbound: check_class_rules(iface_name, "1:1", {'average': iface_inbound['average'], 'peak': iface_inbound['peak'], 'burst': iface_inbound['burst']}) if "floor" in iface_inbound: if not libvirt_version.version_compare(1, 0, 1): test.cancel("Not supported Qos options 'floor'") check_class_rules(net_bridge_name, "1:3", {'floor': iface_inbound["floor"]}) # Check filter rules on interface outbound settings if check_iface and iface_outbound: check_filter_rules(iface_name, iface_outbound) except AssertionError: stacktrace.log_exc_info(sys.exc_info()) test.fail("Failed to check network bandwidth") def check_name_ip(session): """ Check dns resolving on guest """ # Check if bind-utils is installed if not utils_package.package_install(['bind-utils'], session): test.error("Failed to install bind-utils on guest") # Run host command to check if hostname can be resolved if not guest_ipv4 and not guest_ipv6: test.fail("No ip address found from parameters") guest_ip = guest_ipv4 if guest_ipv4 else guest_ipv6 cmd = "host %s | grep %s" % (guest_name, guest_ip) if session.cmd_status(cmd): test.fail("Can't resolve name %s on guest" % guest_name) def check_ipt_rules(check_ipv4=True, check_ipv6=False): """ Check iptables for network/interface """ br_name = ast.literal_eval(net_bridge)["name"] net_forward = ast.literal_eval(params.get("net_forward", "{}")) net_ipv4 = params.get("net_ipv4") net_ipv6 = params.get("net_ipv6") ipt_rules = ("INPUT -i %s -p udp -m udp --dport 53 -j ACCEPT" % br_name, "INPUT -i %s -p tcp -m tcp --dport 53 -j ACCEPT" % br_name, "INPUT -i %s -p udp -m udp --dport 67 -j ACCEPT" % br_name, "INPUT -i %s -p tcp -m tcp --dport 67 -j ACCEPT" % br_name, "FORWARD -i {0} -o {0} -j ACCEPT".format(br_name), "FORWARD -o %s -j REJECT --reject-with icmp" % br_name, "FORWARD -i %s -j REJECT --reject-with icmp" % br_name, "OUTPUT -o %s -p udp -m udp --dport 68 -j ACCEPT" % br_name) net_dev_in = "" net_dev_out = "" if "dev" in net_forward: net_dev_in = " -i %s" % net_forward["dev"] net_dev_out = " -o %s" % net_forward["dev"] if check_ipv4: ipv4_rules = list(ipt_rules) ctr_rule = "" nat_rules = [] if "mode" in net_forward and net_forward["mode"] == "nat": nat_port = ast.literal_eval(params.get("nat_port")) p_start = nat_port["start"] p_end = nat_port["end"] ctr_rule = " -m .* RELATED,ESTABLISHED" nat_rules = [("POSTROUTING -s {0} ! -d {0} -p tcp -j MASQUERADE" " --to-ports {1}-{2}".format(net_ipv4, p_start, p_end)), ("POSTROUTING -s {0} ! -d {0} -p udp -j MASQUERADE" " --to-ports {1}-{2}".format(net_ipv4, p_start, p_end)), ("POSTROUTING -s {0} ! -d {0} -p udp" " -j MASQUERADE".format(net_ipv4))] if nat_rules: ipv4_rules.extend(nat_rules) if (net_ipv4 and "mode" in net_forward and net_forward["mode"] in ["nat", "route"]): rules = [("FORWARD -d %s%s -o %s%s -j ACCEPT" % (net_ipv4, net_dev_in, br_name, ctr_rule)), ("FORWARD -s %s -i %s%s -j ACCEPT" % (net_ipv4, br_name, net_dev_out))] ipv4_rules.extend(rules) output = to_text(process.system_output('iptables-save')) logging.debug("iptables: %s", output) if "mode" in net_forward and net_forward["mode"] == "open": if re.search(r"%s|%s" % (net_ipv4, br_name), output, re.M): test.fail("Find iptable rule for open mode") utils_libvirtd.libvirtd_restart() output_again = to_text(process.system_output('iptables-save')) if re.search(r"%s|%s" % (net_ipv4, br_name), output_again, re.M): test.fail("Find iptable rule for open mode after restart " "libvirtd") else: logging.info("Can't find iptable rule for open mode as expected") else: for ipt in ipv4_rules: if not re.search(r"%s" % ipt, output, re.M): test.fail("Can't find iptable rule:\n%s" % ipt) return ipv4_rules if check_ipv6: ipv6_rules = list(ipt_rules) if (net_ipv6 and "mode" in net_forward and net_forward["mode"] in ["nat", "route"]): rules = [("FORWARD -d %s%s -o %s -j ACCEPT" % (net_ipv6, net_dev_in, br_name)), ("FORWARD -s %s -i %s%s -j ACCEPT" % (net_ipv6, br_name, net_dev_out))] ipv6_rules.extend(rules) output = to_text(process.system_output("ip6tables-save")) logging.debug("iptables: %s", output) for ipt in ipv6_rules: if not output.count(ipt): test.fail("Can't find ipbtable rule:\n%s" % ipt) return ipv6_rules def run_ip_test(session, ip_ver): """ Check iptables on host and ipv6 address on guest """ if ip_ver == "ipv6": # Clean up iptables rules for guest to get ipv6 address session.cmd_status("ip6tables -F") # It may take some time to get the ip address def get_ip_func(): return utils_net.get_guest_ip_addr(session, iface_mac, ip_version=ip_ver) utils_misc.wait_for(get_ip_func, 5) if not get_ip_func(): utils_net.restart_guest_network(session, iface_mac, ip_version=ip_ver) utils_misc.wait_for(get_ip_func, 5) vm_ip = get_ip_func() logging.debug("Guest has ip: %s", vm_ip) if not vm_ip: test.fail("Can't find ip address on guest") ip_gateway = net_ip_address if ip_ver == "ipv6": ip_gateway = net_ipv6_address # Cleanup ip6talbes on host for ping6 test process.system("ip6tables -F") if ip_gateway and not routes: ping_s, _ = ping(dest=ip_gateway, count=5, timeout=10, session=session) if ping_s: test.fail("Failed to ping gateway address: %s" % ip_gateway) def run_guest_libvirt(session): """ Check guest libvirt network """ # Try to install required packages if not utils_package.package_install(['libvirt'], session): test.error("Failed ot install libvirt package on guest") # Try to load tun module first session.cmd("lsmod | grep tun || modprobe tun") # Check network state on guest cmd = ("service libvirtd restart; virsh net-info default" " | grep 'Active:.*yes'") if session.cmd_status(cmd): test.fail("'default' network isn't in active state") # Try to destroy&start default network on guest for opt in ['net-destroy', 'net-start']: cmd = "virsh %s default" % opt status, output = session.cmd_status_output(cmd) logging.debug("Run %s on guest exit %s, output %s" % (cmd, status, output)) if status: test.fail(output) if not utils_package.package_remove("libvirt*", session): test.error("Failed to remove libvirt packages on guest") start_error = "yes" == params.get("start_error", "no") define_error = "yes" == params.get("define_error", "no") restart_error = "yes" == params.get("restart_error", "no") # network specific attributes. net_name = params.get("net_name", "default") net_bridge = params.get("net_bridge", "{'name':'virbr0'}") net_domain = params.get("net_domain") net_ip_address = params.get("net_ip_address") net_ipv6_address = params.get("net_ipv6_address") net_dns_forward = params.get("net_dns_forward") net_dns_txt = params.get("net_dns_txt") net_dns_srv = params.get("net_dns_srv") net_dns_hostip = params.get("net_dns_hostip") net_dns_hostnames = params.get("net_dns_hostnames", "").split() dhcp_start_ipv4 = params.get("dhcp_start_ipv4") dhcp_end_ipv4 = params.get("dhcp_end_ipv4") dhcp_start_ipv6 = params.get("dhcp_start_ipv6") dhcp_end_ipv6 = params.get("dhcp_end_ipv6") guest_name = params.get("guest_name") guest_ipv4 = params.get("guest_ipv4") guest_ipv6 = params.get("guest_ipv6") tftp_root = params.get("tftp_root") pxe_boot = "yes" == params.get("pxe_boot", "no") routes = params.get("routes", "").split() net_bandwidth_inbound = params.get("net_bandwidth_inbound", "{}") net_bandwidth_outbound = params.get("net_bandwidth_outbound", "{}") iface_bandwidth_inbound = params.get("iface_bandwidth_inbound", "{}") iface_bandwidth_outbound = params.get("iface_bandwidth_outbound", "{}") iface_num = params.get("iface_num", "1") iface_source = params.get("iface_source", "{}") multiple_guests = params.get("multiple_guests") create_network = "yes" == params.get("create_network", "no") attach_iface = "yes" == params.get("attach_iface", "no") serial_login = "******" == params.get("serial_login", "no") change_iface_option = "yes" == params.get("change_iface_option", "no") test_bridge = "yes" == params.get("test_bridge", "no") test_dnsmasq = "yes" == params.get("test_dnsmasq", "no") test_dhcp_range = "yes" == params.get("test_dhcp_range", "no") test_dns_host = "yes" == params.get("test_dns_host", "no") test_qos_bandwidth = "yes" == params.get("test_qos_bandwidth", "no") test_pg_bandwidth = "yes" == params.get("test_portgroup_bandwidth", "no") test_qos_remove = "yes" == params.get("test_qos_remove", "no") test_ipv4_address = "yes" == params.get("test_ipv4_address", "no") test_ipv6_address = "yes" == params.get("test_ipv6_address", "no") test_guest_libvirt = "yes" == params.get("test_guest_libvirt", "no") net_no_bridge = "yes" == params.get("no_bridge", "no") net_no_mac = "yes" == params.get("no_mac", "no") net_no_ip = "yes" == params.get("no_ip", "no") net_with_dev = "yes" == params.get("with_dev", "no") username = params.get("username") password = params.get("password") ipt_rules = [] # Destroy VM first if vm.is_alive(): vm.destroy(gracefully=False) # Back up xml file. netxml_backup = NetworkXML.new_from_net_dumpxml("default") iface_mac = vm_xml.VMXML.get_first_mac_by_name(vm_name) params["guest_mac"] = iface_mac vmxml_backup = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) vms_list = [] if "floor" in ast.literal_eval(iface_bandwidth_inbound): if not libvirt_version.version_compare(1, 0, 1): test.cancel("Not supported Qos options 'floor'") # Enabling IPv6 forwarding with RA routes without accept_ra set to 2 # is likely to cause routes loss sysctl_cmd = 'sysctl net.ipv6.conf.all.accept_ra' original_accept_ra = to_text(process.system_output(sysctl_cmd + ' -n')) if test_ipv6_address and original_accept_ra != '2': process.system(sysctl_cmd + '=2') # Build the xml and run test. try: if test_dnsmasq: # Check the settings before modifying network xml if net_dns_forward == "no": run_dnsmasq_default_test("domain-needed", exists=False) run_dnsmasq_default_test("local", "//", exists=False) if net_domain: run_dnsmasq_default_test("domain", net_domain, exists=False) run_dnsmasq_default_test("expand-hosts", exists=False) # Prepare pxe boot directory if pxe_boot: prepare_pxe_boot() # Edit the network xml or create a new one. if create_network: net_ifs = utils_net.get_net_if(state="UP") # Check forward device is valid or not, # if it's not in host interface list, try to set # forward device to first active interface of host forward = ast.literal_eval(params.get("net_forward", "{}")) if ('mode' in forward and forward['mode'] in ['passthrough', 'private', 'bridge', 'macvtap'] and 'dev' in forward and forward['dev'] not in net_ifs): logging.warn("Forward device %s is not a interface" " of host, reset to %s", forward['dev'], net_ifs[0]) forward['dev'] = net_ifs[0] params["net_forward"] = str(forward) forward_iface = params.get("forward_iface") if forward_iface: interface = [x for x in forward_iface.split()] # The guest will use first interface of the list, # check if it's valid or not, if it's not in host # interface list, try to set forward interface to # first active interface of host. if interface[0] not in net_ifs: logging.warn("Forward interface %s is not a " " interface of host, reset to %s", interface[0], net_ifs[0]) interface[0] = net_ifs[0] params["forward_iface"] = " ".join(interface) netxml = libvirt.create_net_xml(net_name, params) if "mode" in forward and forward["mode"] == "open": netxml.mac = utils_net.generate_mac_address_simple() try: if net_no_bridge: netxml.del_bridge() if net_no_ip: netxml.del_ip() netxml.del_ip() if net_no_mac: netxml.del_mac() except xcepts.LibvirtXMLNotFoundError: pass if net_with_dev: net_forward = netxml.forward net_forward.update({"dev": net_ifs[0]}) netxml.forward = net_forward logging.info("netxml before define is %s", netxml) try: netxml.sync() except xcepts.LibvirtXMLError as details: logging.info(str(details)) if define_error: return else: test.fail("Failed to define network") # Check open mode network xml if "mode" in forward and forward["mode"] == "open": netxml_new = NetworkXML.new_from_net_dumpxml(net_name) logging.info("netxml after define is %s", netxml_new) try: if net_no_bridge: net_bridge = str(netxml_new.bridge) if net_no_mac: netxml_new.mac except xcepts.LibvirtXMLNotFoundError as details: test.fail("Failed to check %s xml: %s" % (net_name, details)) logging.info("mac/bridge still exist even if removed before define") # Edit the interface xml. if change_iface_option: modify_iface_xml() # Attach interface if needed if attach_iface: iface_type = params.get("iface_type", "network") iface_model = params.get("iface_model", "virtio") for i in range(int(iface_num)): logging.info("Try to attach interface loop %s" % i) options = ("%s %s --model %s --config" % (iface_type, net_name, iface_model)) ret = virsh.attach_interface(vm_name, options, ignore_status=True) if ret.exit_status: logging.error("Command output %s" % ret.stdout.strip()) test.fail("Failed to attach-interface") if multiple_guests: # Clone more vms for testing for i in range(int(multiple_guests)): guest_name = "%s_%s" % (vm_name, i) timeout = params.get("clone_timeout", 360) utils_libguestfs.virt_clone_cmd(vm_name, guest_name, True, timeout=timeout) vms_list.append(vm.clone(guest_name)) if test_bridge: bridge = ast.literal_eval(net_bridge) br_if = utils_net.Interface(bridge['name']) if not br_if.is_up(): test.fail("Bridge interface isn't up") if test_dnsmasq: # Check dnsmasq process dnsmasq_cmd = to_text(process.system_output("ps -aux|grep dnsmasq", shell=True)) logging.debug(dnsmasq_cmd) if not re.search("dnsmasq --conf-file=/var/lib/libvirt/dnsmasq/%s.conf" % net_name, dnsmasq_cmd): test.fail("Can not find dnsmasq process or the process is not correct") # Check the settings in dnsmasq config file if net_dns_forward == "no": run_dnsmasq_default_test("domain-needed") run_dnsmasq_default_test("local", "//") if net_domain: run_dnsmasq_default_test("domain", net_domain) run_dnsmasq_default_test("expand-hosts") if net_bridge: bridge = ast.literal_eval(net_bridge) run_dnsmasq_default_test("interface", bridge['name'], name=net_name) if 'stp' in bridge and bridge['stp'] == 'on': if 'delay' in bridge and bridge['delay'] != '0': br_delay = float(bridge['delay']) cmd = ("brctl showstp %s | grep 'bridge forward delay'" % bridge['name']) out = to_text(process.system_output( cmd, shell=True, ignore_status=False)) logging.debug("brctl showstp output: %s", out) pattern = (r"\s*forward delay\s+(\d+.\d+)\s+bridge" " forward delay\s+(\d+.\d+)") match_obj = re.search(pattern, out, re.M) if not match_obj or len(match_obj.groups()) != 2: test.fail("Can't see forward delay messages from command") elif (float(match_obj.groups()[0]) != br_delay or float(match_obj.groups()[1]) != br_delay): test.fail("Foward delay setting can't take effect") if dhcp_start_ipv4 and dhcp_end_ipv4: run_dnsmasq_default_test("dhcp-range", "%s,%s" % (dhcp_start_ipv4, dhcp_end_ipv4), name=net_name) if dhcp_start_ipv6 and dhcp_end_ipv6: run_dnsmasq_default_test("dhcp-range", "%s,%s,64" % (dhcp_start_ipv6, dhcp_end_ipv6), name=net_name) if guest_name and guest_ipv4: run_dnsmasq_host_test(iface_mac, guest_ipv4, guest_name) # check the left part in dnsmasq conf run_dnsmasq_default_test("strict-order", name=net_name) run_dnsmasq_default_test("pid-file", "/var/run/libvirt/network/%s.pid" % net_name, name=net_name) run_dnsmasq_default_test("except-interface", "lo", name=net_name) run_dnsmasq_default_test("bind-dynamic", name=net_name) run_dnsmasq_default_test("dhcp-no-override", name=net_name) if dhcp_start_ipv6 and dhcp_start_ipv4: run_dnsmasq_default_test("dhcp-lease-max", "493", name=net_name) else: range_num = int(params.get("dhcp_range", "252")) run_dnsmasq_default_test("dhcp-lease-max", str(range_num+1), name=net_name) run_dnsmasq_default_test("dhcp-hostsfile", "/var/lib/libvirt/dnsmasq/%s.hostsfile" % net_name, name=net_name) run_dnsmasq_default_test("addn-hosts", "/var/lib/libvirt/dnsmasq/%s.addnhosts" % net_name, name=net_name) if dhcp_start_ipv6: run_dnsmasq_default_test("enable-ra", name=net_name) if test_dns_host: if net_dns_txt: dns_txt = ast.literal_eval(net_dns_txt) run_dnsmasq_default_test("txt-record", "%s,%s" % (dns_txt["name"], dns_txt["value"])) if net_dns_srv: dns_srv = ast.literal_eval(net_dns_srv) run_dnsmasq_default_test("srv-host", "_%s._%s.%s,%s,%s,%s,%s" % (dns_srv["service"], dns_srv["protocol"], dns_srv["domain"], dns_srv["target"], dns_srv["port"], dns_srv["priority"], dns_srv["weight"])) if net_dns_hostip and net_dns_hostnames: run_dnsmasq_addnhosts_test(net_dns_hostip, net_dns_hostnames) # Run bandwidth test for network if test_qos_bandwidth: run_bandwidth_test(check_net=True) # Check routes if needed if routes: check_host_routes() try: # Start the VM. vm.start() if start_error: test.fail("VM started unexpectedly") if pxe_boot: # Just check network boot messages here vm.serial_console.read_until_output_matches( ["Loading vmlinuz", "Loading initrd.img"], utils_misc.strip_console_codes) output = vm.serial_console.get_stripped_output() logging.debug("Boot messages: %s", output) else: if serial_login: session = vm.wait_for_serial_login(username=username, password=password) else: session = vm.wait_for_login() if test_dhcp_range: dhcp_range = int(params.get("dhcp_range", "252")) utils_net.restart_guest_network(session, iface_mac) vm_ip = utils_net.get_guest_ip_addr(session, iface_mac) logging.debug("Guest has ip: %s", vm_ip) if not vm_ip and dhcp_range: test.fail("Guest has invalid ip address") elif vm_ip and not dhcp_range: test.fail("Guest has ip address: %s" % vm_ip) dhcp_range = dhcp_range - 1 for vms in vms_list: # Start other VMs. vms.start() sess = vms.wait_for_serial_login() vms_mac = vms.get_virsh_mac_address() # restart guest network to get ip addr utils_net.restart_guest_network(sess, vms_mac) vms_ip = utils_net.get_guest_ip_addr(sess, vms_mac) if not vms_ip and dhcp_range: test.fail("Guest has invalid ip address") elif vms_ip and not dhcp_range: # Get IP address on guest should return Null # if it exceeds the dhcp range test.fail("Guest has ip address: %s" % vms_ip) dhcp_range = dhcp_range - 1 if vms_ip: ping_s, _ = ping(dest=vm_ip, count=5, timeout=10, session=sess) if ping_s: test.fail("Failed to ping, src: %s, " "dst: %s" % (vms_ip, vm_ip)) sess.close() # Check dnsmasq settings if take affect in guest if guest_ipv4: check_name_ip(session) # Run bandwidth test for interface if test_qos_bandwidth: run_bandwidth_test(check_iface=True) # Run bandwidth test for portgroup if test_pg_bandwidth: pg_bandwidth_inbound = params.get( "portgroup_bandwidth_inbound", "").split() pg_bandwidth_outbound = params.get( "portgroup_bandwidth_outbound", "").split() pg_name = params.get("portgroup_name", "").split() pg_default = params.get("portgroup_default", "").split() iface_inbound = ast.literal_eval(iface_bandwidth_inbound) iface_outbound = ast.literal_eval(iface_bandwidth_outbound) iface_name = libvirt.get_ifname_host(vm_name, iface_mac) if_source = ast.literal_eval(iface_source) if "portgroup" in if_source: pg = if_source["portgroup"] else: pg = "default" for (name, df, bw_ib, bw_ob) in zip(pg_name, pg_default, pg_bandwidth_inbound, pg_bandwidth_outbound): if pg == name: inbound = ast.literal_eval(bw_ib) outbound = ast.literal_eval(bw_ob) elif pg == "default" and df == "yes": inbound = ast.literal_eval(bw_ib) outbound = ast.literal_eval(bw_ob) else: continue # Interface bandwidth settings will # overwriting portgroup settings if iface_inbound: inbound = iface_inbound if iface_outbound: outbound = iface_outbound check_class_rules(iface_name, "1:1", inbound) check_filter_rules(iface_name, outbound) if test_qos_remove: # Remove the bandwidth settings in network xml logging.debug("Removing network bandwidth settings...") netxml_backup.sync() vm.destroy(gracefully=False) # Should fail to start vm vm.start() if restart_error: test.fail("VM started unexpectedly") if test_ipv6_address: ipt_rules = check_ipt_rules(check_ipv6=True) if not ("mode" in forward and forward["mode"] == "open"): run_ip_test(session, "ipv6") if test_ipv4_address: ipt_rules = check_ipt_rules(check_ipv4=True) if not ("mode" in forward and forward["mode"] == "open"): run_ip_test(session, "ipv4") if test_guest_libvirt: run_guest_libvirt(session) session.close() except virt_vm.VMStartError as details: logging.info(str(details)) if not (start_error or restart_error): test.fail('VM failed to start:\n%s' % details) # Destroy created network and check iptable rules if net_name != "default": virsh.net_destroy(net_name) if ipt_rules: output_des = to_text(process.system_output('iptables-save')) for ipt in ipt_rules: if re.search(r"%s" % ipt, output_des, re.M): test.fail("Find iptable rule %s after net destroyed" % ipt) finally: # Recover VM. if vm.is_alive(): vm.destroy(gracefully=False) for vms in vms_list: virsh.remove_domain(vms.name, "--remove-all-storage") logging.info("Restoring network...") if net_name == "default": netxml_backup.sync() else: # Destroy and undefine new created network virsh.net_destroy(net_name) virsh.net_undefine(net_name) vmxml_backup.sync() if test_ipv6_address and original_accept_ra != '2': process.system(sysctl_cmd + "=%s" % original_accept_ra)
def run_virsh_attach_detach_interface(test, params, env): """ Test virsh {at|de}tach-interface command. 1) Prepare test environment and its parameters 2) Attach the required interface 3) According test type(only attach or both attach and detach): a.Go on to test detach(if attaching is correct) b.Return GOOD or raise TestFail(if attaching is wrong) 4) Check if attached interface is correct: a.Try to catch it in vm's XML file b.Try to catch it in vm 5) Detach the attached interface 6) Check result """ vm_name = params.get("main_vm") vm = env.get_vm(vm_name) # Test parameters uri = libvirt_vm.normalize_connect_uri(params.get("connect_uri", "default")) vm_ref = params.get("at_detach_iface_vm_ref", "domname") options_suffix = params.get("at_detach_iface_options_suffix", "") status_error = "yes" == params.get("status_error", "no") start_vm = params.get("start_vm") # Should attach must be pass for detach test. correct_attach = "yes" == params.get("correct_attach", "no") # Interface specific attributes. iface_type = params.get("at_detach_iface_type", "network") iface_source = params.get("at_detach_iface_source", "default") iface_mac = params.get("at_detach_iface_mac", "created") virsh_dargs = {'ignore_status': True, 'uri': uri} # Get a bridge name for test if iface_type is bridge. # If there is no bridge other than virbr0, raise TestNAError if iface_type == "bridge": host_bridge = utils_net.Bridge() bridge_list = host_bridge.list_br() try: bridge_list.remove("virbr0") except AttributeError: pass # If no virbr0, just pass is ok logging.debug("Useful bridges:%s", bridge_list) # just choosing one bridge on host. if len(bridge_list): iface_source = bridge_list[0] else: raise error.TestNAError("No useful bridge on host " "other than 'virbr0'.") dom_uuid = vm.get_uuid() dom_id = vm.get_id() # To confirm vm's state if start_vm == "no" and vm.is_alive(): vm.destroy() # Test both detach and attach, So collect info # both of them for result check. # When something wrong with interface, set it to 1 fail_flag = 0 result_info = [] # Set attach-interface domain if vm_ref == "domname": vm_ref = vm_name elif vm_ref == "domid": vm_ref = dom_id elif vm_ref == "domuuid": vm_ref = dom_uuid elif vm_ref == "hexdomid" and dom_id is not None: vm_ref = hex(int(dom_id)) # Get a mac address if iface_mac is 'created'. if iface_mac == "created" or correct_attach: iface_mac = utils_net.generate_mac_address_simple() # Set attach-interface options and Start attach-interface test if correct_attach: options = set_options("network", "default", iface_mac, "", "attach") attach_result = virsh.attach_interface(vm_name, options, **virsh_dargs) else: options = set_options(iface_type, iface_source, iface_mac, options_suffix, "attach") attach_result = virsh.attach_interface(vm_ref, options, **virsh_dargs) attach_status = attach_result.exit_status logging.debug(attach_result) # If attach interface failed. if attach_status: if not status_error: fail_flag = 1 result_info.append("Attach Failed: %s" % attach_result) elif status_error: # Here we just use it to exit, do not mean test failed fail_flag = 1 # If attach interface succeeded. else: if status_error and not correct_attach: fail_flag = 1 result_info.append("Attach Success with wrong command.") if fail_flag and start_vm == "yes": vm.destroy() if len(result_info): raise error.TestFail(result_info) else: # Exit because it is error_test for attach-interface. return # Check dumpxml file whether the interface is added successfully. status, ret = check_dumpxml_iface(vm_name, iface_mac, iface_type, iface_source) if status: fail_flag = 1 result_info.append(ret) # Login to domain to check new interface. if not vm.is_alive(): vm.start() elif vm.state() == "paused": vm.resume() status, ret = login_to_check(vm, iface_mac) if status: fail_flag = 1 result_info.append(ret) # Set detach-interface options options = set_options(iface_type, None, iface_mac, options_suffix, "detach") # Start detach-interface test detach_result = virsh.detach_interface(vm_ref, options, **virsh_dargs) detach_status = detach_result.exit_status logging.debug(detach_result) # Clean up. if check_dumpxml_iface(vm_name, iface_mac) is not None: cleanup_options = "--type %s --mac %s" % (iface_type, iface_mac) virsh.detach_interface(vm_ref, cleanup_options, **virsh_dargs) # Shutdown vm to be afraid of cleaning up failed if vm.is_alive(): vm.destroy() # Check results. if status_error: if detach_status == 0: raise error.TestFail("Detach Success with wrong command.") else: if detach_status != 0: raise error.TestFail("Detach Failed.") else: if fail_flag: raise error.TestFail("Attach-Detach Success but " "something wrong with its " "functional use:%s" % result_info)
def run(test, params, env): global flag_list, flag_list_abs, update_sec, without_sec global check_sec, check_without_sec, newxml # Basic network params net_name = params.get("net_update_net_name", "updatenet") net_section = params.get("network_section") update_command = params.get("update_command", "add-last") options = params.get("cmd_options", "") net_state = params.get("net_state") use_in_guest = params.get("use_in_guest") iface_type = params.get("iface_type", "network") ip_version = params.get("ip_version", "ipv4") new_start_ip = params.get("start_ip") new_end_ip = params.get("end_ip") parent_index = params.get("parent_index") status_error = params.get("status_error", "no") # dhcp host test new_dhcp_host_ip = params.get("new_dhcp_host_ip") new_dhcp_host_id = params.get("new_dhcp_host_id") new_dhcp_host_name = params.get("new_dhcp_host_name") new_dhcp_host_mac = params.get("new_dhcp_host_mac") # ipv4 range/host test ipv4_range_start = params.get("ori_ipv4_range_start") ipv4_range_end = params.get("ori_ipv4_range_end") ipv4_host_mac = params.get("ori_ipv4_host_mac") ipv4_host_ip = params.get("ori_ipv4_host_ip") ipv4_host_name = params.get("ori_ipv4_host_name") # ipv6 range/host test ipv6_range_start = params.get("ori_ipv6_range_start") ipv6_range_end = params.get("ori_ipv6_range_end") ipv6_host_id = params.get("ori_ipv6_host_id") ipv6_host_name = params.get("ori_ipv6_host_name") ipv6_host_ip = params.get("ori_ipv6_host_ip") # dns test dns_name = params.get("ori_dns_name") dns_value = params.get("ori_dns_value") new_dns_name = params.get("new_dns_name") new_dns_value = params.get("new_dns_value") # srv test srv_service = params.get("ori_srv_service") srv_protocol = params.get("ori_srv_protocol") srv_domain = params.get("ori_srv_domain") srv_target = params.get("ori_srv_target") srv_port = params.get("ori_srv_port") srv_priority = params.get("ori_srv_priority") srv_weight = params.get("ori_srv_weight") new_srv_service = params.get("new_srv_service") new_srv_protocol = params.get("new_srv_protocol") new_srv_domain = params.get("new_srv_domain") new_srv_target = params.get("new_srv_target") new_srv_port = params.get("new_srv_port") new_srv_priority = params.get("new_srv_priority") new_srv_weight = params.get("new_srv_weight") # dns host test dns_hostip = params.get("ori_dns_hostip") dns_hostname = params.get("ori_dns_hostname") dns_hostname2 = params.get("ori_dns_hostname2") # setting for without part without_ip_dhcp = params.get("without_ip_dhcp", "no") without_dns = params.get("without_dns", "no") without_dns_host = params.get("without_dns_host", "no") without_dns_txt = params.get("without_dns_txt", "no") without_dns_srv = params.get("without_dns_srv", "no") # setting for update/check/without section update_sec = params.get("update_sec") check_sec = params.get("check_sec") without_sec = params.get("without_sec") check_without_sec = params.get("check_without_sec") # forward test forward_mode = params.get("forward_mode") forward_iface = params.get("forward_iface", "eth2") # other params error_type = params.get("error_type", "") vm_name = params.get("main_vm") loop_time = int(params.get("loop_time", 1)) check_config_round = params.get("check_config_round", -1) ipv4_host_id = ipv6_host_mac = "" dns_enable = params.get("dns_enable", "yes") guest_iface_num = int(params.get("guest_iface_num", 1)) newxml = "" def get_hostfile(): """ Get the content of hostfile """ logging.info("Checking network hostfile...") hostfile = "/var/lib/libvirt/dnsmasq/%s.hostsfile" % net_name with open(hostfile) as hostfile_d: hostfile = hostfile_d.readlines() return hostfile def find_config(check_file, need_find): """ Find configure in check_file :param check_file: The file that will check :param need_find: If need to find the item in check_file, Boolean """ def _find_config(check_func): def __find_config(*args, **kwargs): logging.info("Checking content of %s", check_file) ret = False item = None with open(check_file) as checkfile_d: while True: check_line = checkfile_d.readline() if not check_line: break (ret, item) = check_func(check_line, *args, **kwargs) if ret: break if not ret: if need_find: test.fail("Fail to find %s in %s" % (item, check_file)) else: logging.info("Can not find %s in %s as expected" % (item, check_file)) return __find_config return _find_config conf_file = "/var/lib/libvirt/dnsmasq/%s.conf" % net_name @find_config(conf_file, True) def check_item(check_line, item): """ Check if the item in config file """ if item in check_line: logging.info("Find %s in %s", item, conf_file) return (True, item) else: return (False, item) host_file = "/var/lib/libvirt/dnsmasq/%s.addnhosts" % net_name @find_config(host_file, True) def check_host(check_line, item): """ Check if the item in host_file """ if re.search(item, check_line): logging.info("Find %s in %s", item, host_file) return (True, item) else: return (False, item) @find_config(conf_file, False) def check_item_absent(check_line, item): """ Check if the item not in config file """ if item in check_line: test.fail("Find %s in %s" % (item, conf_file)) else: return (False, item) @find_config(host_file, False) def check_host_absent(check_line, item): """ Check if the item not in host_file """ if re.search(item, check_line): test.fail("Find %s in %s" % (item, host_file)) else: return (False, item) def section_update(ori_pre, new_pre): """ Deal with update section and without section in func :param ori_pre: prefix of original section parameter name :param new_pre: prefix of new section parameter name """ global flag_list, flag_list_abs, update_sec, without_sec global check_sec, check_without_sec, newxml if update_sec: for sec in update_sec.split(","): newxml = newxml.replace(names[ori_pre+sec], names[new_pre+sec]) if update_command != "delete": check_sec = update_sec if without_sec: for sec_no in without_sec.split(","): newxml = re.sub(sec_no+"=\".*?\"", "", newxml) if update_command == "modify": check_without_sec = without_sec if check_sec: for c_sec in check_sec.split(","): flag_list.append(names[new_pre+c_sec]) if check_without_sec: for c_sec_no in check_without_sec.split(","): flag_list_abs.append(names[ori_pre+c_sec_no]) dns_host_xml = """ <host ip='%s'> <hostname>%s</hostname> <hostname>%s</hostname> </host> """ % (dns_hostip, dns_hostname, dns_hostname2) virtual_net = """ <network> <name>%s</name> <forward mode='nat'/> <bridge name='%s' stp='on' delay='0' /> <mac address='52:54:00:03:78:6c'/> <dns enable='%s'> <txt name='%s' value='%s'/> <srv service='%s' protocol='%s' domain='%s' target='%s' port='%s' priority='%s' weight='%s'/> %s </dns> <ip address='192.168.100.1' netmask='255.255.255.0'> <dhcp> <range start='%s' end='%s' /> <host mac='%s' ip='%s' name='%s' /> </dhcp> </ip> <ip family='ipv6' address='2001:db8:ca2:2::1' prefix='64'> <dhcp> <range start='%s' end='%s'/> <host id='%s' name='%s' ip='%s'/> </dhcp> </ip> <route family='ipv6' address='2001:db8:ca2:2::' prefix='64' gateway='2001:db8:ca2:2::4'/> <ip address='192.168.101.1' netmask='255.255.255.0'/> <ip family='ipv6' address='2001:db8:ca2:3::1' prefix='64' /> </network> """ % (net_name, net_name, dns_enable, dns_name, dns_value, srv_service, srv_protocol, srv_domain, srv_target, srv_port, srv_priority, srv_weight, dns_host_xml, ipv4_range_start, ipv4_range_end, ipv4_host_mac, ipv4_host_ip, ipv4_host_name, ipv6_range_start, ipv6_range_end, ipv6_host_id, ipv6_host_name, ipv6_host_ip) port_group = """ <portgroup name='engineering' default='no'> <virtualport type='802.1Qbh'> <parameters profileid='test'/> </virtualport> <bandwidth> <inbound average='1000' peak='5000' burst='5120'/> <outbound average='1000' peak='5000' burst='5120'/> </bandwidth> </portgroup> """ if use_in_guest == "yes": vm = env.get_vm(vm_name) vmxml = vm_xml.VMXML.new_from_dumpxml(vm_name) vmxml_backup = vmxml.copy() backup_files = [] try: for loop in range(loop_time): if loop_time > 1: logging.info("Round %s:", loop+1) update_command = params.get("update_command").split(",")[loop] if net_section == "ip-dhcp-host": new_dhcp_host_ip = params.get("new_dhcp_host_ip").split(",")[loop] new_dhcp_host_name = params.get("new_dhcp_host_name").split(",")[loop] new_dhcp_host_mac = params.get("new_dhcp_host_mac").split(",")[loop] status_error = params.get("status_error").split(",")[loop] elif net_section == "dns-txt": net_state = params.get("net_state").split(",")[loop] # Get a tmp_dir. tmp_dir = data_dir.get_tmp_dir() # Write new xml into a tempfile tmp_file = tempfile.NamedTemporaryFile(prefix=("new_xml_"), dir=tmp_dir) xmlfile = tmp_file.name tmp_file.close() # Generate testxml test_xml = network_xml.NetworkXML(network_name=net_name) test_xml.xml = virtual_net names = locals() if net_section == "portgroup": portgroup_xml = network_xml.PortgroupXML() portgroup_xml.xml = port_group test_xml.portgroup = portgroup_xml elif net_section == "forward-interface": if forward_mode == "bridge": forward_iface = utils_net.get_net_if(state="UP")[0] test_xml.forward = {'mode': forward_mode} test_xml.forward_interface = [{'dev': forward_iface}] del test_xml.bridge del test_xml.mac for ip_num in range(4): del test_xml.ip del test_xml.routes del test_xml.dns section_index = 0 if ip_version == "ipv6": section_index = 1 element = "/%s" % net_section.replace('-', '/') try: section_xml = test_xml.get_section_string(xpath=element, index=section_index) except xcepts.LibvirtXMLNotFoundError: newxml = section_xml = test_xml.get_section_string(xpath="/ip/dhcp/range") newxml = section_xml logging.debug("section xml is %s", newxml) flag_list = [] flag_list_abs = [] if (update_command == "delete" and error_type not in ["host-mismatch", "range-mismatch"] and not without_sec and not update_sec): logging.info("The delete xml is %s", newxml) else: if net_section == "bridge": new_bridge_name = net_name + "_new" flag_list.append(new_bridge_name) newxml = section_xml.replace(net_name, new_bridge_name) logging.info("The new bridge xml is %s", newxml) elif net_section == "forward": new_mode = "route" flag_list.append(new_mode) newxml = section_xml.replace("nat", new_mode) logging.info("The new forward xml is %s", newxml) elif net_section == "ip": new_netmask = "255.255.0.0" flag_list.append(new_netmask) newxml = section_xml.replace("255.255.255.0", new_netmask) elif net_section == "ip-dhcp-range": newxml = section_xml.replace(names[ip_version+"_range_start"], new_start_ip) newxml = newxml.replace(names[ip_version+"_range_end"], new_end_ip) for location in ["end", "start"]: if names["new_"+location+"_ip"] == "": newxml = newxml.replace(location+"=\"\"", "") else: flag_list.append(names["new_"+location+"_ip"]) elif net_section == "portgroup": new_inbound_average = "2000" flag_list.append(new_inbound_average) newxml = section_xml.replace("1000", new_inbound_average) newxml = newxml.replace("default=\"no\"", "default=\"yes\"") flag_list.append("default=('|\")yes") if update_command in ['add-first', 'add-last', 'add']: newxml = newxml.replace("engineering", "sales") flag_list.append("sales") elif net_section == "forward-interface": new_forward_iface = params.get("new_forward_iface") if error_type != "interface-duplicate": find_iface = 0 if not new_forward_iface and forward_mode == "bridge": new_iface_list = utils_net.get_net_if(qdisc="(mq|pfifo_fast)", state="(UP|DOWN)", optional="MULTICAST,UP") logging.info("new_iface_list is %s", new_iface_list) for iface in new_iface_list: if iface[0] != forward_iface: new_forward_iface = iface[0] find_iface = 1 break if not find_iface: test.cancel("Can not find another physical interface to attach") else: new_forward_iface = forward_iface flag_list.append(new_forward_iface) newxml = section_xml.replace(forward_iface, new_forward_iface) elif net_section == "ip-dhcp-host": if (update_sec is None and update_command not in ['modify', 'delete']): if ip_version == "ipv4": update_sec = "ip,mac,name" elif ip_version == "ipv6": update_sec = "ip,id,name" section_update(ip_version+"_host_", "new_dhcp_host_") elif net_section == "dns-txt": section_update("dns_", "new_dns_") elif net_section == "dns-srv": section_update("srv_", "new_srv_") elif net_section == "dns-host": if without_sec == "hostname": newxml = re.sub("<hostname>.*</hostname>\n", "", newxml) flag_list.append("ip=.*?"+dns_hostip) flag_list.append(dns_hostname) flag_list.append(dns_hostname2) # for negative test may have net_section do not match issues elif status_error == "no": test.fail("Unknown network section") logging.info("The new xml of %s is %s", net_section, newxml) with open(xmlfile, 'w') as xmlfile_d: xmlfile_d.write(newxml) if without_ip_dhcp == "yes": test_xml.del_element(element="/ip/dhcp") test_xml.del_element(element="/ip/dhcp") if without_dns == "yes": test_xml.del_element(element='/dns') if without_dns_host == "yes": test_xml.del_element(element='/dns/host') if without_dns_txt == "yes": test_xml.del_element(element='/dns/txt') if without_dns_srv == "yes": test_xml.del_element(element='/dns/srv') # Only do net define/start in first loop if (net_section == "ip-dhcp-range" and use_in_guest == "yes" and without_ip_dhcp == "no"): test_xml.del_element(element="/ip/dhcp", index=section_index) if loop == 0: try: # Define and start network test_xml.debug_xml() test_xml.define() ori_net_xml = virsh.net_dumpxml(net_name).stdout.strip() if "ipv6" in ori_net_xml: host_ifaces = utils_net.get_net_if(state="UP") backup_files.append( remote.RemoteFile(address='127.0.0.1', client='scp', username=params.get('username'), password=params.get('password'), port='22', remote_path='/proc/sys/net/ipv6/' 'conf/all/accept_ra')) process.run("echo 2 > /proc/sys/net/ipv6/conf/all/accept_ra", shell=True) for host_iface in host_ifaces: backup_files.append( remote.RemoteFile(address='127.0.0.1', client='scp', username=params.get('username'), password=params.get('password'), port='22', remote_path='/proc/sys/net/ipv6/' 'conf/%s/accept_ra' % host_iface)) process.run("echo 2 > /proc/sys/net/ipv6/conf/%s/accept_ra" % host_iface, shell=True) process.run("cat /proc/sys/net/ipv6/conf/%s/accept_ra" % host_iface) if net_state == "active" or net_state == "transient": test_xml.start() if net_state == "transient": test_xml.del_defined() list_result = virsh.net_list("--all --name").stdout.strip() if net_name not in list_result: test.fail("Can not find %s in net-list" % net_name) except xcepts.LibvirtXMLError as detail: test.error("Failed to define a test network.\n" "Detail: %s." % detail) else: # setting net status for following loops if net_state == "active": test_xml.set_active(True) elif net_state == "inactive": test_xml.set_active(False) # get hostfile before update if without_ip_dhcp == "yes" and net_state == "active": hostfile_before = get_hostfile() if hostfile_before != []: test.fail("hostfile is not empty before update: %s" % hostfile_before) logging.info("hostfile is empty before update") # Get dnsmasq pid before update check_dnsmasq = ((update_command == "add" or update_command == "delete") and net_section == "ip-dhcp-range" and status_error == "no" and net_state == "active" and options != "--config") if check_dnsmasq: cmd = "ps aux|grep dnsmasq|grep -v grep|grep %s|awk '{print $2}'" % net_name pid_list_bef = results_stdout_52lts(process.run(cmd, shell=True)).strip().split('\n') if parent_index: options += " --parent-index %s" % parent_index # Check config before update if net_state == "active": dns_txt = dns_srv = dns_host_str = None try: dns_txt = test_xml.get_section_string(xpath="/dns/txt") dns_srv = test_xml.get_section_string(xpath="/dns/srv") dns_host_str = test_xml.get_section_string(xpath="/dns/host") except xcepts.LibvirtXMLNotFoundError: pass txt_record = "txt-record=%s,%s" % (dns_name, dns_value) srv_host = "srv-host=_%s._%s.%s,%s,%s,%s,%s" % (srv_service, srv_protocol, srv_domain, srv_target, srv_port, srv_priority, srv_weight) hostline = "%s.*%s.*%s" % (dns_hostip, dns_hostname, dns_hostname2) if dns_txt: check_item(txt_record) if dns_srv: check_item(srv_host) if dns_host_str: check_host(hostline) # Do net-update operation cmd_result = virsh.net_update(net_name, update_command, net_section, xmlfile, options, debug=True) if cmd_result.exit_status: err = cmd_result.stderr.strip() if re.search("not supported", err): test.cancel("Skip the test: %s" % err) elif status_error == "yes": # index-mismatch error info and judgement index_err1 = "the address family of a host entry IP must match the" + \ " address family of the dhcp element's parent" index_err2 = "XML error: Invalid to specify MAC address.* in network" + \ ".* IPv6 static host definition" index_err3 = "mismatch of address family in range.* for network" mismatch_expect = (error_type == "index-mismatch" and (re.search(index_err1, err) or re.search(index_err2, err) or re.search(index_err3, err))) err_dic = {} # multi-host error info err_dic["multi-hosts"] = "dhcp is supported only for a single.*" + \ " address on each network" # range-mismatch error info err_dic["range-mismatch"] = "couldn't locate a matching dhcp " + \ "range entry in network " # host-mismatch error info err_dic["host-mismatch"] = "couldn't locate a matching dhcp " + \ "host entry in network " # dns-mismatch error info err_dic["dns-mismatch"] = "couldn't locate a matching DNS TXT " + \ "record in network " # range-duplicate error info err_dic["range-duplicate"] = "there is an existing dhcp range" + \ " entry in network.* that matches" # host-duplicate error info err_dic["host-duplicate"] = "there is an existing dhcp host" + \ " entry in network.* that matches" # out_of_range error info err_dic["out-of-range"] = "range.* is not entirely within network" # no end for range error err_dic["range-no-end"] = "Missing 'end' attribute in dhcp " + \ "range for network" # no match item for host modify err err_dic["no-match-item"] = "couldn't locate an existing dhcp" + \ " host entry with.* in network" # no host name and mac for modify err err_dic["host-no-name-mac"] = "Static host definition in IPv4 network" + \ ".* must have mac or name attribute" # no host ip for modify err err_dic["host-no-ip"] = "Missing IP address in static host " + \ "definition for network" # wrong command name err_dic["wrong-command-name"] = "unrecognized command name" # wrong section name err_dic["wrong-section-name"] = "unrecognized section name" # delete with only id err_dic["only-id"] = "At least one of name, mac, or ip attribute must be" + \ " specified for static host definition in network" # options exclusive err_dic["opt-exclusive"] = "Options --current and.* are mutually exclusive" # range_reverse error info if ip_version == "ipv4": err_dic["range-reverse"] = "range.* is reversed" elif ip_version == "ipv6": err_dic["range-reverse"] = "range.*start larger than end" # --live with inactive net err_dic["invalid-state"] = "network is not running" err_dic["transient"] = "cannot change persistent config of a transient network" err_dic["dns-disable"] = "Extra data in disabled network" err_dic["interface-duplicate"] = "there is an existing interface entry " + \ "in network.* that matches" if (error_type in list(err_dic.keys()) and re.search(err_dic[error_type], err) or mismatch_expect): logging.info("Get expect error: %s", err) else: test.fail("Do not get expect err msg: %s, %s" % (error_type, err_dic)) else: test.fail("Failed to execute net-update command") elif status_error == "yes": test.fail("Expect fail, but succeed") # Get dnsmasq pid after update if check_dnsmasq: pid_list_aft = results_stdout_52lts(process.run(cmd, shell=True)).strip().split('\n') for pid in pid_list_aft: if pid in pid_list_bef: test.fail("dnsmasq do not updated") # Check the actual xml cmd_result = virsh.net_dumpxml(net_name) actual_net_xml = cmd_result.stdout.strip() new_xml_obj = network_xml.NetworkXML.new_from_net_dumpxml(net_name) logging.info("After net-update, the actual net xml is %s", actual_net_xml) if "ip-dhcp" in net_section: if ip_version == "ipv6": new_xml_obj.del_element(element="/ip", index=0) if ip_version == "ipv4": new_xml_obj.del_element(element="/ip", index=1) config_not_work = ("--config" in options and net_state == "active" and "--live" not in options) if config_not_work: if update_command != "delete": flag_list_abs = flag_list flag_list = [] else: flag_list = flag_list_abs flag_list_abs = [] logging.info("The check list is %s, absent list is %s", flag_list, flag_list_abs) if (update_command == "delete" and status_error == "no" and not config_not_work and loop_time == 1): try: section_str = new_xml_obj.get_section_string(xpath=element) except xcepts.LibvirtXMLNotFoundError: section_str = None logging.info("Can not find section %s in xml after delete", element) if section_str is not None: test.fail("The actual net xml is not expected," "element still exists") elif ("duplicate" not in error_type and error_type != "range-mismatch" and error_type != "host-mismatch"): # check xml should exists for flag_string in flag_list: logging.info("checking %s should in xml in positive test," "and absent in negative test", flag_string) if not re.search(flag_string, actual_net_xml): if ((status_error == "no" and update_command != "delete") or (update_command == "delete" and status_error == "yes")): test.fail("The actual net xml failed to update," "or expect delete fail but xml missing" ":%s" % flag_string) else: if ((status_error == "yes" and update_command != "delete") or (status_error == "no" and update_command == "delete" and not config_not_work)): test.fail("Expect test fail, but find xml %s" " actual, or expect delete succeed," "but fail in fact" % flag_string) # check xml should not exists for flag_string_abs in flag_list_abs: logging.info("checking %s should NOT in xml in positive test," "and exist in negative test", flag_string_abs) if re.search(flag_string_abs, actual_net_xml): if status_error == "no": test.fail("Expect absent in net xml, but exists" "in fact: %s" % flag_string_abs) else: if status_error == "yes": test.fail("Should exists in net xml, but it " "disappeared: %s" % flag_string_abs) # Check if add-last, add-fist works well if (status_error == "no" and not config_not_work and update_command in ["add-last", "add", "add-first"]): if update_command == "add-first": find_index = 0 else: find_index = -1 section_str_aft = new_xml_obj.get_section_string(xpath=element, index=find_index) logging.info("xpath is %s, find_index is %s, section_str_aft is %s", element, find_index, section_str_aft) for flag_string in flag_list: logging.info("flag_string is %s", flag_string) if not re.search(flag_string, section_str_aft): test.fail("Can not find %s in %s" % (flag_string, section_str_aft)) logging.info("%s %s in right place", update_command, section_str_aft) # Check the positive test result if status_error == "no": # Check the network conf file # live update if ("--live" in options or "--config" not in options) and net_state == "active": if net_section == "ip-dhcp-range" and update_command == "add-first": ip_range = "%s,%s" % (new_start_ip, new_end_ip) if ip_version == "ipv6": ip_range = ip_range + ",64" check_item(ip_range) if "dns" in net_section: if update_command == "delete" and loop == 0: if net_section == "dns-srv": check_item_absent(srv_host) if net_section == "dns-txt": check_item_absent(txt_record) if net_section == "dns-host": check_host_absent(dns_host_str) elif "add" in update_command: if net_section == "dns-srv": for sec in update_sec.split(","): srv_host = srv_host.replace(names["srv_"+sec], names["new_srv_"+sec]) check_item(srv_host) if net_section == "dns-txt": for sec in update_sec.split(","): txt_record = txt_record.replace(names["dns_"+sec], names["new_dns_"+sec]) check_item(txt_record) if net_section == "dns-host": check_host(hostline) # Check the hostfile if (net_section == "ip-dhcp-host" and update_command != "modify" and update_command != "delete"): dic_hostfile = {} for sec in ["ip", "mac", "name", "id"]: if update_sec is not None and sec in update_sec.split(","): dic_hostfile[sec] = names["new_dhcp_host_"+sec]+"," else: dic_hostfile[sec] = names[ip_version+"_host_"+sec]+"," if sec == "ip" and ip_version == "ipv6": dic_hostfile[sec] = "[" + dic_hostfile[sec].strip(",") + "]" if without_sec is not None and sec in without_sec.split(","): dic_hostfile[sec] = "" if ip_version == "ipv4": host_info = (dic_hostfile["mac"] + dic_hostfile["ip"] + dic_hostfile["name"]) if dic_hostfile["mac"] == "": host_info = dic_hostfile["name"] + dic_hostfile["ip"] if ip_version == "ipv6": host_info = ("id:" + dic_hostfile["id"] + dic_hostfile["name"] + dic_hostfile["ip"]) hostfile = get_hostfile() host_info_patten = host_info.strip(",") + "\n" if host_info_patten in hostfile: logging.info("host info %s is in hostfile %s", host_info, hostfile) else: test.fail("Can not find %s in host file: %s" % (host_info, hostfile)) # Check the net in guest if use_in_guest == "yes" and loop == 0: # Detach all interfaces of vm first iface_index = 0 mac_list = vm_xml.VMXML.get_iface_dev(vm_name) for mac in mac_list: iface_dict = vm_xml.VMXML.get_iface_by_mac(vm_name, mac) virsh.detach_interface(vm_name, "--type %s --mac %s --config" % (iface_dict.get('type'), mac)) vm.free_mac_address(iface_index) iface_index += 1 # attach new interface to guest for j in range(guest_iface_num): if net_section == "ip-dhcp-host" and new_dhcp_host_mac: mac = new_dhcp_host_mac else: mac = utils_net.generate_mac_address_simple() ret = virsh.attach_interface(vm_name, "--type %s --source %s --mac %s --config" % (iface_type, net_name, mac)) if ret.exit_status: test.fail("Fail to attach new interface to guest: %s" % ret.stderr.strip()) # The sleep here is to make sure the update make effect time.sleep(2) # Start guest and check ip/mac/hostname... vm.start() logging.debug("vm xml is %s", vm.get_xml()) session = vm.wait_for_serial_login() if "ip-dhcp" in net_section: dhclient_cmd = "(if pgrep dhclient;" \ "then pkill dhclient; sleep 3; fi) " \ "&& dhclient -%s" % ip_version[-1] session.cmd(dhclient_cmd) iface_ip = utils_net.get_guest_ip_addr(session, mac, ip_version=ip_version, timeout=10) if net_section == "ip-dhcp-range": if new_start_ip <= iface_ip <= new_end_ip: logging.info("getting ip %s is in range [%s ~ %s]", iface_ip, new_start_ip, new_end_ip) else: test.fail("getting ip %s not in range [%s ~ %s]" % (iface_ip, new_start_ip, new_end_ip)) if net_section == "ip-dhcp-host": if iface_ip == new_dhcp_host_ip: logging.info("getting ip is same with set: %s", iface_ip) else: test.fail("getting ip %s is not same with setting %s" % (iface_ip, new_dhcp_host_ip)) hostname = session.cmd_output("hostname").strip('\n') if hostname == new_dhcp_host_name.split('.')[0]: logging.info("getting hostname same with setting: %s", hostname) else: test.fail("getting hostname %s is not same with " "setting: %s" % (hostname, new_dhcp_host_name)) session.close() # Check network connection for macvtap if (use_in_guest == "yes" and net_section == "forward-interface" and forward_mode == "bridge"): xml_obj_use = network_xml.NetworkXML.new_from_net_dumpxml(net_name) net_conn = int(xml_obj_use.connection) iface_conn = xml_obj_use.get_interface_connection() conn_count = 0 for k in iface_conn: conn_count = conn_count + int(k) logging.info("net_conn=%s, conn_count=%s, guest_iface_num=%s" % (net_conn, conn_count, guest_iface_num)) if (net_conn != conn_count or (loop == 1 and guest_iface_num != net_conn)): test.fail("Can not get expected connection num: " "net_conn = %s, iface_conn = %s" % (net_conn, conn_count)) #Check --config option after net destroyed if (int(check_config_round) == loop or (loop_time == 1 and "--current" in options and net_state == "active")): test_xml.set_active(False) logging.info("Checking xml after net destroyed") cmd_result = virsh.net_dumpxml(net_name) inactive_xml = cmd_result.stdout.strip() logging.info("inactive_xml is %s", inactive_xml) #Check the inactive xml for check_str in flag_list: if update_command != "delete": if "--config" in options: if not re.search(check_str, inactive_xml): test.fail("Can not find xml after net destroyed") if "--current" in options: if re.search(check_str, inactive_xml): test.fail("XML still exists after net destroyed") else: if "--config" in options: if re.search(check_str, inactive_xml): test.fail("XML still exists after net destroyed") if "--current" in options: if not re.search(check_str, inactive_xml): test.fail("Can not find xml after net destroyed") logging.info("Check for net inactive PASS") finally: if test_xml.get_active(): test_xml.del_active() if test_xml.get_defined(): test_xml.del_defined() if os.path.exists(xmlfile): os.remove(xmlfile) if use_in_guest == "yes": vm = env.get_vm(vm_name) if vm.is_alive(): vm.destroy(gracefully=False) vmxml_backup.sync() for config_file in backup_files: config_file._reset_file()
def run(test, params, env): global flag_list, flag_list_abs, update_sec, without_sec global check_sec, check_without_sec, newxml # Basic network params net_name = params.get("net_update_net_name", "updatenet") net_section = params.get("network_section") update_command = params.get("update_command", "add-last") options = params.get("cmd_options", "") net_state = params.get("net_state") use_in_guest = params.get("use_in_guest") iface_type = params.get("iface_type", "network") ip_version = params.get("ip_version", "ipv4") new_start_ip = params.get("start_ip") new_end_ip = params.get("end_ip") parent_index = params.get("parent_index") status_error = params.get("status_error", "no") # dhcp host test new_dhcp_host_ip = params.get("new_dhcp_host_ip") new_dhcp_host_id = params.get("new_dhcp_host_id") new_dhcp_host_name = params.get("new_dhcp_host_name") new_dhcp_host_mac = params.get("new_dhcp_host_mac") # ipv4 range/host test ipv4_range_start = params.get("ori_ipv4_range_start") ipv4_range_end = params.get("ori_ipv4_range_end") ipv4_host_mac = params.get("ori_ipv4_host_mac") ipv4_host_ip = params.get("ori_ipv4_host_ip") ipv4_host_name = params.get("ori_ipv4_host_name") # ipv6 range/host test ipv6_range_start = params.get("ori_ipv6_range_start") ipv6_range_end = params.get("ori_ipv6_range_end") ipv6_host_id = params.get("ori_ipv6_host_id") ipv6_host_name = params.get("ori_ipv6_host_name") ipv6_host_ip = params.get("ori_ipv6_host_ip") # dns test dns_name = params.get("ori_dns_name") dns_value = params.get("ori_dns_value") new_dns_name = params.get("new_dns_name") new_dns_value = params.get("new_dns_value") # srv test srv_service = params.get("ori_srv_service") srv_protocol = params.get("ori_srv_protocol") srv_domain = params.get("ori_srv_domain") srv_target = params.get("ori_srv_target") srv_port = params.get("ori_srv_port") srv_priority = params.get("ori_srv_priority") srv_weight = params.get("ori_srv_weight") new_srv_service = params.get("new_srv_service") new_srv_protocol = params.get("new_srv_protocol") new_srv_domain = params.get("new_srv_domain") new_srv_target = params.get("new_srv_target") new_srv_port = params.get("new_srv_port") new_srv_priority = params.get("new_srv_priority") new_srv_weight = params.get("new_srv_weight") # dns host test dns_hostip = params.get("ori_dns_hostip") dns_hostname = params.get("ori_dns_hostname") dns_hostname2 = params.get("ori_dns_hostname2") # setting for without part without_ip_dhcp = params.get("without_ip_dhcp", "no") without_dns = params.get("without_dns", "no") without_dns_host = params.get("without_dns_host", "no") without_dns_txt = params.get("without_dns_txt", "no") without_dns_srv = params.get("without_dns_srv", "no") without_dns_forwarder = params.get("without_dns_forwarder", "no") # setting for update/check/without section update_sec = params.get("update_sec") check_sec = params.get("check_sec") without_sec = params.get("without_sec") check_without_sec = params.get("check_without_sec") # forward test forward_mode = params.get("forward_mode") forward_iface = params.get("forward_iface", "eth2") # other params error_type = params.get("error_type", "") vm_name = params.get("main_vm") loop_time = int(params.get("loop_time", 1)) check_config_round = params.get("check_config_round", -1) ipv4_host_id = ipv6_host_mac = "" dns_enable = params.get("dns_enable", "yes") guest_iface_num = int(params.get("guest_iface_num", 1)) newxml = "" def get_hostfile(): """ Get the content of hostfile """ logging.info("Checking network hostfile...") hostfile = "/var/lib/libvirt/dnsmasq/%s.hostsfile" % net_name with open(hostfile) as hostfile_d: hostfile = hostfile_d.readlines() return hostfile def find_config(check_file, need_find): """ Find configure in check_file :param check_file: The file that will check :param need_find: If need to find the item in check_file, Boolean """ def _find_config(check_func): def __find_config(*args, **kwargs): logging.info("Checking content of %s", check_file) ret = False item = None with open(check_file) as checkfile_d: while True: check_line = checkfile_d.readline() if not check_line: break (ret, item) = check_func(check_line, *args, **kwargs) if ret: break if not ret: if need_find: test.fail("Fail to find %s in %s" % (item, check_file)) else: logging.info("Can not find %s in %s as expected" % (item, check_file)) return __find_config return _find_config conf_file = "/var/lib/libvirt/dnsmasq/%s.conf" % net_name @find_config(conf_file, True) def check_item(check_line, item): """ Check if the item in config file """ if item in check_line: logging.info("Find %s in %s", item, conf_file) return (True, item) else: return (False, item) host_file = "/var/lib/libvirt/dnsmasq/%s.addnhosts" % net_name @find_config(host_file, True) def check_host(check_line, item): """ Check if the item in host_file """ if re.search(item, check_line): logging.info("Find %s in %s", item, host_file) return (True, item) else: return (False, item) @find_config(conf_file, False) def check_item_absent(check_line, item): """ Check if the item not in config file """ if item in check_line: test.fail("Find %s in %s" % (item, conf_file)) else: return (False, item) @find_config(host_file, False) def check_host_absent(check_line, item): """ Check if the item not in host_file """ if re.search(item, check_line): test.fail("Find %s in %s" % (item, host_file)) else: return (False, item) def section_update(ori_pre, new_pre): """ Deal with update section and without section in func :param ori_pre: prefix of original section parameter name :param new_pre: prefix of new section parameter name """ global flag_list, flag_list_abs, update_sec, without_sec global check_sec, check_without_sec, newxml if update_sec: for sec in update_sec.split(","): newxml = newxml.replace(names[ori_pre + sec], names[new_pre + sec]) if update_command != "delete": check_sec = update_sec if without_sec: for sec_no in without_sec.split(","): newxml = re.sub(sec_no + "=\".*?\"", "", newxml) if update_command == "modify": check_without_sec = without_sec if check_sec: for c_sec in check_sec.split(","): flag_list.append(names[new_pre + c_sec]) if check_without_sec: for c_sec_no in check_without_sec.split(","): flag_list_abs.append(names[ori_pre + c_sec_no]) dns_host_xml = """ <host ip='%s'> <hostname>%s</hostname> <hostname>%s</hostname> </host> """ % (dns_hostip, dns_hostname, dns_hostname2) virtual_net = """ <network> <name>%s</name> <forward mode='nat'/> <bridge name='%s' stp='on' delay='0' /> <mac address='52:54:00:03:78:6c'/> <domain name="example.com" localOnly="no"/> <mtu size="9000"/> <dns enable='%s'> <forwarder domain='example.com' addr="8.8.4.4"/> <txt name='%s' value='%s'/> <srv service='%s' protocol='%s' domain='%s' target='%s' port='%s' priority='%s' weight='%s'/> %s </dns> <ip address='192.168.100.1' netmask='255.255.255.0'> <dhcp> <range start='%s' end='%s' /> <host mac='%s' ip='%s' name='%s' /> </dhcp> </ip> <ip family='ipv6' address='2001:db8:ca2:2::1' prefix='64'> <dhcp> <range start='%s' end='%s'/> <host id='%s' name='%s' ip='%s'/> </dhcp> </ip> <route family='ipv6' address='2001:db8:ca2:2::' prefix='64' gateway='2001:db8:ca2:2::4'/> <ip address='192.168.101.1' netmask='255.255.255.0'/> <ip family='ipv6' address='2001:db8:ca2:3::1' prefix='64' /> </network> """ % (net_name, net_name, dns_enable, dns_name, dns_value, srv_service, srv_protocol, srv_domain, srv_target, srv_port, srv_priority, srv_weight, dns_host_xml, ipv4_range_start, ipv4_range_end, ipv4_host_mac, ipv4_host_ip, ipv4_host_name, ipv6_range_start, ipv6_range_end, ipv6_host_id, ipv6_host_name, ipv6_host_ip) port_group = """ <portgroup name='engineering' default='no'> <virtualport type='802.1Qbh'> <parameters profileid='test'/> </virtualport> <bandwidth> <inbound average='1000' peak='5000' burst='5120'/> <outbound average='1000' peak='5000' burst='5120'/> </bandwidth> </portgroup> """ if use_in_guest == "yes": vm = env.get_vm(vm_name) vmxml = vm_xml.VMXML.new_from_dumpxml(vm_name) vmxml_backup = vmxml.copy() backup_files = [] try: for loop in range(loop_time): if loop_time > 1: logging.info("Round %s:", loop + 1) update_command = params.get("update_command").split(",")[loop] if net_section == "ip-dhcp-host": new_dhcp_host_ip = params.get("new_dhcp_host_ip").split( ",")[loop] new_dhcp_host_name = params.get( "new_dhcp_host_name").split(",")[loop] new_dhcp_host_mac = params.get("new_dhcp_host_mac").split( ",")[loop] status_error = params.get("status_error").split(",")[loop] elif net_section == "dns-txt": net_state = params.get("net_state").split(",")[loop] # Get a tmp_dir. tmp_dir = data_dir.get_tmp_dir() # Write new xml into a tempfile tmp_file = tempfile.NamedTemporaryFile(prefix=("new_xml_"), dir=tmp_dir) xmlfile = tmp_file.name tmp_file.close() # Generate testxml test_xml = network_xml.NetworkXML(network_name=net_name) test_xml.xml = virtual_net names = locals() if net_section == "portgroup": portgroup_xml = network_xml.PortgroupXML() portgroup_xml.xml = port_group test_xml.portgroup = portgroup_xml elif net_section == "forward-interface": if forward_mode == "bridge": forward_iface = utils_net.get_net_if(state="UP")[0] test_xml.forward = {'mode': forward_mode} test_xml.forward_interface = [{'dev': forward_iface}] del test_xml.bridge del test_xml.mac for ip_num in range(4): del test_xml.ip del test_xml.routes del test_xml.dns del test_xml.mtu del test_xml.domain_name section_index = 0 if ip_version == "ipv6": section_index = 1 element = "/%s" % net_section.replace('-', '/') try: section_xml = test_xml.get_section_string(xpath=element, index=section_index) except xcepts.LibvirtXMLNotFoundError: newxml = section_xml = test_xml.get_section_string( xpath="/ip/dhcp/range") newxml = section_xml logging.debug("section xml is %s", newxml) flag_list = [] flag_list_abs = [] if (update_command == "delete" and error_type not in ["host-mismatch", "range-mismatch"] and not without_sec and not update_sec): logging.info("The delete xml is %s", newxml) else: if net_section == "bridge": new_bridge_name = net_name + "_new" flag_list.append(new_bridge_name) newxml = section_xml.replace(net_name, new_bridge_name) logging.info("The new bridge xml is %s", newxml) elif net_section == "forward": new_mode = "route" flag_list.append(new_mode) newxml = section_xml.replace("nat", new_mode) logging.info("The new forward xml is %s", newxml) elif net_section == "ip": new_netmask = "255.255.0.0" flag_list.append(new_netmask) newxml = section_xml.replace("255.255.255.0", new_netmask) elif net_section == "ip-dhcp-range": newxml = section_xml.replace( names[ip_version + "_range_start"], new_start_ip) newxml = newxml.replace(names[ip_version + "_range_end"], new_end_ip) for location in ["end", "start"]: if names["new_" + location + "_ip"] == "": newxml = newxml.replace(location + "=\"\"", "") else: flag_list.append(names["new_" + location + "_ip"]) elif net_section == "portgroup": new_inbound_average = "2000" flag_list.append(new_inbound_average) newxml = section_xml.replace("1000", new_inbound_average) newxml = newxml.replace("default=\"no\"", "default=\"yes\"") flag_list.append("default=('|\")yes") if update_command in ['add-first', 'add-last', 'add']: newxml = newxml.replace("engineering", "sales") flag_list.append("sales") if update_command == "modify" and status_error == "yes": newxml = newxml.replace("engineering", "") elif net_section == "forward-interface": new_forward_iface = params.get("new_forward_iface") if error_type != "interface-duplicate": find_iface = 0 if not new_forward_iface and forward_mode == "bridge": new_iface_list = utils_net.get_net_if( qdisc="(mq|pfifo_fast)", state="(UP|DOWN)", optional="MULTICAST,UP") logging.info("new_iface_list is %s", new_iface_list) for iface in new_iface_list: if iface[0] != forward_iface: new_forward_iface = iface[0] find_iface = 1 break if not find_iface: test.cancel( "Can not find another physical interface to attach" ) else: new_forward_iface = forward_iface flag_list.append(new_forward_iface) newxml = section_xml.replace(forward_iface, new_forward_iface) elif net_section == "ip-dhcp-host": if (update_sec is None and update_command not in ['modify', 'delete']): if ip_version == "ipv4": update_sec = "ip,mac,name" elif ip_version == "ipv6": update_sec = "ip,id,name" section_update(ip_version + "_host_", "new_dhcp_host_") elif net_section == "dns-txt": section_update("dns_", "new_dns_") elif net_section == "dns-srv": section_update("srv_", "new_srv_") elif net_section == "dns-host": if without_sec == "hostname": newxml = re.sub("<hostname>.*</hostname>\n", "", newxml) if status_error == 'no': flag_list.append("ip=.*?" + dns_hostip) flag_list.append(dns_hostname) flag_list.append(dns_hostname2) # for negative test may have net_section do not match issues elif status_error == "no": test.fail("Unknown network section") logging.info("The new xml of %s is %s", net_section, newxml) with open(xmlfile, 'w') as xmlfile_d: xmlfile_d.write(newxml) if without_ip_dhcp == "yes": test_xml.del_element(element="/ip/dhcp") test_xml.del_element(element="/ip/dhcp") if without_dns == "yes": test_xml.del_element(element='/dns') if without_dns_host == "yes": test_xml.del_element(element='/dns/host') if without_dns_txt == "yes": test_xml.del_element(element='/dns/txt') if without_dns_srv == "yes": test_xml.del_element(element='/dns/srv') if without_dns_forwarder == "yes": test_xml.del_element(element='/dns/forwarder') # Only do net define/start in first loop if (net_section == "ip-dhcp-range" and use_in_guest == "yes" and without_ip_dhcp == "no"): test_xml.del_element(element="/ip/dhcp", index=section_index) if error_type == "index-nonexist": for idx in [3, 2, 1]: test_xml.del_element(element="/ip", index=idx) test_xml.del_element(element="/route") if loop == 0: try: # Define and start network test_xml.debug_xml() test_xml.define() ori_net_xml = virsh.net_dumpxml(net_name).stdout.strip() if "ipv6" in ori_net_xml: host_ifaces = utils_net.get_net_if(state="UP") backup_files.append( remote.RemoteFile(address='127.0.0.1', client='scp', username=params.get('username'), password=params.get('password'), port='22', remote_path='/proc/sys/net/ipv6/' 'conf/all/accept_ra')) process.run( "echo 2 > /proc/sys/net/ipv6/conf/all/accept_ra", shell=True) for host_iface in host_ifaces: backup_files.append( remote.RemoteFile( address='127.0.0.1', client='scp', username=params.get('username'), password=params.get('password'), port='22', remote_path='/proc/sys/net/ipv6/' 'conf/%s/accept_ra' % host_iface)) process.run( "echo 2 > /proc/sys/net/ipv6/conf/%s/accept_ra" % host_iface, shell=True) process.run( "cat /proc/sys/net/ipv6/conf/%s/accept_ra" % host_iface) if net_state == "active" or net_state == "transient": test_xml.start() if net_state == "transient": test_xml.del_defined() list_result = virsh.net_list("--all --name").stdout.strip() if net_name not in list_result: test.fail("Can not find %s in net-list" % net_name) except xcepts.LibvirtXMLError as detail: test.error("Failed to define a test network.\n" "Detail: %s." % detail) else: # setting net status for following loops if net_state == "active": test_xml.set_active(True) elif net_state == "inactive": test_xml.set_active(False) # get hostfile before update if without_ip_dhcp == "yes" and net_state == "active": hostfile_before = get_hostfile() if hostfile_before != []: test.fail("hostfile is not empty before update: %s" % hostfile_before) logging.info("hostfile is empty before update") # Get dnsmasq pid before update check_dnsmasq = ((update_command == "add" or update_command == "delete") and net_section == "ip-dhcp-range" and status_error == "no" and net_state == "active" and options != "--config") if check_dnsmasq: cmd = "ps aux|grep dnsmasq|grep -v grep|grep %s|awk '{print $2}'" % net_name pid_list_bef = process.run( cmd, shell=True).stdout_text.strip().split('\n') if parent_index: options += " --parent-index %s" % parent_index # Check config before update if net_state == "active": dns_txt = dns_srv = dns_host_str = None try: dns_txt = test_xml.get_section_string(xpath="/dns/txt") dns_srv = test_xml.get_section_string(xpath="/dns/srv") dns_host_str = test_xml.get_section_string( xpath="/dns/host") except xcepts.LibvirtXMLNotFoundError: pass txt_record = "txt-record=%s,%s" % (dns_name, dns_value) srv_host = "srv-host=_%s._%s.%s,%s,%s,%s,%s" % ( srv_service, srv_protocol, srv_domain, srv_target, srv_port, srv_priority, srv_weight) hostline = "%s.*%s.*%s" % (dns_hostip, dns_hostname, dns_hostname2) if dns_txt: check_item(txt_record) if dns_srv: check_item(srv_host) if dns_host_str: check_host(hostline) # Do net-update operation cmd_result = virsh.net_update(net_name, update_command, net_section, xmlfile, options, debug=True) if cmd_result.exit_status: err = cmd_result.stderr.strip() if status_error == "yes": # index-mismatch error info and judgement index_err1 = "the address family of a host entry IP must match the" + \ " address family of the dhcp element's parent" index_err2 = "XML error: Invalid to specify MAC address.* in network" + \ ".* IPv6 static host definition" index_err3 = "mismatch of address family in range.* for network" mismatch_expect = (error_type == "index-mismatch" and (re.search(index_err1, err) or re.search(index_err2, err) or re.search(index_err3, err))) err_dic = {} # multi-host error info err_dic["multi-hosts"] = "dhcp is supported only for a single.*" + \ " address on each network" # range-mismatch error info err_dic["range-mismatch"] = "couldn't locate a matching dhcp " + \ "range entry in network " # index-nonexist error info err_dic["index-nonexist"] = "couldn't update dhcp host entry " + \ "- no <ip.* element found at index 1 in network" # host-mismatch error info err_dic["host-mismatch"] = "couldn't locate a matching dhcp " + \ "host entry in network " # dns-mismatch error info err_dic["dns-mismatch"] = "couldn't locate a matching DNS TXT " + \ "record in network " # range-duplicate error info err_dic["range-duplicate"] = "there is an existing dhcp range" + \ " entry in network.* that matches" # host-duplicate error info err_dic["host-duplicate"] = "there is an existing dhcp host" + \ " entry in network.* that matches" # out_of_range error info err_dic[ "out-of-range"] = "range.* is not entirely within network" # no end for range error err_dic["range-no-end"] = "Missing 'end' attribute in dhcp " + \ "range for network" # no match item for host modify err err_dic["no-match-item"] = "couldn't locate an existing dhcp" + \ " host entry with.* in network" # no host name and mac for modify err err_dic["host-no-name-mac"] = "Static host definition in IPv4 network" + \ ".* must have mac or name attribute" # no host ip for modify err err_dic["host-no-ip"] = "Missing IP address in static host " + \ "definition for network" # wrong command name err_dic["wrong-command-name"] = "unrecognized command name" # wrong section name err_dic["wrong-section-name"] = "unrecognized section name" # delete with only id err_dic["only-id"] = "At least one of name, mac, or ip attribute must be" + \ " specified for static host definition in network" # options exclusive err_dic[ "opt-exclusive"] = "Options --current and.* are mutually exclusive" # range_reverse error info if ip_version == "ipv4": err_dic["range-reverse"] = "range.* is reversed" elif ip_version == "ipv6": err_dic[ "range-reverse"] = "range.*start larger than end" # --live with inactive net err_dic["invalid-state"] = "network is not running" err_dic[ "transient"] = "cannot change persistent config of a transient network" err_dic["dns-disable"] = "Extra data in disabled network" err_dic[ "modify"] = "cannot be modified, only added or deleted" err_dic["not-support"] = "can't update.*section of network" err_dic["unrecognized"] = "unrecognized section name" err_dic["interface-duplicate"] = "there is an existing interface entry " + \ "in network.* that matches" err_dic[ "XML error"] = "XML error: Missing required name attribute in portgroup" if (error_type in list(err_dic.keys()) and re.search(err_dic[error_type], err) or mismatch_expect): logging.debug("Get expect error: %s", err) else: test.fail("Do not get expect err msg: %s, %s" % (error_type, err_dic)) else: test.fail("Failed to execute net-update command") elif status_error == "yes": test.fail("Expect fail, but succeed") # Get dnsmasq pid after update if check_dnsmasq: pid_list_aft = process.run( cmd, shell=True).stdout_text.strip().split('\n') for pid in pid_list_aft: if pid in pid_list_bef: test.fail("dnsmasq do not updated") # Check the actual xml cmd_result = virsh.net_dumpxml(net_name) actual_net_xml = cmd_result.stdout.strip() new_xml_obj = network_xml.NetworkXML.new_from_net_dumpxml(net_name) logging.info("After net-update, the actual net xml is %s", actual_net_xml) if "ip-dhcp" in net_section: if ip_version == "ipv6": new_xml_obj.del_element(element="/ip", index=0) if ip_version == "ipv4": new_xml_obj.del_element(element="/ip", index=1) config_not_work = ("--config" in options and net_state == "active" and "--live" not in options) if config_not_work: if update_command != "delete": flag_list_abs = flag_list flag_list = [] else: flag_list = flag_list_abs flag_list_abs = [] logging.info("The check list is %s, absent list is %s", flag_list, flag_list_abs) if (update_command == "delete" and status_error == "no" and not config_not_work and loop_time == 1): try: section_str = new_xml_obj.get_section_string(xpath=element) except xcepts.LibvirtXMLNotFoundError: section_str = None logging.info("Can not find section %s in xml after delete", element) if section_str is not None: test.fail("The actual net xml is not expected," "element still exists") elif ("duplicate" not in error_type and error_type != "range-mismatch" and error_type != "host-mismatch" and error_type != "not-support"): # check xml should exists for flag_string in flag_list: logging.info( "checking %s should in xml in positive test," "and absent in negative test", flag_string) if not re.search(flag_string, actual_net_xml): if ((status_error == "no" and update_command != "delete") or (update_command == "delete" and status_error == "yes")): test.fail("The actual net xml failed to update," "or expect delete fail but xml missing" ":%s" % flag_string) else: if ((status_error == "yes" and update_command != "delete") or (status_error == "no" and update_command == "delete" and not config_not_work)): test.fail("Expect test fail, but find xml %s" " actual, or expect delete succeed," "but fail in fact" % flag_string) # check xml should not exists for flag_string_abs in flag_list_abs: logging.info( "checking %s should NOT in xml in positive test," "and exist in negative test", flag_string_abs) if re.search(flag_string_abs, actual_net_xml): if status_error == "no": test.fail("Expect absent in net xml, but exists" "in fact: %s" % flag_string_abs) else: if status_error == "yes": test.fail("Should exists in net xml, but it " "disappeared: %s" % flag_string_abs) # Check if add-last, add-fist works well if (status_error == "no" and not config_not_work and update_command in ["add-last", "add", "add-first"]): if update_command == "add-first": find_index = 0 else: find_index = -1 section_str_aft = new_xml_obj.get_section_string( xpath=element, index=find_index) logging.info( "xpath is %s, find_index is %s, section_str_aft is %s", element, find_index, section_str_aft) for flag_string in flag_list: logging.info("flag_string is %s", flag_string) if not re.search(flag_string, section_str_aft): test.fail("Can not find %s in %s" % (flag_string, section_str_aft)) logging.info("%s %s in right place", update_command, section_str_aft) # Check the positive test result if status_error == "no": # Check the network conf file # live update if ("--live" in options or "--config" not in options) and net_state == "active": if net_section == "ip-dhcp-range" and update_command == "add-first": ip_range = "%s,%s" % (new_start_ip, new_end_ip) if ip_version == "ipv6": ip_range = ip_range + ",64" check_item(ip_range) if "dns" in net_section: if update_command == "delete" and loop == 0: if net_section == "dns-srv": check_item_absent(srv_host) if net_section == "dns-txt": check_item_absent(txt_record) if net_section == "dns-host": check_host_absent(dns_host_str) elif "add" in update_command: if net_section == "dns-srv": for sec in update_sec.split(","): srv_host = srv_host.replace( names["srv_" + sec], names["new_srv_" + sec]) check_item(srv_host) if net_section == "dns-txt": for sec in update_sec.split(","): txt_record = txt_record.replace( names["dns_" + sec], names["new_dns_" + sec]) check_item(txt_record) if net_section == "dns-host": check_host(hostline) # Check the hostfile if (net_section == "ip-dhcp-host" and update_command != "modify" and update_command != "delete"): dic_hostfile = {} for sec in ["ip", "mac", "name", "id"]: if update_sec is not None and sec in update_sec.split( ","): dic_hostfile[sec] = names["new_dhcp_host_" + sec] + "," else: dic_hostfile[sec] = names[ip_version + "_host_" + sec] + "," if sec == "ip" and ip_version == "ipv6": dic_hostfile[sec] = "[" + dic_hostfile[ sec].strip(",") + "]" if without_sec is not None and sec in without_sec.split( ","): dic_hostfile[sec] = "" if ip_version == "ipv4": host_info = (dic_hostfile["mac"] + dic_hostfile["ip"] + dic_hostfile["name"]) if dic_hostfile["mac"] == "": host_info = dic_hostfile[ "name"] + dic_hostfile["ip"] if ip_version == "ipv6": host_info = ("id:" + dic_hostfile["id"] + dic_hostfile["name"] + dic_hostfile["ip"]) hostfile = get_hostfile() host_info_patten = host_info.strip(",") + "\n" if host_info_patten in hostfile: logging.info("host info %s is in hostfile %s", host_info, hostfile) else: test.fail("Can not find %s in host file: %s" % (host_info, hostfile)) # Check the net in guest if use_in_guest == "yes" and loop == 0: # Detach all interfaces of vm first iface_index = 0 mac_list = vm_xml.VMXML.get_iface_dev(vm_name) for mac in mac_list: iface_dict = vm_xml.VMXML.get_iface_by_mac( vm_name, mac) virsh.detach_interface( vm_name, "--type %s --mac %s --config" % (iface_dict.get('type'), mac)) vm.free_mac_address(iface_index) iface_index += 1 # attach new interface to guest for j in range(guest_iface_num): if net_section == "ip-dhcp-host" and new_dhcp_host_mac: mac = new_dhcp_host_mac else: mac = utils_net.generate_mac_address_simple() ret = virsh.attach_interface( vm_name, "--type %s --source %s --mac %s --config" % (iface_type, net_name, mac)) if ret.exit_status: test.fail( "Fail to attach new interface to guest: %s" % ret.stderr.strip()) # The sleep here is to make sure the update make effect time.sleep(2) # Start guest and check ip/mac/hostname... vm.start() logging.debug("vm xml is %s", vm.get_xml()) session = vm.wait_for_serial_login(internal_timeout=60) if "ip-dhcp" in net_section: dhclient_cmd = "(if pgrep dhclient;" \ "then pkill dhclient; sleep 3; fi) " \ "&& dhclient -%s -lf /dev/stdout" % ip_version[-1] leases = session.cmd_output(dhclient_cmd) iface_ip = utils_net.get_guest_ip_addr( session, mac, ip_version=ip_version, timeout=10) if net_section == "ip-dhcp-range": if new_start_ip <= iface_ip <= new_end_ip: logging.info("getting ip %s is in range [%s ~ %s]", iface_ip, new_start_ip, new_end_ip) else: test.fail("getting ip %s not in range [%s ~ %s]" % (iface_ip, new_start_ip, new_end_ip)) if net_section == "ip-dhcp-host": if iface_ip == new_dhcp_host_ip: logging.info("getting ip is same with set: %s", iface_ip) else: test.fail( "getting ip %s is not same with setting %s" % (iface_ip, new_dhcp_host_ip)) hostname = session.cmd_output("hostname -s").strip( '\n') # option host-name "redhatipv4-2" dhcp_hostname = "option host-name \"%s\"" % new_dhcp_host_name.split( '.')[0] if hostname == new_dhcp_host_name.split( '.')[0] or dhcp_hostname in leases: logging.info( "getting hostname same with setting: %s", new_dhcp_host_name.split('.')[0]) else: test.fail("getting hostname %s is not same with " "setting: %s" % (hostname, new_dhcp_host_name)) session.close() # Check network connection for macvtap if (use_in_guest == "yes" and net_section == "forward-interface" and forward_mode == "bridge"): xml_obj_use = network_xml.NetworkXML.new_from_net_dumpxml( net_name) net_conn = int(xml_obj_use.connection) iface_conn = xml_obj_use.get_interface_connection() conn_count = 0 for k in iface_conn: conn_count = conn_count + int(k) logging.info( "net_conn=%s, conn_count=%s, guest_iface_num=%s" % (net_conn, conn_count, guest_iface_num)) if (net_conn != conn_count or (loop == 1 and guest_iface_num != net_conn)): test.fail("Can not get expected connection num: " "net_conn = %s, iface_conn = %s" % (net_conn, conn_count)) #Check --config option after net destroyed if (int(check_config_round) == loop or (loop_time == 1 and "--current" in options and net_state == "active")): test_xml.set_active(False) logging.info("Checking xml after net destroyed") cmd_result = virsh.net_dumpxml(net_name) inactive_xml = cmd_result.stdout.strip() logging.info("inactive_xml is %s", inactive_xml) #Check the inactive xml for check_str in flag_list: if update_command != "delete": if "--config" in options: if not re.search(check_str, inactive_xml): test.fail( "Can not find xml after net destroyed") if "--current" in options: if re.search(check_str, inactive_xml): test.fail( "XML still exists after net destroyed") else: if "--config" in options: if re.search(check_str, inactive_xml): test.fail( "XML still exists after net destroyed") if "--current" in options: if not re.search(check_str, inactive_xml): test.fail( "Can not find xml after net destroyed") logging.info("Check for net inactive PASS") finally: if test_xml.get_active(): test_xml.del_active() if test_xml.get_defined(): test_xml.del_defined() if os.path.exists(xmlfile): os.remove(xmlfile) if use_in_guest == "yes": vm = env.get_vm(vm_name) if vm.is_alive(): vm.destroy(gracefully=False) vmxml_backup.sync() for config_file in backup_files: config_file._reset_file()
def run(test, params, env): """ Test virsh {at|de}tach-interface command. 1) Prepare test environment and its parameters 2) Attach the required interface 3) According test type(only attach or both attach and detach): a.Go on to test detach(if attaching is correct) b.Return GOOD or raise TestFail(if attaching is wrong) 4) Check if attached interface is correct: a.Try to catch it in vm's XML file b.Try to catch it in vm 5) Detach the attached interface 6) Check result """ vm_name = params.get("main_vm") vm = env.get_vm(vm_name) backup_xml = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) # Test parameters uri = libvirt_vm.normalize_connect_uri(params.get("connect_uri", "default")) vm_ref = params.get("at_detach_iface_vm_ref", "domname") options_suffix = params.get("at_detach_iface_options_suffix", "") status_error = "yes" == params.get("status_error", "no") start_vm = params.get("start_vm") # Should attach must be pass for detach test. correct_attach = "yes" == params.get("correct_attach", "no") readonly = ("yes" == params.get("readonly", "no")) # Interface specific attributes. iface_type = params.get("at_detach_iface_type", "network") if iface_type == "bridge": try: utils_path.find_command("brctl") except utils_path.CmdNotFoundError: test.cancel("Command 'brctl' is missing. You must " "install it.") iface_source = params.get("at_detach_iface_source", "default") iface_mode = params.get("at_detach_iface_mode", "vepa") iface_mac = params.get("at_detach_iface_mac", "created") iface_target = params.get("at_detach_iface_target") iface_model = params.get("at_detach_iface_model") iface_inbound = params.get("at_detach_iface_inbound") iface_outbound = params.get("at_detach_iface_outbound") iface_rom = params.get("at_detach_rom_bar") iface_link = params.get("at_detach_link_state") iface_boot = params.get("at_detach_boot_order") iface_driver = params.get("at_detach_iface_driver") iface_driver_host = params.get("at_detach_driver_host") iface_driver_guest = params.get("at_detach_driver_guest") iface_backend = params.get("at_detach_iface_backend") save_restore = params.get("save_restore", "no") restart_libvirtd = params.get("restart_libvirtd", "no") attach_cmd = params.get("attach_cmd", "attach-interface") virsh_dargs = {'ignore_status': True, 'debug': True, 'uri': uri} # Get iface name if iface_type is direct if iface_type == "direct": iface_source = utils_net.get_net_if(state="UP")[0] # Get a bridge name for test if iface_type is bridge. # If there is no bridge other than virbr0, raise TestCancel if iface_type == "bridge": host_bridge = utils_net.Bridge() bridge_list = host_bridge.list_br() try: bridge_list.remove("virbr0") except AttributeError: pass # If no virbr0, just pass is ok logging.debug("Useful bridges:%s", bridge_list) # just choosing one bridge on host. if len(bridge_list): iface_source = bridge_list[0] else: test.cancel("No useful bridge on host " "other than 'virbr0'.") # Test both detach and attach, So collect info # both of them for result check. # When something wrong with interface, set it to 1 fail_flag = 0 result_info = [] # Get a mac address if iface_mac is 'created'. if iface_mac == "created" or correct_attach: iface_mac = utils_net.generate_mac_address_simple() # Record all iface parameters in iface_dict iface_dict = {} update_list = [ "driver", "driver_host", "driver_guest", "model", "rom", "inbound", "outbound", "link", "target", "mac", "source", "boot", "backend", "type", "mode" ] names = locals() for update_item in update_list: if names["iface_"+update_item]: iface_dict.update({update_item: names["iface_"+update_item]}) else: iface_dict.update({update_item: None}) logging.info("iface_dict is %s", iface_dict) # Format the params iface_format = format_param(iface_dict) logging.info("iface_format is %s", iface_format) try: # Generate xml file if using attach-device command if attach_cmd == "attach-device": # Change boot order to disk libvirt.change_boot_order(vm_name, "disk", "1") vm.destroy() vm.start() # Generate attached xml xml_file_tmp = libvirt.modify_vm_iface(vm_name, "get_xml", iface_format) new_iface = Interface(type_name=iface_type) new_iface.xml = xml_file_tmp new_iface.del_address() xml_file = new_iface.xml # To confirm vm's state and make sure os fully started if start_vm == "no": if vm.is_alive(): vm.destroy() else: vm.wait_for_login().close() # Set attach-interface domain dom_uuid = vm.get_uuid() dom_id = vm.get_id() if vm_ref == "domname": vm_ref = vm_name elif vm_ref == "domid": vm_ref = dom_id elif vm_ref == "domuuid": vm_ref = dom_uuid elif vm_ref == "hexdomid" and dom_id is not None: vm_ref = hex(int(dom_id)) # Set attach-interface options and Start attach-interface test if correct_attach: options = set_options("network", "default", iface_mac, "", "attach") if readonly: virsh_dargs.update({'readonly': True, 'debug': True}) attach_result = virsh.attach_interface(vm_name, options, **virsh_dargs) else: if attach_cmd == "attach-interface": options = set_options(iface_type, iface_source, iface_mac, options_suffix, "attach", iface_target, iface_model, iface_inbound, iface_outbound) attach_result = virsh.attach_interface(vm_ref, options, **virsh_dargs) elif attach_cmd == "attach-device": attach_result = virsh.attach_device(vm_name, xml_file, ignore_status=True, debug=True) attach_status = attach_result.exit_status logging.debug(attach_result) # If attach interface failed. if attach_status: if not status_error: fail_flag = 1 result_info.append("Attach Failed: %s" % attach_result.stderr) elif status_error: # Here we just use it to exit, do not mean test failed fail_flag = 1 # If attach interface succeeded. else: if status_error and not correct_attach: fail_flag = 1 result_info.append("Attach Success with wrong command.") if fail_flag and start_vm == "yes": vm.destroy() if len(result_info): test.fail(result_info) else: # Exit because it is error_test for attach-interface. return if "print-xml" in options_suffix: iface_obj = Interface(type_name=iface_type) iface_obj.xml = attach_result.stdout.strip() if (iface_obj.type_name == iface_type and iface_obj.source['dev'] == iface_source and iface_obj.target['dev'] == iface_target and iface_obj.model == iface_model and iface_obj.bandwidth.inbound == eval(iface_format['inbound']) and iface_obj.bandwidth.outbound == eval(iface_format['outbound'])): logging.info("Print ml all element check pass") else: test.fail("Print xml do not show as expected") # Check dumpxml file whether the interface is added successfully. status, ret = check_dumpxml_iface(vm_name, iface_format) if "print-xml" not in options_suffix: if status: fail_flag = 1 result_info.append(ret) else: if status == 0: test.fail("Attach interface effect in xml with print-xml option") else: return # Login to domain to check new interface. if not vm.is_alive(): vm.start() elif vm.state() == "paused": vm.resume() status, ret = login_to_check(vm, iface_mac) if status: fail_flag = 1 result_info.append(ret) # Check on host for direct type if iface_type == 'direct': cmd_result = process.run("ip -d link show test").stdout_text.strip() logging.info("cmd output is %s", cmd_result) check_patten = ("%s@%s.*\n.*%s.*\n.*macvtap.*mode.*%s" % (iface_target, iface_source, iface_mac, iface_mode)) logging.info("check patten is %s", check_patten) if not re.search(check_patten, cmd_result): logging.error("Can not find %s in ip link" % check_patten) fail_flag = 1 result_info.append(cmd_result) # Do operation and check again if restart_libvirtd == "yes": libvirtd = utils_libvirtd.Libvirtd() libvirtd.restart() if save_restore == "yes": check_save_restore(vm_name) status, ret = check_dumpxml_iface(vm_name, iface_format) if status: fail_flag = 1 result_info.append(ret) # Set detach-interface options options = set_options(iface_type, None, iface_mac, options_suffix, "detach") # Start detach-interface test if save_restore == "yes" and vm_ref == dom_id: vm_ref = vm_name detach_result = virsh.detach_interface(vm_ref, options, **virsh_dargs) detach_status = detach_result.exit_status detach_msg = detach_result.stderr.strip() logging.debug(detach_result) if detach_status == 0 and status_error == 0: # Check the xml after detach and clean up if needed. time.sleep(5) status, _ = check_dumpxml_iface(vm_name, iface_format) if status == 0: detach_status = 1 detach_msg = "xml still exist after detach" cleanup_options = "--type %s --mac %s" % (iface_type, iface_mac) virsh.detach_interface(vm_ref, cleanup_options, **virsh_dargs) else: logging.info("After detach, the interface xml disappeared") # Check results. if status_error: if detach_status == 0: test.fail("Detach Success with wrong command.") else: if detach_status != 0: test.fail("Detach Failed: %s" % detach_msg) else: if fail_flag: test.fail("Attach-Detach Success but " "something wrong with its " "functional use:%s" % result_info) finally: if vm.is_alive(): vm.destroy() backup_xml.sync()