add new topology parameter: arch
[csit.git] / resources / libraries / python / topology.py
index 7e94007..13dbddd 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2016 Cisco and/or its affiliates.
+# Copyright (c) 2018 Cisco and/or its affiliates.
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
 # You may obtain a copy of the License at:
 
 """Defines nodes and topology structure."""
 
+from collections import Counter
+
 from yaml import load
 
 from robot.api import logger
-from robot.libraries.BuiltIn import BuiltIn
+from robot.libraries.BuiltIn import BuiltIn, RobotNotRunningError
 from robot.api.deco import keyword
 
-__all__ = ["DICT__nodes", 'Topology']
+__all__ = ["DICT__nodes", 'Topology', 'NodeType']
 
 
 def load_topo_from_yaml():
     """Load topology from file defined in "${TOPOLOGY_PATH}" variable.
 
-    :return: Nodes from loaded topology.
+    :returns: Nodes from loaded topology.
     """
-    topo_path = BuiltIn().get_variable_value("${TOPOLOGY_PATH}")
+    try:
+        topo_path = BuiltIn().get_variable_value("${TOPOLOGY_PATH}")
+    except RobotNotRunningError:
+        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 +51,7 @@ class NodeType(object):
 
 
 class NodeSubTypeTG(object):
+    """Defines node sub-type TG - traffic generator."""
     # T-Rex traffic generator
     TREX = 'TREX'
     # Moongen
@@ -58,9 +66,189 @@ class Topology(object):
     """Topology data manipulation and extraction methods.
 
     Defines methods used for manipulation and extraction of data from
-    the used topology.
+    the active topology.
+
+    "Active topology" contains initially data from the topology file and can be
+    extended with additional data from the DUTs like internal interface indexes
+    or names. Additional data which can be filled to the active topology are
+        - additional internal representation (index, name, ...)
+        - operational data (dynamic ports)
+
+    To access the port data it is recommended to use a port key because the key
+    does not rely on the data retrieved from nodes, this allows to call most of
+    the methods without having filled active topology with internal nodes data.
     """
 
