FIX: Remove old restart sequence - Honeycomb
[csit.git] / resources / libraries / python / CpuUtils.py
index 5d8b8a6..67bf312 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2018 Cisco and/or its affiliates.
+# Copyright (c) 2019 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:
 
 """CPU utilities library."""
 
-from resources.libraries.python.ssh import SSH
+from robot.libraries.BuiltIn import BuiltIn
+
+from resources.libraries.python.Constants import Constants
+from resources.libraries.python.ssh import exec_cmd_no_error
+from resources.libraries.python.topology import Topology
 
 __all__ = ["CpuUtils"]
 
@@ -49,7 +53,6 @@ class CpuUtils(object):
         :returns: True if SMT is enabled, False if SMT is disabled.
         :rtype: bool
         """
-
         cpu_mems = [item[-4:] for item in cpu_info]
         cpu_mems_len = len(cpu_mems) / CpuUtils.NR_OF_THREADS
         count = 0
@@ -67,22 +70,11 @@ class CpuUtils(object):
         :type nodes: dict
         :raises RuntimeError: If the ssh command "lscpu -p" fails.
         """
-        ssh = SSH()
         for node in nodes.values():
-            ssh.connect(node)
-            cmd = "lscpu -p"
-            ret, stdout, stderr = ssh.exec_command(cmd)
-#           parsing of "lscpu -p" output:
-#           # CPU,Core,Socket,Node,,L1d,L1i,L2,L3
-#           0,0,0,0,,0,0,0,0
-#           1,1,0,0,,1,1,1,0
-            if ret != 0:
-                raise RuntimeError(
-                    "Failed to execute ssh command, ret: {} err: {}".format(
-                        ret, stderr))
+            stdout, _ = exec_cmd_no_error(node, 'lscpu -p')
             node['cpuinfo'] = list()
             for line in stdout.split("\n"):
-                if len(line) > 0 and line[0] != "#":
+                if line and line[0] != "#":
                     node['cpuinfo'].append([CpuUtils.__str2int(x) for x in
                                             line.split(",")])
 
@@ -117,7 +109,6 @@ class CpuUtils(object):
         :raises RuntimeError: If node cpuinfo is not available
             or if SMT is not enabled.
         """
-
         cpu_node = int(cpu_node)
         cpu_info = node.get("cpuinfo")
         if cpu_info is None:
@@ -160,7 +151,6 @@ class CpuUtils(object):
         :rtype: list
         :raises RuntimeError: If we require more cpus than available.
         """
-
         cpu_list = CpuUtils.cpu_list_per_node(node, cpu_node, smt_used)
 
         cpu_list_len = len(cpu_list)
@@ -202,7 +192,6 @@ class CpuUtils(object):
         :returns: Cpu numbers related to numa from argument.
         :rtype: str
         """
-
         cpu_list = CpuUtils.cpu_slice_of_list_per_node(node, cpu_node,
                                                        skip_cnt=skip_cnt,
                                                        cpu_cnt=cpu_cnt,
@@ -229,7 +218,6 @@ class CpuUtils(object):
         :returns: String of node related range of CPU numbers.
         :rtype: str
         """
