Add VXLAN test
[csit.git] / resources / libraries / python / topology.py
index 522de37..e2c069f 100644 (file)
@@ -26,7 +26,7 @@ __all__ = ["DICT__nodes", 'Topology']
 
 
 def load_topo_from_yaml():
-    """Loads topology from file defined in "${TOPOLOGY_PATH}" variable
+    """Load topology from file defined in "${TOPOLOGY_PATH}" variable
 
     :return: nodes from loaded topology
     """
@@ -43,6 +43,15 @@ class NodeType(object):
     # Traffic Generator (this node has traffic generator on it)
     TG = 'TG'
 
+
+class NodeSubTypeTG(object):
+    #T-Rex traffic generator
+    TREX = 'TREX'
+    # Moongen
+    MOONGEN = 'MOONGEN'
+    # IxNetwork
+    IXNET = 'IXNET'
+
 DICT__nodes = load_topo_from_yaml()
 
 
@@ -53,9 +62,6 @@ class Topology(object):
     the used topology.
     """
 
-    def __init__(self):
-        pass
-
     @staticmethod
     def get_node_by_hostname(nodes, hostname):
         """Get node from nodes of the topology by hostname.
@@ -94,7 +100,7 @@ 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 name according to key and value
 
         :param node: :param node: the node dictionary
         :param key: key by which to select the interface.
@@ -176,7 +182,7 @@ class Topology(object):
         return list_mac
 
     def _extract_vpp_interface_by_mac(self, interfaces_list, mac_address):
-        """Returns interface dictionary from interface_list by mac address.
+        """Return interface dictionary from interface_list by mac address.
 
         Extracts interface dictionary from all of the interfaces in interfaces
         list parsed from json according to mac_address of the interface
@@ -187,7 +193,7 @@ class Topology(object):
 
         interface_dict = {}
         list_mac_address = self.convert_mac_to_number_list(mac_address)
-        logger.trace(list_mac_address.__str__())
+        logger.trace(str(list_mac_address))
         for interface in interfaces_list:
             # TODO: create vat json integrity checking and move there
             if "l2_address" not in interface:
@@ -230,7 +236,7 @@ class Topology(object):
         return interface_name
 
     def _update_node_interface_data_from_json(self, node, interface_dump_json):
-        """ Update node vpp data in node__DICT from json interface dump.
+        """Update node vpp data in node__DICT from json interface dump.
 
         This method updates vpp interface names and sw indexexs according to
         interface mac addresses found in interface_dump_json
@@ -246,6 +252,9 @@ class Topology(object):
             if_mac = ifc['mac_address']
             interface_dict = self._extract_vpp_interface_by_mac(interface_list,
                                                                 if_mac)
+            if not interface_dict:
+                raise Exception('Interface {0} not found by MAC {1}'.
+                        format(ifc, if_mac))
             ifc['name'] = interface_dict["interface_name"]
             ifc['vpp_sw_index'] = interface_dict["sw_if_index"]
 
@@ -307,7 +316,7 @@ class Topology(object):
         InterfaceSetup.tg_set_interfaces_udev_rules(node)
 
     def update_all_interface_data_on_all_nodes(self, nodes):
-        """ Update interface names on all nodes in DICT__nodes
+        """Update interface names on all nodes in DICT__nodes
 
         :param nodes: Nodes in the topology.
         :type nodes: dict
@@ -315,7 +324,7 @@ class Topology(object):
         This method updates the topology dictionary by querying interface lists
         of all nodes mentioned in the topology dictionary.
         It does this by dumping interface list to json output from all devices
-        using vpe_api_test, and pairing known information from topology
+        using vpp_api_test, and pairing known information from topology
         (mac address/pci address of interface) to state from VPP.
         For TG/linux nodes add interface name only.
         """
@@ -345,6 +354,22 @@ class Topology(object):
 
         return None
 