+    @staticmethod
+    def add_new_port(node, ptype):
+        """Add new port to the node to active topology.
+
+        :param node: Node to add new port on.
+        :param ptype: Port type, used as key prefix.
+        :type node: dict
+        :type ptype: str
+        :returns: Port key or None
+        :rtype: string or None
+        """
+        max_ports = 1000000
+        iface = None
+        for i in range(1, max_ports):
+            if node['interfaces'].get(str(ptype) + str(i)) is None:
+                iface = str(ptype) + str(i)
+                node['interfaces'][iface] = dict()
+                break
+        return iface
+
+    @staticmethod
+    def remove_port(node, iface_key):
+        """Remove required port from active topology.
+
+        :param node: Node to remove port on.
+        :param: iface_key: Topology key of the interface.
+        :type node: dict
+        :type iface_key: str
+        :returns: Nothing
+        """
+        try:
+            node['interfaces'].pop(iface_key)
+        except KeyError:
+            pass
+
+    @staticmethod
+    def remove_all_ports(node, ptype):
+        """Remove all ports with ptype as prefix.
+
+        :param node: Node to remove ports on.
+        :param: ptype: Port type, used as key prefix.
+        :type node: dict
+        :type ptype: str
+        :returns: Nothing
+        """
+        for if_key in list(node['interfaces']):
+            if if_key.startswith(str(ptype)):
+                node['interfaces'].pop(if_key)
+
+    @staticmethod
+    def remove_all_added_ports_on_all_duts_from_topology(nodes):
+        """Remove all added ports on all DUT nodes in the topology.
+
+        :param nodes: Nodes in the topology.
+        :type nodes: dict
+        :returns: Nothing
+        """
+        port_types = ('subinterface', 'vlan_subif', 'memif', 'tap', 'vhost',
+                      'loopback', 'gre_tunnel', 'vxlan_tunnel')
+
+        for node_data in nodes.values():
+            if node_data['type'] == NodeType.DUT:
+                for ptype in port_types:
+                    Topology.remove_all_ports(node_data, ptype)
+
+    @staticmethod
+    def update_interface_sw_if_index(node, iface_key, sw_if_index):
+        """Update sw_if_index on the interface from the node.
+
+        :param node: Node to update sw_if_index on.
+        :param iface_key: Topology key of the interface.
+        :param sw_if_index: Internal index to store.
+        :type node: dict
+        :type iface_key: str
+        :type sw_if_index: int
+        """
+        node['interfaces'][iface_key]['vpp_sw_index'] = int(sw_if_index)
+
+    @staticmethod
+    def update_interface_name(node, iface_key, name):
+        """Update name on the interface from the node.
+
+        :param node: Node to update name on.
+        :param iface_key: Topology key of the interface.
+        :param name: Interface name to store.
+        :type node: dict
+        :type iface_key: str
+        :type name: str
+        """
+        node['interfaces'][iface_key]['name'] = str(name)
+
+    @staticmethod
+    def update_interface_mac_address(node, iface_key, mac_address):
+        """Update mac_address on the interface from the node.
+
+        :param node: Node to update MAC on.
+        :param iface_key: Topology key of the interface.
+        :param mac_address: MAC address.
+        :type node: dict
+        :type iface_key: str
+        :type mac_address: str
+        """
+        node['interfaces'][iface_key]['mac_address'] = str(mac_address)
+
+    @staticmethod
+    def update_interface_vhost_socket(node, iface_key, vhost_socket):
+        """Update vhost socket name on the interface from the node.
+
+        :param node: Node to update socket name on.
+        :param iface_key: Topology key of the interface.
+        :param vhost_socket: Path to named socket on node.
+        :type node: dict
+        :type iface_key: str
+        :type vhost_socket: str
+        """
+        node['interfaces'][iface_key]['vhost_socket'] = str(vhost_socket)
+
+    @staticmethod
+    def update_interface_memif_socket(node, iface_key, memif_socket):
+        """Update memif socket name on the interface from the node.
+
+        :param node: Node to update socket name on.
+        :param iface_key: Topology key of the interface.
+        :param memif_socket: Path to named socket on node.
+        :type node: dict
+        :type iface_key: str
+        :type memif_socket: str
+        """
+        node['interfaces'][iface_key]['memif_socket'] = str(memif_socket)
+
+    @staticmethod
+    def update_interface_memif_id(node, iface_key, memif_id):
+        """Update memif ID on the interface from the node.
+
+        :param node: Node to update memif ID on.
+        :param iface_key: Topology key of the interface.
+        :param memif_id: Memif interface ID.
+        :type node: dict
+        :type iface_key: str
+        :type memif_id: str
+        """
+        node['interfaces'][iface_key]['memif_id'] = str(memif_id)
+
+    @staticmethod
+    def update_interface_memif_role(node, iface_key, memif_role):
+        """Update memif role on the interface from the node.
+
+        :param node: Node to update memif role on.
+        :param iface_key: Topology key of the interface.
+        :param memif_role: Memif role.
+        :type node: dict
+        :type iface_key: str
+        :type memif_role: str
+        """
+        node['interfaces'][iface_key]['memif_role'] = str(memif_role)
+
+    @staticmethod
+    def update_interface_tap_dev_name(node, iface_key, dev_name):
+        """Update device name on the tap interface from the node.
+
+        :param node: Node to update tap device name on.
+        :param iface_key: Topology key of the interface.
+        :param dev_name: Device name of the tap interface.
+        :type node: dict
+        :type iface_key: str
+        :type dev_name: str
+        :returns: Nothing
+        """
+        node['interfaces'][iface_key]['dev_name'] = str(dev_name)
+
     @staticmethod
     def get_node_by_hostname(nodes, hostname):
         """Get node from nodes of the topology by hostname.
@@ -69,7 +257,7 @@ class Topology(object):
         :param hostname: Host name.
         :type nodes: dict
         :type hostname: str
-        :return: Node dictionary or None if not found.
+        :returns: Node dictionary or None if not found.
         """
         for node in nodes.values():
             if node['host'] == hostname:
@@ -83,7 +271,7 @@ class Topology(object):
 
         :param nodes: Nodes of the test topology.
         :type nodes: dict
