+ 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 unbind_pci_devices_from_other_driver(node, driver, *pci_addrs):
+ """Unbind PCI devices from driver other than input driver on node.
+
+ :param node: DUT node.
+ :param driver: Driver to not unbind from. If None or empty string,
+ will attempt to unbind from the current driver.
+ :param pci_addrs: PCI device addresses.
+ :type node: dict
+ :type driver: str
+ :type pci_addrs: list
+ """
+ for pci_addr in pci_addrs:
+ cur_driver = DUTSetup.get_pci_dev_driver(node, pci_addr)
+ if not cur_driver:
+ return
+ if not driver or cur_driver != driver:
+ DUTSetup.pci_driver_unbind(node, pci_addr)
+
+ @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.
+
+ :param node: DUT node.
+ :param pci_addr: PCI device address.
+ :type node: dict
+ :type pci_addr: str
+ :returns: Driver or None
+ :raises RuntimeError: If it is not possible to get the interface driver
+ information from the node.
+ """
+ driver_path = f"/sys/bus/pci/devices/{pci_addr}/driver"
+ cmd = f"test -d {driver_path}"
+ ret_code, ret_val, _ = exec_cmd(node, cmd)
+ if int(ret_code):
+ # the directory doesn't exist which means the device is not bound
+ # to any driver
+ return None
+ cmd = f"basename $(readlink -f {driver_path})"
+ ret_val, _ = exec_cmd_no_error(node, cmd)
+ return ret_val.strip()
+
+ @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
+ """
+ for node in nodes.values():
+ if node[u"type"] == NodeType.DUT:
+ DUTSetup.verify_kernel_module(node, module, force_load)
+
+ @staticmethod
+ def verify_uio_driver_on_all_duts(nodes):
+ """Verify if uio driver kernel module is loaded on all DUTs. If module
+ is not present it will try to load it.
+
+ :param nodes: DUT nodes.
+ :type nodes: dict
+ """
+ for node in nodes.values():
+ if node[u"type"] == NodeType.DUT:
+ uio_driver = Topology.get_uio_driver(node)
+ DUTSetup.verify_kernel_module(node, uio_driver, force_load=True)
+
+ @staticmethod
+ def load_kernel_module(node, module):
+ """Load kernel module on node.
+
+ :param node: DUT node.
+ :param module: Module to load.
+ :type node: dict
+ :type module: str
+ :returns: nothing
+ :raises RuntimeError: If loading failed.
+ """
+ command = f"modprobe {module}"
+ message = f"Failed to load {module} on host {node[u'host']}"
+
+ exec_cmd_no_error(node, command, timeout=30, sudo=True, message=message)
+
+ @staticmethod
+ def running_in_container(node):
+ """This method tests if topology node is running inside container.
+
+ :param node: Topology node.
+ :type node: dict
+ :returns: True if running in docker container, false if not or failed
+ to detect.
+ :rtype: bool
+ """
+ command = "cat /.dockerenv"
+ try:
+ exec_cmd_no_error(node, command, timeout=30)
+ except RuntimeError:
+ return False
+ return True
+
+ @staticmethod
+ def get_docker_mergeddir(node, uuid=None):
+ """Get Docker overlay for MergedDir diff.
+
+ :param node: DUT node.
+ :param uuid: Docker UUID.
+ :type node: dict
+ :type uuid: str
+ :returns: Docker container MergedDir.
+ :rtype: str
+ :raises RuntimeError: If getting output failed.
+ """
+ if not uuid:
+ command = 'fgrep "hostname" /proc/self/mountinfo | cut -f 4 -d" "'
+ message = "Failed to get UUID!"
+ stdout, _ = exec_cmd_no_error(node, command, message=message)
+ uuid = stdout.split(sep="/")[-2]
+ command = (
+ f"docker inspect "
+ f"--format='{{{{.GraphDriver.Data.MergedDir}}}}' {uuid}"
+ )
+ message = f"Failed to get directory of {uuid} on host {node[u'host']}"
+
+ stdout, _ = exec_cmd_no_error(node, command, sudo=True, message=message)
+ return stdout.strip()
+
+ @staticmethod
+ def get_hugepages_info(node, hugesize=None):
+ """Get number of huge pages in system.
+
+ :param node: Node in the topology.
+ :param hugesize: Size of hugepages. Default system huge size if None.
+ :type node: dict
+ :type hugesize: int
+ :returns: Number of huge pages in system.
+ :rtype: dict
+ :raises RuntimeError: If reading failed.
+ """
+ if not hugesize:
+ hugesize = "$(grep Hugepagesize /proc/meminfo | awk '{ print $2 }')"
+ command = f"cat /sys/kernel/mm/hugepages/hugepages-{hugesize}kB/*"
+ stdout, _ = exec_cmd_no_error(node, command)
+ try:
+ line = stdout.splitlines()
+ return {
+ "free_hugepages": int(line[0]),
+ "nr_hugepages": int(line[1]),
+ "nr_hugepages_mempolicy": int(line[2]),
+ "nr_overcommit_hugepages": int(line[3]),
+ "resv_hugepages": int(line[4]),
+ "surplus_hugepages": int(line[5])
+ }
+ except ValueError:
+ logger.trace(u"Reading huge pages information failed!")
+
+ @staticmethod
+ def check_huge_page(
+ node, huge_mnt, mem_size, hugesize=2048, allocate=False):
+ """Check if there is enough HugePages in system. If allocate is set to
+ true, try to allocate more HugePages.
+
+ :param node: Node in the topology.
+ :param huge_mnt: HugePage mount point.
+ :param mem_size: Reqeusted memory in MB.
+ :param hugesize: HugePage size in KB.
+ :param allocate: Whether to allocate more memory if not enough.
+ :type node: dict
+ :type huge_mnt: str
+ :type mem_size: int
+ :type hugesize: int
+ :type allocate: bool
+ :raises RuntimeError: Mounting hugetlbfs failed or not enough HugePages
+ or increasing map count failed.
+ """
+ # Get huge pages information.
+ hugepages = DUTSetup.get_hugepages_info(node, hugesize=hugesize)
+
+ # Check if hugepages requested are available on node.
+ if hugepages[u"nr_overcommit_hugepages"]:
+ # If overcommit is used, we need to know how many additional pages
+ # we can allocate
+ huge_available = hugepages[u"nr_overcommit_hugepages"] - \
+ hugepages[u"surplus_hugepages"]
+ else:
+ # Fallbacking to free_hugepages which were used before to detect.
+ huge_available = hugepages[u"free_hugepages"]