From: Miroslav Miklus Date: Fri, 5 Aug 2016 14:16:45 +0000 (+0200) Subject: CSIT-338 PCI numa_node discovery X-Git-Url: https://gerrit.fd.io/r/gitweb?p=csit.git;a=commitdiff_plain;h=e710b52146d8c5c9db250d29c01f4f8ebdaf3261 CSIT-338 PCI numa_node discovery Allow to discover PCI - numa node relationship. Change-Id: I04a445e42b3cbbf450b990ebbc2c83ac313815f1 Signed-off-by: Miroslav Miklus --- diff --git a/resources/libraries/python/InterfaceUtil.py b/resources/libraries/python/InterfaceUtil.py index 9537b2ba1d..9ecdb7503a 100644 --- a/resources/libraries/python/InterfaceUtil.py +++ b/resources/libraries/python/InterfaceUtil.py @@ -427,7 +427,26 @@ class InterfaceUtil(object): InterfaceUtil.tg_set_interfaces_udev_rules(node) @staticmethod - def update_all_interface_data_on_all_nodes(nodes, skip_tg=False): + def iface_update_numa_node(node): + """For all interfaces from topology file update numa node based on + information from the node. + + :param node: Node from topology. + :type node: dict + :return: nothing + """ + ssh = SSH() + for if_key in Topology.get_node_interfaces(node): + if_pci = Topology.get_interface_pci_addr(node, if_key) + ssh.connect(node) + cmd = "cat /sys/bus/pci/devices/{}/numa_node".format(if_pci) + (ret, out, _) = ssh.exec_command(cmd) + if ret == 0: + Topology.set_interface_numa_node(node, if_key, int(out)) + + @staticmethod + def update_all_interface_data_on_all_nodes(nodes, skip_tg=False, + numa_node=False): """Update interface names on all nodes in DICT__nodes. This method updates the topology dictionary by querying interface lists @@ -435,8 +454,10 @@ class InterfaceUtil(object): :param nodes: Nodes in the topology. :param skip_tg: Skip TG node + :param numa_node: Retrieve numa_node location. :type nodes: dict :type skip_tg: bool + :type numa_node: bool """ for node_data in nodes.values(): if node_data['type'] == NodeType.DUT: @@ -444,6 +465,12 @@ class InterfaceUtil(object): elif node_data['type'] == NodeType.TG and not skip_tg: InterfaceUtil.update_tg_interface_data_on_node(node_data) + if numa_node: + if node_data['type'] == NodeType.DUT: + InterfaceUtil.iface_update_numa_node(node_data) + elif node_data['type'] == NodeType.TG and not skip_tg: + InterfaceUtil.iface_update_numa_node(node_data) + @staticmethod def create_vlan_subinterface(node, interface, vlan): """Create VLAN subinterface on node. diff --git a/resources/libraries/python/topology.py b/resources/libraries/python/topology.py index 80cbb1f4e1..a5c67d313c 100644 --- a/resources/libraries/python/topology.py +++ b/resources/libraries/python/topology.py @@ -13,6 +13,8 @@ """Defines nodes and topology structure.""" +from collections import Counter + from yaml import load from robot.api import logger @@ -32,7 +34,7 @@ def load_topo_from_yaml(): with open(topo_path) as work_file: return load(work_file.read())['nodes'] - +# pylint: disable=invalid-name class NodeType(object): """Defines node types used in topology dictionaries.""" # Device Under Test (this node has VPP running on it) @@ -331,6 +333,56 @@ class Topology(object): except KeyError: return None + @staticmethod + def get_interface_numa_node(node, iface_key): + """Get interface numa node. + + Returns physical relation to numa node, numa_id. + + :param node: Node to get numa id on. + :param iface_key: Interface key from topology file. + :type node: dict + :type iface_key: str + :return: numa node id, None if not available. + :rtype: int + """ + try: + return node['interfaces'][iface_key].get('numa_node') + except KeyError: + return None + + @staticmethod + def get_interfaces_numa_node(node, *iface_keys): + """Get numa node on which are located most of the interfaces. + + Return numa node with highest count of interfaces provided as arguments. + Return 0 if the interface does not have numa_node information available. + If all interfaces have unknown location (-1), then return 0. + If most of interfaces have unknown location (-1), but there are + some interfaces with known location, then return the second most + location of the provided interfaces. + + :param node: Node from DICT__nodes. + :param iface_keys: Interface keys for lookup. + :type node: dict + :type iface_keys: strings + """ + numa_list = [] + for if_key in iface_keys: + try: + numa_list.append(node['interfaces'][if_key].get('numa_node')) + except KeyError: + pass + + numa_cnt_mc = Counter(numa_list).most_common() + + if len(numa_cnt_mc) > 0 and numa_cnt_mc[0][0] != -1: + return numa_cnt_mc[0][0] + elif len(numa_cnt_mc) > 1 and numa_cnt_mc[0][0] == -1: + return numa_cnt_mc[1][0] + else: + return 0 + @staticmethod def get_interface_mac(node, iface_key): """Get MAC address for the interface. @@ -415,6 +467,17 @@ class Topology(object): except KeyError: return None + @staticmethod + def get_node_interfaces(node): + """Get all node interfaces. + + :param node: Node to get list of interfaces from. + :type node: dict + :return: Return list of keys of all interfaces. + :rtype: list + """ + return node['interfaces'].keys() + @staticmethod def get_node_link_mac(node, link_name): """Return interface mac address by link name. @@ -623,3 +686,19 @@ class Topology(object): :rtype: str """ return node['host'] + + @staticmethod + def set_interface_numa_node(node, iface_key, numa_node_id): + """Set interface numa_node location. + + :param node: Node to set numa_node on. + :param iface_key: Interface key from topology file. + :type node: dict + :type iface_key: str + :return: Return iface_key or None if not found. + """ + try: + node['interfaces'][iface_key]['numa_node'] = numa_node_id + return iface_key + except KeyError: + return None diff --git a/tests/perf/__init__.robot b/tests/perf/__init__.robot index afdcc700e9..7efa763f57 100644 --- a/tests/perf/__init__.robot +++ b/tests/perf/__init__.robot @@ -19,3 +19,5 @@ | Suite Setup | Run Keywords | Setup Framework | ${nodes} | ... | AND | Setup All DUTs | ${nodes} | ... | AND | Get CPU Layout from all nodes | ${nodes} +| ... | AND | Update All Interface Data On All Nodes +| ... | ${nodes} | skip_tg=${True} | numa_node=${True}