Hoststack perf infrastructure refactoring
[csit.git] / resources / libraries / python / DUTSetup.py
index 3544997..431ccfb 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2019 Cisco and/or its affiliates.
+# Copyright (c) 2020 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:
 
 """DUT setup library."""
 
+from time import sleep
 from robot.api import logger
 
 from resources.libraries.python.Constants import Constants
-from resources.libraries.python.ssh import SSH, exec_cmd_no_error
+from resources.libraries.python.ssh import SSH, exec_cmd, exec_cmd_no_error
 from resources.libraries.python.topology import NodeType, Topology
 
 
@@ -132,6 +133,8 @@ class DUTSetup:
         :type node: dict
         :type service: str
         """
+        DUTSetup.get_service_logs(node, service)
+
         command = f"supervisorctl stop {service}" \
             if DUTSetup.running_in_container(node) \
             else f"service {service} stop"
@@ -141,8 +144,6 @@ class DUTSetup:
             node, command, timeout=180, sudo=True, message=message
         )
 
-        DUTSetup.get_service_logs(node, service)
-
     @staticmethod
     def stop_service_on_all_duts(nodes, service):
         """Stop the named service on all DUTs.
@@ -157,11 +158,62 @@ class DUTSetup:
                 DUTSetup.stop_service(node, service)
 
     @staticmethod
-    def get_vpp_pid(node):
-        """Get PID of running VPP process.
+    def kill_program(node, program, namespace=None):
+        """Kill program on the specified topology node.
+
+        :param node: Topology node.
+        :param program: Program name.
+        :param namespace: Namespace program is running in.
+        :type node: dict
+        :type program: str
+        :type namespace: str
+        """
+        host = node[u"host"]
+        cmd_timeout = 5
+        if namespace in (None, u"default"):
+            shell_cmd = u"sh -c"
+        else:
+            shell_cmd = f"ip netns exec {namespace} sh -c"
+
+        pgrep_cmd = f"{shell_cmd} \'pgrep {program}\'"
+        ret_code, _, _ = exec_cmd(node, pgrep_cmd, timeout=cmd_timeout,
+                                  sudo=True)
+        if ret_code == 0:
+            logger.trace(f"{program} is not running on {host}")
+            return
+        ret_code, _, _ = exec_cmd(node, f"{shell_cmd} \'pkill {program}\'",
+                                  timeout=cmd_timeout, sudo=True)
+        for attempt in range(5):
+            ret_code, _, _ = exec_cmd(node, pgrep_cmd, timeout=cmd_timeout,
+                                      sudo=True)
+            if ret_code != 0:
+                logger.trace(f"Attempt {attempt}: {program} is dead on {host}")
+                return
+            sleep(1)
+        logger.trace(f"SIGKILLing {program} on {host}")
+        ret_code, _, _ = exec_cmd(node, f"{shell_cmd} \'pkill -9 {program}\'",
+                                  timeout=cmd_timeout, sudo=True)
+
+    @staticmethod
+    def verify_program_installed(node, program):
+        """Verify that program is installed on the specified topology node.
+
+        :param node: Topology node.
+        :param program: Program name.
+        :type node: dict
+        :type program: str
+        """
+        cmd = f"command -v {program}"
+        exec_cmd_no_error(node, cmd, message=f"{program} is not installed")
+
+    @staticmethod
+    def get_pid(node, process):
+        """Get PID of running process.
 
         :param node: DUT node.
+        :param process: process name.
         :type node: dict
+        :type process: str
         :returns: PID
         :rtype: int
         :raises RuntimeError: If it is not possible to get the PID.
@@ -171,26 +223,24 @@ class DUTSetup:
 
         retval = None
         for i in range(3):
-            logger.trace(f"Try {i}: Get VPP PID")
-            ret_code, stdout, stderr = ssh.exec_command(u"pidof vpp")
+            logger.trace(f"Try {i}: Get {process} PID")
+            ret_code, stdout, stderr = ssh.exec_command(f"pidof {process}")
 
             if int(ret_code):
                 raise RuntimeError(
-                    f"Not possible to get PID of VPP process on node: "
+                    f"Not possible to get PID of {process} process on node: "
                     f"{node[u'host']}\n {stdout + stderr}"
                 )
 
             pid_list = stdout.split()
             if len(pid_list) == 1:
-                retval = int(stdout)
-            elif not pid_list:
-                logger.debug(f"No VPP PID found on node {node[u'host']}")
+                return [int(stdout)]
+            if not pid_list:
+                logger.debug(f"No {process} PID found on node {node[u'host']}")
                 continue
-            else:
-                logger.debug(
-                    f"More then one VPP PID found on node {node[u'host']}"
-                )
-                retval = [int(pid) for pid in pid_list]
+            logger.debug(f"More than one {process} PID found " \
+                         f"on node {node[u'host']}")
+            retval = [int(pid) for pid in pid_list]
 
         return retval
 
@@ -206,7 +256,7 @@ class DUTSetup:
         pids = dict()
         for node in nodes.values():
             if node[u"type"] == NodeType.DUT:
-                pids[node[u"host"]] = DUTSetup.get_vpp_pid(node)
+                pids[node[u"host"]] = DUTSetup.get_pid(node, u"vpp")
         return pids
 
     @staticmethod
@@ -295,7 +345,7 @@ class DUTSetup:
         :type pf_pci_addr: str
         :type vf_id: int
         :returns: Virtual Function PCI address.
-        :rtype: int
+        :rtype: str
         :raises RuntimeError: If failed to get Virtual Function PCI address.
         """
         command = f"sh -c \"basename $(readlink " \
@@ -826,7 +876,7 @@ class DUTSetup:
             # If we want to allocate hugepage dynamically
             if allocate:
                 mem_needed = (mem_size * 1024) - (huge_free * huge_size)
-                huge_to_allocate = ((mem_needed / huge_size) * 2) + huge_total
+                huge_to_allocate = ((mem_needed // huge_size) * 2) + huge_total
                 max_map_count = huge_to_allocate*4
                 # Increase maximum number of memory map areas a process may have
                 ret_code, _, _ = ssh.exec_command_sudo(