1 # Copyright (c) 2016 Cisco and/or its affiliates.
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at:
6 # http://www.apache.org/licenses/LICENSE-2.0
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
14 """CPU utilities library"""
16 from resources.libraries.python.ssh import SSH
18 __all__ = ["CpuUtils"]
20 class CpuUtils(object):
24 def __str2int(string):
25 """Conversion from string to integer, 0 in case of empty string.
27 :param string: Input string.
29 :returns: Integer converted from string, 0 in case of ValueError.
38 def get_cpu_layout_from_all_nodes(nodes):
39 """Retrieve cpu layout from all nodes, assuming all nodes
42 :param nodes: DICT__nodes from Topology.DICT__nodes.
44 :raises RuntimeError: If the ssh command "lscpu -p" fails.
47 for node in nodes.values():
50 ret, stdout, stderr = ssh.exec_command(cmd)
51 # parsing of "lscpu -p" output:
52 # # CPU,Core,Socket,Node,,L1d,L1i,L2,L3
57 "Failed to execute ssh command, ret: {} err: {}".format(
59 node['cpuinfo'] = list()
60 for line in stdout.split("\n"):
61 if len(line) > 0 and line[0] != "#":
62 node['cpuinfo'].append([CpuUtils.__str2int(x) for x in
66 def cpu_node_count(node):
67 """Return count of numa nodes.
69 :param node: Targeted node.
71 :returns: Count of numa nodes.
73 :raises RuntimeError: If node cpuinfo is not available.
75 cpuinfo = node.get("cpuinfo")
76 if cpuinfo is not None:
77 return node["cpuinfo"][-1][3] + 1
79 raise RuntimeError("Node cpuinfo not available.")
82 def cpu_list_per_node(node, cpu_node):
83 """Return node related list of CPU numbers.
85 :param node: Node dictionary with cpuinfo.
86 :param cpu_node: Numa node number.
89 :returns: List of cpu numbers related to numa from argument.
91 :raises RuntimeError: If node cpuinfo is not available.
93 cpu_node = int(cpu_node)
94 cpuinfo = node.get("cpuinfo")
96 if cpuinfo is not None:
98 if cpu[3] == cpu_node:
99 cpulist.append(cpu[0])
101 raise RuntimeError("Node cpuinfo not available.")
106 def cpu_list_per_node_str(node, cpu_node, skip_cnt=0,
108 """Return string of node related list of CPU numbers.
110 :param node: Node dictionary with cpuinfo.
111 :param cpu_node: Numa node number.
112 :param skip_cnt: Skip first "skip_cnt" CPUs.
113 :param cpu_cnt: Count of cpus to return, if 0 then return all.
114 :param sep: Separator, default: 1,2,3,4,....
120 :returns: Cpu numbers related to numa from argument.
122 :raises RuntimeError: If we require more cpus than available.
125 cpu_list = CpuUtils.cpu_list_per_node(node, cpu_node)
126 cpu_list_len = len(cpu_list)
128 cpu_cnt = cpu_list_len - skip_cnt
130 if cpu_cnt + skip_cnt > cpu_list_len:
131 raise RuntimeError("cpu_cnt + skip_cnt > length(cpu list).")
133 cpu_flist = sep.join(str(a) for a in
134 cpu_list[skip_cnt:skip_cnt+cpu_cnt])
139 def cpu_range_per_node_str(node, cpu_node, skip_cnt=0, cpu_cnt=0, sep="-"):
140 """Return string of node related range of CPU numbers, e.g. 0-4.
142 :param node: Node dictionary with cpuinfo.
143 :param cpu_node: Numa node number.
144 :param skip_cnt: Skip first "skip_cnt" CPUs.
145 :param cpu_cnt: Count of cpus to return, if 0 then return all.
146 :param sep: Separator, default: 0-4.
152 :returns: String of node related range of CPU numbers.
154 :raises RuntimeError: If we require more cpus than available.
157 cpu_list = CpuUtils.cpu_list_per_node(node, cpu_node)
158 cpu_list_len = len(cpu_list)
160 cpu_cnt = cpu_list_len - skip_cnt
162 if cpu_cnt + skip_cnt > cpu_list_len:
163 raise RuntimeError("cpu_cnt + skip_cnt > length(cpu list).")
165 first = cpu_list[skip_cnt]
166 last = cpu_list[skip_cnt + cpu_cnt - 1]
167 return "{}{}{}".format(first, sep, last)