blob: 9a79039f69e2fb6fbce39fe501cd8a92d9dc61e1 [file] [log] [blame]
John DeNisco68b0ee32017-09-27 16:35:23 -04001# 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:
5#
6# http://www.apache.org/licenses/LICENSE-2.0
7#
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.
13
14"""Library that supports Auto Configuration."""
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -080015from __future__ import absolute_import, division, print_function
John DeNisco68b0ee32017-09-27 16:35:23 -040016
17import logging
18import os
19import re
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -080020from ipaddress import ip_address
21
John DeNisco68b0ee32017-09-27 16:35:23 -040022import yaml
23
24from vpplib.VPPUtil import VPPUtil
25from vpplib.VppPCIUtil import VppPCIUtil
26from vpplib.VppHugePageUtil import VppHugePageUtil
27from vpplib.CpuUtils import CpuUtils
28from vpplib.VppGrubUtil import VppGrubUtil
29from vpplib.QemuUtils import QemuUtils
30
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -080031# Python2/3 compatible
32try:
33 input = raw_input # noqa
34except NameError:
35 pass
36
John DeNisco68b0ee32017-09-27 16:35:23 -040037__all__ = ["AutoConfig"]
38
39# Constants
40MIN_SYSTEM_CPUS = 2
41MIN_TOTAL_HUGE_PAGES = 1024
42MAX_PERCENT_FOR_HUGE_PAGES = 70
43
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020044IPERFVM_XML = "configs/iperf-vm.xml"
45IPERFVM_IMAGE = "images/xenial-mod.img"
46IPERFVM_ISO = "configs/cloud-config.iso"
John DeNisco9fa5cf42018-02-06 15:23:05 -050047
John DeNisco68b0ee32017-09-27 16:35:23 -040048
49class AutoConfig(object):
50 """Auto Configuration Tools"""
51
John DeNiscoddecfb32017-11-15 08:50:57 -050052 def __init__(self, rootdir, filename, clean=False):
John DeNisco68b0ee32017-09-27 16:35:23 -040053 """
54 The Auto Configure class.
55
56 :param rootdir: The root directory for all the auto configuration files
57 :param filename: The autoconfiguration file
John DeNiscoddecfb32017-11-15 08:50:57 -050058 :param clean: When set initialize the nodes from the auto-config file
John DeNisco68b0ee32017-09-27 16:35:23 -040059 :type rootdir: str
60 :type filename: str
John DeNiscoddecfb32017-11-15 08:50:57 -050061 :type clean: bool
John DeNisco68b0ee32017-09-27 16:35:23 -040062 """
63 self._autoconfig_filename = rootdir + filename
64 self._rootdir = rootdir
65 self._metadata = {}
66 self._nodes = {}
67 self._vpp_devices_node = {}
68 self._hugepage_config = ""
John DeNiscoddecfb32017-11-15 08:50:57 -050069 self._clean = clean
John DeNisco68b0ee32017-09-27 16:35:23 -040070 self._loadconfig()
John DeNisco9fa5cf42018-02-06 15:23:05 -050071 self._sockfilename = ""
John DeNisco68b0ee32017-09-27 16:35:23 -040072
73 def get_nodes(self):
74 """
75 Returns the nodes dictionary.
76
77 :returns: The nodes
78 :rtype: dictionary
79 """
80
81 return self._nodes
82
83 @staticmethod
84 def _autoconfig_backup_file(filename):
85 """
86 Create a backup file.
87
88 :param filename: The file to backup
89 :type filename: str
90 """
91
92 # Does a copy of the file exist, if not create one
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020093 ofile = filename + ".orig"
94 (ret, stdout, stderr) = VPPUtil.exec_command("ls {}".format(ofile))
John DeNisco68b0ee32017-09-27 16:35:23 -040095 if ret != 0:
96 logging.debug(stderr)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020097 if stdout.strip("\n") != ofile:
98 cmd = "sudo cp {} {}".format(filename, ofile)
John DeNisco68b0ee32017-09-27 16:35:23 -040099 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
100 if ret != 0:
101 logging.debug(stderr)
102
John DeNiscoddecfb32017-11-15 08:50:57 -0500103 # noinspection PyBroadException
John DeNisco68b0ee32017-09-27 16:35:23 -0400104 @staticmethod
John DeNiscoa3db0782017-10-17 11:07:22 -0400105 def _ask_user_ipv4():
106 """
107 Asks the user for a number within a range.
108 default is returned if return is entered.
109
John DeNiscoc6b2a202017-11-01 12:37:47 -0400110 :returns: IP address with cidr
111 :rtype: str
John DeNiscoa3db0782017-10-17 11:07:22 -0400112 """
113
114 while True:
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -0800115 answer = input("Please enter the IPv4 Address [n.n.n.n/n]: ")
John DeNiscoa3db0782017-10-17 11:07:22 -0400116 try:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200117 ipinput = answer.split("/")
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -0800118 ipaddr = ip_address(ipinput[0])
John DeNiscoc6b2a202017-11-01 12:37:47 -0400119 if len(ipinput) > 1:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200120 plen = answer.split("/")[1]
John DeNiscoc6b2a202017-11-01 12:37:47 -0400121 else:
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -0800122 answer = input("Please enter the netmask [n.n.n.n]: ")
123 plen = ip_address(answer).netmask_bits()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200124 return "{}/{}".format(ipaddr, plen)
Paul Vinciguerra582eac52020-04-03 12:18:40 -0400125 except ValueError:
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -0800126 print("Please enter a valid IPv4 address.")
John DeNiscoa3db0782017-10-17 11:07:22 -0400127
John DeNiscoa3db0782017-10-17 11:07:22 -0400128 @staticmethod
John DeNisco68b0ee32017-09-27 16:35:23 -0400129 def _ask_user_range(question, first, last, default):
130 """
131 Asks the user for a number within a range.
132 default is returned if return is entered.
133
134 :param question: Text of a question.
135 :param first: First number in the range
136 :param last: Last number in the range
137 :param default: The value returned when return is entered
138 :type question: string
139 :type first: int
140 :type last: int
141 :type default: int
142 :returns: The answer to the question
143 :rtype: int
144 """
145
146 while True:
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -0800147 answer = input(question)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200148 if answer == "":
John DeNisco68b0ee32017-09-27 16:35:23 -0400149 answer = default
150 break
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200151 if re.findall(r"[0-9+]", answer):
John DeNisco68b0ee32017-09-27 16:35:23 -0400152 if int(answer) in range(first, last + 1):
153 break
154 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200155 print(
156 "Please a value between {} and {} or Return.".format(
157 first, last
158 )
159 )
John DeNisco68b0ee32017-09-27 16:35:23 -0400160 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200161 print(
162 "Please a number between {} and {} or Return.".format(first, last)
163 )
John DeNisco68b0ee32017-09-27 16:35:23 -0400164
165 return int(answer)
166
167 @staticmethod
168 def _ask_user_yn(question, default):
169 """
170 Asks the user for a yes or no question.
171
172 :param question: Text of a question.
173 :param default: The value returned when return is entered
174 :type question: string
175 :type default: string
176 :returns: The answer to the question
177 :rtype: string
178 """
179
180 input_valid = False
181 default = default.lower()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200182 answer = ""
John DeNisco68b0ee32017-09-27 16:35:23 -0400183 while not input_valid:
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -0800184 answer = input(question)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200185 if answer == "":
John DeNisco68b0ee32017-09-27 16:35:23 -0400186 answer = default
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200187 if re.findall(r"[YyNn]", answer):
John DeNisco68b0ee32017-09-27 16:35:23 -0400188 input_valid = True
189 answer = answer[0].lower()
190 else:
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -0800191 print("Please answer Y, N or Return.")
John DeNisco68b0ee32017-09-27 16:35:23 -0400192
193 return answer
194
195 def _loadconfig(self):
196 """
197 Load the testbed configuration, given the auto configuration file.
198
199 """
200
201 # Get the Topology, from the topology layout file
202 topo = {}
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200203 with open(self._autoconfig_filename, "r") as stream:
John DeNisco68b0ee32017-09-27 16:35:23 -0400204 try:
205 topo = yaml.load(stream)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200206 if "metadata" in topo:
207 self._metadata = topo["metadata"]
John DeNisco68b0ee32017-09-27 16:35:23 -0400208 except yaml.YAMLError as exc:
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -0800209 raise RuntimeError(
210 "Couldn't read the Auto config file {}.".format(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200211 self._autoconfig_filename, exc
212 )
213 )
John DeNisco68b0ee32017-09-27 16:35:23 -0400214
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200215 systemfile = self._rootdir + self._metadata["system_config_file"]
John DeNiscoddecfb32017-11-15 08:50:57 -0500216 if self._clean is False and os.path.isfile(systemfile):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200217 with open(systemfile, "r") as sysstream:
John DeNisco68b0ee32017-09-27 16:35:23 -0400218 try:
219 systopo = yaml.load(sysstream)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200220 if "nodes" in systopo:
221 self._nodes = systopo["nodes"]
John DeNisco68b0ee32017-09-27 16:35:23 -0400222 except yaml.YAMLError as sysexc:
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -0800223 raise RuntimeError(
224 "Couldn't read the System config file {}.".format(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200225 systemfile, sysexc
226 )
227 )
John DeNisco68b0ee32017-09-27 16:35:23 -0400228 else:
229 # Get the nodes from Auto Config
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200230 if "nodes" in topo:
231 self._nodes = topo["nodes"]
John DeNisco68b0ee32017-09-27 16:35:23 -0400232
233 # Set the root directory in all the nodes
234 for i in self._nodes.items():
235 node = i[1]
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200236 node["rootdir"] = self._rootdir
John DeNisco68b0ee32017-09-27 16:35:23 -0400237
238 def updateconfig(self):
239 """
240 Update the testbed configuration, given the auto configuration file.
241 We will write the system configuration file with the current node
242 information
243
244 """
245
246 # Initialize the yaml data
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200247 ydata = {"metadata": self._metadata, "nodes": self._nodes}
John DeNisco68b0ee32017-09-27 16:35:23 -0400248
249 # Write the system config file
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200250 filename = self._rootdir + self._metadata["system_config_file"]
251 with open(filename, "w") as yamlfile:
John DeNiscoddecfb32017-11-15 08:50:57 -0500252 yaml.dump(ydata, yamlfile)
John DeNisco68b0ee32017-09-27 16:35:23 -0400253
254 def _update_auto_config(self):
255 """
256 Write the auto configuration file with the new configuration data,
257 input from the user.
258
259 """
260
261 # Initialize the yaml data
262 nodes = {}
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200263 with open(self._autoconfig_filename, "r") as stream:
John DeNisco68b0ee32017-09-27 16:35:23 -0400264 try:
265 ydata = yaml.load(stream)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200266 if "nodes" in ydata:
267 nodes = ydata["nodes"]
John DeNisco68b0ee32017-09-27 16:35:23 -0400268 except yaml.YAMLError as exc:
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -0800269 print(exc)
John DeNisco68b0ee32017-09-27 16:35:23 -0400270 return
271
272 for i in nodes.items():
273 key = i[0]
274 node = i[1]
275
276 # Interfaces
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200277 node["interfaces"] = {}
278 for item in self._nodes[key]["interfaces"].items():
John DeNisco68b0ee32017-09-27 16:35:23 -0400279 port = item[0]
280 interface = item[1]
281
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200282 node["interfaces"][port] = {}
283 addr = "{}".format(interface["pci_address"])
284 node["interfaces"][port]["pci_address"] = addr
285 if "mac_address" in interface:
286 node["interfaces"][port]["mac_address"] = interface["mac_address"]
John DeNisco68b0ee32017-09-27 16:35:23 -0400287
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200288 if "total_other_cpus" in self._nodes[key]["cpu"]:
289 node["cpu"]["total_other_cpus"] = self._nodes[key]["cpu"][
290 "total_other_cpus"
291 ]
292 if "total_vpp_cpus" in self._nodes[key]["cpu"]:
293 node["cpu"]["total_vpp_cpus"] = self._nodes[key]["cpu"][
294 "total_vpp_cpus"
295 ]
296 if "reserve_vpp_main_core" in self._nodes[key]["cpu"]:
297 node["cpu"]["reserve_vpp_main_core"] = self._nodes[key]["cpu"][
298 "reserve_vpp_main_core"
299 ]
John DeNisco68b0ee32017-09-27 16:35:23 -0400300
301 # TCP
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200302 if "active_open_sessions" in self._nodes[key]["tcp"]:
303 node["tcp"]["active_open_sessions"] = self._nodes[key]["tcp"][
304 "active_open_sessions"
305 ]
306 if "passive_open_sessions" in self._nodes[key]["tcp"]:
307 node["tcp"]["passive_open_sessions"] = self._nodes[key]["tcp"][
308 "passive_open_sessions"
309 ]
John DeNisco68b0ee32017-09-27 16:35:23 -0400310
311 # Huge pages
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200312 node["hugepages"]["total"] = self._nodes[key]["hugepages"]["total"]
John DeNisco68b0ee32017-09-27 16:35:23 -0400313
314 # Write the auto config config file
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200315 with open(self._autoconfig_filename, "w") as yamlfile:
John DeNiscoddecfb32017-11-15 08:50:57 -0500316 yaml.dump(ydata, yamlfile)
John DeNisco68b0ee32017-09-27 16:35:23 -0400317
318 def apply_huge_pages(self):
319 """
320 Apply the huge page config
321
322 """
323
324 for i in self._nodes.items():
325 node = i[1]
326
327 hpg = VppHugePageUtil(node)
328 hpg.hugepages_dryrun_apply()
329
330 @staticmethod
John DeNisco68b0ee32017-09-27 16:35:23 -0400331 def _apply_vpp_cpu(node):
332 """
333 Apply the VPP cpu config
334
335 :param node: Node dictionary with cpuinfo.
336 :type node: dict
337 """
338
339 # Get main core
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200340 cpu = "\n"
341 if "vpp_main_core" in node["cpu"]:
342 vpp_main_core = node["cpu"]["vpp_main_core"]
John DeNiscoddecfb32017-11-15 08:50:57 -0500343 else:
344 vpp_main_core = 0
Paul Vinciguerrab11c2882019-04-30 20:57:04 -0700345 if vpp_main_core != 0:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200346 cpu += " main-core {}\n".format(vpp_main_core)
John DeNisco68b0ee32017-09-27 16:35:23 -0400347
348 # Get workers
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200349 vpp_workers = node["cpu"]["vpp_workers"]
John DeNisco68b0ee32017-09-27 16:35:23 -0400350 vpp_worker_len = len(vpp_workers)
351 if vpp_worker_len > 0:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200352 vpp_worker_str = ""
John DeNisco68b0ee32017-09-27 16:35:23 -0400353 for i, worker in enumerate(vpp_workers):
354 if i > 0:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200355 vpp_worker_str += ","
John DeNisco68b0ee32017-09-27 16:35:23 -0400356 if worker[0] == worker[1]:
357 vpp_worker_str += "{}".format(worker[0])
358 else:
359 vpp_worker_str += "{}-{}".format(worker[0], worker[1])
360
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200361 cpu += " corelist-workers {}\n".format(vpp_worker_str)
John DeNisco68b0ee32017-09-27 16:35:23 -0400362
363 return cpu
364
365 @staticmethod
366 def _apply_vpp_devices(node):
367 """
368 Apply VPP PCI Device configuration to vpp startup.
369
370 :param node: Node dictionary with cpuinfo.
371 :type node: dict
372 """
373
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200374 devices = ""
375 ports_per_numa = node["cpu"]["ports_per_numa"]
John DeNisco68b0ee32017-09-27 16:35:23 -0400376
377 for item in ports_per_numa.items():
378 value = item[1]
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200379 interfaces = value["interfaces"]
John DeNisco68b0ee32017-09-27 16:35:23 -0400380
381 # if 0 was specified for the number of vpp workers, use 1 queue
382 num_rx_queues = None
383 num_tx_queues = None
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200384 if "rx_queues" in value:
385 num_rx_queues = value["rx_queues"]
386 if "tx_queues" in value:
387 num_tx_queues = value["tx_queues"]
John DeNisco68b0ee32017-09-27 16:35:23 -0400388
389 num_rx_desc = None
390 num_tx_desc = None
391
392 # Create the devices string
393 for interface in interfaces:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200394 pci_address = interface["pci_address"]
John DeNisco68b0ee32017-09-27 16:35:23 -0400395 pci_address = pci_address.lstrip("'").rstrip("'")
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200396 devices += "\n"
397 devices += " dev {} {{ \n".format(pci_address)
John DeNisco68b0ee32017-09-27 16:35:23 -0400398 if num_rx_queues:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200399 devices += " num-rx-queues {}\n".format(num_rx_queues)
John DeNisco68b0ee32017-09-27 16:35:23 -0400400 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200401 devices += " num-rx-queues {}\n".format(1)
John DeNisco68b0ee32017-09-27 16:35:23 -0400402 if num_tx_queues:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200403 devices += " num-tx-queues {}\n".format(num_tx_queues)
John DeNisco68b0ee32017-09-27 16:35:23 -0400404 if num_rx_desc:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200405 devices += " num-rx-desc {}\n".format(num_rx_desc)
John DeNisco68b0ee32017-09-27 16:35:23 -0400406 if num_tx_desc:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200407 devices += " num-tx-desc {}\n".format(num_tx_desc)
408 devices += " }"
John DeNisco68b0ee32017-09-27 16:35:23 -0400409
John DeNiscodf35a202020-06-19 15:28:48 -0400410 return devices
411
412 @staticmethod
413 def _apply_buffers(node):
414 """
415 Apply VPP PCI Device configuration to vpp startup.
416
417 :param node: Node dictionary with cpuinfo.
418 :type node: dict
419 """
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200420 buffers = ""
421 total_mbufs = node["cpu"]["total_mbufs"]
John DeNiscodf35a202020-06-19 15:28:48 -0400422
John DeNiscoa7da67f2018-01-26 14:55:33 -0500423 # If the total mbufs is not 0 or less than the default, set num-bufs
424 logging.debug("Total mbufs: {}".format(total_mbufs))
Paul Vinciguerrab11c2882019-04-30 20:57:04 -0700425 if total_mbufs != 0 and total_mbufs > 16384:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200426 buffers += " buffers-per-numa {}".format(total_mbufs)
John DeNisco68b0ee32017-09-27 16:35:23 -0400427
John DeNiscodf35a202020-06-19 15:28:48 -0400428 return buffers
John DeNisco68b0ee32017-09-27 16:35:23 -0400429
430 @staticmethod
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200431 def _calc_vpp_workers(
432 node,
433 vpp_workers,
434 numa_node,
435 other_cpus_end,
436 total_vpp_workers,
437 reserve_vpp_main_core,
438 ):
John DeNisco68b0ee32017-09-27 16:35:23 -0400439 """
440 Calculate the VPP worker information
441
442 :param node: Node dictionary
443 :param vpp_workers: List of VPP workers
444 :param numa_node: Numa node
445 :param other_cpus_end: The end of the cpus allocated for cores
446 other than vpp
447 :param total_vpp_workers: The number of vpp workers needed
448 :param reserve_vpp_main_core: Is there a core needed for
449 the vpp main core
450 :type node: dict
451 :type numa_node: int
452 :type other_cpus_end: int
453 :type total_vpp_workers: int
454 :type reserve_vpp_main_core: bool
455 :returns: Is a core still needed for the vpp main core
456 :rtype: bool
457 """
458
459 # Can we fit the workers in one of these slices
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200460 cpus = node["cpu"]["cpus_per_node"][numa_node]
John DeNisco68b0ee32017-09-27 16:35:23 -0400461 for cpu in cpus:
462 start = cpu[0]
463 end = cpu[1]
464 if start <= other_cpus_end:
465 start = other_cpus_end + 1
466
467 if reserve_vpp_main_core:
468 start += 1
469
470 workers_end = start + total_vpp_workers - 1
John DeNiscoa7da67f2018-01-26 14:55:33 -0500471
John DeNisco68b0ee32017-09-27 16:35:23 -0400472 if workers_end <= end:
473 if reserve_vpp_main_core:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200474 node["cpu"]["vpp_main_core"] = start - 1
John DeNisco68b0ee32017-09-27 16:35:23 -0400475 reserve_vpp_main_core = False
476 if total_vpp_workers:
477 vpp_workers.append((start, workers_end))
478 break
479
480 # We still need to reserve the main core
481 if reserve_vpp_main_core:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200482 node["cpu"]["vpp_main_core"] = other_cpus_end + 1
John DeNisco68b0ee32017-09-27 16:35:23 -0400483
484 return reserve_vpp_main_core
485
486 @staticmethod
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200487 def _calc_desc_and_queues(
488 total_numa_nodes, total_ports_per_numa, total_rx_queues, ports_per_numa_value
489 ):
John DeNisco68b0ee32017-09-27 16:35:23 -0400490 """
491 Calculate the number of descriptors and queues
492
493 :param total_numa_nodes: The total number of numa nodes
494 :param total_ports_per_numa: The total number of ports for this
495 numa node
John DeNiscoa7da67f2018-01-26 14:55:33 -0500496 :param total_rx_queues: The total number of rx queues / port
John DeNisco68b0ee32017-09-27 16:35:23 -0400497 :param ports_per_numa_value: The value from the ports_per_numa
498 dictionary
499 :type total_numa_nodes: int
500 :type total_ports_per_numa: int
John DeNiscoa7da67f2018-01-26 14:55:33 -0500501 :type total_rx_queues: int
John DeNisco68b0ee32017-09-27 16:35:23 -0400502 :type ports_per_numa_value: dict
503 :returns The total number of message buffers
John DeNisco68b0ee32017-09-27 16:35:23 -0400504 :rtype: int
505 """
506
John DeNisco68b0ee32017-09-27 16:35:23 -0400507 # Get the number of rx queues
John DeNiscoa7da67f2018-01-26 14:55:33 -0500508 rx_queues = max(1, total_rx_queues)
509 tx_queues = rx_queues * total_numa_nodes + 1
John DeNisco68b0ee32017-09-27 16:35:23 -0400510
511 # Get the descriptor entries
512 desc_entries = 1024
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200513 ports_per_numa_value["rx_queues"] = rx_queues
514 total_mbufs = (
515 (rx_queues * desc_entries) + (tx_queues * desc_entries)
516 ) * total_ports_per_numa
John DeNisco68b0ee32017-09-27 16:35:23 -0400517
John DeNiscoa7da67f2018-01-26 14:55:33 -0500518 return total_mbufs
John DeNisco68b0ee32017-09-27 16:35:23 -0400519
520 @staticmethod
521 def _create_ports_per_numa(node, interfaces):
522 """
523 Create a dictionary or ports per numa node
524 :param node: Node dictionary
525 :param interfaces: All the interfaces to be used by vpp
526 :type node: dict
527 :type interfaces: dict
528 :returns: The ports per numa dictionary
529 :rtype: dict
530 """
531
532 # Make a list of ports by numa node
533 ports_per_numa = {}
534 for item in interfaces.items():
535 i = item[1]
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200536 if i["numa_node"] not in ports_per_numa:
537 ports_per_numa[i["numa_node"]] = {"interfaces": []}
538 ports_per_numa[i["numa_node"]]["interfaces"].append(i)
John DeNisco68b0ee32017-09-27 16:35:23 -0400539 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200540 ports_per_numa[i["numa_node"]]["interfaces"].append(i)
541 node["cpu"]["ports_per_numa"] = ports_per_numa
John DeNisco68b0ee32017-09-27 16:35:23 -0400542
543 return ports_per_numa
544
545 def calculate_cpu_parameters(self):
546 """
547 Calculate the cpu configuration.
548
549 """
550
551 # Calculate the cpu parameters, needed for the
552 # vpp_startup and grub configuration
553 for i in self._nodes.items():
554 node = i[1]
555
556 # get total number of nic ports
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200557 interfaces = node["interfaces"]
John DeNisco68b0ee32017-09-27 16:35:23 -0400558
559 # Make a list of ports by numa node
560 ports_per_numa = self._create_ports_per_numa(node, interfaces)
561
562 # Get the number of cpus to skip, we never use the first cpu
563 other_cpus_start = 1
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200564 other_cpus_end = other_cpus_start + node["cpu"]["total_other_cpus"] - 1
John DeNisco68b0ee32017-09-27 16:35:23 -0400565 other_workers = None
Paul Vinciguerrab11c2882019-04-30 20:57:04 -0700566 if other_cpus_end != 0:
John DeNisco68b0ee32017-09-27 16:35:23 -0400567 other_workers = (other_cpus_start, other_cpus_end)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200568 node["cpu"]["other_workers"] = other_workers
John DeNisco68b0ee32017-09-27 16:35:23 -0400569
570 # Allocate the VPP main core and workers
571 vpp_workers = []
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200572 reserve_vpp_main_core = node["cpu"]["reserve_vpp_main_core"]
573 total_vpp_cpus = node["cpu"]["total_vpp_cpus"]
574 total_rx_queues = node["cpu"]["total_rx_queues"]
John DeNisco68b0ee32017-09-27 16:35:23 -0400575
576 # If total_vpp_cpus is 0 or is less than the numa nodes with ports
577 # then we shouldn't get workers
John DeNisco9fa5cf42018-02-06 15:23:05 -0500578 total_workers_node = 0
579 if len(ports_per_numa):
jdeniscoaf86c632019-04-05 12:25:50 -0400580 total_workers_node = total_vpp_cpus // len(ports_per_numa)
John DeNiscoa7da67f2018-01-26 14:55:33 -0500581 total_main = 0
John DeNisco68b0ee32017-09-27 16:35:23 -0400582 if reserve_vpp_main_core:
John DeNiscoa7da67f2018-01-26 14:55:33 -0500583 total_main = 1
John DeNisco68b0ee32017-09-27 16:35:23 -0400584 total_mbufs = 0
Paul Vinciguerrab11c2882019-04-30 20:57:04 -0700585 if total_main + total_workers_node != 0:
John DeNisco68b0ee32017-09-27 16:35:23 -0400586 for item in ports_per_numa.items():
587 numa_node = item[0]
588 value = item[1]
589
590 # Get the number of descriptors and queues
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -0800591 mbufs = self._calc_desc_and_queues(
592 len(ports_per_numa),
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200593 len(value["interfaces"]),
594 total_rx_queues,
595 value,
596 )
John DeNisco68b0ee32017-09-27 16:35:23 -0400597 total_mbufs += mbufs
598
599 # Get the VPP workers
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -0800600 reserve_vpp_main_core = self._calc_vpp_workers(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200601 node,
602 vpp_workers,
603 numa_node,
604 other_cpus_end,
605 total_workers_node,
606 reserve_vpp_main_core,
607 )
John DeNisco68b0ee32017-09-27 16:35:23 -0400608
609 total_mbufs *= 2.5
610 total_mbufs = int(total_mbufs)
611 else:
612 total_mbufs = 0
613
614 # Save the info
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200615 node["cpu"]["vpp_workers"] = vpp_workers
616 node["cpu"]["total_mbufs"] = total_mbufs
John DeNisco68b0ee32017-09-27 16:35:23 -0400617
618 # Write the config
619 self.updateconfig()
620
621 @staticmethod
622 def _apply_vpp_tcp(node):
623 """
John DeNiscodf35a202020-06-19 15:28:48 -0400624 Apply the tcp config
John DeNisco68b0ee32017-09-27 16:35:23 -0400625
626 :param node: Node dictionary with cpuinfo.
627 :type node: dict
628 """
629
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200630 active_open_sessions = node["tcp"]["active_open_sessions"]
John DeNisco68b0ee32017-09-27 16:35:23 -0400631 aos = int(active_open_sessions)
632
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200633 passive_open_sessions = node["tcp"]["passive_open_sessions"]
John DeNisco68b0ee32017-09-27 16:35:23 -0400634 pos = int(passive_open_sessions)
635
636 # Generate the api-segment gid vpp sheit in any case
637 if (aos + pos) == 0:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200638 tcp = "\n".join(["api-segment {", " gid vpp", "}"])
639 return tcp.rstrip("\n")
640
641 tcp = "\n".join(
642 [
643 "# TCP stack-related configuration parameters",
644 "# expecting {:d} client sessions, {:d} server sessions\n".format(
645 aos, pos
646 ),
647 "heapsize 4g\n",
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -0800648 "api-segment {",
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200649 " global-size 2000M",
650 " api-size 1G",
651 "}\n",
652 "session {",
653 " event-queue-length {:d}".format(aos + pos),
654 " preallocated-sessions {:d}".format(aos + pos),
655 " v4-session-table-buckets {:d}".format((aos + pos) // 4),
656 " v4-session-table-memory 3g\n",
657 ]
658 )
John DeNisco68b0ee32017-09-27 16:35:23 -0400659 if aos > 0:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200660 tcp = (
661 tcp + " v4-halfopen-table-buckets {:d}".format((aos + pos) // 4) + "\n"
662 )
John DeNisco68b0ee32017-09-27 16:35:23 -0400663 tcp = tcp + " v4-halfopen-table-memory 3g\n"
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200664 tcp = (
665 tcp
666 + " local-endpoints-table-buckets {:d}".format((aos + pos) // 4)
667 + "\n"
668 )
John DeNisco8996cb62018-03-01 15:05:22 -0500669 tcp = tcp + " local-endpoints-table-memory 3g\n"
John DeNisco68b0ee32017-09-27 16:35:23 -0400670 tcp = tcp + "}\n\n"
671
672 tcp = tcp + "tcp {\n"
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -0800673 tcp = tcp + " preallocated-connections {:d}".format(aos + pos) + "\n"
John DeNisco68b0ee32017-09-27 16:35:23 -0400674 if aos > 0:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200675 tcp = tcp + " preallocated-half-open-connections {:d}".format(aos) + "\n"
John DeNisco68b0ee32017-09-27 16:35:23 -0400676 tcp = tcp + "}\n\n"
677
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200678 return tcp.rstrip("\n")
John DeNisco68b0ee32017-09-27 16:35:23 -0400679
680 def apply_vpp_startup(self):
681 """
682 Apply the vpp startup configration
683
684 """
685
686 # Apply the VPP startup configruation
687 for i in self._nodes.items():
688 node = i[1]
689
690 # Get the startup file
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200691 rootdir = node["rootdir"]
692 sfile = rootdir + node["vpp"]["startup_config_file"]
John DeNisco68b0ee32017-09-27 16:35:23 -0400693
John DeNiscodf35a202020-06-19 15:28:48 -0400694 # Get the buffers
John DeNisco68b0ee32017-09-27 16:35:23 -0400695 devices = self._apply_vpp_devices(node)
696
697 # Get the CPU config
698 cpu = self._apply_vpp_cpu(node)
699
John DeNiscodf35a202020-06-19 15:28:48 -0400700 # Get the buffer configuration
701 buffers = self._apply_buffers(node)
John DeNisco68b0ee32017-09-27 16:35:23 -0400702 # Get the TCP configuration, if any
703 tcp = self._apply_vpp_tcp(node)
704
705 # Make a backup if needed
706 self._autoconfig_backup_file(sfile)
707
708 # Get the template
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200709 tfile = sfile + ".template"
710 (ret, stdout, stderr) = VPPUtil.exec_command("cat {}".format(tfile))
John DeNisco68b0ee32017-09-27 16:35:23 -0400711 if ret != 0:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200712 raise RuntimeError(
713 "Executing cat command failed to node {}".format(node["host"])
714 )
715 startup = stdout.format(cpu=cpu, buffers=buffers, devices=devices, tcp=tcp)
John DeNisco68b0ee32017-09-27 16:35:23 -0400716
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200717 (ret, stdout, stderr) = VPPUtil.exec_command("rm {}".format(sfile))
John DeNisco68b0ee32017-09-27 16:35:23 -0400718 if ret != 0:
719 logging.debug(stderr)
720
721 cmd = "sudo cat > {0} << EOF\n{1}\n".format(sfile, startup)
722 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
723 if ret != 0:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200724 raise RuntimeError("Writing config failed node {}".format(node["host"]))
John DeNisco68b0ee32017-09-27 16:35:23 -0400725
726 def apply_grub_cmdline(self):
727 """
728 Apply the grub cmdline
729
730 """
731
732 for i in self._nodes.items():
733 node = i[1]
734
735 # Get the isolated CPUs
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200736 other_workers = node["cpu"]["other_workers"]
737 vpp_workers = node["cpu"]["vpp_workers"]
738 if "vpp_main_core" in node["cpu"]:
739 vpp_main_core = node["cpu"]["vpp_main_core"]
John DeNiscoddecfb32017-11-15 08:50:57 -0500740 else:
741 vpp_main_core = 0
John DeNisco68b0ee32017-09-27 16:35:23 -0400742 all_workers = []
743 if other_workers is not None:
744 all_workers = [other_workers]
Paul Vinciguerrab11c2882019-04-30 20:57:04 -0700745 if vpp_main_core != 0:
John DeNisco68b0ee32017-09-27 16:35:23 -0400746 all_workers += [(vpp_main_core, vpp_main_core)]
747 all_workers += vpp_workers
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200748 isolated_cpus = ""
John DeNisco68b0ee32017-09-27 16:35:23 -0400749 for idx, worker in enumerate(all_workers):
750 if worker is None:
751 continue
752 if idx > 0:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200753 isolated_cpus += ","
John DeNisco68b0ee32017-09-27 16:35:23 -0400754 if worker[0] == worker[1]:
755 isolated_cpus += "{}".format(worker[0])
756 else:
757 isolated_cpus += "{}-{}".format(worker[0], worker[1])
758
759 vppgrb = VppGrubUtil(node)
760 current_cmdline = vppgrb.get_current_cmdline()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200761 if "grub" not in node:
762 node["grub"] = {}
763 node["grub"]["current_cmdline"] = current_cmdline
764 node["grub"]["default_cmdline"] = vppgrb.apply_cmdline(node, isolated_cpus)
John DeNisco68b0ee32017-09-27 16:35:23 -0400765
766 self.updateconfig()
767
768 def get_hugepages(self):
769 """
770 Get the hugepage configuration
771
772 """
773
774 for i in self._nodes.items():
775 node = i[1]
776
777 hpg = VppHugePageUtil(node)
778 max_map_count, shmmax = hpg.get_huge_page_config()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200779 node["hugepages"]["max_map_count"] = max_map_count
780 node["hugepages"]["shmax"] = shmmax
John DeNisco68b0ee32017-09-27 16:35:23 -0400781 total, free, size, memtotal, memfree = hpg.get_actual_huge_pages()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200782 node["hugepages"]["actual_total"] = total
783 node["hugepages"]["free"] = free
784 node["hugepages"]["size"] = size
785 node["hugepages"]["memtotal"] = memtotal
786 node["hugepages"]["memfree"] = memfree
John DeNisco68b0ee32017-09-27 16:35:23 -0400787
788 self.updateconfig()
789
790 def get_grub(self):
791 """
792 Get the grub configuration
793
794 """
795
796 for i in self._nodes.items():
797 node = i[1]
798
799 vppgrb = VppGrubUtil(node)
800 current_cmdline = vppgrb.get_current_cmdline()
801 default_cmdline = vppgrb.get_default_cmdline()
802
803 # Get the total number of isolated CPUs
804 current_iso_cpus = 0
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200805 iso_cpur = re.findall(r"isolcpus=[\w+\-,]+", current_cmdline)
John DeNisco68b0ee32017-09-27 16:35:23 -0400806 iso_cpurl = len(iso_cpur)
807 if iso_cpurl > 0:
808 iso_cpu_str = iso_cpur[0]
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200809 iso_cpu_str = iso_cpu_str.split("=")[1]
810 iso_cpul = iso_cpu_str.split(",")
John DeNisco68b0ee32017-09-27 16:35:23 -0400811 for iso_cpu in iso_cpul:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200812 isocpuspl = iso_cpu.split("-")
Paul Vinciguerrab11c2882019-04-30 20:57:04 -0700813 if len(isocpuspl) == 1:
John DeNisco68b0ee32017-09-27 16:35:23 -0400814 current_iso_cpus += 1
815 else:
816 first = int(isocpuspl[0])
817 second = int(isocpuspl[1])
818 if first == second:
819 current_iso_cpus += 1
820 else:
821 current_iso_cpus += second - first
822
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200823 if "grub" not in node:
824 node["grub"] = {}
825 node["grub"]["current_cmdline"] = current_cmdline
826 node["grub"]["default_cmdline"] = default_cmdline
827 node["grub"]["current_iso_cpus"] = current_iso_cpus
John DeNisco68b0ee32017-09-27 16:35:23 -0400828
829 self.updateconfig()
830
831 @staticmethod
832 def _get_device(node):
833 """
834 Get the device configuration for a single node
835
836 :param node: Node dictionary with cpuinfo.
837 :type node: dict
838
839 """
840
841 vpp = VppPCIUtil(node)
842 vpp.get_all_devices()
843
844 # Save the device information
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200845 node["devices"] = {}
846 node["devices"]["dpdk_devices"] = vpp.get_dpdk_devices()
847 node["devices"]["kernel_devices"] = vpp.get_kernel_devices()
848 node["devices"]["other_devices"] = vpp.get_other_devices()
849 node["devices"]["linkup_devices"] = vpp.get_link_up_devices()
John DeNisco68b0ee32017-09-27 16:35:23 -0400850
851 def get_devices_per_node(self):
852 """
853 Get the device configuration for all the nodes
854
855 """
856
857 for i in self._nodes.items():
858 node = i[1]
859 # Update the interface data
860
861 self._get_device(node)
862
863 self.updateconfig()
864
865 @staticmethod
866 def get_cpu_layout(node):
867 """
868 Get the cpu layout
869
870 using lscpu -p get the cpu layout.
871 Returns a list with each item representing a single cpu.
872
873 :param node: Node dictionary.
874 :type node: dict
875 :returns: The cpu layout
876 :rtype: list
877 """
878
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200879 cmd = "lscpu -p"
John DeNisco68b0ee32017-09-27 16:35:23 -0400880 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
881 if ret != 0:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200882 raise RuntimeError(
883 "{} failed on node {} {}".format(cmd, node["host"], stderr)
884 )
John DeNisco68b0ee32017-09-27 16:35:23 -0400885
886 pcpus = []
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200887 lines = stdout.split("\n")
John DeNisco68b0ee32017-09-27 16:35:23 -0400888 for line in lines:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200889 if line == "" or line[0] == "#":
John DeNisco68b0ee32017-09-27 16:35:23 -0400890 continue
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200891 linesplit = line.split(",")
892 layout = {
893 "cpu": linesplit[0],
894 "core": linesplit[1],
895 "socket": linesplit[2],
896 "node": linesplit[3],
897 }
John DeNisco68b0ee32017-09-27 16:35:23 -0400898
899 # cpu, core, socket, node
900 pcpus.append(layout)
901
902 return pcpus
903
904 def get_cpu(self):
905 """
906 Get the cpu configuration
907
908 """
909
910 # Get the CPU layout
911 CpuUtils.get_cpu_layout_from_all_nodes(self._nodes)
912
913 for i in self._nodes.items():
914 node = i[1]
915
916 # Get the cpu layout
917 layout = self.get_cpu_layout(node)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200918 node["cpu"]["layout"] = layout
John DeNisco68b0ee32017-09-27 16:35:23 -0400919
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200920 cpuinfo = node["cpuinfo"]
John DeNisco68b0ee32017-09-27 16:35:23 -0400921 smt_enabled = CpuUtils.is_smt_enabled(cpuinfo)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200922 node["cpu"]["smt_enabled"] = smt_enabled
John DeNisco68b0ee32017-09-27 16:35:23 -0400923
924 # We don't want to write the cpuinfo
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200925 node["cpuinfo"] = ""
John DeNisco68b0ee32017-09-27 16:35:23 -0400926
927 # Write the config
928 self.updateconfig()
929
930 def discover(self):
931 """
932 Get the current system configuration.
933
934 """
935
936 # Get the Huge Page configuration
937 self.get_hugepages()
938
939 # Get the device configuration
940 self.get_devices_per_node()
941
942 # Get the CPU configuration
943 self.get_cpu()
944
945 # Get the current grub cmdline
946 self.get_grub()
947
948 def _modify_cpu_questions(self, node, total_cpus, numa_nodes):
949 """
950 Ask the user questions related to the cpu configuration.
951
952 :param node: Node dictionary
953 :param total_cpus: The total number of cpus in the system
954 :param numa_nodes: The list of numa nodes in the system
955 :type node: dict
956 :type total_cpus: int
957 :type numa_nodes: list
958 """
959
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200960 print(
961 "\nYour system has {} core(s) and {} Numa Nodes.".format(
962 total_cpus, len(numa_nodes)
963 )
964 )
965 print(
966 "To begin, we suggest not reserving any cores for "
967 "VPP or other processes."
968 )
969 print(
970 "Then to improve performance start reserving cores and "
971 "adding queues as needed."
972 )
John DeNisco68b0ee32017-09-27 16:35:23 -0400973
jdeniscoaf86c632019-04-05 12:25:50 -0400974 # Leave 1 for the general system
975 total_cpus -= 1
976 max_vpp_cpus = min(total_cpus, 4)
John DeNisco68b0ee32017-09-27 16:35:23 -0400977 total_vpp_cpus = 0
978 if max_vpp_cpus > 0:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200979 question = (
980 "\nHow many core(s) shall we reserve for "
981 "VPP [0-{}][0]? ".format(max_vpp_cpus)
982 )
John DeNisco68b0ee32017-09-27 16:35:23 -0400983 total_vpp_cpus = self._ask_user_range(question, 0, max_vpp_cpus, 0)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200984 node["cpu"]["total_vpp_cpus"] = total_vpp_cpus
John DeNisco68b0ee32017-09-27 16:35:23 -0400985
jdeniscoaf86c632019-04-05 12:25:50 -0400986 total_other_cpus = 0
987 max_other_cores = total_cpus - total_vpp_cpus
988 if max_other_cores > 0:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200989 question = (
990 "How many core(s) do you want to reserve for "
991 "processes other than VPP? [0-{}][0]? ".format(str(max_other_cores))
992 )
jdeniscoaf86c632019-04-05 12:25:50 -0400993 total_other_cpus = self._ask_user_range(question, 0, max_other_cores, 0)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200994 node["cpu"]["total_other_cpus"] = total_other_cpus
John DeNiscoa7da67f2018-01-26 14:55:33 -0500995
jdeniscoaf86c632019-04-05 12:25:50 -0400996 max_main_cpus = total_cpus - total_vpp_cpus - total_other_cpus
John DeNisco68b0ee32017-09-27 16:35:23 -0400997 reserve_vpp_main_core = False
998 if max_main_cpus > 0:
999 question = "Should we reserve 1 core for the VPP Main thread? "
1000 question += "[y/N]? "
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001001 answer = self._ask_user_yn(question, "n")
1002 if answer == "y":
John DeNisco68b0ee32017-09-27 16:35:23 -04001003 reserve_vpp_main_core = True
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001004 node["cpu"]["reserve_vpp_main_core"] = reserve_vpp_main_core
1005 node["cpu"]["vpp_main_core"] = 0
John DeNisco68b0ee32017-09-27 16:35:23 -04001006
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001007 question = (
1008 "How many RX queues per port shall we use for "
1009 "VPP [1-4][1]? ".format(max_vpp_cpus)
1010 )
John DeNiscoa7da67f2018-01-26 14:55:33 -05001011 total_rx_queues = self._ask_user_range(question, 1, 4, 1)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001012 node["cpu"]["total_rx_queues"] = total_rx_queues
John DeNiscoa7da67f2018-01-26 14:55:33 -05001013
John DeNiscoddecfb32017-11-15 08:50:57 -05001014 def modify_cpu(self, ask_questions=True):
John DeNisco68b0ee32017-09-27 16:35:23 -04001015 """
1016 Modify the cpu configuration, asking for the user for the values.
1017
John DeNiscoddecfb32017-11-15 08:50:57 -05001018 :param ask_questions: When true ask the user for config parameters
1019
John DeNisco68b0ee32017-09-27 16:35:23 -04001020 """
1021
1022 # Get the CPU layout
1023 CpuUtils.get_cpu_layout_from_all_nodes(self._nodes)
1024
1025 for i in self._nodes.items():
1026 node = i[1]
1027 total_cpus = 0
1028 total_cpus_per_slice = 0
1029 cpus_per_node = {}
1030 numa_nodes = []
1031 cores = []
1032 cpu_layout = self.get_cpu_layout(node)
1033
1034 # Assume the number of cpus per slice is always the same as the
1035 # first slice
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001036 first_node = "0"
John DeNisco68b0ee32017-09-27 16:35:23 -04001037 for cpu in cpu_layout:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001038 if cpu["node"] != first_node:
John DeNisco68b0ee32017-09-27 16:35:23 -04001039 break
1040 total_cpus_per_slice += 1
1041
1042 # Get the total number of cpus, cores, and numa nodes from the
1043 # cpu layout
1044 for cpul in cpu_layout:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001045 numa_node = cpul["node"]
1046 core = cpul["core"]
1047 cpu = cpul["cpu"]
John DeNisco68b0ee32017-09-27 16:35:23 -04001048 total_cpus += 1
1049
1050 if numa_node not in cpus_per_node:
1051 cpus_per_node[numa_node] = []
1052 cpuperslice = int(cpu) % total_cpus_per_slice
1053 if cpuperslice == 0:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001054 cpus_per_node[numa_node].append(
1055 (int(cpu), int(cpu) + total_cpus_per_slice - 1)
1056 )
John DeNisco68b0ee32017-09-27 16:35:23 -04001057 if numa_node not in numa_nodes:
1058 numa_nodes.append(numa_node)
1059 if core not in cores:
1060 cores.append(core)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001061 node["cpu"]["cpus_per_node"] = cpus_per_node
John DeNisco68b0ee32017-09-27 16:35:23 -04001062
1063 # Ask the user some questions
jdeniscoaf86c632019-04-05 12:25:50 -04001064 if ask_questions and total_cpus >= 4:
John DeNiscoddecfb32017-11-15 08:50:57 -05001065 self._modify_cpu_questions(node, total_cpus, numa_nodes)
John DeNisco68b0ee32017-09-27 16:35:23 -04001066
1067 # Populate the interfaces with the numa node
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001068 if "interfaces" in node:
1069 ikeys = node["interfaces"].keys()
John DeNiscoddecfb32017-11-15 08:50:57 -05001070 VPPUtil.get_interfaces_numa_node(node, *tuple(ikeys))
John DeNisco68b0ee32017-09-27 16:35:23 -04001071
1072 # We don't want to write the cpuinfo
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001073 node["cpuinfo"] = ""
John DeNisco68b0ee32017-09-27 16:35:23 -04001074
1075 # Write the configs
1076 self._update_auto_config()
1077 self.updateconfig()
1078
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001079 def _modify_other_devices(self, node, other_devices, kernel_devices, dpdk_devices):
John DeNisco68b0ee32017-09-27 16:35:23 -04001080 """
1081 Modify the devices configuration, asking for the user for the values.
1082
1083 """
1084
1085 odevices_len = len(other_devices)
1086 if odevices_len > 0:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001087 print(
1088 "\nThese device(s) are currently NOT being used " "by VPP or the OS.\n"
1089 )
John DeNisco68b0ee32017-09-27 16:35:23 -04001090 VppPCIUtil.show_vpp_devices(other_devices, show_interfaces=False)
1091 question = "\nWould you like to give any of these devices"
1092 question += " back to the OS [Y/n]? "
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001093 answer = self._ask_user_yn(question, "Y")
1094 if answer == "y":
John DeNisco68b0ee32017-09-27 16:35:23 -04001095 vppd = {}
1096 for dit in other_devices.items():
1097 dvid = dit[0]
1098 device = dit[1]
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001099 question = "Would you like to use device {} for".format(dvid)
John DeNisco68b0ee32017-09-27 16:35:23 -04001100 question += " the OS [y/N]? "
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001101 answer = self._ask_user_yn(question, "n")
1102 if answer == "y":
1103 if (
1104 "unused" in device
1105 and len(device["unused"]) != 0
1106 and device["unused"][0] != ""
1107 ):
1108 driver = device["unused"][0]
1109 ret = VppPCIUtil.bind_vpp_device(node, driver, dvid)
jdenisco7c37a672018-11-20 11:25:17 -05001110 if ret:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001111 logging.debug("Could not bind device {}".format(dvid))
jdenisco7c37a672018-11-20 11:25:17 -05001112 else:
1113 vppd[dvid] = device
John DeNisco68b0ee32017-09-27 16:35:23 -04001114 for dit in vppd.items():
1115 dvid = dit[0]
1116 device = dit[1]
1117 kernel_devices[dvid] = device
1118 del other_devices[dvid]
1119
1120 odevices_len = len(other_devices)
1121 if odevices_len > 0:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001122 print("\nThese device(s) are still NOT being used " "by VPP or the OS.\n")
John DeNisco68b0ee32017-09-27 16:35:23 -04001123 VppPCIUtil.show_vpp_devices(other_devices, show_interfaces=False)
1124 question = "\nWould you like use any of these for VPP [y/N]? "
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001125 answer = self._ask_user_yn(question, "N")
1126 if answer == "y":
John DeNisco68b0ee32017-09-27 16:35:23 -04001127 vppd = {}
1128 for dit in other_devices.items():
1129 dvid = dit[0]
1130 device = dit[1]
1131 question = "Would you like to use device {} ".format(dvid)
1132 question += "for VPP [y/N]? "
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001133 answer = self._ask_user_yn(question, "n")
1134 if answer == "y":
John DeNisco68b0ee32017-09-27 16:35:23 -04001135 vppd[dvid] = device
1136 for dit in vppd.items():
1137 dvid = dit[0]
1138 device = dit[1]
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001139 if (
1140 "unused" in device
1141 and len(device["unused"]) != 0
1142 and device["unused"][0] != ""
1143 ):
1144 driver = device["unused"][0]
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001145 logging.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001146 "Binding device {} to driver {}".format(dvid, driver)
1147 )
jdenisco7c37a672018-11-20 11:25:17 -05001148 ret = VppPCIUtil.bind_vpp_device(node, driver, dvid)
1149 if ret:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001150 logging.debug("Could not bind device {}".format(dvid))
jdenisco7c37a672018-11-20 11:25:17 -05001151 else:
1152 dpdk_devices[dvid] = device
1153 del other_devices[dvid]
John DeNisco68b0ee32017-09-27 16:35:23 -04001154
John DeNiscoddecfb32017-11-15 08:50:57 -05001155 def update_interfaces_config(self):
1156 """
1157 Modify the interfaces directly from the config file.
1158
1159 """
1160
1161 for i in self._nodes.items():
1162 node = i[1]
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001163 devices = node["devices"]
1164 all_devices = devices["other_devices"]
1165 all_devices.update(devices["dpdk_devices"])
1166 all_devices.update(devices["kernel_devices"])
John DeNiscoddecfb32017-11-15 08:50:57 -05001167
1168 current_ifcs = {}
1169 interfaces = {}
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001170 if "interfaces" in node:
1171 current_ifcs = node["interfaces"]
John DeNiscoddecfb32017-11-15 08:50:57 -05001172 if current_ifcs:
1173 for ifc in current_ifcs.values():
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001174 dvid = ifc["pci_address"]
John DeNiscoddecfb32017-11-15 08:50:57 -05001175 if dvid in all_devices:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001176 VppPCIUtil.vpp_create_interface(
1177 interfaces, dvid, all_devices[dvid]
1178 )
1179 node["interfaces"] = interfaces
John DeNiscoddecfb32017-11-15 08:50:57 -05001180
1181 self.updateconfig()
1182
John DeNisco68b0ee32017-09-27 16:35:23 -04001183 def modify_devices(self):
1184 """
1185 Modify the devices configuration, asking for the user for the values.
1186
1187 """
1188
1189 for i in self._nodes.items():
1190 node = i[1]
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001191 devices = node["devices"]
1192 other_devices = devices["other_devices"]
1193 kernel_devices = devices["kernel_devices"]
1194 dpdk_devices = devices["dpdk_devices"]
John DeNisco68b0ee32017-09-27 16:35:23 -04001195
1196 if other_devices:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001197 self._modify_other_devices(
1198 node, other_devices, kernel_devices, dpdk_devices
1199 )
John DeNisco68b0ee32017-09-27 16:35:23 -04001200
1201 # Get the devices again for this node
1202 self._get_device(node)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001203 devices = node["devices"]
1204 kernel_devices = devices["kernel_devices"]
1205 dpdk_devices = devices["dpdk_devices"]
John DeNisco68b0ee32017-09-27 16:35:23 -04001206
1207 klen = len(kernel_devices)
1208 if klen > 0:
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001209 print("\nThese devices are safe to be used with VPP.\n")
John DeNisco68b0ee32017-09-27 16:35:23 -04001210 VppPCIUtil.show_vpp_devices(kernel_devices)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001211 question = (
1212 "\nWould you like to use any of these " "device(s) for VPP [y/N]? "
1213 )
1214 answer = self._ask_user_yn(question, "n")
1215 if answer == "y":
John DeNisco68b0ee32017-09-27 16:35:23 -04001216 vppd = {}
1217 for dit in kernel_devices.items():
1218 dvid = dit[0]
1219 device = dit[1]
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001220 question = "Would you like to use device {} ".format(dvid)
John DeNisco68b0ee32017-09-27 16:35:23 -04001221 question += "for VPP [y/N]? "
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001222 answer = self._ask_user_yn(question, "n")
1223 if answer == "y":
John DeNisco68b0ee32017-09-27 16:35:23 -04001224 vppd[dvid] = device
1225 for dit in vppd.items():
1226 dvid = dit[0]
1227 device = dit[1]
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001228 if (
1229 "unused" in device
1230 and len(device["unused"]) != 0
1231 and device["unused"][0] != ""
1232 ):
1233 driver = device["unused"][0]
1234 question = "Would you like to bind the driver {} for {} [y/N]? ".format(
1235 driver, dvid
1236 )
1237 answer = self._ask_user_yn(question, "n")
1238 if answer == "y":
1239 logging.debug(
1240 "Binding device {} to driver {}".format(
1241 dvid, driver
1242 )
1243 )
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001244 ret = VppPCIUtil.bind_vpp_device(node, driver, dvid)
1245 if ret:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001246 logging.debug(
1247 "Could not bind device {}".format(dvid)
1248 )
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001249 dpdk_devices[dvid] = device
1250 del kernel_devices[dvid]
John DeNisco68b0ee32017-09-27 16:35:23 -04001251
1252 dlen = len(dpdk_devices)
1253 if dlen > 0:
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001254 print("\nThese device(s) are already using DPDK.\n")
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001255 VppPCIUtil.show_vpp_devices(dpdk_devices, show_interfaces=False)
John DeNisco68b0ee32017-09-27 16:35:23 -04001256 question = "\nWould you like to remove any of "
1257 question += "these device(s) [y/N]? "
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001258 answer = self._ask_user_yn(question, "n")
1259 if answer == "y":
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001260 vppdl = {}
John DeNisco68b0ee32017-09-27 16:35:23 -04001261 for dit in dpdk_devices.items():
1262 dvid = dit[0]
1263 device = dit[1]
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001264 question = "Would you like to remove {} [y/N]? ".format(dvid)
1265 answer = self._ask_user_yn(question, "n")
1266 if answer == "y":
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001267 vppdl[dvid] = device
1268 for dit in vppdl.items():
John DeNisco68b0ee32017-09-27 16:35:23 -04001269 dvid = dit[0]
1270 device = dit[1]
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001271 if (
1272 "unused" in device
1273 and len(device["unused"]) != 0
1274 and device["unused"][0] != ""
1275 ):
1276 driver = device["unused"][0]
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001277 logging.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001278 "Binding device {} to driver {}".format(dvid, driver)
1279 )
1280 ret = VppPCIUtil.bind_vpp_device(node, driver, dvid)
jdenisco7c37a672018-11-20 11:25:17 -05001281 if ret:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001282 logging.debug("Could not bind device {}".format(dvid))
jdenisco7c37a672018-11-20 11:25:17 -05001283 else:
1284 kernel_devices[dvid] = device
1285 del dpdk_devices[dvid]
John DeNisco68b0ee32017-09-27 16:35:23 -04001286
1287 interfaces = {}
1288 for dit in dpdk_devices.items():
1289 dvid = dit[0]
1290 device = dit[1]
1291 VppPCIUtil.vpp_create_interface(interfaces, dvid, device)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001292 node["interfaces"] = interfaces
John DeNisco68b0ee32017-09-27 16:35:23 -04001293
John DeNisco68b0ee32017-09-27 16:35:23 -04001294 self._update_auto_config()
1295 self.updateconfig()
1296
1297 def modify_huge_pages(self):
1298 """
1299 Modify the huge page configuration, asking for the user for the values.
1300
1301 """
1302
1303 for i in self._nodes.items():
1304 node = i[1]
1305
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001306 total = node["hugepages"]["actual_total"]
1307 free = node["hugepages"]["free"]
1308 size = node["hugepages"]["size"]
1309 memfree = node["hugepages"]["memfree"].split(" ")[0]
1310 hugesize = int(size.split(" ")[0])
John DeNisco68b0ee32017-09-27 16:35:23 -04001311 # The max number of huge pages should be no more than
1312 # 70% of total free memory
John DeNiscodf35a202020-06-19 15:28:48 -04001313 maxpages = (int(memfree) * MAX_PERCENT_FOR_HUGE_PAGES // 100) // hugesize
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001314 print("\nThere currently {} {} huge pages free.".format(free, size))
1315 question = "Do you want to reconfigure the number of " "huge pages [y/N]? "
1316 answer = self._ask_user_yn(question, "n")
1317 if answer == "n":
1318 node["hugepages"]["total"] = total
John DeNisco68b0ee32017-09-27 16:35:23 -04001319 continue
1320
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001321 print("\nThere currently a total of {} huge pages.".format(total))
1322 question = "How many huge pages do you want [{} - {}][{}]? ".format(
1323 MIN_TOTAL_HUGE_PAGES, maxpages, MIN_TOTAL_HUGE_PAGES
1324 )
John DeNisco68b0ee32017-09-27 16:35:23 -04001325 answer = self._ask_user_range(question, 1024, maxpages, 1024)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001326 node["hugepages"]["total"] = str(answer)
John DeNisco68b0ee32017-09-27 16:35:23 -04001327
1328 # Update auto-config.yaml
1329 self._update_auto_config()
1330
1331 # Rediscover just the hugepages
1332 self.get_hugepages()
1333
1334 def get_tcp_params(self):
1335 """
1336 Get the tcp configuration
1337
1338 """
1339 # maybe nothing to do here?
1340 self.updateconfig()
1341
1342 def acquire_tcp_params(self):
1343 """
1344 Ask the user for TCP stack configuration parameters
1345
1346 """
1347
1348 for i in self._nodes.items():
1349 node = i[1]
1350
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001351 question = (
1352 "\nHow many active-open / tcp client sessions are "
1353 "expected [0-10000000][0]? "
1354 )
John DeNisco68b0ee32017-09-27 16:35:23 -04001355 answer = self._ask_user_range(question, 0, 10000000, 0)
1356 # Less than 10K is equivalent to 0
1357 if int(answer) < 10000:
1358 answer = 0
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001359 node["tcp"]["active_open_sessions"] = answer
John DeNisco68b0ee32017-09-27 16:35:23 -04001360
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001361 question = (
1362 "How many passive-open / tcp server sessions are "
1363 "expected [0-10000000][0]? "
1364 )
John DeNisco68b0ee32017-09-27 16:35:23 -04001365 answer = self._ask_user_range(question, 0, 10000000, 0)
1366 # Less than 10K is equivalent to 0
1367 if int(answer) < 10000:
1368 answer = 0
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001369 node["tcp"]["passive_open_sessions"] = answer
John DeNisco68b0ee32017-09-27 16:35:23 -04001370
1371 # Update auto-config.yaml
1372 self._update_auto_config()
1373
1374 # Rediscover tcp parameters
1375 self.get_tcp_params()
1376
1377 @staticmethod
1378 def patch_qemu(node):
1379 """
1380 Patch qemu with the correct patches.
1381
1382 :param node: Node dictionary
1383 :type node: dict
1384 """
1385
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001386 print('\nWe are patching the node "{}":\n'.format(node["host"]))
John DeNisco68b0ee32017-09-27 16:35:23 -04001387 QemuUtils.build_qemu(node, force_install=True, apply_patch=True)
1388
1389 @staticmethod
1390 def cpu_info(node):
1391 """
1392 print the CPU information
1393
1394 """
1395
1396 cpu = CpuUtils.get_cpu_info_per_node(node)
1397
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001398 item = "Model name"
John DeNisco68b0ee32017-09-27 16:35:23 -04001399 if item in cpu:
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001400 print("{:>20}: {}".format(item, cpu[item]))
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001401 item = "CPU(s)"
John DeNisco68b0ee32017-09-27 16:35:23 -04001402 if item in cpu:
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001403 print("{:>20}: {}".format(item, cpu[item]))
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001404 item = "Thread(s) per core"
John DeNisco68b0ee32017-09-27 16:35:23 -04001405 if item in cpu:
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001406 print("{:>20}: {}".format(item, cpu[item]))
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001407 item = "Core(s) per socket"
John DeNisco68b0ee32017-09-27 16:35:23 -04001408 if item in cpu:
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001409 print("{:>20}: {}".format(item, cpu[item]))
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001410 item = "Socket(s)"
John DeNisco68b0ee32017-09-27 16:35:23 -04001411 if item in cpu:
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001412 print("{:>20}: {}".format(item, cpu[item]))
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001413 item = "NUMA node(s)"
John DeNisco68b0ee32017-09-27 16:35:23 -04001414 numa_nodes = 0
1415 if item in cpu:
1416 numa_nodes = int(cpu[item])
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001417 for i in range(0, numa_nodes):
John DeNisco68b0ee32017-09-27 16:35:23 -04001418 item = "NUMA node{} CPU(s)".format(i)
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001419 print("{:>20}: {}".format(item, cpu[item]))
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001420 item = "CPU max MHz"
John DeNisco68b0ee32017-09-27 16:35:23 -04001421 if item in cpu:
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001422 print("{:>20}: {}".format(item, cpu[item]))
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001423 item = "CPU min MHz"
John DeNisco68b0ee32017-09-27 16:35:23 -04001424 if item in cpu:
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001425 print("{:>20}: {}".format(item, cpu[item]))
John DeNisco68b0ee32017-09-27 16:35:23 -04001426
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001427 if node["cpu"]["smt_enabled"]:
1428 smt = "Enabled"
John DeNisco68b0ee32017-09-27 16:35:23 -04001429 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001430 smt = "Disabled"
1431 print("{:>20}: {}".format("SMT", smt))
John DeNisco68b0ee32017-09-27 16:35:23 -04001432
1433 # VPP Threads
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001434 print("\nVPP Threads: (Name: Cpu Number)")
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001435 vpp_processes = cpu["vpp_processes"]
John DeNisco68b0ee32017-09-27 16:35:23 -04001436 for i in vpp_processes.items():
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001437 print(" {:10}: {:4}".format(i[0], i[1]))
John DeNisco68b0ee32017-09-27 16:35:23 -04001438
1439 @staticmethod
1440 def device_info(node):
1441 """
1442 Show the device information.
1443
1444 """
1445
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001446 if "cpu" in node and "total_mbufs" in node["cpu"]:
1447 total_mbufs = node["cpu"]["total_mbufs"]
Paul Vinciguerrab11c2882019-04-30 20:57:04 -07001448 if total_mbufs != 0:
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001449 print("Total Number of Buffers: {}".format(total_mbufs))
John DeNisco68b0ee32017-09-27 16:35:23 -04001450
1451 vpp = VppPCIUtil(node)
1452 vpp.get_all_devices()
1453 linkup_devs = vpp.get_link_up_devices()
1454 if len(linkup_devs):
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001455 print("\nDevices with link up (can not be used with VPP):")
John DeNisco68b0ee32017-09-27 16:35:23 -04001456 vpp.show_vpp_devices(linkup_devs, show_header=False)
1457 # for dev in linkup_devs:
1458 # print (" " + dev)
1459 kernel_devs = vpp.get_kernel_devices()
1460 if len(kernel_devs):
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001461 print("\nDevices bound to kernel drivers:")
John DeNisco68b0ee32017-09-27 16:35:23 -04001462 vpp.show_vpp_devices(kernel_devs, show_header=False)
1463 else:
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001464 print("\nNo devices bound to kernel drivers")
John DeNisco68b0ee32017-09-27 16:35:23 -04001465
1466 dpdk_devs = vpp.get_dpdk_devices()
1467 if len(dpdk_devs):
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001468 print("\nDevices bound to DPDK drivers:")
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001469 vpp.show_vpp_devices(dpdk_devs, show_interfaces=True, show_header=False)
John DeNisco68b0ee32017-09-27 16:35:23 -04001470 else:
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001471 print("\nNo devices bound to DPDK drivers")
John DeNisco68b0ee32017-09-27 16:35:23 -04001472
jdenisco7c37a672018-11-20 11:25:17 -05001473 other_devs = vpp.get_other_devices()
1474 if len(other_devs):
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001475 print("\nDevices not bound to Kernel or DPDK drivers:")
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001476 vpp.show_vpp_devices(other_devs, show_interfaces=True, show_header=False)
jdenisco7c37a672018-11-20 11:25:17 -05001477 else:
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001478 print("\nNo devices not bound to Kernel or DPDK drivers")
jdenisco7c37a672018-11-20 11:25:17 -05001479
John DeNisco68b0ee32017-09-27 16:35:23 -04001480 vpputl = VPPUtil()
1481 interfaces = vpputl.get_hardware(node)
1482 if interfaces == {}:
1483 return
1484
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001485 print("\nDevices in use by VPP:")
John DeNisco68b0ee32017-09-27 16:35:23 -04001486
1487 if len(interfaces.items()) < 2:
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001488 print("None")
John DeNisco68b0ee32017-09-27 16:35:23 -04001489 return
1490
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001491 print(
1492 "{:30} {:4} {:4} {:7} {:4} {:7}".format(
1493 "Name", "Numa", "RXQs", "RXDescs", "TXQs", "TXDescs"
1494 )
1495 )
John DeNisco68b0ee32017-09-27 16:35:23 -04001496 for intf in sorted(interfaces.items()):
1497 name = intf[0]
1498 value = intf[1]
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001499 if name == "local0":
John DeNisco68b0ee32017-09-27 16:35:23 -04001500 continue
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001501 numa = rx_qs = rx_ds = tx_qs = tx_ds = ""
1502 if "numa" in value:
1503 numa = int(value["numa"])
1504 if "rx queues" in value:
1505 rx_qs = int(value["rx queues"])
1506 if "rx descs" in value:
1507 rx_ds = int(value["rx descs"])
1508 if "tx queues" in value:
1509 tx_qs = int(value["tx queues"])
1510 if "tx descs" in value:
1511 tx_ds = int(value["tx descs"])
John DeNisco68b0ee32017-09-27 16:35:23 -04001512
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001513 print(
1514 "{:30} {:>4} {:>4} {:>7} {:>4} {:>7}".format(
1515 name, numa, rx_qs, rx_ds, tx_qs, tx_ds
1516 )
1517 )
John DeNisco68b0ee32017-09-27 16:35:23 -04001518
1519 @staticmethod
1520 def hugepage_info(node):
1521 """
1522 Show the huge page information.
1523
1524 """
1525
1526 hpg = VppHugePageUtil(node)
1527 hpg.show_huge_pages()
1528
1529 @staticmethod
jdeniscoaf86c632019-04-05 12:25:50 -04001530 def has_interfaces(node):
1531 """
1532 Check for interfaces, return tru if there is at least one
1533
1534 :returns: boolean
1535 """
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001536 if "interfaces" in node and len(node["interfaces"]):
jdeniscoaf86c632019-04-05 12:25:50 -04001537 return True
1538 else:
1539 return False
1540
1541 @staticmethod
John DeNisco68b0ee32017-09-27 16:35:23 -04001542 def min_system_resources(node):
1543 """
1544 Check the system for basic minimum resources, return true if
1545 there is enough.
1546
1547 :returns: boolean
John DeNisco68b0ee32017-09-27 16:35:23 -04001548 """
1549
1550 min_sys_res = True
1551
1552 # CPUs
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001553 if "layout" in node["cpu"]:
1554 total_cpus = len(node["cpu"]["layout"])
John DeNisco68b0ee32017-09-27 16:35:23 -04001555 if total_cpus < 2:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001556 print(
1557 "\nThere is only {} CPU(s) available on this system. "
1558 "This is not enough to run VPP.".format(total_cpus)
1559 )
John DeNisco68b0ee32017-09-27 16:35:23 -04001560 min_sys_res = False
1561
1562 # System Memory
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001563 if (
1564 "free" in node["hugepages"]
1565 and "memfree" in node["hugepages"]
1566 and "size" in node["hugepages"]
1567 ):
1568 free = node["hugepages"]["free"]
1569 memfree = float(node["hugepages"]["memfree"].split(" ")[0])
1570 hugesize = float(node["hugepages"]["size"].split(" ")[0])
John DeNisco68b0ee32017-09-27 16:35:23 -04001571
1572 memhugepages = MIN_TOTAL_HUGE_PAGES * hugesize
1573 percentmemhugepages = (memhugepages / memfree) * 100
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001574 if free is "0" and percentmemhugepages > MAX_PERCENT_FOR_HUGE_PAGES:
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001575 print(
1576 "\nThe System has only {} of free memory. You will not "
1577 "be able to allocate enough Huge Pages for VPP.".format(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001578 int(memfree)
1579 )
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001580 )
John DeNisco68b0ee32017-09-27 16:35:23 -04001581 min_sys_res = False
1582
1583 return min_sys_res
1584
1585 def sys_info(self):
1586 """
1587 Print the system information
1588
1589 """
1590
1591 for i in self._nodes.items():
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001592 print("\n==============================")
John DeNisco68b0ee32017-09-27 16:35:23 -04001593 name = i[0]
1594 node = i[1]
1595
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001596 print("NODE: {}\n".format(name))
John DeNisco68b0ee32017-09-27 16:35:23 -04001597
1598 # CPU
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001599 print("CPU:")
John DeNisco68b0ee32017-09-27 16:35:23 -04001600 self.cpu_info(node)
1601
1602 # Grub
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001603 print("\nGrub Command Line:")
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001604 if "grub" in node:
1605 print(" Current: {}".format(node["grub"]["current_cmdline"]))
1606 print(" Configured: {}".format(node["grub"]["default_cmdline"]))
John DeNisco68b0ee32017-09-27 16:35:23 -04001607
1608 # Huge Pages
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001609 print("\nHuge Pages:")
John DeNisco68b0ee32017-09-27 16:35:23 -04001610 self.hugepage_info(node)
1611
1612 # Devices
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001613 print("\nDevices:")
John DeNisco68b0ee32017-09-27 16:35:23 -04001614 self.device_info(node)
1615
1616 # Status
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001617 print("\nVPP Service Status:")
John DeNisco68b0ee32017-09-27 16:35:23 -04001618 state, errors = VPPUtil.status(node)
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001619 print(" {}".format(state))
John DeNisco68b0ee32017-09-27 16:35:23 -04001620 for e in errors:
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001621 print(" {}".format(e))
John DeNisco68b0ee32017-09-27 16:35:23 -04001622
1623 # Minimum system resources
1624 self.min_system_resources(node)
1625
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001626 print("\n==============================")
John DeNiscoa3db0782017-10-17 11:07:22 -04001627
1628 def _ipv4_interface_setup_questions(self, node):
1629 """
1630 Ask the user some questions and get a list of interfaces
1631 and IPv4 addresses associated with those interfaces
1632
1633 :param node: Node dictionary.
1634 :type node: dict
1635 :returns: A list or interfaces with ip addresses
1636 :rtype: dict
1637 """
1638
1639 vpputl = VPPUtil()
1640 interfaces = vpputl.get_hardware(node)
1641 if interfaces == {}:
1642 return
1643
1644 interfaces_with_ip = []
1645 for intf in sorted(interfaces.items()):
1646 name = intf[0]
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001647 if name == "local0":
John DeNiscoa3db0782017-10-17 11:07:22 -04001648 continue
1649
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001650 question = "Would you like add address to " "interface {} [Y/n]? ".format(
1651 name
1652 )
1653 answer = self._ask_user_yn(question, "y")
1654 if answer == "y":
John DeNiscoa3db0782017-10-17 11:07:22 -04001655 address = {}
John DeNiscoc6b2a202017-11-01 12:37:47 -04001656 addr = self._ask_user_ipv4()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001657 address["name"] = name
1658 address["addr"] = addr
John DeNiscoa3db0782017-10-17 11:07:22 -04001659 interfaces_with_ip.append(address)
1660
1661 return interfaces_with_ip
1662
1663 def ipv4_interface_setup(self):
1664 """
1665 After asking the user some questions, get a list of interfaces
1666 and IPv4 addresses associated with those interfaces
1667
1668 """
1669
1670 for i in self._nodes.items():
1671 node = i[1]
1672
1673 # Show the current interfaces with IP addresses
1674 current_ints = VPPUtil.get_int_ip(node)
Paul Vinciguerrab11c2882019-04-30 20:57:04 -07001675 if current_ints != {}:
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001676 print("\nThese are the current interfaces with IP addresses:")
John DeNiscoa3db0782017-10-17 11:07:22 -04001677 for items in sorted(current_ints.items()):
1678 name = items[0]
1679 value = items[1]
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001680 if "address" not in value:
1681 address = "Not Set"
John DeNiscoa3db0782017-10-17 11:07:22 -04001682 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001683 address = value["address"]
1684 print("{:30} {:20} {:10}".format(name, address, value["state"]))
1685 question = "\nWould you like to keep this configuration " "[Y/n]? "
1686 answer = self._ask_user_yn(question, "y")
1687 if answer == "y":
John DeNiscoa3db0782017-10-17 11:07:22 -04001688 continue
1689 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001690 print("\nThere are currently no interfaces with IP " "addresses.")
John DeNiscoa3db0782017-10-17 11:07:22 -04001691
1692 # Create a script that add the ip addresses to the interfaces
1693 # and brings the interfaces up
1694 ints_with_addrs = self._ipv4_interface_setup_questions(node)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001695 content = ""
John DeNiscoa3db0782017-10-17 11:07:22 -04001696 for ints in ints_with_addrs:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001697 name = ints["name"]
1698 addr = ints["addr"]
1699 setipstr = "set int ip address {} {}\n".format(name, addr)
1700 setintupstr = "set int state {} up\n".format(name)
John DeNiscoa3db0782017-10-17 11:07:22 -04001701 content += setipstr + setintupstr
1702
1703 # Write the content to the script
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001704 rootdir = node["rootdir"]
1705 filename = rootdir + "/vpp/vpp-config/scripts/set_int_ipv4_and_up"
1706 with open(filename, "w+") as sfile:
John DeNiscoa3db0782017-10-17 11:07:22 -04001707 sfile.write(content)
1708
1709 # Execute the script
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001710 cmd = "vppctl exec {}".format(filename)
John DeNiscoa3db0782017-10-17 11:07:22 -04001711 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
1712 if ret != 0:
1713 logging.debug(stderr)
1714
1715 print("\nA script as been created at {}".format(filename))
1716 print("This script can be run using the following:")
John DeNiscoc6b2a202017-11-01 12:37:47 -04001717 print("vppctl exec {}\n".format(filename))
1718
1719 def _create_vints_questions(self, node):
1720 """
1721 Ask the user some questions and get a list of interfaces
1722 and IPv4 addresses associated with those interfaces
1723
1724 :param node: Node dictionary.
1725 :type node: dict
1726 :returns: A list or interfaces with ip addresses
1727 :rtype: list
1728 """
1729
1730 vpputl = VPPUtil()
1731 interfaces = vpputl.get_hardware(node)
1732 if interfaces == {}:
1733 return []
1734
1735 # First delete all the Virtual interfaces
1736 for intf in sorted(interfaces.items()):
1737 name = intf[0]
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001738 if name[:7] == "Virtual":
1739 cmd = "vppctl delete vhost-user {}".format(name)
John DeNiscoc6b2a202017-11-01 12:37:47 -04001740 (ret, stdout, stderr) = vpputl.exec_command(cmd)
1741 if ret != 0:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001742 logging.debug(
1743 "{} failed on node {} {}".format(cmd, node["host"], stderr)
1744 )
John DeNiscoc6b2a202017-11-01 12:37:47 -04001745
1746 # Create a virtual interface, for each interface the user wants to use
1747 interfaces = vpputl.get_hardware(node)
1748 if interfaces == {}:
1749 return []
1750 interfaces_with_virtual_interfaces = []
1751 inum = 1
1752 for intf in sorted(interfaces.items()):
1753 name = intf[0]
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001754 if name == "local0":
John DeNiscoc6b2a202017-11-01 12:37:47 -04001755 continue
1756
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001757 question = (
1758 "Would you like connect this interface {} to "
1759 "the VM [Y/n]? ".format(name)
1760 )
1761 answer = self._ask_user_yn(question, "y")
1762 if answer == "y":
1763 sockfilename = "/var/run/vpp/{}.sock".format(name.replace("/", "_"))
John DeNiscoc6b2a202017-11-01 12:37:47 -04001764 if os.path.exists(sockfilename):
1765 os.remove(sockfilename)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001766 cmd = "vppctl create vhost-user socket {} server".format(sockfilename)
John DeNiscoc6b2a202017-11-01 12:37:47 -04001767 (ret, stdout, stderr) = vpputl.exec_command(cmd)
1768 if ret != 0:
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001769 raise RuntimeError(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001770 "Couldn't execute the command {}, {}.".format(cmd, stderr)
1771 )
1772 vintname = stdout.rstrip("\r\n")
John DeNiscoc6b2a202017-11-01 12:37:47 -04001773
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001774 cmd = "chmod 777 {}".format(sockfilename)
John DeNisco9fa5cf42018-02-06 15:23:05 -05001775 (ret, stdout, stderr) = vpputl.exec_command(cmd)
1776 if ret != 0:
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001777 raise RuntimeError(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001778 "Couldn't execute the command {}, {}.".format(cmd, stderr)
1779 )
John DeNisco9fa5cf42018-02-06 15:23:05 -05001780
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001781 interface = {
1782 "name": name,
1783 "virtualinterface": "{}".format(vintname),
1784 "bridge": "{}".format(inum),
1785 }
John DeNiscoc6b2a202017-11-01 12:37:47 -04001786 inum += 1
1787 interfaces_with_virtual_interfaces.append(interface)
1788
1789 return interfaces_with_virtual_interfaces
1790
1791 def create_and_bridge_virtual_interfaces(self):
1792 """
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001793 After asking the user some questions, create a VM and connect
1794 the interfaces to VPP interfaces
John DeNiscoc6b2a202017-11-01 12:37:47 -04001795
1796 """
1797
1798 for i in self._nodes.items():
1799 node = i[1]
1800
1801 # Show the current bridge and interface configuration
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001802 print("\nThis the current bridge configuration:")
John DeNiscoc6b2a202017-11-01 12:37:47 -04001803 VPPUtil.show_bridge(node)
1804 question = "\nWould you like to keep this configuration [Y/n]? "
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001805 answer = self._ask_user_yn(question, "y")
1806 if answer == "y":
John DeNiscoc6b2a202017-11-01 12:37:47 -04001807 continue
1808
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001809 # Create a script that builds a bridge configuration with
1810 # physical interfaces and virtual interfaces
John DeNiscoc6b2a202017-11-01 12:37:47 -04001811 ints_with_vints = self._create_vints_questions(node)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001812 content = ""
John DeNiscoc6b2a202017-11-01 12:37:47 -04001813 for intf in ints_with_vints:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001814 vhoststr = "\n".join(
1815 [
1816 "comment { The following command creates the socket }",
1817 "comment { and returns a virtual interface }",
1818 "comment {{ create vhost-user socket "
1819 "/var/run/vpp/sock{}.sock server }}\n".format(intf["bridge"]),
1820 ]
1821 )
John DeNiscoc6b2a202017-11-01 12:37:47 -04001822
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001823 setintdnstr = "set interface state {} down\n".format(intf["name"])
John DeNiscoc6b2a202017-11-01 12:37:47 -04001824
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001825 setintbrstr = "set interface l2 bridge {} {}\n".format(
1826 intf["name"], intf["bridge"]
1827 )
1828 setvintbrstr = "set interface l2 bridge {} {}\n".format(
1829 intf["virtualinterface"], intf["bridge"]
1830 )
John DeNiscoc6b2a202017-11-01 12:37:47 -04001831
1832 # set interface state VirtualEthernet/0/0/0 up
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001833 setintvststr = "set interface state {} up\n".format(
1834 intf["virtualinterface"]
1835 )
John DeNiscoc6b2a202017-11-01 12:37:47 -04001836
1837 # set interface state VirtualEthernet/0/0/0 down
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001838 setintupstr = "set interface state {} up\n".format(intf["name"])
John DeNiscoc6b2a202017-11-01 12:37:47 -04001839
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001840 content += (
1841 vhoststr
1842 + setintdnstr
1843 + setintbrstr
1844 + setvintbrstr
1845 + setintvststr
1846 + setintupstr
1847 )
John DeNiscoc6b2a202017-11-01 12:37:47 -04001848
1849 # Write the content to the script
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001850 rootdir = node["rootdir"]
1851 filename = rootdir + "/vpp/vpp-config/scripts/create_vms_and_connect_to_vpp"
1852 with open(filename, "w+") as sfile:
John DeNiscoc6b2a202017-11-01 12:37:47 -04001853 sfile.write(content)
1854
1855 # Execute the script
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001856 cmd = "vppctl exec {}".format(filename)
John DeNiscoc6b2a202017-11-01 12:37:47 -04001857 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
1858 if ret != 0:
1859 logging.debug(stderr)
1860
1861 print("\nA script as been created at {}".format(filename))
1862 print("This script can be run using the following:")
1863 print("vppctl exec {}\n".format(filename))
John DeNisco9fa5cf42018-02-06 15:23:05 -05001864
1865 def _iperf_vm_questions(self, node):
1866 """
1867 Ask the user some questions and get a list of interfaces
1868 and IPv4 addresses associated with those interfaces
1869
1870 :param node: Node dictionary.
1871 :type node: dict
1872 :returns: A list or interfaces with ip addresses
1873 :rtype: list
1874 """
1875
1876 vpputl = VPPUtil()
1877 interfaces = vpputl.get_hardware(node)
1878 if interfaces == {}:
1879 return []
1880
1881 # First delete all the Virtual interfaces
1882 for intf in sorted(interfaces.items()):
1883 name = intf[0]
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001884 if name[:7] == "Virtual":
1885 cmd = "vppctl delete vhost-user {}".format(name)
John DeNisco9fa5cf42018-02-06 15:23:05 -05001886 (ret, stdout, stderr) = vpputl.exec_command(cmd)
1887 if ret != 0:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001888 logging.debug(
1889 "{} failed on node {} {}".format(cmd, node["host"], stderr)
1890 )
John DeNisco9fa5cf42018-02-06 15:23:05 -05001891
1892 # Create a virtual interface, for each interface the user wants to use
1893 interfaces = vpputl.get_hardware(node)
1894 if interfaces == {}:
1895 return []
1896 interfaces_with_virtual_interfaces = []
1897 inum = 1
1898
1899 while True:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001900 print("\nPlease pick one interface to connect to the iperf VM.")
John DeNisco9fa5cf42018-02-06 15:23:05 -05001901 for intf in sorted(interfaces.items()):
1902 name = intf[0]
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001903 if name == "local0":
John DeNisco9fa5cf42018-02-06 15:23:05 -05001904 continue
1905
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001906 question = (
1907 "Would you like connect this interface {} to "
1908 "the VM [y/N]? ".format(name)
1909 )
1910 answer = self._ask_user_yn(question, "n")
1911 if answer == "y":
1912 self._sockfilename = "/var/run/vpp/{}.sock".format(
1913 name.replace("/", "_")
1914 )
John DeNisco9fa5cf42018-02-06 15:23:05 -05001915 if os.path.exists(self._sockfilename):
1916 os.remove(self._sockfilename)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001917 cmd = "vppctl create vhost-user socket {} server".format(
1918 self._sockfilename
1919 )
John DeNisco9fa5cf42018-02-06 15:23:05 -05001920 (ret, stdout, stderr) = vpputl.exec_command(cmd)
1921 if ret != 0:
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001922 raise RuntimeError(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001923 "Couldn't execute the command {}, {}.".format(cmd, stderr)
1924 )
1925 vintname = stdout.rstrip("\r\n")
John DeNisco9fa5cf42018-02-06 15:23:05 -05001926
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001927 cmd = "chmod 777 {}".format(self._sockfilename)
John DeNisco9fa5cf42018-02-06 15:23:05 -05001928 (ret, stdout, stderr) = vpputl.exec_command(cmd)
1929 if ret != 0:
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001930 raise RuntimeError(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001931 "Couldn't execute the command {}, {}.".format(cmd, stderr)
1932 )
John DeNisco9fa5cf42018-02-06 15:23:05 -05001933
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001934 interface = {
1935 "name": name,
1936 "virtualinterface": "{}".format(vintname),
1937 "bridge": "{}".format(inum),
1938 }
John DeNisco9fa5cf42018-02-06 15:23:05 -05001939 inum += 1
1940 interfaces_with_virtual_interfaces.append(interface)
1941 return interfaces_with_virtual_interfaces
1942
1943 def create_and_bridge_iperf_virtual_interface(self):
1944 """
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001945 After asking the user some questions, and create and bridge a
1946 virtual interface to be used with iperf VM
John DeNisco9fa5cf42018-02-06 15:23:05 -05001947
1948 """
1949
1950 for i in self._nodes.items():
1951 node = i[1]
1952
1953 # Show the current bridge and interface configuration
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001954 print("\nThis the current bridge configuration:")
John DeNisco9fa5cf42018-02-06 15:23:05 -05001955 ifaces = VPPUtil.show_bridge(node)
1956 question = "\nWould you like to keep this configuration [Y/n]? "
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001957 answer = self._ask_user_yn(question, "y")
1958 if answer == "y":
1959 self._sockfilename = "/var/run/vpp/{}.sock".format(
1960 ifaces[0]["name"].replace("/", "_")
1961 )
John DeNisco9fa5cf42018-02-06 15:23:05 -05001962 if os.path.exists(self._sockfilename):
1963 continue
1964
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08001965 # Create a script that builds a bridge configuration with
1966 # physical interfaces and virtual interfaces
John DeNisco9fa5cf42018-02-06 15:23:05 -05001967 ints_with_vints = self._iperf_vm_questions(node)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001968 content = ""
John DeNisco9fa5cf42018-02-06 15:23:05 -05001969 for intf in ints_with_vints:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001970 vhoststr = "\n".join(
1971 [
1972 "comment { The following command creates the socket }",
1973 "comment { and returns a virtual interface }",
1974 "comment {{ create vhost-user socket "
1975 "/var/run/vpp/sock{}.sock server }}\n".format(intf["bridge"]),
1976 ]
1977 )
John DeNisco9fa5cf42018-02-06 15:23:05 -05001978
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001979 setintdnstr = "set interface state {} down\n".format(intf["name"])
John DeNisco9fa5cf42018-02-06 15:23:05 -05001980
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001981 setintbrstr = "set interface l2 bridge {} {}\n".format(
1982 intf["name"], intf["bridge"]
1983 )
1984 setvintbrstr = "set interface l2 bridge {} {}\n".format(
1985 intf["virtualinterface"], intf["bridge"]
1986 )
John DeNisco9fa5cf42018-02-06 15:23:05 -05001987
1988 # set interface state VirtualEthernet/0/0/0 up
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001989 setintvststr = "set interface state {} up\n".format(
1990 intf["virtualinterface"]
1991 )
John DeNisco9fa5cf42018-02-06 15:23:05 -05001992
1993 # set interface state VirtualEthernet/0/0/0 down
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001994 setintupstr = "set interface state {} up\n".format(intf["name"])
John DeNisco9fa5cf42018-02-06 15:23:05 -05001995
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001996 content += (
1997 vhoststr
1998 + setintdnstr
1999 + setintbrstr
2000 + setvintbrstr
2001 + setintvststr
2002 + setintupstr
2003 )
John DeNisco9fa5cf42018-02-06 15:23:05 -05002004
2005 # Write the content to the script
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002006 rootdir = node["rootdir"]
2007 filename = rootdir + "/vpp/vpp-config/scripts/create_iperf_vm"
2008 with open(filename, "w+") as sfile:
John DeNisco9fa5cf42018-02-06 15:23:05 -05002009 sfile.write(content)
2010
2011 # Execute the script
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002012 cmd = "vppctl exec {}".format(filename)
John DeNisco9fa5cf42018-02-06 15:23:05 -05002013 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
2014 if ret != 0:
2015 logging.debug(stderr)
2016
2017 print("\nA script as been created at {}".format(filename))
2018 print("This script can be run using the following:")
2019 print("vppctl exec {}\n".format(filename))
2020
2021 @staticmethod
2022 def destroy_iperf_vm(name):
2023 """
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08002024 After asking the user some questions, create a VM and connect
2025 the interfaces to VPP interfaces
John DeNisco9fa5cf42018-02-06 15:23:05 -05002026
2027 :param name: The name of the VM to be be destroyed
2028 :type name: str
2029 """
2030
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002031 cmd = "virsh list"
John DeNisco9fa5cf42018-02-06 15:23:05 -05002032 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
2033 if ret != 0:
2034 logging.debug(stderr)
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08002035 raise RuntimeError(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002036 "Couldn't execute the command {} : {}".format(cmd, stderr)
2037 )
John DeNisco9fa5cf42018-02-06 15:23:05 -05002038
2039 if re.findall(name, stdout):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002040 cmd = "virsh destroy {}".format(name)
John DeNisco9fa5cf42018-02-06 15:23:05 -05002041 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
2042 if ret != 0:
2043 logging.debug(stderr)
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08002044 raise RuntimeError(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002045 "Couldn't execute the command {} : {}".format(cmd, stderr)
2046 )
John DeNisco9fa5cf42018-02-06 15:23:05 -05002047
2048 def create_iperf_vm(self, vmname):
2049 """
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08002050 After asking the user some questions, create a VM and connect
2051 the interfaces to VPP interfaces
John DeNisco9fa5cf42018-02-06 15:23:05 -05002052
2053 """
2054
2055 # Read the iperf VM template file
2056 distro = VPPUtil.get_linux_distro()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002057 if distro[0] == "Ubuntu":
2058 tfilename = "{}/vpp/vpp-config/configs/iperf-ubuntu.xml.template".format(
2059 self._rootdir
2060 )
John DeNisco9fa5cf42018-02-06 15:23:05 -05002061 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002062 tfilename = "{}/vpp/vpp-config/configs/iperf-centos.xml.template".format(
2063 self._rootdir
2064 )
John DeNisco9fa5cf42018-02-06 15:23:05 -05002065
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002066 with open(tfilename, "r") as tfile:
John DeNisco9fa5cf42018-02-06 15:23:05 -05002067 tcontents = tfile.read()
2068 tfile.close()
2069
2070 # Add the variables
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002071 imagename = "{}/vpp/vpp-config/{}".format(self._rootdir, IPERFVM_IMAGE)
2072 isoname = "{}/vpp/vpp-config/{}".format(self._rootdir, IPERFVM_ISO)
2073 tcontents = tcontents.format(
2074 vmname=vmname,
2075 imagename=imagename,
2076 isoname=isoname,
2077 vhostsocketname=self._sockfilename,
2078 )
John DeNisco9fa5cf42018-02-06 15:23:05 -05002079
2080 # Write the xml
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002081 ifilename = "{}/vpp/vpp-config/{}".format(self._rootdir, IPERFVM_XML)
2082 with open(ifilename, "w+") as ifile:
John DeNisco9fa5cf42018-02-06 15:23:05 -05002083 ifile.write(tcontents)
2084 ifile.close()
2085
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002086 cmd = "virsh create {}".format(ifilename)
John DeNisco9fa5cf42018-02-06 15:23:05 -05002087 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
2088 if ret != 0:
2089 logging.debug(stderr)
Paul Vinciguerra339bc6b2018-12-19 02:05:25 -08002090 raise RuntimeError(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002091 "Couldn't execute the command {} : {}".format(cmd, stderr)
2092 )