-        :return: Links in the topology.
+        :returns: Links in the topology.
         :rtype: list
         """
         links = []
@@ -99,35 +287,59 @@ class Topology(object):
 
     @staticmethod
     def _get_interface_by_key_value(node, key, value):
-        """Return node interface name according to key and value.
+        """Return node interface key from topology file
+        according to key and value.
 
         :param node: The node dictionary.
         :param key: Key by which to select the interface.
         :param value: Value that should be found using the key.
-        :return:
+        :type node: dict
+        :type key: string
+        :type value: string
+        :returns: Interface key from topology file
+        :rtype: string
         """
         interfaces = node['interfaces']
         retval = None
-        for interface in interfaces.values():
-            k_val = interface.get(key)
+        for if_key, if_val in interfaces.iteritems():
+            k_val = if_val.get(key)
             if k_val is not None:
                 if k_val == value:
-                    retval = interface['name']
+                    retval = if_key
                     break
         return retval
 
-    def get_interface_by_link_name(self, node, link_name):
-        """Return interface name of link on node.
+    @staticmethod
+    def get_interface_by_name(node, iface_name):
+        """Return interface key based on name from DUT/TG.
+
+        This method returns interface key based on interface name
+        retrieved from the DUT, or TG.
+
+        :param node: The node topology dictionary.
+        :param iface_name: Interface name (string form).
+        :type node: dict
+        :type iface_name: string
+        :returns: Interface key.
+        :rtype: str
+        """
+        return Topology._get_interface_by_key_value(node, "name", iface_name)
+
+    @staticmethod
+    def get_interface_by_link_name(node, link_name):
+        """Return interface key of link on node.
 
         This method returns the interface name associated with a given link
         for a given node.
 
-        :param link_name: Name of the link that a interface is connected to.
         :param node: The node topology dictionary.
-        :return: Interface name of the interface connected to the given link.
+        :param link_name: Name of the link that a interface is connected to.
+        :type node: dict
+        :type link_name: string
+        :returns: Interface key of the interface connected to the given link.
         :rtype: str
         """
-        return self._get_interface_by_key_value(node, "link", link_name)
+        return Topology._get_interface_by_key_value(node, "link", link_name)
 
     def get_interfaces_by_link_names(self, node, link_names):
         """Return dictionary of dictionaries {"interfaceN", interface name}.
@@ -135,10 +347,12 @@ class Topology(object):
         This method returns the interface names associated with given links
         for a given node.
 
+        :param node: The node topology directory.
         :param link_names: List of names of the link that a interface is
         connected to.
-        :param node: The node topology directory.
-        :return: Dictionary of interface names that are connected to the given
+        :type node: dict
+        :type link_names: list
+        :returns: Dictionary of interface names that are connected to the given
         links.
         :rtype: dict
         """
@@ -146,155 +360,282 @@ class Topology(object):
         interface_key_tpl = "interface{}"
         interface_number = 1
         for link_name in link_names:
-            interface_name = self.get_interface_by_link_name(node, link_name)
+            interface = self.get_interface_by_link_name(node, link_name)
+            interface_name = self.get_interface_name(node, interface)
             interface_key = interface_key_tpl.format(str(interface_number))
             retval[interface_key] = interface_name
             interface_number += 1
         return retval
 
-    def get_interface_by_sw_index(self, node, sw_index):
+    @staticmethod
+    def get_interface_by_sw_index(node, sw_index):
         """Return interface name of link on node.
 
         This method returns the interface name associated with a software
         interface index assigned to the interface by vpp for a given node.
 
-        :param sw_index: Sw_index of the link that a interface is connected to.
         :param node: The node topology dictionary.
-        :return: Interface name of the interface connected to the given link.
+        :param sw_index: Sw_index of the link that a interface is connected to.
+        :type node: dict
+        :type sw_index: int
+        :returns: Interface name of the interface connected to the given link.
         :rtype: str
         """
-        return self._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, interface):
-        """Get VPP sw_if_index for the interface.
+    def get_interface_sw_index(node, iface_key):
+        """Get VPP sw_if_index for the interface using interface key.
 
         :param node: Node to get interface sw_if_index on.
-        :param interface: Interface identifier.
+        :param iface_key: Interface key from topology file, or sw_index.
         :type node: dict
-        :type interface: str or int
-        :return: Return sw_if_index or None if not found.
+        :type iface_key: str/int
+        :returns: Return sw_if_index or None if not found.
         """
         try:
