feat(ipsec): Use strings instead of enums in Robot
[csit.git] / resources / libraries / python / NATUtil.py
index 422feae..e5f530a 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2020 Cisco and/or its affiliates.
+# Copyright (c) 2023 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:
@@ -62,23 +62,17 @@ class NATUtil:
         pass
 
     @staticmethod
-    def enable_nat44_plugin(
-            node, inside_vrf=0, outside_vrf=0, users=0, user_memory=0,
-            sessions=0, session_memory=0, user_sessions=0, mode=u""):
+    def enable_nat44_ed_plugin(
+            node, inside_vrf=0, outside_vrf=0, sessions=0, session_memory=0,
+            mode=u""):
         """Enable NAT44 plugin.
 
         :param node: DUT node.
         :param inside_vrf: Inside VRF ID.
         :param outside_vrf: Outside VRF ID.
-        :param users: Maximum number of users. Used only in endpoint-independent
-            mode.
-        :param user_memory: User memory size - overwrite auto calculated hash
-            allocation parameter if non-zero.
         :param sessions: Maximum number of sessions.
         :param session_memory: Session memory size - overwrite auto calculated
             hash allocation parameter if non-zero.
-        :param user_sessions: Maximum number of sessions per user. Used only in
-            endpoint-independent mode.
         :param mode: NAT44 mode. Valid values:
             - endpoint-independent
             - endpoint-dependent
@@ -88,24 +82,18 @@ class NATUtil:
         :type node: dict
         :type inside_vrf: str or int
         :type outside_vrf: str or int
-        :type users: str or int
-        :type user_memory: str or int
         :type sessions: str or int
         :type session_memory: str or int
-        :type user_sessions: str or int
         :type mode: str
         """
