tests: replace pycodestyle with black
[vpp.git] / extras / vpp_config / vpplib / AutoConfig.py
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:
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."""
15 from __future__ import absolute_import, division, print_function
16
17 import logging
18 import os
19 import re
20 from ipaddress import ip_address
21
22 import yaml
23
24 from vpplib.VPPUtil import VPPUtil
25 from vpplib.VppPCIUtil import VppPCIUtil
26 from vpplib.VppHugePageUtil import VppHugePageUtil
27 from vpplib.CpuUtils import CpuUtils
28 from vpplib.VppGrubUtil import VppGrubUtil
29 from vpplib.QemuUtils import QemuUtils
30
31 #  Python2/3 compatible
32 try:
33     input = raw_input  # noqa
34 except NameError:
35     pass
36
37 __all__ = ["AutoConfig"]
38
39 # Constants
40 MIN_SYSTEM_CPUS = 2
41 MIN_TOTAL_HUGE_PAGES = 1024
42 MAX_PERCENT_FOR_HUGE_PAGES = 70
43
44 IPERFVM_XML = "configs/iperf-vm.xml"
45 IPERFVM_IMAGE = "images/xenial-mod.img"
46 IPERFVM_ISO = "configs/cloud-config.iso"
47
48
49 class AutoConfig(object):
50     """Auto Configuration Tools"""
51
52     def __init__(self, rootdir, filename, clean=False):
53         """
54         The Auto Configure class.
55
56         :param rootdir: The root directory for all the auto configuration files
57         :param filename: The autoconfiguration file
58         :param clean: When set initialize the nodes from the auto-config file
59         :type rootdir: str
60         :type filename: str
61         :type clean: bool
62         """
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 = ""
69         self._clean = clean
70         self._loadconfig()
71         self._sockfilename = ""
72
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
93         ofile = filename + ".orig"
94         (ret, stdout, stderr) = VPPUtil.exec_command("ls {}".format(ofile))
95         if ret != 0:
96             logging.debug(stderr)
97             if stdout.strip("\n") != ofile:
98                 cmd = "sudo cp {} {}".format(filename, ofile)
99                 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
100                 if ret != 0:
101                     logging.debug(stderr)
102
103     # noinspection PyBroadException
104     @staticmethod
105     def _ask_user_ipv4():
106         """
107         Asks the user for a number within a range.
108         default is returned if return is entered.
109
110         :returns: IP address with cidr
111         :rtype: str
112         """
113
114         while True:
115             answer = input("Please enter the IPv4 Address [n.n.n.n/n]: ")
116             try:
117                 ipinput = answer.split("/")
118                 ipaddr = ip_address(ipinput[0])
119                 if len(ipinput) > 1:
120                     plen = answer.split("/")[1]
121                 else:
122                     answer = input("Please enter the netmask [n.n.n.n]: ")
123                     plen = ip_address(answer).netmask_bits()
124                 return "{}/{}".format(ipaddr, plen)
125             except ValueError:
126                 print("Please enter a valid IPv4 address.")
127
128     @staticmethod
129     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:
147             answer = input(question)
148             if answer == "":
149                 answer = default
150                 break
151             if re.findall(r"[0-9+]", answer):
152                 if int(answer) in range(first, last + 1):
153                     break
154                 else:
155                     print(
156                         "Please a value between {} and {} or Return.".format(
157                             first, last
158                         )
159                     )
160             else:
161                 print(
162                     "Please a number between {} and {} or Return.".format(first, last)
163                 )
164
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()
182         answer = ""
183         while not input_valid:
184             answer = input(question)
185             if answer == "":
186                 answer = default
187             if re.findall(r"[YyNn]", answer):
188                 input_valid = True
189                 answer = answer[0].lower()
190             else:
191                 print("Please answer Y, N or Return.")
192
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 = {}
203         with open(self._autoconfig_filename, "r") as stream:
204             try:
205                 topo = yaml.load(stream)
206                 if "metadata" in topo:
207                     self._metadata = topo["metadata"]
208             except yaml.YAMLError as exc:
209                 raise RuntimeError(
210                     "Couldn't read the Auto config file {}.".format(
211                         self._autoconfig_filename, exc
212                     )
213                 )
214
215         systemfile = self._rootdir + self._metadata["system_config_file"]
216         if self._clean is False and os.path.isfile(systemfile):
217             with open(systemfile, "r") as sysstream:
218                 try:
219                     systopo = yaml.load(sysstream)
220                     if "nodes" in systopo:
221                         self._nodes = systopo["nodes"]
222                 except yaml.YAMLError as sysexc:
223                     raise RuntimeError(
224                         "Couldn't read the System config file {}.".format(
225                             systemfile, sysexc
226                         )
227                     )
228         else:
229             # Get the nodes from Auto Config
230             if "nodes" in topo:
231                 self._nodes = topo["nodes"]
232
233         # Set the root directory in all the nodes
234         for i in self._nodes.items():
235             node = i[1]
236             node["rootdir"] = self._rootdir
237
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
247         ydata = {"metadata": self._metadata, "nodes": self._nodes}
248
249         # Write the system config file
250         filename = self._rootdir + self._metadata["system_config_file"]
251         with open(filename, "w") as yamlfile:
252             yaml.dump(ydata, yamlfile)
253
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 = {}
263         with open(self._autoconfig_filename, "r") as stream:
264             try:
265                 ydata = yaml.load(stream)
266                 if "nodes" in ydata:
267                     nodes = ydata["nodes"]
268             except yaml.YAMLError as exc:
269                 print(exc)
270                 return
271
272         for i in nodes.items():
273             key = i[0]
274             node = i[1]
275
276             # Interfaces
277             node["interfaces"] = {}
278             for item in self._nodes[key]["interfaces"].items():
279                 port = item[0]
280                 interface = item[1]
281
282                 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"]
287
288             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                 ]
300
301             # TCP
302             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                 ]
310
311             # Huge pages
312             node["hugepages"]["total"] = self._nodes[key]["hugepages"]["total"]
313
314         # Write the auto config config file
315         with open(self._autoconfig_filename, "w") as yamlfile:
316             yaml.dump(ydata, yamlfile)
317
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
331     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
340         cpu = "\n"
341         if "vpp_main_core" in node["cpu"]:
342             vpp_main_core = node["cpu"]["vpp_main_core"]
343         else:
344             vpp_main_core = 0
345         if vpp_main_core != 0:
346             cpu += "  main-core {}\n".format(vpp_main_core)
347
348         # Get workers
349         vpp_workers = node["cpu"]["vpp_workers"]
350         vpp_worker_len = len(vpp_workers)
351         if vpp_worker_len > 0:
352             vpp_worker_str = ""
353             for i, worker in enumerate(vpp_workers):
354                 if i > 0:
355                     vpp_worker_str += ","
356                 if worker[0] == worker[1]:
357                     vpp_worker_str += "{}".format(worker[0])
358                 else:
359                     vpp_worker_str += "{}-{}".format(worker[0], worker[1])
360
361             cpu += "  corelist-workers {}\n".format(vpp_worker_str)
362
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
374         devices = ""
375         ports_per_numa = node["cpu"]["ports_per_numa"]
376
377         for item in ports_per_numa.items():
378             value = item[1]
379             interfaces = value["interfaces"]
380
381             # if 0 was specified for the number of vpp workers, use 1 queue
382             num_rx_queues = None
383             num_tx_queues = None
384             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"]
388
389             num_rx_desc = None
390             num_tx_desc = None
391
392             # Create the devices string
393             for interface in interfaces:
394                 pci_address = interface["pci_address"]
395                 pci_address = pci_address.lstrip("'").rstrip("'")
396                 devices += "\n"
397                 devices += "  dev {} {{ \n".format(pci_address)
398                 if num_rx_queues:
399                     devices += "    num-rx-queues {}\n".format(num_rx_queues)
400                 else:
401                     devices += "    num-rx-queues {}\n".format(1)
402                 if num_tx_queues:
403                     devices += "    num-tx-queues {}\n".format(num_tx_queues)
404                 if num_rx_desc:
405                     devices += "    num-rx-desc {}\n".format(num_rx_desc)
406                 if num_tx_desc:
407                     devices += "    num-tx-desc {}\n".format(num_tx_desc)
408                 devices += "  }"
409
410         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         """
420         buffers = ""
421         total_mbufs = node["cpu"]["total_mbufs"]
422
423         # If the total mbufs is not 0 or less than the default, set num-bufs
424         logging.debug("Total mbufs: {}".format(total_mbufs))
425         if total_mbufs != 0 and total_mbufs > 16384:
426             buffers += "  buffers-per-numa {}".format(total_mbufs)
427
428         return buffers
429
430     @staticmethod
431     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     ):
439         """
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
460         cpus = node["cpu"]["cpus_per_node"][numa_node]
461         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
471
472             if workers_end <= end:
473                 if reserve_vpp_main_core:
474                     node["cpu"]["vpp_main_core"] = start - 1
475                 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:
482             node["cpu"]["vpp_main_core"] = other_cpus_end + 1
483
484         return reserve_vpp_main_core
485
486     @staticmethod
487     def _calc_desc_and_queues(
488         total_numa_nodes, total_ports_per_numa, total_rx_queues, ports_per_numa_value
489     ):
490         """
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
496         :param total_rx_queues: The total number of rx queues / port
497         :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
501         :type total_rx_queues: int
502         :type ports_per_numa_value: dict
503         :returns The total number of message buffers
504         :rtype: int
505         """
506
507         # Get the number of rx queues
508         rx_queues = max(1, total_rx_queues)
509         tx_queues = rx_queues * total_numa_nodes + 1
510
511         # Get the descriptor entries
512         desc_entries = 1024
513         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
517
518         return total_mbufs
519
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]
536             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)
539             else:
540                 ports_per_numa[i["numa_node"]]["interfaces"].append(i)
541         node["cpu"]["ports_per_numa"] = ports_per_numa
542
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
557             interfaces = node["interfaces"]
558
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
564             other_cpus_end = other_cpus_start + node["cpu"]["total_other_cpus"] - 1
565             other_workers = None
566             if other_cpus_end != 0:
567                 other_workers = (other_cpus_start, other_cpus_end)
568             node["cpu"]["other_workers"] = other_workers
569
570             # Allocate the VPP main core and workers
571             vpp_workers = []
572             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"]
575
576             # If total_vpp_cpus is 0 or is less than the numa nodes with ports
577             #  then we shouldn't get workers
578             total_workers_node = 0
579             if len(ports_per_numa):
580                 total_workers_node = total_vpp_cpus // len(ports_per_numa)
581             total_main = 0
582             if reserve_vpp_main_core:
583                 total_main = 1
584             total_mbufs = 0
585             if total_main + total_workers_node != 0:
586                 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
591                     mbufs = self._calc_desc_and_queues(
592                         len(ports_per_numa),
593                         len(value["interfaces"]),
594                         total_rx_queues,
595                         value,
596                     )
597                     total_mbufs += mbufs
598
599                     # Get the VPP workers
600                     reserve_vpp_main_core = self._calc_vpp_workers(
601                         node,
602                         vpp_workers,
603                         numa_node,
604                         other_cpus_end,
605                         total_workers_node,
606                         reserve_vpp_main_core,
607                     )
608
609                 total_mbufs *= 2.5
610                 total_mbufs = int(total_mbufs)
611             else:
612                 total_mbufs = 0
613
614             # Save the info
615             node["cpu"]["vpp_workers"] = vpp_workers
616             node["cpu"]["total_mbufs"] = total_mbufs
617
618         # Write the config
619         self.updateconfig()
620
621     @staticmethod
622     def _apply_vpp_tcp(node):
623         """
624         Apply the tcp config
625
626         :param node: Node dictionary with cpuinfo.
627         :type node: dict
628         """
629
630         active_open_sessions = node["tcp"]["active_open_sessions"]
631         aos = int(active_open_sessions)
632
633         passive_open_sessions = node["tcp"]["passive_open_sessions"]
634         pos = int(passive_open_sessions)
635
636         # Generate the api-segment gid vpp sheit in any case
637         if (aos + pos) == 0:
638             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",
648                 "api-segment {",
649                 "  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         )
659         if aos > 0:
660             tcp = (
661                 tcp + "  v4-halfopen-table-buckets {:d}".format((aos + pos) // 4) + "\n"
662             )
663             tcp = tcp + "  v4-halfopen-table-memory 3g\n"
664             tcp = (
665                 tcp
666                 + "  local-endpoints-table-buckets {:d}".format((aos + pos) // 4)
667                 + "\n"
668             )
669             tcp = tcp + "  local-endpoints-table-memory 3g\n"
670         tcp = tcp + "}\n\n"
671
672         tcp = tcp + "tcp {\n"
673         tcp = tcp + "  preallocated-connections {:d}".format(aos + pos) + "\n"
674         if aos > 0:
675             tcp = tcp + "  preallocated-half-open-connections {:d}".format(aos) + "\n"
676         tcp = tcp + "}\n\n"
677
678         return tcp.rstrip("\n")
679
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
691             rootdir = node["rootdir"]
692             sfile = rootdir + node["vpp"]["startup_config_file"]
693
694             # Get the buffers
695             devices = self._apply_vpp_devices(node)
696
697             # Get the CPU config
698             cpu = self._apply_vpp_cpu(node)
699
700             # Get the buffer configuration
701             buffers = self._apply_buffers(node)
702             # 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
709             tfile = sfile + ".template"
710             (ret, stdout, stderr) = VPPUtil.exec_command("cat {}".format(tfile))
711             if ret != 0:
712                 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)
716
717             (ret, stdout, stderr) = VPPUtil.exec_command("rm {}".format(sfile))
718             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:
724                 raise RuntimeError("Writing config failed node {}".format(node["host"]))
725
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
736             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"]
740             else:
741                 vpp_main_core = 0
742             all_workers = []
743             if other_workers is not None:
744                 all_workers = [other_workers]
745             if vpp_main_core != 0:
746                 all_workers += [(vpp_main_core, vpp_main_core)]
747             all_workers += vpp_workers
748             isolated_cpus = ""
749             for idx, worker in enumerate(all_workers):
750                 if worker is None:
751                     continue
752                 if idx > 0:
753                     isolated_cpus += ","
754                 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()
761             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)
765
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()
779             node["hugepages"]["max_map_count"] = max_map_count
780             node["hugepages"]["shmax"] = shmmax
781             total, free, size, memtotal, memfree = hpg.get_actual_huge_pages()
782             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
787
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
805             iso_cpur = re.findall(r"isolcpus=[\w+\-,]+", current_cmdline)
806             iso_cpurl = len(iso_cpur)
807             if iso_cpurl > 0:
808                 iso_cpu_str = iso_cpur[0]
809                 iso_cpu_str = iso_cpu_str.split("=")[1]
810                 iso_cpul = iso_cpu_str.split(",")
811                 for iso_cpu in iso_cpul:
812                     isocpuspl = iso_cpu.split("-")
813                     if len(isocpuspl) == 1:
814                         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
823             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
828
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
845         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()
850
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
879         cmd = "lscpu -p"
880         (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
881         if ret != 0:
882             raise RuntimeError(
883                 "{} failed on node {} {}".format(cmd, node["host"], stderr)
884             )
885
886         pcpus = []
887         lines = stdout.split("\n")
888         for line in lines:
889             if line == "" or line[0] == "#":
890                 continue
891             linesplit = line.split(",")
892             layout = {
893                 "cpu": linesplit[0],
894                 "core": linesplit[1],
895                 "socket": linesplit[2],
896                 "node": linesplit[3],
897             }
898
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)
918             node["cpu"]["layout"] = layout
919
920             cpuinfo = node["cpuinfo"]
921             smt_enabled = CpuUtils.is_smt_enabled(cpuinfo)
922             node["cpu"]["smt_enabled"] = smt_enabled
923
924             # We don't want to write the cpuinfo
925             node["cpuinfo"] = ""
926
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
960         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         )
973
974         # Leave 1 for the general system
975         total_cpus -= 1
976         max_vpp_cpus = min(total_cpus, 4)
977         total_vpp_cpus = 0
978         if max_vpp_cpus > 0:
979             question = (
980                 "\nHow many core(s) shall we reserve for "
981                 "VPP [0-{}][0]? ".format(max_vpp_cpus)
982             )
983             total_vpp_cpus = self._ask_user_range(question, 0, max_vpp_cpus, 0)
984             node["cpu"]["total_vpp_cpus"] = total_vpp_cpus
985
986         total_other_cpus = 0
987         max_other_cores = total_cpus - total_vpp_cpus
988         if max_other_cores > 0:
989             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             )
993             total_other_cpus = self._ask_user_range(question, 0, max_other_cores, 0)
994             node["cpu"]["total_other_cpus"] = total_other_cpus
995
996         max_main_cpus = total_cpus - total_vpp_cpus - total_other_cpus
997         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]? "
1001             answer = self._ask_user_yn(question, "n")
1002             if answer == "y":
1003                 reserve_vpp_main_core = True
1004             node["cpu"]["reserve_vpp_main_core"] = reserve_vpp_main_core
1005             node["cpu"]["vpp_main_core"] = 0
1006
1007         question = (
1008             "How many RX queues per port shall we use for "
1009             "VPP [1-4][1]? ".format(max_vpp_cpus)
1010         )
1011         total_rx_queues = self._ask_user_range(question, 1, 4, 1)
1012         node["cpu"]["total_rx_queues"] = total_rx_queues
1013
1014     def modify_cpu(self, ask_questions=True):
1015         """
1016         Modify the cpu configuration, asking for the user for the values.
1017
1018         :param ask_questions: When true ask the user for config parameters
1019
1020         """
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
1036             first_node = "0"
1037             for cpu in cpu_layout:
1038                 if cpu["node"] != first_node:
1039                     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:
1045                 numa_node = cpul["node"]
1046                 core = cpul["core"]
1047                 cpu = cpul["cpu"]
1048                 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:
1054                     cpus_per_node[numa_node].append(
1055                         (int(cpu), int(cpu) + total_cpus_per_slice - 1)
1056                     )
1057                 if numa_node not in numa_nodes:
1058                     numa_nodes.append(numa_node)
1059                 if core not in cores:
1060                     cores.append(core)
1061             node["cpu"]["cpus_per_node"] = cpus_per_node
1062
1063             # Ask the user some questions
1064             if ask_questions and total_cpus >= 4:
1065                 self._modify_cpu_questions(node, total_cpus, numa_nodes)
1066
1067             # Populate the interfaces with the numa node
1068             if "interfaces" in node:
1069                 ikeys = node["interfaces"].keys()
1070                 VPPUtil.get_interfaces_numa_node(node, *tuple(ikeys))
1071
1072             # We don't want to write the cpuinfo
1073             node["cpuinfo"] = ""
1074
1075         # Write the configs
1076         self._update_auto_config()
1077         self.updateconfig()
1078
1079     def _modify_other_devices(self, node, other_devices, kernel_devices, dpdk_devices):
1080         """
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:
1087             print(
1088                 "\nThese device(s) are currently NOT being used " "by VPP or the OS.\n"
1089             )
1090             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]? "
1093             answer = self._ask_user_yn(question, "Y")
1094             if answer == "y":
1095                 vppd = {}
1096                 for dit in other_devices.items():
1097                     dvid = dit[0]
1098                     device = dit[1]
1099                     question = "Would you like to use device {} for".format(dvid)
1100                     question += " the OS [y/N]? "
1101                     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)
1110                             if ret:
1111                                 logging.debug("Could not bind device {}".format(dvid))
1112                             else:
1113                                 vppd[dvid] = device
1114                 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:
1122             print("\nThese device(s) are still NOT being used " "by VPP or the OS.\n")
1123             VppPCIUtil.show_vpp_devices(other_devices, show_interfaces=False)
1124             question = "\nWould you like use any of these for VPP [y/N]? "
1125             answer = self._ask_user_yn(question, "N")
1126             if answer == "y":
1127                 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]? "
1133                     answer = self._ask_user_yn(question, "n")
1134                     if answer == "y":
1135                         vppd[dvid] = device
1136                 for dit in vppd.items():
1137                     dvid = dit[0]
1138                     device = dit[1]
1139                     if (
1140                         "unused" in device
1141                         and len(device["unused"]) != 0
1142                         and device["unused"][0] != ""
1143                     ):
1144                         driver = device["unused"][0]
1145                         logging.debug(
1146                             "Binding device {} to driver {}".format(dvid, driver)
1147                         )
1148                         ret = VppPCIUtil.bind_vpp_device(node, driver, dvid)
1149                         if ret:
1150                             logging.debug("Could not bind device {}".format(dvid))
1151                         else:
1152                             dpdk_devices[dvid] = device
1153                             del other_devices[dvid]
1154
1155     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]
1163             devices = node["devices"]
1164             all_devices = devices["other_devices"]
1165             all_devices.update(devices["dpdk_devices"])
1166             all_devices.update(devices["kernel_devices"])
1167
1168             current_ifcs = {}
1169             interfaces = {}
1170             if "interfaces" in node:
1171                 current_ifcs = node["interfaces"]
1172             if current_ifcs:
1173                 for ifc in current_ifcs.values():
1174                     dvid = ifc["pci_address"]
1175                     if dvid in all_devices:
1176                         VppPCIUtil.vpp_create_interface(
1177                             interfaces, dvid, all_devices[dvid]
1178                         )
1179             node["interfaces"] = interfaces
1180
1181         self.updateconfig()
1182
1183     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]
1191             devices = node["devices"]
1192             other_devices = devices["other_devices"]
1193             kernel_devices = devices["kernel_devices"]
1194             dpdk_devices = devices["dpdk_devices"]
1195
1196             if other_devices:
1197                 self._modify_other_devices(
1198                     node, other_devices, kernel_devices, dpdk_devices
1199                 )
1200
1201                 # Get the devices again for this node
1202                 self._get_device(node)
1203                 devices = node["devices"]
1204                 kernel_devices = devices["kernel_devices"]
1205                 dpdk_devices = devices["dpdk_devices"]
1206
1207             klen = len(kernel_devices)
1208             if klen > 0:
1209                 print("\nThese devices are safe to be used with VPP.\n")
1210                 VppPCIUtil.show_vpp_devices(kernel_devices)
1211                 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":
1216                     vppd = {}
1217                     for dit in kernel_devices.items():
1218                         dvid = dit[0]
1219                         device = dit[1]
1220                         question = "Would you like to use device {} ".format(dvid)
1221                         question += "for VPP [y/N]? "
1222                         answer = self._ask_user_yn(question, "n")
1223                         if answer == "y":
1224                             vppd[dvid] = device
1225                     for dit in vppd.items():
1226                         dvid = dit[0]
1227                         device = dit[1]
1228                         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                                 )
1244                                 ret = VppPCIUtil.bind_vpp_device(node, driver, dvid)
1245                                 if ret:
1246                                     logging.debug(
1247                                         "Could not bind device {}".format(dvid)
1248                                     )
1249                         dpdk_devices[dvid] = device
1250                         del kernel_devices[dvid]
1251
1252             dlen = len(dpdk_devices)
1253             if dlen > 0:
1254                 print("\nThese device(s) are already using DPDK.\n")
1255                 VppPCIUtil.show_vpp_devices(dpdk_devices, show_interfaces=False)
1256                 question = "\nWould you like to remove any of "
1257                 question += "these device(s) [y/N]? "
1258                 answer = self._ask_user_yn(question, "n")
1259                 if answer == "y":
1260                     vppdl = {}
1261                     for dit in dpdk_devices.items():
1262                         dvid = dit[0]
1263                         device = dit[1]
1264                         question = "Would you like to remove {} [y/N]? ".format(dvid)
1265                         answer = self._ask_user_yn(question, "n")
1266                         if answer == "y":
1267                             vppdl[dvid] = device
1268                     for dit in vppdl.items():
1269                         dvid = dit[0]
1270                         device = dit[1]
1271                         if (
1272                             "unused" in device
1273                             and len(device["unused"]) != 0
1274                             and device["unused"][0] != ""
1275                         ):
1276                             driver = device["unused"][0]
1277                             logging.debug(
1278                                 "Binding device {} to driver {}".format(dvid, driver)
1279                             )
1280                             ret = VppPCIUtil.bind_vpp_device(node, driver, dvid)
1281                             if ret:
1282                                 logging.debug("Could not bind device {}".format(dvid))
1283                             else:
1284                                 kernel_devices[dvid] = device
1285                                 del dpdk_devices[dvid]
1286
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)
1292             node["interfaces"] = interfaces
1293
1294         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
1306             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])
1311             # The max number of huge pages should be no more than
1312             # 70% of total free memory
1313             maxpages = (int(memfree) * MAX_PERCENT_FOR_HUGE_PAGES // 100) // hugesize
1314             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
1319                 continue
1320
1321             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             )
1325             answer = self._ask_user_range(question, 1024, maxpages, 1024)
1326             node["hugepages"]["total"] = str(answer)
1327
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
1351             question = (
1352                 "\nHow many active-open / tcp client sessions are "
1353                 "expected [0-10000000][0]? "
1354             )
1355             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
1359             node["tcp"]["active_open_sessions"] = answer
1360
1361             question = (
1362                 "How many passive-open / tcp server sessions are "
1363                 "expected [0-10000000][0]? "
1364             )
1365             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
1369             node["tcp"]["passive_open_sessions"] = answer
1370
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
1386         print('\nWe are patching the node "{}":\n'.format(node["host"]))
1387         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
1398         item = "Model name"
1399         if item in cpu:
1400             print("{:>20}:    {}".format(item, cpu[item]))
1401         item = "CPU(s)"
1402         if item in cpu:
1403             print("{:>20}:    {}".format(item, cpu[item]))
1404         item = "Thread(s) per core"
1405         if item in cpu:
1406             print("{:>20}:    {}".format(item, cpu[item]))
1407         item = "Core(s) per socket"
1408         if item in cpu:
1409             print("{:>20}:    {}".format(item, cpu[item]))
1410         item = "Socket(s)"
1411         if item in cpu:
1412             print("{:>20}:    {}".format(item, cpu[item]))
1413         item = "NUMA node(s)"
1414         numa_nodes = 0
1415         if item in cpu:
1416             numa_nodes = int(cpu[item])
1417         for i in range(0, numa_nodes):
1418             item = "NUMA node{} CPU(s)".format(i)
1419             print("{:>20}:    {}".format(item, cpu[item]))
1420         item = "CPU max MHz"
1421         if item in cpu:
1422             print("{:>20}:    {}".format(item, cpu[item]))
1423         item = "CPU min MHz"
1424         if item in cpu:
1425             print("{:>20}:    {}".format(item, cpu[item]))
1426
1427         if node["cpu"]["smt_enabled"]:
1428             smt = "Enabled"
1429         else:
1430             smt = "Disabled"
1431         print("{:>20}:    {}".format("SMT", smt))
1432
1433         # VPP Threads
1434         print("\nVPP Threads: (Name: Cpu Number)")
1435         vpp_processes = cpu["vpp_processes"]
1436         for i in vpp_processes.items():
1437             print("  {:10}: {:4}".format(i[0], i[1]))
1438
1439     @staticmethod
1440     def device_info(node):
1441         """
1442         Show the device information.
1443
1444         """
1445
1446         if "cpu" in node and "total_mbufs" in node["cpu"]:
1447             total_mbufs = node["cpu"]["total_mbufs"]
1448             if total_mbufs != 0:
1449                 print("Total Number of Buffers: {}".format(total_mbufs))
1450
1451         vpp = VppPCIUtil(node)
1452         vpp.get_all_devices()
1453         linkup_devs = vpp.get_link_up_devices()
1454         if len(linkup_devs):
1455             print("\nDevices with link up (can not be used with VPP):")
1456             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):
1461             print("\nDevices bound to kernel drivers:")
1462             vpp.show_vpp_devices(kernel_devs, show_header=False)
1463         else:
1464             print("\nNo devices bound to kernel drivers")
1465
1466         dpdk_devs = vpp.get_dpdk_devices()
1467         if len(dpdk_devs):
1468             print("\nDevices bound to DPDK drivers:")
1469             vpp.show_vpp_devices(dpdk_devs, show_interfaces=True, show_header=False)
1470         else:
1471             print("\nNo devices bound to DPDK drivers")
1472
1473         other_devs = vpp.get_other_devices()
1474         if len(other_devs):
1475             print("\nDevices not bound to Kernel or DPDK drivers:")
1476             vpp.show_vpp_devices(other_devs, show_interfaces=True, show_header=False)
1477         else:
1478             print("\nNo devices not bound to Kernel or DPDK drivers")
1479
1480         vpputl = VPPUtil()
1481         interfaces = vpputl.get_hardware(node)
1482         if interfaces == {}:
1483             return
1484
1485         print("\nDevices in use by VPP:")
1486
1487         if len(interfaces.items()) < 2:
1488             print("None")
1489             return
1490
1491         print(
1492             "{:30} {:4} {:4} {:7} {:4} {:7}".format(
1493                 "Name", "Numa", "RXQs", "RXDescs", "TXQs", "TXDescs"
1494             )
1495         )
1496         for intf in sorted(interfaces.items()):
1497             name = intf[0]
1498             value = intf[1]
1499             if name == "local0":
1500                 continue
1501             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"])
1512
1513             print(
1514                 "{:30} {:>4} {:>4} {:>7} {:>4} {:>7}".format(
1515                     name, numa, rx_qs, rx_ds, tx_qs, tx_ds
1516                 )
1517             )
1518
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
1530     def has_interfaces(node):
1531         """
1532         Check for interfaces, return tru if there is at least one
1533
1534         :returns: boolean
1535         """
1536         if "interfaces" in node and len(node["interfaces"]):
1537             return True
1538         else:
1539             return False
1540
1541     @staticmethod
1542     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
1548         """
1549
1550         min_sys_res = True
1551
1552         # CPUs
1553         if "layout" in node["cpu"]:
1554             total_cpus = len(node["cpu"]["layout"])
1555             if total_cpus < 2:
1556                 print(
1557                     "\nThere is only {} CPU(s) available on this system. "
1558                     "This is not enough to run VPP.".format(total_cpus)
1559                 )
1560                 min_sys_res = False
1561
1562         # System Memory
1563         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])
1571
1572             memhugepages = MIN_TOTAL_HUGE_PAGES * hugesize
1573             percentmemhugepages = (memhugepages / memfree) * 100
1574             if free is "0" and percentmemhugepages > MAX_PERCENT_FOR_HUGE_PAGES:
1575                 print(
1576                     "\nThe System has only {} of free memory. You will not "
1577                     "be able to allocate enough Huge Pages for VPP.".format(
1578                         int(memfree)
1579                     )
1580                 )
1581                 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():
1592             print("\n==============================")
1593             name = i[0]
1594             node = i[1]
1595
1596             print("NODE: {}\n".format(name))
1597
1598             # CPU
1599             print("CPU:")
1600             self.cpu_info(node)
1601
1602             # Grub
1603             print("\nGrub Command Line:")
1604             if "grub" in node:
1605                 print("  Current: {}".format(node["grub"]["current_cmdline"]))
1606                 print("  Configured: {}".format(node["grub"]["default_cmdline"]))
1607
1608             # Huge Pages
1609             print("\nHuge Pages:")
1610             self.hugepage_info(node)
1611
1612             # Devices
1613             print("\nDevices:")
1614             self.device_info(node)
1615
1616             # Status
1617             print("\nVPP Service Status:")
1618             state, errors = VPPUtil.status(node)
1619             print("  {}".format(state))
1620             for e in errors:
1621                 print("  {}".format(e))
1622
1623             # Minimum system resources
1624             self.min_system_resources(node)
1625
1626             print("\n==============================")
1627
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]
1647             if name == "local0":
1648                 continue
1649
1650             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":
1655                 address = {}
1656                 addr = self._ask_user_ipv4()
1657                 address["name"] = name
1658                 address["addr"] = addr
1659                 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)
1675             if current_ints != {}:
1676                 print("\nThese are the current interfaces with IP addresses:")
1677                 for items in sorted(current_ints.items()):
1678                     name = items[0]
1679                     value = items[1]
1680                     if "address" not in value:
1681                         address = "Not Set"
1682                     else:
1683                         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":
1688                     continue
1689             else:
1690                 print("\nThere are currently no interfaces with IP " "addresses.")
1691
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)
1695             content = ""
1696             for ints in ints_with_addrs:
1697                 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)
1701                 content += setipstr + setintupstr
1702
1703             # Write the content to the script
1704             rootdir = node["rootdir"]
1705             filename = rootdir + "/vpp/vpp-config/scripts/set_int_ipv4_and_up"
1706             with open(filename, "w+") as sfile:
1707                 sfile.write(content)
1708
1709             # Execute the script
1710             cmd = "vppctl exec {}".format(filename)
1711             (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:")
1717             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]
1738             if name[:7] == "Virtual":
1739                 cmd = "vppctl delete vhost-user {}".format(name)
1740                 (ret, stdout, stderr) = vpputl.exec_command(cmd)
1741                 if ret != 0:
1742                     logging.debug(
1743                         "{} failed on node {} {}".format(cmd, node["host"], stderr)
1744                     )
1745
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]
1754             if name == "local0":
1755                 continue
1756
1757             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("/", "_"))
1764                 if os.path.exists(sockfilename):
1765                     os.remove(sockfilename)
1766                 cmd = "vppctl create vhost-user socket {} server".format(sockfilename)
1767                 (ret, stdout, stderr) = vpputl.exec_command(cmd)
1768                 if ret != 0:
1769                     raise RuntimeError(
1770                         "Couldn't execute the command {}, {}.".format(cmd, stderr)
1771                     )
1772                 vintname = stdout.rstrip("\r\n")
1773
1774                 cmd = "chmod 777 {}".format(sockfilename)
1775                 (ret, stdout, stderr) = vpputl.exec_command(cmd)
1776                 if ret != 0:
1777                     raise RuntimeError(
1778                         "Couldn't execute the command {}, {}.".format(cmd, stderr)
1779                     )
1780
1781                 interface = {
1782                     "name": name,
1783                     "virtualinterface": "{}".format(vintname),
1784                     "bridge": "{}".format(inum),
1785                 }
1786                 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         """
1793         After asking the user some questions, create a VM and connect
1794         the interfaces to VPP interfaces
1795
1796         """
1797
1798         for i in self._nodes.items():
1799             node = i[1]
1800
1801             # Show the current bridge and interface configuration
1802             print("\nThis the current bridge configuration:")
1803             VPPUtil.show_bridge(node)
1804             question = "\nWould you like to keep this configuration [Y/n]? "
1805             answer = self._ask_user_yn(question, "y")
1806             if answer == "y":
1807                 continue
1808
1809             # Create a script that builds a bridge configuration with
1810             # physical interfaces and virtual interfaces
1811             ints_with_vints = self._create_vints_questions(node)
1812             content = ""
1813             for intf in ints_with_vints:
1814                 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                 )
1822
1823                 setintdnstr = "set interface state {} down\n".format(intf["name"])
1824
1825                 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                 )
1831
1832                 # set interface state VirtualEthernet/0/0/0 up
1833                 setintvststr = "set interface state {} up\n".format(
1834                     intf["virtualinterface"]
1835                 )
1836
1837                 # set interface state VirtualEthernet/0/0/0 down
1838                 setintupstr = "set interface state {} up\n".format(intf["name"])
1839
1840                 content += (
1841                     vhoststr
1842                     + setintdnstr
1843                     + setintbrstr
1844                     + setvintbrstr
1845                     + setintvststr
1846                     + setintupstr
1847                 )
1848
1849             # Write the content to the script
1850             rootdir = node["rootdir"]
1851             filename = rootdir + "/vpp/vpp-config/scripts/create_vms_and_connect_to_vpp"
1852             with open(filename, "w+") as sfile:
1853                 sfile.write(content)
1854
1855             # Execute the script
1856             cmd = "vppctl exec {}".format(filename)
1857             (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))
1864
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]
1884             if name[:7] == "Virtual":
1885                 cmd = "vppctl delete vhost-user {}".format(name)
1886                 (ret, stdout, stderr) = vpputl.exec_command(cmd)
1887                 if ret != 0:
1888                     logging.debug(
1889                         "{} failed on node {} {}".format(cmd, node["host"], stderr)
1890                     )
1891
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:
1900             print("\nPlease pick one interface to connect to the iperf VM.")
1901             for intf in sorted(interfaces.items()):
1902                 name = intf[0]
1903                 if name == "local0":
1904                     continue
1905
1906                 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                     )
1915                     if os.path.exists(self._sockfilename):
1916                         os.remove(self._sockfilename)
1917                     cmd = "vppctl create vhost-user socket {} server".format(
1918                         self._sockfilename
1919                     )
1920                     (ret, stdout, stderr) = vpputl.exec_command(cmd)
1921                     if ret != 0:
1922                         raise RuntimeError(
1923                             "Couldn't execute the command {}, {}.".format(cmd, stderr)
1924                         )
1925                     vintname = stdout.rstrip("\r\n")
1926
1927                     cmd = "chmod 777 {}".format(self._sockfilename)
1928                     (ret, stdout, stderr) = vpputl.exec_command(cmd)
1929                     if ret != 0:
1930                         raise RuntimeError(
1931                             "Couldn't execute the command {}, {}.".format(cmd, stderr)
1932                         )
1933
1934                     interface = {
1935                         "name": name,
1936                         "virtualinterface": "{}".format(vintname),
1937                         "bridge": "{}".format(inum),
1938                     }
1939                     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         """
1945         After asking the user some questions, and create and bridge a
1946         virtual interface to be used with iperf VM
1947
1948         """
1949
1950         for i in self._nodes.items():
1951             node = i[1]
1952
1953             # Show the current bridge and interface configuration
1954             print("\nThis the current bridge configuration:")
1955             ifaces = VPPUtil.show_bridge(node)
1956             question = "\nWould you like to keep this configuration [Y/n]? "
1957             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                 )
1962                 if os.path.exists(self._sockfilename):
1963                     continue
1964
1965             # Create a script that builds a bridge configuration with
1966             # physical interfaces and virtual interfaces
1967             ints_with_vints = self._iperf_vm_questions(node)
1968             content = ""
1969             for intf in ints_with_vints:
1970                 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                 )
1978
1979                 setintdnstr = "set interface state {} down\n".format(intf["name"])
1980
1981                 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                 )
1987
1988                 # set interface state VirtualEthernet/0/0/0 up
1989                 setintvststr = "set interface state {} up\n".format(
1990                     intf["virtualinterface"]
1991                 )
1992
1993                 # set interface state VirtualEthernet/0/0/0 down
1994                 setintupstr = "set interface state {} up\n".format(intf["name"])
1995
1996                 content += (
1997                     vhoststr
1998                     + setintdnstr
1999                     + setintbrstr
2000                     + setvintbrstr
2001                     + setintvststr
2002                     + setintupstr
2003                 )
2004
2005             # Write the content to the script
2006             rootdir = node["rootdir"]
2007             filename = rootdir + "/vpp/vpp-config/scripts/create_iperf_vm"
2008             with open(filename, "w+") as sfile:
2009                 sfile.write(content)
2010
2011             # Execute the script
2012             cmd = "vppctl exec {}".format(filename)
2013             (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         """
2024         After asking the user some questions, create a VM and connect
2025         the interfaces to VPP interfaces
2026
2027         :param name: The name of the VM to be be destroyed
2028         :type name: str
2029         """
2030
2031         cmd = "virsh list"
2032         (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
2033         if ret != 0:
2034             logging.debug(stderr)
2035             raise RuntimeError(
2036                 "Couldn't execute the command {} : {}".format(cmd, stderr)
2037             )
2038
2039         if re.findall(name, stdout):
2040             cmd = "virsh destroy {}".format(name)
2041             (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
2042             if ret != 0:
2043                 logging.debug(stderr)
2044                 raise RuntimeError(
2045                     "Couldn't execute the command {} : {}".format(cmd, stderr)
2046                 )
2047
2048     def create_iperf_vm(self, vmname):
2049         """
2050         After asking the user some questions, create a VM and connect
2051         the interfaces to VPP interfaces
2052
2053         """
2054
2055         # Read the iperf VM template file
2056         distro = VPPUtil.get_linux_distro()
2057         if distro[0] == "Ubuntu":
2058             tfilename = "{}/vpp/vpp-config/configs/iperf-ubuntu.xml.template".format(
2059                 self._rootdir
2060             )
2061         else:
2062             tfilename = "{}/vpp/vpp-config/configs/iperf-centos.xml.template".format(
2063                 self._rootdir
2064             )
2065
2066         with open(tfilename, "r") as tfile:
2067             tcontents = tfile.read()
2068         tfile.close()
2069
2070         # Add the variables
2071         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         )
2079
2080         # Write the xml
2081         ifilename = "{}/vpp/vpp-config/{}".format(self._rootdir, IPERFVM_XML)
2082         with open(ifilename, "w+") as ifile:
2083             ifile.write(tcontents)
2084         ifile.close()
2085
2086         cmd = "virsh create {}".format(ifilename)
2087         (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
2088         if ret != 0:
2089             logging.debug(stderr)
2090             raise RuntimeError(
2091                 "Couldn't execute the command {} : {}".format(cmd, stderr)
2092             )