-            return int(interface)
-        except ValueError:
-            for port in node['interfaces'].values():
-                port_name = port.get('name')
-                if port_name == interface:
-                    return port.get('vpp_sw_index')
+            if isinstance(iface_key, basestring):
+                return node['interfaces'][iface_key].get('vpp_sw_index')
+            # TODO: use only iface_key, do not use integer
+            else:
+                return int(iface_key)
+        except (KeyError, ValueError):
             return None
 
     @staticmethod
-    def get_interface_mtu(node, interface):
+    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
+        :returns: 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.
 
         Returns physical layer MTU (max. size of Ethernet frame).
         :param node: Node to get interface MTU on.
-        :param interface: Interface name.
+        :param iface_key: Interface key from topology file.
         :type node: dict
-        :type interface: str
-        :return: MTU or None if not found.
+        :type iface_key: str
+        :returns: MTU or None if not found.
         :rtype: int
         """
-        for port in node['interfaces'].values():
-            port_name = port.get('name')
-            if port_name == interface:
-                return port.get('mtu')
+        try:
+            return node['interfaces'][iface_key].get('mtu')
+        except KeyError:
+            return None
 
-        return None
+    @staticmethod
+    def get_interface_name(node, iface_key):
+        """Get interface name (retrieved from DUT/TG).
+
+        Returns name in string format, retrieved from the node.
+        :param node: Node to get interface name on.
+        :param iface_key: Interface key from topology file.
+        :type node: dict
+        :type iface_key: str
+        :returns: Interface name or None if not found.
+        :rtype: str
+        """
+        try:
+            return node['interfaces'][iface_key].get('name')
+        except KeyError:
+            return None
 
     @staticmethod
-    def get_interface_mac_by_port_key(node, port_key):
-        """Get MAC address for the interface based on port key.
+    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
 
-        :param node: Node to get interface mac on.
-        :param port_key: Dictionary key name of interface.
+        :returns: 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 port_key: str
-        :return: Return MAC or None if not found.
+        :type interface: str or int
+        :type wanted_format: str
+
+        :returns: 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.
         """
-        for port_name, port_data in node['interfaces'].iteritems():
-            if port_name == port_key:
-                return port_data['mac_address']
 
-        return None
+        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_mac(node, interface):
-        """Get MAC address for the interface.
+    def get_interface_numa_node(node, iface_key):
+        """Get interface numa node.
 
-        :param node: Node to get interface sw_index on.
-        :param interface: Interface name.
+        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 interface: str
-        :return: Return MAC or None if not found.
+        :type iface_key: str
+        :returns: numa node id, None if not available.
+        :rtype: int
         """
-        for port in node['interfaces'].values():
-            port_name = port.get('name')
-            if port_name == interface:
-                return port.get('mac_address')
+        try:
+            return node['interfaces'][iface_key].get('numa_node')
+        except KeyError:
+            return None
 
-        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_adjacent_node_and_interface_by_key(nodes_info, node, port_key):
-        """Get node and interface adjacent to specified interface
-        on local network.
+    def get_interface_mac(node, iface_key):
+        """Get MAC address for the interface.
 
-        :param nodes_info: Dictionary containing information on all nodes
-        in topology.
-        :param node: Node that contains specified interface.
-        :param port_key: Interface port key.
-        :type nodes_info: dict
+        :param node: Node to get interface mac on.
+        :param iface_key: Interface key from topology file.
         :type node: dict
