Revert "fix(IPsecUtil): Delete keywords no longer used"
[csit.git] / resources / libraries / python / topology.py
index ed87edf..22ed366 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2019 Cisco and/or its affiliates.
+# Copyright (c) 2024 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:
@@ -22,6 +22,8 @@ from yaml import safe_load
 from robot.api import logger
 from robot.libraries.BuiltIn import BuiltIn, RobotNotRunningError
 
+from resources.libraries.python.Constants import Constants
+
 __all__ = [
     u"DICT__nodes", u"Topology", u"NodeType", u"SocketType", u"NodeSubTypeTG"
 ]
@@ -69,6 +71,8 @@ class SocketType:
     PAPI = u"PAPI"
     # VPP PAPI Stats (legacy option until stats are migrated to Socket PAPI)
     STATS = u"STATS"
+    # VPP Socket CLI
+    CLI = u"CLI"
 
 
 DICT__nodes = load_topo_from_yaml()
@@ -171,7 +175,9 @@ class Topology:
         """
         port_types = (
             u"subinterface", u"vlan_subif", u"memif", u"tap", u"vhost",
-            u"loopback", u"gre_tunnel", u"vxlan_tunnel", u"eth_bond", u"eth_avf"
+            u"loopback", u"gre_tunnel", u"vxlan_tunnel", u"eth_bond",
+            u"eth_avf", u"eth_rdma", u"geneve_tunnel", u"eth_af_xdp",
+            u"gtpu_tunnel"
         )
 
         for node_data in nodes.values():
@@ -373,16 +379,19 @@ class Topology:
         return links
 
     @staticmethod
-    def _get_interface_by_key_value(node, key, value):
+    def _get_interface_by_key_value(node, key, value, subsequent=False):
         """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.
+        :param subsequent: Use second interface of the link. Useful for
+            back-to-back links. Default: False
         :type node: dict
         :type key: string
         :type value: string
+        :type subsequent: bool
         :returns: Interface key from topology file
         :rtype: string
         """
@@ -392,8 +401,11 @@ class Topology:
             k_val = if_val.get(key)
             if k_val is not None:
                 if k_val == value:
-                    retval = if_key
-                    break
+                    if subsequent:
+                        subsequent = False
+                    else:
+                        retval = if_key
+                        break
         return retval
 
     @staticmethod
@@ -413,7 +425,7 @@ class Topology:
         return Topology._get_interface_by_key_value(node, u"name", iface_name)
 
     @staticmethod
-    def get_interface_by_link_name(node, link_name):
+    def get_interface_by_link_name(node, link_name, subsequent=False):
         """Return interface key of link on node.
 
         This method returns the interface name associated with a given link
@@ -421,12 +433,17 @@ class Topology:
 
         :param node: The node topology dictionary.
         :param link_name: Name of the link that a interface is connected to.
+        :param subsequent: Use second interface of the link. Useful for
+            back-to-back links. Default: False
         :type node: dict
         :type link_name: string
+        :type subsequent: bool
         :returns: Interface key of the interface connected to the given link.
         :rtype: str
         """
-        return Topology._get_interface_by_key_value(node, u"link", link_name)
+        return Topology._get_interface_by_key_value(
+            node, u"link", link_name, subsequent=subsequent
+        )
 
     def get_interfaces_by_link_names(self, node, link_names):
         """Return dictionary of dictionaries {"interfaceN", interface name}.
@@ -703,6 +720,22 @@ class Topology:
         except KeyError:
             return None
 