+    @staticmethod
+    def get_interface_mac_by_port_key(node, port_key):
+        """Get MAC address for the interface based on port key.
+
+        :param node: Node to get interface mac on.
+        :param port_key: Dictionary key name of interface.
+        :type node: dict
+        :type port_key: str
+        :return: Return MAC or None if not found.
+        """
+        for port_name, port_data in node['interfaces'].iteritems():
+            if port_name == port_key:
+                return port_data['mac_address']
+
+        return None
+
     @staticmethod
     def get_interface_mac(node, interface):
         """Get MAC address for the interface.
@@ -365,19 +390,64 @@ class Topology(object):
         return None
 
     @staticmethod
-    def get_adjacent_interface(node, interface_name):
-        """Get interface adjacent to specified interface on local network.
-
-           :param node: Node that contains specified interface.
-           :param interface_name: Interface name.
-           :type node: dict
-           :type interface_name: str
-           :return: Return interface or None if not found.
-           :rtype: dict
+    def get_adjacent_node_and_interface_by_key(nodes_info, node, port_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 port_key: Interface port key.
+        :type nodes_info: dict
+        :type node: dict
+        :type port_key: str
+        :return: Return (node, interface info) tuple or None if not found.
+        :rtype: (dict, dict)
         """
         link_name = None
         # get link name where the interface belongs to
-        for _, port_data in node['interfaces'].iteritems():
+        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: 
+            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_adjacent_node_and_interface(nodes_info, node, interface_name):
+        """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.
+        :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)
+        """
+        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_data['name'] == interface_name:
                 link_name = port_data['link']
                 break
@@ -386,7 +456,7 @@ class Topology(object):
             return None
 
         # find link
-        for _, node_data in DICT__nodes.iteritems():
+        for node_data in nodes_info.values():
             # skip self
             if node_data['host'] == node['host']:
                 continue
@@ -395,7 +465,7 @@ class Topology(object):
                 if 'link' not in interface_data:
                     continue
                 if interface_data['link'] == link_name:
-                    return node_data['interfaces'][interface]
+                    return node_data, node_data['interfaces'][interface]
 
     @staticmethod
     def get_interface_pci_addr(node, interface):
@@ -444,7 +514,7 @@ class Topology(object):
 
     @staticmethod
     def _get_node_active_link_names(node):
-        """Returns list of link names that are other than mgmt links
+        """Return list of link names that are other than mgmt links
 
         :param node: node topology dictionary
         :return: list of strings that represent link names occupied by the node
@@ -460,7 +530,7 @@ class Topology(object):
 
     @keyword('Get active links connecting "${node1}" and "${node2}"')
     def get_active_connecting_links(self, node1, node2):
-        """Returns list of link names that connect together node1 and node2
+        """Return list of link names that connect together node1 and node2
 
         :param node1: node topology dictionary
         :param node2: node topology dictionary
@@ -502,7 +572,7 @@ class Topology(object):
         :param node2: Second node.
         :type node1: dict
         :type node2: dict
-        :return: Engress interfaces.
+        :return: Egress interfaces.
         :rtype: list
         """
         interfaces = []
@@ -530,10 +600,70 @@ class Topology(object):
         :param node2: Second node.
         :type node1: dict
         :type node2: dict
-        :return: Engress interface.
+        :return: Egress interface.
         :rtype: str
         """
         interfaces = self.get_egress_interfaces_for_nodes(node1, node2)
         if not interfaces:
-            raise RuntimeError('No engress interface for nodes')
+            raise RuntimeError('No egress interface for nodes')
         return interfaces[0]
+
+    @keyword('Get link data useful in circular topology test from tg "${tgen}"'
+             ' dut1 "${dut1}" dut2 "${dut2}"')
+    def get_links_dict_from_nodes(self, tgen, dut1, dut2):
+        """Return link combinations used in tests in circular topology.
+
+        For the time being it returns links from the Node path:
+        TG->DUT1->DUT2->TG
+        :param tgen: traffic generator node data
+        :param dut1: DUT1 node data
+        :param dut2: DUT2 node data
+        :type tgen: dict
+        :type dut1: dict
+        :type dut2: dict
+        :return: dictionary of possible link combinations
+        the naming convention until changed to something more general is
+        implemented is this:
+        DUT1_DUT2_LINK: link name between DUT! and DUT2
+        DUT1_TG_LINK: link name between DUT1 and TG
+        DUT2_TG_LINK: link name between DUT2 and TG
+        TG_TRAFFIC_LINKS: list of link names that generated traffic is sent
+        to and from
+        DUT1_BD_LINKS: list of link names that will be connected by the bridge
+        domain on DUT1
+        DUT2_BD_LINKS: list of link names that will be connected by the bridge
+        domain on DUT2
+        """
+        # TODO: replace with generic function.
+        dut1_dut2_link = self.get_first_active_connecting_link(dut1, dut2)
+        dut1_tg_link = self.get_first_active_connecting_link(dut1, tgen)
+        dut2_tg_link = self.get_first_active_connecting_link(dut2, tgen)
+        tg_traffic_links = [dut1_tg_link, dut2_tg_link]
+        dut1_bd_links = [dut1_dut2_link, dut1_tg_link]
+        dut2_bd_links = [dut1_dut2_link, dut2_tg_link]
+        topology_links = {'DUT1_DUT2_LINK': dut1_dut2_link,
+                          'DUT1_TG_LINK': dut1_tg_link,
+                          'DUT2_TG_LINK': dut2_tg_link,
+                          'TG_TRAFFIC_LINKS': tg_traffic_links,
+                          'DUT1_BD_LINKS': dut1_bd_links,
+                          'DUT2_BD_LINKS': dut2_bd_links}
+        return topology_links
+
+    @staticmethod
+    def is_tg_node(node):
+        """Find out whether the node is TG
+
+        :param node: node to examine
+        :return: True if node is type of TG; False otherwise
+        """
+        return node['type'] == NodeType.TG
+
+    @staticmethod
+    def get_node_hostname(node):
+        """Return host (hostname/ip address) of the node.
+
+        :param node: Node created from topology.
+        :type node: dict
+        :return: host as 'str' type
+        """
+        return node['host']