-
         cpu_list = CpuUtils.cpu_slice_of_list_per_node(node, cpu_node,
                                                        skip_cnt=skip_cnt,
                                                        cpu_cnt=cpu_cnt,
@@ -246,3 +234,136 @@ class CpuUtils(object):
             cpu_range = "{}{}{}".format(cpu_list[0], sep, cpu_list[-1])
 
         return cpu_range
+
+    @staticmethod
+    def cpu_slice_of_list_for_nf(node, cpu_node, nf_chains=1, nf_nodes=1,
+                                 nf_chain=1, nf_node=1, nf_dtc=1, nf_mtcr=2,
+                                 nf_dtcr=1, skip_cnt=0):
+        """Return list of DUT node related list of CPU numbers. The main
+        computing unit is physical core count.
+
+        :param node: DUT node.
+        :param cpu_node: Numa node number.
+        :param nf_chains: Number of NF chains.
+        :param nf_nodes: Number of NF nodes in chain.
+        :param nf_chain: Chain number indexed from 1.
+        :param nf_node: Node number indexed from 1.
+        :param vs_dtc: Amount of physical cores for vswitch dataplane.
+        :param nf_dtc: Amount of physical cores for NF dataplane.
+        :param nf_mtcr: NF main thread per core ratio.
+        :param nf_dtcr: NF dataplane thread per core ratio.
+        :param skip_cnt: Skip first "skip_cnt" CPUs.
+        :type node: dict
+        :param cpu_node: int.
+        :type nf_chains: int
+        :type nf_nodes: int
+        :type nf_chain: int
+        :type nf_node: int
+        :type vs_dtc: int
+        :type nf_dtc: int or float
+        :type nf_mtcr: int
+        :type nf_dtcr: int
+        :type skip_cnt: int
+        :returns: List of CPUs allocated to NF.
+        :rtype: list
+        :raises RuntimeError: If we require more cpus than available or if
+        placement is not possible due to wrong parameters.
+        """
+        if nf_chain - 1 >= nf_chains:
+            raise RuntimeError("ChainID is higher than total number of chains!")
+        if nf_node - 1 >= nf_nodes:
+            raise RuntimeError("NodeID is higher than chain nodes!")
+
+        smt_used = CpuUtils.is_smt_enabled(node['cpuinfo'])
+        cpu_list = CpuUtils.cpu_list_per_node(node, cpu_node, smt_used)
+        # CPU thread sibling offset.
+        sib = len(cpu_list) / CpuUtils.NR_OF_THREADS
+
+        if not smt_used and not isinstance(nf_dtc, int):
+            raise RuntimeError("Cannot allocate if SMT is not enabled!")
+        # TODO: Workaround as we are using physical core as main unit, we must
+        # adjust number of physical dataplane cores in case of float for further
+        # array referencing. As rounding method in Py2.7 and Py3.x differs, we
+        # are using static mapping. This can be rewritten using flat arrays and
+        # different logic (from Physical core unit to Logical core unit).
+        dtc = 1 if not isinstance(nf_dtc, int) else nf_dtc
+
+        mt_req = ((nf_chains * nf_nodes) + nf_mtcr - 1) / nf_mtcr
+        dt_req = ((nf_chains * nf_nodes) + nf_dtcr - 1) / nf_dtcr
+        cpu_req = skip_cnt + mt_req + dt_req
+
+        if smt_used and cpu_req > len(cpu_list) / CpuUtils.NR_OF_THREADS:
+            raise RuntimeError("Not enough CPU cores available for placement!")
+        elif not smt_used and cpu_req > len(cpu_list):
+            raise RuntimeError("Not enough CPU cores available for placement!")
+
+        offset = (nf_node - 1) + (nf_chain - 1) * nf_nodes
+        try:
+            mt_odd = (offset / mt_req) & 1
+            mt_skip = skip_cnt + (offset % mt_req)
+            dt_odd = (offset / dt_req) & 1
+            dt_skip = skip_cnt + mt_req + (offset % dt_req) * dtc
+        except ZeroDivisionError:
+            raise RuntimeError("Invalid placement combination!")
+        if smt_used:
+            mt_list = [cpu for cpu in cpu_list[mt_skip+sib:mt_skip+sib + 1]] \
+                if mt_odd else [cpu for cpu in cpu_list[mt_skip:mt_skip + 1]]
+            dt_list = [cpu for cpu in cpu_list[dt_skip+sib:dt_skip+sib + dtc]] \
+                if dt_odd else [cpu for cpu in cpu_list[dt_skip:dt_skip + dtc]]
+            if isinstance(nf_dtc, int):
+                dt_list = \
+                    [cpu for cpu in cpu_list[dt_skip:dt_skip + dtc]]
+                dt_list += \
+                    [cpu for cpu in cpu_list[dt_skip+sib:dt_skip+sib + dtc]]
+        else:
+            mt_list = [cpu for cpu in cpu_list[mt_skip:mt_skip + 1]]
+            dt_list = [cpu for cpu in cpu_list[dt_skip:dt_skip + dtc]]
+
+        return mt_list + dt_list
+
+    @staticmethod
+    def get_affinity_nf(nodes, node, nf_chains=1, nf_nodes=1, nf_chain=1,
+                        nf_node=1, vs_dtc=1, nf_dtc=1, nf_mtcr=2, nf_dtcr=1):
+
+        """Get affinity of NF (network function). Result will be used to compute
+        the amount of CPUs and also affinity.
+
+        :param nodes: Physical topology nodes.
+        :param node: SUT node.
+        :param nf_chains: Number of NF chains.
+        :param nf_nodes: Number of NF nodes in chain.
+        :param nf_chain: Chain number indexed from 1.
+        :param nf_node: Node number indexed from 1.
+        :param vs_dtc: Amount of physical cores for vswitch dataplane.
+        :param nf_dtc: Amount of physical cores for NF dataplane.
+        :param nf_mtcr: NF main thread per core ratio.
+        :param nf_dtcr: NF dataplane thread per core ratio.
+        :type nodes: dict
+        :type node: dict
+        :type nf_chains: int
+        :type nf_nodes: int
+        :type nf_chain: int
+        :type nf_node: int
+        :type vs_dtc: int
+        :type nf_dtc: int or float
+        :type nf_mtcr: int
+        :type nf_dtcr: int
+        :returns: List of CPUs allocated to NF.
+        :rtype: list
+        """
+        skip_cnt = Constants.CPU_CNT_SYSTEM + Constants.CPU_CNT_MAIN + vs_dtc
+
+        interface_list = []
+        interface_list.append(
+            BuiltIn().get_variable_value('${{{node}_if1}}'.format(node=node)))
+        interface_list.append(
+            BuiltIn().get_variable_value('${{{node}_if2}}'.format(node=node)))
+
+        cpu_node = Topology.get_interfaces_numa_node(
+            nodes[node], *interface_list)
+
+        return CpuUtils.cpu_slice_of_list_for_nf(
+            node=nodes[node], cpu_node=cpu_node, nf_chains=nf_chains,
+            nf_nodes=nf_nodes, nf_chain=nf_chain, nf_node=nf_node,
+            nf_mtcr=nf_mtcr, nf_dtcr=nf_dtcr, nf_dtc=nf_dtc, skip_cnt=skip_cnt)
+