+    @staticmethod
+    def get_interface_ip4_prefix_length(node, iface_key):
+        """Get IP4 address prefix length for the interface.
+
+        :param node: Node to get prefix length on.
+        :param iface_key: Interface key from topology file.
+        :type node: dict
+        :type iface_key: str
+        :returns: Prefix length from topology file or the default
+            IP4 prefix length if not found.
+        :rtype: int
+        :raises: KeyError if iface_key is not found.
+        """
+        return node[u"interfaces"][iface_key].get(u"ip4_prefix_length", \
+            Constants.DEFAULT_IP4_PREFIX_LENGTH)
+
     @staticmethod
     def get_adjacent_node_and_interface(nodes_info, node, iface_key):
         """Get node and interface adjacent to specified interface
@@ -733,7 +766,9 @@ class Topology:
         # find link
         for node_data in nodes_info.values():
             # skip self
-            if node_data[u"host"] == node[u"host"]:
+            l_hash = node_data[u"host"]+str(node_data[u"port"])
+            r_hash = node[u"host"]+str(node[u"port"])
+            if l_hash == r_hash:
                 continue
             for if_key, if_val \
                     in node_data[u"interfaces"].items():
@@ -816,13 +851,15 @@ class Topology:
         return None
 
     @staticmethod
-    def _get_node_active_link_names(node, filter_list=None):
+    def _get_node_active_link_names(node, filter_list=None, topo_has_dut=True):
         """Return list of link names that are other than mgmt links.
 
         :param node: Node topology dictionary.
         :param filter_list: Link filter criteria.
+        :param topo_has_dut: Whether we require back-to-back links.
         :type node: dict
         :type filter_list: list of strings
+        :type topo_has_dut: bool
         :returns: List of link names occupied by the node.
         :rtype: None or list of string
         """
@@ -842,6 +879,17 @@ class Topology:
                     link_names.append(interface[u"link"])
         if not link_names:
             link_names = None
+        if not topo_has_dut:
+            new_link_names = list()
+            for link_name in link_names:
+                count = 0
+                for interface in interfaces.values():
+                    link = interface.get(u"link", None)
+                    if link == link_name:
+                        count += 1
+                if count == 2:
+                    new_link_names.append(link_name)
+            link_names = new_link_names
         return link_names
 
     def get_active_connecting_links(
@@ -860,14 +908,19 @@ class Topology:
         :rtype: list
         """
 
-        logger.trace(f"node1: {str(node1)}")
-        logger.trace(f"node2: {str(node2)}")
-        node1_links = self._get_node_active_link_names(
-            node1, filter_list=filter_list_node1
-        )
-        node2_links = self._get_node_active_link_names(
-            node2, filter_list=filter_list_node2
-        )
+        if node1 != node2:
+            node1_links = self._get_node_active_link_names(
+                node1, filter_list=filter_list_node1
+            )
+            node2_links = self._get_node_active_link_names(
+                node2, filter_list=filter_list_node2
+            )
+        else:
+            # Looking for back-to-back links.
+            node1_links = self._get_node_active_link_names(
+                node1, filter_list=filter_list_node1, topo_has_dut=False
+            )
+            node2_links = node1_links
 
         connecting_links = None
         if node1_links is None:
@@ -875,7 +928,10 @@ class Topology:
         elif node2_links is None:
             logger.error(u"Unable to find active links for node2")
         else:
-            connecting_links = list(set(node1_links).intersection(node2_links))
+            # Not using set operations, as we need deterministic order.
+            connecting_links = [
+                link for link in node1_links if link in node2_links
+            ]
 
         return connecting_links
 
@@ -1031,6 +1087,19 @@ class Topology:
         except KeyError:
             return None
 
+    def get_bus(node):
+        """Return bus configuration of the node.
+
+        :param node: Node created from topology.
+        :type node: dict
+        :returns: bus configuration string.
+        :rtype: str
+        """
+        try:
+            return node[u"bus"]
+        except KeyError:
+            return None
+
     @staticmethod
     def get_uio_driver(node):
         """Return uio-driver configuration of the node.
@@ -1051,8 +1120,10 @@ class Topology:
 
         :param node: Node to set numa_node on.
         :param iface_key: Interface key from topology file.
+        :param numa_node_id: Num_node ID.
         :type node: dict
         :type iface_key: str
+        :type numa_node_id: int
         :returns: Return iface_key or None if not found.
         """
         try:
@@ -1117,4 +1188,5 @@ class Topology:
         """
         for node in nodes.values():
             if u"sockets" in list(node.keys()):
+                # Containers are disconnected and destroyed already.
                 node.pop(u"sockets")