+ def get_virtfn_pci_addr(node, pf_pci_addr, vf_id):
+ """Get PCI address of Virtual Function.
+
+ :param node: DUT node.
+ :param pf_pci_addr: Physical Function PCI address.
+ :param vf_id: Virtual Function number.
+ :type node: dict
+ :type pf_pci_addr: str
+ :type vf_id: int
+ :returns: Virtual Function PCI address.
+ :rtype: int
+ :raises RuntimeError: If failed to get Virtual Function PCI address.
+ """
+ command = f"sh -c \"basename $(readlink " \
+ f"/sys/bus/pci/devices/{pf_pci_addr}/virtfn{vf_id})\""
+ message = u"Failed to get virtual function PCI address."
+
+ stdout, _ = exec_cmd_no_error(
+ node, command, timeout=30, sudo=True, message=message
+ )
+
+ return stdout.strip()
+
+ @staticmethod
+ def get_sriov_numvfs(node, pf_pci_addr):
+ """Get number of SR-IOV VFs.
+
+ :param node: DUT node.
+ :param pf_pci_addr: Physical Function PCI device address.
+ :type node: dict
+ :type pf_pci_addr: str
+ :returns: Number of VFs.
+ :rtype: int
+ :raises RuntimeError: If PCI device is not SR-IOV capable.
+ """
+ pci = pf_pci_addr.replace(u":", r"\:")
+ command = f"cat /sys/bus/pci/devices/{pci}/sriov_numvfs"
+ message = f"PCI device {pf_pci_addr} is not a SR-IOV device."
+
+ for _ in range(3):
+ stdout, _ = exec_cmd_no_error(
+ node, command, timeout=30, sudo=True, message=message
+ )
+ try:
+ sriov_numvfs = int(stdout)
+ except ValueError:
+ logger.trace(
+ f"Reading sriov_numvfs info failed on {node[u'host']}"
+ )
+ else:
+ return sriov_numvfs
+
+ @staticmethod
+ def set_sriov_numvfs(node, pf_pci_addr, numvfs=0):
+ """Init or reset SR-IOV virtual functions by setting its number on PCI
+ device on DUT. Setting to zero removes all VFs.
+
+ :param node: DUT node.
+ :param pf_pci_addr: Physical Function PCI device address.
+ :param numvfs: Number of VFs to initialize, 0 - removes the VFs.
+ :type node: dict
+ :type pf_pci_addr: str
+ :type numvfs: int
+ :raises RuntimeError: Failed to create VFs on PCI.
+ """
+ pci = pf_pci_addr.replace(u":", r"\:")
+ command = f"sh -c \"echo {numvfs} | " \
+ f"tee /sys/bus/pci/devices/{pci}/sriov_numvfs\""
+ message = f"Failed to create {numvfs} VFs on {pf_pci_addr} device " \
+ f"on {node[u'host']}"
+
+ exec_cmd_no_error(
+ node, command, timeout=120, sudo=True, message=message
+ )
+
+ @staticmethod
+ def pci_driver_unbind(node, pci_addr):
+ """Unbind PCI device from current driver on node.
+
+ :param node: DUT node.
+ :param pci_addr: PCI device address.
+ :type node: dict
+ :type pci_addr: str
+ :raises RuntimeError: If PCI device unbind failed.
+ """
+ pci = pci_addr.replace(u":", r"\:")
+ command = f"sh -c \"echo {pci_addr} | " \
+ f"tee /sys/bus/pci/devices/{pci}/driver/unbind\""
+ message = f"Failed to unbind PCI device {pci_addr} on {node[u'host']}"
+
+ exec_cmd_no_error(
+ node, command, timeout=120, sudo=True, message=message
+ )
+
+ @staticmethod
+ def pci_driver_bind(node, pci_addr, driver):
+ """Bind PCI device to driver on node.
+
+ :param node: DUT node.
+ :param pci_addr: PCI device address.
+ :param driver: Driver to bind.
+ :type node: dict
+ :type pci_addr: str
+ :type driver: str
+ :raises RuntimeError: If PCI device bind failed.
+ """
+ message = f"Failed to bind PCI device {pci_addr} to {driver} " \
+ f"on host {node[u'host']}"
+ pci = pci_addr.replace(u":", r"\:")
+ command = f"sh -c \"echo {driver} | " \
+ f"tee /sys/bus/pci/devices/{pci}/driver_override\""
+
+ exec_cmd_no_error(
+ node, command, timeout=120, sudo=True, message=message
+ )
+
+ command = f"sh -c \"echo {pci_addr} | " \
+ f"tee /sys/bus/pci/drivers/{driver}/bind\""
+
+ exec_cmd_no_error(
+ node, command, timeout=120, sudo=True, message=message
+ )
+
+ command = f"sh -c \"echo | " \
+ f"tee /sys/bus/pci/devices/{pci}/driver_override\""
+
+ exec_cmd_no_error(
+ node, command, timeout=120, sudo=True, message=message
+ )
+
+ @staticmethod
+ def pci_vf_driver_unbind(node, pf_pci_addr, vf_id):
+ """Unbind Virtual Function from driver on node.
+
+ :param node: DUT node.
+ :param pf_pci_addr: PCI device address.
+ :param vf_id: Virtual Function ID.
+ :type node: dict
+ :type pf_pci_addr: str
+ :type vf_id: int
+ :raises RuntimeError: If Virtual Function unbind failed.
+ """
+ vf_pci_addr = DUTSetup.get_virtfn_pci_addr(node, pf_pci_addr, vf_id)
+ pf_pci = pf_pci_addr.replace(u":", r"\:")
+ vf_path = f"/sys/bus/pci/devices/{pf_pci}/virtfn{vf_id}"
+
+ command = f"sh -c \"echo {vf_pci_addr} | tee {vf_path}/driver/unbind\""
+ message = f"Failed to unbind VF {vf_pci_addr} on {node[u'host']}"
+
+ exec_cmd_no_error(
+ node, command, timeout=120, sudo=True, message=message
+ )
+
+ @staticmethod
+ def pci_vf_driver_bind(node, pf_pci_addr, vf_id, driver):
+ """Bind Virtual Function to driver on node.
+
+ :param node: DUT node.
+ :param pf_pci_addr: PCI device address.
+ :param vf_id: Virtual Function ID.
+ :param driver: Driver to bind.
+ :type node: dict
+ :type pf_pci_addr: str
+ :type vf_id: int
+ :type driver: str
+ :raises RuntimeError: If PCI device bind failed.
+ """
+ vf_pci_addr = DUTSetup.get_virtfn_pci_addr(node, pf_pci_addr, vf_id)
+ pf_pci = pf_pci_addr.replace(u":", r'\:')
+ vf_path = f"/sys/bus/pci/devices/{pf_pci}/virtfn{vf_id}"
+
+ message = f"Failed to bind VF {vf_pci_addr} to {driver} " \
+ f"on {node[u'host']}"
+ command = f"sh -c \"echo {driver} | tee {vf_path}/driver_override\""
+
+ exec_cmd_no_error(
+ node, command, timeout=120, sudo=True, message=message
+ )
+
+ command = f"sh -c \"echo {vf_pci_addr} | " \
+ f"tee /sys/bus/pci/drivers/{driver}/bind\""
+
+ exec_cmd_no_error(
+ node, command, timeout=120, sudo=True, message=message
+ )
+
+ command = f"sh -c \"echo | tee {vf_path}/driver_override\""
+
+ exec_cmd_no_error(
+ node, command, timeout=120, sudo=True, message=message
+ )
+
+ @staticmethod
+ def get_pci_dev_driver(node, pci_addr):
+ """Get current PCI device driver on node.
+
+ .. note::
+ # lspci -vmmks 0000:00:05.0
+ Slot: 00:05.0
+ Class: Ethernet controller
+ Vendor: Red Hat, Inc
+ Device: Virtio network device
+ SVendor: Red Hat, Inc
+ SDevice: Device 0001
+ PhySlot: 5
+ Driver: virtio-pci
+
+ :param node: DUT node.
+ :param pci_addr: PCI device address.
+ :type node: dict
+ :type pci_addr: str
+ :returns: Driver or None
+ :raises RuntimeError: If PCI rescan or lspci command execution failed.
+ :raises RuntimeError: If it is not possible to get the interface driver
+ information from the node.
+ """
+ ssh = SSH()
+ ssh.connect(node)
+
+ for i in range(3):
+ logger.trace(f"Try number {i}: Get PCI device driver")
+
+ cmd = f"lspci -vmmks {pci_addr}"
+ ret_code, stdout, _ = ssh.exec_command(cmd)
+ if int(ret_code):
+ raise RuntimeError(f"'{cmd}' failed on '{node[u'host']}'")
+
+ for line in stdout.splitlines():
+ if not line:
+ continue
+ name = None
+ value = None
+ try:
+ name, value = line.split(u"\t", 1)
+ except ValueError:
+ if name == u"Driver:":
+ return None
+ if name == u"Driver:":
+ return value
+
+ if i < 2:
+ logger.trace(
+ f"Driver for PCI device {pci_addr} not found, "
+ f"executing pci rescan and retrying"
+ )
+ cmd = u"sh -c \"echo 1 > /sys/bus/pci/rescan\""
+ ret_code, _, _ = ssh.exec_command_sudo(cmd)
+ if int(ret_code) != 0:
+ raise RuntimeError(f"'{cmd}' failed on '{node[u'host']}'")
+
+ return None
+
+ @staticmethod
+ def verify_kernel_module(node, module, force_load=False):
+ """Verify if kernel module is loaded on node. If parameter force
+ load is set to True, then try to load the modules.
+
+ :param node: Node.
+ :param module: Module to verify.
+ :param force_load: If True then try to load module.
+ :type node: dict
+ :type module: str
+ :type force_load: bool
+ :raises RuntimeError: If module is not loaded or failed to load.
+ """
+ command = f"grep -w {module} /proc/modules"
+ message = f"Kernel module {module} is not loaded " \
+ f"on host {node[u'host']}"
+
+ try:
+ exec_cmd_no_error(
+ node, command, timeout=30, sudo=False, message=message
+ )
+ except RuntimeError:
+ if force_load:
+ # Module is not loaded and we want to load it
+ DUTSetup.load_kernel_module(node, module)
+ else:
+ raise
+
+ @staticmethod
+ def verify_kernel_module_on_all_duts(nodes, module, force_load=False):
+ """Verify if kernel module is loaded on all DUTs. If parameter force
+ load is set to True, then try to load the modules.
+
+ :param nodes: DUT nodes.
+ :param module: Module to verify.
+ :param force_load: If True then try to load module.
+ :type nodes: dict
+ :type module: str
+ :type force_load: bool
+ """