-        cmd = u"nat44_plugin_enable_disable"
+        cmd = u"nat44_ed_plugin_enable_disable"
         err_msg = f"Failed to enable NAT44 plugin on the host {node[u'host']}!"
         args_in = dict(
             enable=True,
             inside_vrf=int(inside_vrf),
             outside_vrf=int(outside_vrf),
-            users=int(users),
-            user_memory=int(user_memory),
             sessions=int(sessions),
             session_memory=int(session_memory),
-            user_sessions=int(user_sessions),
             flags=getattr(
                 Nat44ConfigFlags,
                 f"NAT44_IS_{mode.replace(u'-', u'_').upper()}"
@@ -194,27 +182,26 @@ class NATUtil:
             """Delete and re-add the NAT range setting."""
             with PapiSocketExecutor(node) as papi_exec:
                 args_in[u"is_add"] = False
-                papi_exec.add(cmd, **args_in)
+                papi_exec.add(cmd, **args_in).get_reply(err_msg)
                 args_in[u"is_add"] = True
-                papi_exec.add(cmd, **args_in)
-                papi_exec.get_replies(err_msg)
+                papi_exec.add(cmd, **args_in).get_reply(err_msg)
 
         return resetter
 
     @staticmethod
-    def show_nat_config(node):
-        """Show the NAT configuration.
+    def show_nat44_config(node):
+        """Show the NAT44 plugin running configuration.
 
         :param node: DUT node.
         :type node: dict
         """
-        cmd = u"nat_show_config"
-        err_msg = f"Failed to get NAT configuration on host {node[u'host']}"
+        cmd = u"nat44_show_running_config"
+        err_msg = f"Failed to get NAT44 configuration on host {node[u'host']}"
 
         with PapiSocketExecutor(node) as papi_exec:
             reply = papi_exec.add(cmd).get_reply(err_msg)
 
-        logger.debug(f"NAT Configuration:\n{pformat(reply)}")
+        logger.debug(f"NAT44 Configuration:\n{pformat(reply)}")
 
     @staticmethod
     def show_nat44_summary(node):
@@ -282,34 +269,77 @@ class NATUtil:
         :rtype: int
         """
         # vpp-device tests have not dedicated physical core so
-        # ${thr_count_int} == 0 but we need to use one thread
+        # ${dp_count_int} == 0 but we need to use one thread
         threads = 1 if not int(threads) else int(threads)
         rest, mult = modf(log2(sessions/(10*threads)))
         return 2 ** (int(mult) + (1 if rest else 0)) * 10
 
     @staticmethod
     def get_nat44_sessions_number(node, proto):
-        """Get number of established NAT44 sessions from actual NAT44 mapping
-        data.
+        """Get number of expected NAT44 sessions from NAT44 mapping data.
+
+        This keyword uses output from a CLI command,
+        so it can start failing when VPP changes the output format.
+        TODO: Switch to API (or stat segment) when available.
+
+        The current implementation supports both 2202 and post-2202 format.
+        (The Gerrit number changing the output format is 34877.)
+
+        For TCP proto, the expected state after rampup is
+        some number of sessions in transitory state (VPP has seen the FINs),
+        and some number of sessions in established state (meaning
+        some FINs were lost in the last trial).
+        While the two states may need slightly different number of cycles
+        to process next packet, the current implementation considers
+        both of them the "fast path", so they are both counted as expected.
+
+        As the tests should fail if a session is timed-out,
+        the logic substracts timed out sessions for the returned value
+        (only available for post-2202 format).
+
+        TODO: Investigate if it is worth to insert additional rampup trials
+        in TPUT tests to ensure all sessions are transitory before next
+        measurement.
 
         :param node: DUT node.
         :param proto: Required protocol - TCP/UDP/ICMP.
         :type node: dict
         :type proto: str
-        :returns: Number of established NAT44 sessions.
+        :returns: Number of active established NAT44 sessions.
         :rtype: int
         :raises ValueError: If not supported protocol.
+        :raises RuntimeError: If output is not formatted as expected.
         """
-        nat44_data = dict()
-        if proto in [u"UDP", u"TCP", u"ICMP"]:
-            for line in NATUtil.show_nat44_summary(node).splitlines():
-                sum_k, sum_v = line.split(u":") if u":" in line \
-                    else (line, None)
-                nat44_data[sum_k] = sum_v.strip() if isinstance(sum_v, str) \
-                    else sum_v
-        else:
+        proto_l = proto.strip().lower()
+        if proto_l not in [u"udp", u"tcp", u"icmp"]:
             raise ValueError(f"Unsupported protocol: {proto}!")
-        return nat44_data.get(f"total {proto.lower()} sessions", 0)
+        summary_text = NATUtil.show_nat44_summary(node)
+        summary_lines = summary_text.splitlines()
+        # Output from VPP v22.02 and before, delete when no longer needed.
+        pattern_2202 = f"total {proto_l} sessions:"
+        if pattern_2202 in summary_text:
+            for line in summary_lines:
+                if pattern_2202 not in line:
+                    continue
+                return int(line.split(u":", 1)[1].strip())
+        # Post-2202, the proto info and session info are not on the same line.
+        found = False
+        for line in summary_lines:
+            if not found:
+                if f"{proto_l} sessions:" in line:
+                    found = True
+                continue
+            # Proto is found, find the line we are interested in.
+            if u"total" not in line:
+                raise RuntimeError(f"show nat summary: no {proto} total.")
+            # We have the line with relevant numbers.
+            total_part, timed_out_part = line.split(u"(", 1)
+            timed_out_part = timed_out_part.split(u")", 1)[0]
+            total_count = int(total_part.split(u":", 1)[1].strip())
+            timed_out_count = int(timed_out_part.split(u":", 1)[1].strip())
+            active_count = total_count - timed_out_count
+            return active_count
+        raise RuntimeError(u"Unknown format of show nat44 summary")
 
     # DET44 PAPI calls
     # DET44 means deterministic mode of NAT44
@@ -396,10 +426,9 @@ class NATUtil:
             """Delete and re-add the deterministic NAT mapping."""
             with PapiSocketExecutor(node) as papi_exec:
                 args_in[u"is_add"] = False
-                papi_exec.add(cmd, **args_in)
+                papi_exec.add(cmd, **args_in).get_reply(err_msg)
                 args_in[u"is_add"] = True
-                papi_exec.add(cmd, **args_in)
-                papi_exec.get_replies(err_msg)
+                papi_exec.add(cmd, **args_in).get_reply(err_msg)
 
         return resetter