X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=resources%2Flibraries%2Fpython%2FCpuUtils.py;h=91db83eb5cfd9fc4ee41f41d61aecacf20315c46;hb=91051c28e269f08bd58e8301366d3cca78c948ef;hp=f4d52f866298882201c4ad56b4403a87d0a57a3f;hpb=b4e5c717f5e2c39ded81f0c6f7b0f9f61945befd;p=csit.git diff --git a/resources/libraries/python/CpuUtils.py b/resources/libraries/python/CpuUtils.py index f4d52f8662..91db83eb5c 100644 --- a/resources/libraries/python/CpuUtils.py +++ b/resources/libraries/python/CpuUtils.py @@ -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: @@ -13,7 +13,11 @@ """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 @@ -59,27 +62,21 @@ class CpuUtils(object): return bool(count == cpu_mems_len) @staticmethod - def get_cpu_layout_from_all_nodes(nodes): - """Retrieve cpu layout from all nodes, assuming all nodes - are Linux nodes. + def get_cpu_info_from_all_nodes(nodes): + """Assuming all nodes are Linux nodes, retrieve the following + cpu information from all nodes: + - cpu architecture + - cpu layout :param nodes: DICT__nodes from Topology.DICT__nodes. :type nodes: dict - :raises RuntimeError: If the ssh command "lscpu -p" fails. + :raises RuntimeError: If an ssh command retrieving cpu information + 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, 'uname -m') + node['arch'] = stdout.strip() + stdout, _ = exec_cmd_no_error(node, 'lscpu -p') node['cpuinfo'] = list() for line in stdout.split("\n"): if line and line[0] != "#": @@ -117,7 +114,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 +156,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 +197,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 +223,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 +239,128 @@ 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 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 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 not 1 <= nf_chain <= nf_chains: + raise RuntimeError("ChainID is out of range!") + if not 1 <= nf_node <= nf_nodes: + raise RuntimeError("NodeID is out of range!") + + 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 + + dtc_is_integer = isinstance(nf_dtc, int) + if not smt_used and not dtc_is_integer: + raise RuntimeError("Cannot allocate if SMT is not enabled!") + # TODO: Please reword the following todo if it is still relevant + # 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). + if not dtc_is_integer: + nf_dtc = 1 + + mt_req = ((nf_chains * nf_nodes) + nf_mtcr - 1) // nf_mtcr + dt_req = ((nf_chains * nf_nodes) + nf_dtcr - 1) // nf_dtcr + + if (skip_cnt + mt_req + dt_req) > (sib if smt_used else len(cpu_list)): + raise RuntimeError("Not enough CPU cores available for placement!") + + offset = (nf_node - 1) + (nf_chain - 1) * nf_nodes + mt_skip = skip_cnt + (offset % mt_req) + dt_skip = skip_cnt + mt_req + (offset % dt_req) * nf_dtc + + result = cpu_list[dt_skip:dt_skip + nf_dtc] + if smt_used: + if (offset // mt_req) & 1: # check oddness + mt_skip += sib + + dt_skip += sib + if dtc_is_integer: + result.extend(cpu_list[dt_skip:dt_skip + nf_dtc]) + elif (offset // dt_req) & 1: # check oddness + result = cpu_list[dt_skip:dt_skip + nf_dtc] + + result[0:0] = cpu_list[mt_skip:mt_skip + 1] + return result + + @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) +