CSIT-338 PCI numa_node discovery 30/2230/5
authorMiroslav Miklus <mmiklus@cisco.com>
Fri, 5 Aug 2016 14:16:45 +0000 (16:16 +0200)
committerJan Gelety <jgelety@cisco.com>
Mon, 8 Aug 2016 16:59:05 +0000 (16:59 +0000)
Allow to discover PCI - numa node relationship.

Change-Id: I04a445e42b3cbbf450b990ebbc2c83ac313815f1
Signed-off-by: Miroslav Miklus <mmiklus@cisco.com>
resources/libraries/python/InterfaceUtil.py
resources/libraries/python/topology.py
tests/perf/__init__.robot

index 9537b2b..9ecdb75 100644 (file)
@@ -427,7 +427,26 @@ class InterfaceUtil(object):
         InterfaceUtil.tg_set_interfaces_udev_rules(node)
 
     @staticmethod
         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
         """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 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 nodes: dict
         :type skip_tg: bool
+        :type numa_node: bool
         """
         for node_data in nodes.values():
             if node_data['type'] == NodeType.DUT:
         """
         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)
 
             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.
     @staticmethod
     def create_vlan_subinterface(node, interface, vlan):
         """Create VLAN subinterface on node.
index 80cbb1f..a5c67d3 100644 (file)
@@ -13,6 +13,8 @@
 
 """Defines nodes and topology structure."""
 
 
 """Defines nodes and topology structure."""
 
+from collections import Counter
+
 from yaml import load
 
 from robot.api import logger
 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']
 
     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)
 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
 
         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.
     @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
 
         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.
     @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']
         :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
index afdcc70..7efa763 100644 (file)
@@ -19,3 +19,5 @@
 | Suite Setup | Run Keywords | Setup Framework | ${nodes}
 | ...         | AND          | Setup All DUTs | ${nodes}
 | ...         | AND          | Get CPU Layout from all nodes | ${nodes}
 | 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}