CSIT-474: CSIT doc auto-generation
[csit.git] / resources / libraries / python / topology.py
index 80cbb1f..3c903e5 100644 (file)
@@ -13,6 +13,8 @@
 
 """Defines nodes and topology structure."""
 
+from collections import Counter
+
 from yaml import load
 
 from robot.api import logger
@@ -27,12 +29,16 @@ def load_topo_from_yaml():
 
     :return: Nodes from loaded topology.
     """
-    topo_path = BuiltIn().get_variable_value("${TOPOLOGY_PATH}")
+    try:
+        topo_path = BuiltIn().get_variable_value("${TOPOLOGY_PATH}")
+    except:
+        return ''
 
     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)
@@ -44,6 +50,7 @@ class NodeType(object):
 
 
 class NodeSubTypeTG(object):
+    """Defines node sub-type TG - traffic generator."""
     # T-Rex traffic generator
     TREX = 'TREX'
     # Moongen
@@ -276,11 +283,12 @@ class Topology(object):
         :return: Interface name of the interface connected to the given link.
         :rtype: str
         """
-        return Topology._get_interface_by_key_value(node, "vpp_sw_index", sw_index)
+        return Topology._get_interface_by_key_value(node, "vpp_sw_index",
+                                                    sw_index)
 
     @staticmethod
     def get_interface_sw_index(node, iface_key):
-        """Get VPP sw_if_index for the interface.
+        """Get VPP sw_if_index for the interface using interface key.
 
         :param node: Node to get interface sw_if_index on.
         :param iface_key: Interface key from topology file, or sw_index.
@@ -297,6 +305,26 @@ class Topology(object):
         except (KeyError, ValueError):
             return None
 
+    @staticmethod
+    def get_interface_sw_index_by_name(node, iface_name):
+        """Get VPP sw_if_index for the interface using interface name.
+
+        :param node: Node to get interface sw_if_index on.
+        :param iface_name: Interface name.
+        :type node: dict
+        :type iface_name: str
+        :return: Return sw_if_index or None if not found.
+        :raises TypeError: If provided interface name is not a string.
+        """
+        try:
+            if isinstance(iface_name, basestring):
+                iface_key = Topology.get_interface_by_name(node, iface_name)
+                return node['interfaces'][iface_key].get('vpp_sw_index')
+            else:
+                raise TypeError("Interface name must be a string.")
+        except (KeyError, ValueError):
+            return None
+
     @staticmethod
     def get_interface_mtu(node, iface_key):
         """Get interface MTU.
@@ -324,13 +352,140 @@ class Topology(object):
         :type node: dict
         :type iface_key: str
         :return: Interface name or None if not found.
-        :rtype: int
+        :rtype: str
         """
         try:
             return node['interfaces'][iface_key].get('name')
         except KeyError:
             return None
 
+    @staticmethod
+    def convert_interface_reference_to_key(node, interface):
+        """Takes interface reference in any format
+        (name, link name, interface key or sw_if_index)
+        and converts to interface key using Topology methods.
+
+        :param node: Node in topology.
+        :param interface: Name, sw_if_index, link name or key of an interface
+        on the node.
+        Valid formats are: sw_if_index, key, name.
+        :type node: dict
+        :type interface: str or int
+
+        :return: Interface key.
+        :rtype: str
+
+        :raises TypeError: If provided with invalid interface argument.
+        :raises RuntimeError: If the interface does not exist in topology.
+        """
+
+        if isinstance(interface, int):
+            key = Topology.get_interface_by_sw_index(node, interface)
+            if key is None:
+                raise RuntimeError("Interface with sw_if_index={0} does not "
+                                   "exist in topology.".format(interface))
+        elif interface in Topology.get_node_interfaces(node):
+            key = interface
+        elif interface in Topology.get_links({"dut": node}):
+            key = Topology.get_interface_by_link_name(node, interface)
+        elif isinstance(interface, basestring):
+            key = Topology.get_interface_by_name(node, interface)
+            if key is None:
+                raise RuntimeError("Interface with key, name or link name "
+                                   "\"{0}\" does not exist in topology."
+                                   .format(interface))
+        else:
+            raise TypeError("Type of interface argument must be integer"
+                            " or string.")
+        return key
+
+    @staticmethod
+    def convert_interface_reference(node, interface, wanted_format):
+        """Takes interface reference in any format
+        (name, link name, topology key or sw_if_index) and returns
+        its equivalent in the desired format.
+
+        :param node: Node in topology.
+        :param interface: Name, sw_if_index, link name or key of an interface
+        on the node.
+        :param wanted_format: Format of return value wanted.
+        Valid options are: sw_if_index, key, name.
+        :type node: dict
+        :type interface: str or int
+        :type wanted_format: str
+
+        :return: Interface name, interface key or sw_if_index.
+        :rtype: str or int
+
+        :raises TypeError, ValueError: If provided with invalid arguments.
+        :raises RuntimeError: If the interface does not exist in topology.
+        """
+
+        key = Topology.convert_interface_reference_to_key(node, interface)
+
+        conversions = {
+            "key": lambda x, y: y,
+            "name": Topology.get_interface_name,
+            "sw_if_index": Topology.get_interface_sw_index
+        }
+
+        try:
+            return conversions[wanted_format](node, key)
+        except KeyError:
+            raise ValueError("Unrecognized return value wanted: {0}."
+                             "Valid options are key, name, sw_if_index"
+                             .format(wanted_format))
+
+    @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 +570,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.
@@ -451,7 +617,7 @@ class Topology(object):
                         if filt == interface['model']:
                             link_names.append(interface['link'])
                 elif (filter_list is not None) and ('model' not in interface):
-                    logger.trace("Cannot apply filter on interface: {}" \
+                    logger.trace("Cannot apply filter on interface: {}"
                                  .format(str(interface)))
                 else:
                     link_names.append(interface['link'])
@@ -471,8 +637,8 @@ class Topology(object):
         :param filter_list_node2: Link filter criteria for node2.
         :type node1: dict
         :type node2: dict
-        :type filter_list1: list of strings
-        :type filter_list2: list of strings
+        :type filter_list_node1: list of strings
+        :type filter_list_node2: list of strings
         :return: List of strings that represent connecting link names.
         :rtype: list
         """
@@ -515,7 +681,8 @@ class Topology(object):
         else:
             return connecting_links[0]
 
-    @keyword('Get egress interfaces name on "${node1}" for link with "${node2}"')
+    @keyword('Get egress interfaces name on "${node1}" for link with '
+             '"${node2}"')
     def get_egress_interfaces_name_for_nodes(self, node1, node2):
         """Get egress interfaces on node1 for link with node2.
 
@@ -623,3 +790,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