-        :type port_key: str
-        :return: Return (node, interface info) tuple or None if not found.
-        :rtype: (dict, dict)
+        :type iface_key: str
+        :returns: Return MAC or None if not found.
         """
-        link_name = None
-        # get link name where the interface belongs to
-        for port_name, port_data in node['interfaces'].iteritems():
-            if port_name == 'mgmt':
-                continue
-            if port_name == port_key:
-                link_name = port_data['link']
-                break
-
-        if link_name is None:
+        try:
+            return node['interfaces'][iface_key].get('mac_address')
+        except KeyError:
             return None
 
-        # find link
-        for node_data in nodes_info.values():
-            # skip self
-            if node_data['host'] == node['host']:
-                continue
-            for interface, interface_data \
-                    in node_data['interfaces'].iteritems():
-                if 'link' not in interface_data:
-                    continue
-                if interface_data['link'] == link_name:
-                    return node_data, node_data['interfaces'][interface]
+    @staticmethod
+    def get_interface_ip4(node, iface_key):
+        """Get IP4 address for the interface.
+
+        :param node: Node to get interface mac on.
+        :param iface_key: Interface key from topology file.
+        :type node: dict
+        :type iface_key: str
+        :returns: Return IP4 or None if not found.
+        """
+        try:
+            return node['interfaces'][iface_key].get('ip4_address', None)
+        except KeyError:
+            return None
 
     @staticmethod
-    def get_adjacent_node_and_interface(nodes_info, node, interface_name):
+    def get_adjacent_node_and_interface(nodes_info, node, iface_key):
         """Get node and interface adjacent to specified interface
         on local network.
 
         :param nodes_info: Dictionary containing information on all nodes
         in topology.
         :param node: Node that contains specified interface.
-        :param interface_name: Interface name.
+        :param iface_key: Interface key from topology file.
         :type nodes_info: dict
         :type node: dict
-        :type interface_name: str
-        :return: Return (node, interface info) tuple or None if not found.
-        :rtype: (dict, dict)
+        :type iface_key: str
+        :returns: Return (node, interface_key) tuple or None if not found.
+        :rtype: (dict, str)
         """
         link_name = None
         # get link name where the interface belongs to
-        for port_name, port_data in node['interfaces'].iteritems():
-            if port_data['name'] == interface_name:
-                link_name = port_data['link']
+        for if_key, if_val in node['interfaces'].iteritems():
+            if if_key == 'mgmt':
+                continue
+            if if_key == iface_key:
+                link_name = if_val['link']
                 break
 
         if link_name is None:
@@ -305,42 +646,53 @@ class Topology(object):
             # skip self
             if node_data['host'] == node['host']:
                 continue
-            for interface, interface_data \
+            for if_key, if_val \
                     in node_data['interfaces'].iteritems():
-                if 'link' not in interface_data:
+                if 'link' not in if_val:
                     continue
-                if interface_data['link'] == link_name:
-                    return node_data, node_data['interfaces'][interface]
+                if if_val['link'] == link_name:
+                    return node_data, if_key
 
     @staticmethod
-    def get_interface_pci_addr(node, interface):
+    def get_interface_pci_addr(node, iface_key):
         """Get interface PCI address.
 
         :param node: Node to get interface PCI address on.
-        :param interface: Interface name.
+        :param iface_key: Interface key from topology file.
         :type node: dict
-        :type interface: str
-        :return: Return PCI address or None if not found.
+        :type iface_key: str
+        :returns: Return PCI address or None if not found.
         """
-        for port in node['interfaces'].values():
-            if interface == port.get('name'):
-                return port.get('pci_address')
-        return None
+        try:
+            return node['interfaces'][iface_key].get('pci_address')
+        except KeyError:
+            return None
 
     @staticmethod
-    def get_interface_driver(node, interface):
+    def get_interface_driver(node, iface_key):
         """Get interface driver.
 
         :param node: Node to get interface driver on.
-        :param interface: Interface name.
+        :param iface_key: Interface key from topology file.
         :type node: dict
-        :type interface: str
-        :return: Return interface driver or None if not found.
+        :type iface_key: str
+        :returns: Return interface driver or None if not found.
         """
-        for port in node['interfaces'].values():
-            if interface == port.get('name'):
-                return port.get('driver')
-        return None
+        try:
+            return node['interfaces'][iface_key].get('driver')
+        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
+        :returns: Return list of keys of all interfaces.
+        :rtype: list
+        """
+        return node['interfaces'].keys()
 
     @staticmethod
     def get_node_link_mac(node, link_name):
@@ -350,7 +702,7 @@ class Topology(object):
         :param link_name: Link name.
         :type node: dict
         :type link_name: str
-        :return: MAC address string.
+        :returns: MAC address string.
         :rtype: str
         """
         for port in node['interfaces'].values():
@@ -366,7 +718,7 @@ class Topology(object):
         :param filter_list: Link filter criteria.
         :type node: dict
         :type filter_list: list of strings
-        :return: List of strings that represent link names occupied by the node.
+        :returns: List of strings representing link names occupied by the node.
         :rtype: list
         """
         interfaces = node['interfaces']
@@ -378,7 +730,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'])
@@ -398,9 +750,9 @@ 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
-        :return: List of strings that represent connecting link names.
+        :type filter_list_node1: list of strings
+        :type filter_list_node2: list of strings
+        :returns: List of strings that represent connecting link names.
         :rtype: list
         """
 
@@ -432,7 +784,7 @@ class Topology(object):
         :param node2: Connected node.
         :type node1: dict
         :type node2: dict
-        :return: Name of link connecting the two nodes together.
+        :returns: Name of link connecting the two nodes together.
         :rtype: str
         :raises: RuntimeError
         """
@@ -442,15 +794,16 @@ class Topology(object):
         else:
             return connecting_links[0]
 
-    @keyword('Get egress interfaces on "${node1}" for link with "${node2}"')
-    def get_egress_interfaces_for_nodes(self, node1, 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.
 
         :param node1: First node, node to get egress interface on.
         :param node2: Second node.
         :type node1: dict
         :type node2: dict
-        :return: Egress interfaces.
+        :returns: Egress interfaces.
         :rtype: list
         """
         interfaces = []
@@ -469,19 +822,19 @@ class Topology(object):
             interfaces.append(name)
         return interfaces
 
-    @keyword('Get first egress interface on "${node1}" for link with '
+    @keyword('Get first egress interface name on "${node1}" for link with '
              '"${node2}"')
     def get_first_egress_interface_for_nodes(self, node1, node2):
         """Get first egress interface on node1 for link with node2.
 
-        :param node1: First node, node to get egress interface on.
+        :param node1: First node, node to get egress interface name on.
         :param node2: Second node.
         :type node1: dict
         :type node2: dict
-        :return: Egress interface.
+        :returns: Egress interface name.
         :rtype: str
         """
-        interfaces = self.get_egress_interfaces_for_nodes(node1, node2)
+        interfaces = self.get_egress_interfaces_name_for_nodes(node1, node2)
         if not interfaces:
             raise RuntimeError('No egress interface for nodes')
         return interfaces[0]
@@ -511,7 +864,7 @@ class Topology(object):
         :type tgen: dict
         :type dut1: dict
         :type dut2: dict
-        :return: Dictionary of possible link combinations.
+        :returns: Dictionary of possible link combinations.
         :rtype: dict
         """
         # TODO: replace with generic function.
@@ -535,7 +888,7 @@ class Topology(object):
 
         :param node: Node to examine.
         :type node: dict
-        :return: True if node is type of TG, otherwise False.
+        :returns: True if node is type of TG, otherwise False.
         :rtype: bool
         """
         return node['type'] == NodeType.TG
@@ -546,7 +899,67 @@ class Topology(object):
 
         :param node: Node created from topology.
         :type node: dict
-        :return: Hostname or IP address.
+        :returns: Hostname or IP address.
         :rtype: str
         """
         return node['host']
+
+    @staticmethod
+    def get_node_arch(node):
+        """Return arch of the node.
+           Default to x86_64 if no arch present
+
+        :param node: Node created from topology.
+        :type node: dict
+        :returns: Node architecture
+        :rtype: str
+        """
+        try:
+            return node['arch']
+        except KeyError:
+            node['arch'] = 'x86_64'
+            return 'x86_64'
+
+    @staticmethod
+    def get_cryptodev(node):
+        """Return Crytodev configuration of the node.
+
+        :param node: Node created from topology.
+        :type node: dict
+        :returns: Cryptodev configuration string.
+        :rtype: str
+        """
+        try:
+            return node['cryptodev']
+        except KeyError:
+            return None
+
+    @staticmethod
+    def get_uio_driver(node):
+        """Return uio-driver configuration of the node.
+
+        :param node: Node created from topology.
+        :type node: dict
+        :returns: uio-driver configuration string.
+        :rtype: str
+        """
+        try:
+            return node['uio_driver']
+        except KeyError:
+            return None
+
+    @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
+        :returns: 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