A bit of cleanup, updated the README, started vhost test.
[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
16 import logging
17 import os
18 import re
19 import yaml
20 from netaddr import IPAddress
21
22 from vpplib.VPPUtil import VPPUtil
23 from vpplib.VppPCIUtil import VppPCIUtil
24 from vpplib.VppHugePageUtil import VppHugePageUtil
25 from vpplib.CpuUtils import CpuUtils
26 from vpplib.VppGrubUtil import VppGrubUtil
27 from vpplib.QemuUtils import QemuUtils
28
29 __all__ = ["AutoConfig"]
30
31 # Constants
32 MIN_SYSTEM_CPUS = 2
33 MIN_TOTAL_HUGE_PAGES = 1024
34 MAX_PERCENT_FOR_HUGE_PAGES = 70
35
36
37 class AutoConfig(object):
38     """Auto Configuration Tools"""
39
40     def __init__(self, rootdir, filename):
41         """
42         The Auto Configure class.
43
44         :param rootdir: The root directory for all the auto configuration files
45         :param filename: The autoconfiguration file
46         :type rootdir: str
47         :type filename: str
48         """
49         self._autoconfig_filename = rootdir + filename
50         self._rootdir = rootdir
51         self._metadata = {}
52         self._nodes = {}
53         self._vpp_devices_node = {}
54         self._hugepage_config = ""
55         self._loadconfig()
56
57     def get_nodes(self):
58         """
59         Returns the nodes dictionary.
60
61         :returns: The nodes
62         :rtype: dictionary
63         """
64
65         return self._nodes
66
67     @staticmethod
68     def _autoconfig_backup_file(filename):
69         """
70         Create a backup file.
71
72         :param filename: The file to backup
73         :type filename: str
74         """
75
76         # Does a copy of the file exist, if not create one
77         ofile = filename + '.orig'
78         (ret, stdout, stderr) = VPPUtil.exec_command('ls {}'.format(ofile))
79         if ret != 0:
80             logging.debug(stderr)
81             if stdout.strip('\n') != ofile:
82                 cmd = 'sudo cp {} {}'.format(filename, ofile)
83                 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
84                 if ret != 0:
85                     logging.debug(stderr)
86
87     @staticmethod
88     def _ask_user_ipv4():
89         """
90         Asks the user for a number within a range.
91         default is returned if return is entered.
92
93         :returns: IP address with cidr
94         :rtype: str
95         """
96
97         while True:
98             answer = raw_input("Please enter the IPv4 Address [n.n.n.n/n]: ")
99             try:
100                 ipinput = answer.split('/')
101                 ipaddr = IPAddress(ipinput[0])
102                 if len(ipinput) > 1:
103                     plen = answer.split('/')[1]
104                 else:
105                     answer = raw_input("Please enter the netmask [n.n.n.n]: ")
106                     plen = IPAddress(answer).netmask_bits()
107                 return '{}/{}'.format(ipaddr, plen)
108             except:
109                 print "Please enter a valid IPv4 address."
110                 continue
111
112     @staticmethod
113     def _ask_user_range(question, first, last, default):
114         """
115         Asks the user for a number within a range.
116         default is returned if return is entered.
117
118         :param question: Text of a question.
119         :param first: First number in the range
120         :param last: Last number in the range
121         :param default: The value returned when return is entered
122         :type question: string
123         :type first: int
124         :type last: int
125         :type default: int
126         :returns: The answer to the question
127         :rtype: int
128         """
129
130         while True:
131             answer = raw_input(question)
132             if answer == '':
133                 answer = default
134                 break
135             if re.findall(r'[0-9+]', answer):
136                 if int(answer) in range(first, last + 1):
137                     break
138                 else:
139                     print "Please a value between {} and {} or Return.". \
140                         format(first, last)
141             else:
142                 print "Please a number between {} and {} or Return.". \
143                     format(first, last)
144
145         return int(answer)
146
147     @staticmethod
148     def _ask_user_yn(question, default):
149         """
150         Asks the user for a yes or no question.
151
152         :param question: Text of a question.
153         :param default: The value returned when return is entered
154         :type question: string
155         :type default: string
156         :returns: The answer to the question
157         :rtype: string
158         """
159
160         input_valid = False
161         default = default.lower()
162         answer = ''
163         while not input_valid:
164             answer = raw_input(question)
165             if answer == '':
166                 answer = default
167             if re.findall(r'[YyNn]', answer):
168                 input_valid = True
169                 answer = answer[0].lower()
170             else:
171                 print "Please answer Y, N or Return."
172
173         return answer
174
175     def _loadconfig(self):
176         """
177         Load the testbed configuration, given the auto configuration file.
178
179         """
180
181         # Get the Topology, from the topology layout file
182         topo = {}
183         with open(self._autoconfig_filename, 'r') as stream:
184             try:
185                 topo = yaml.load(stream)
186                 if 'metadata' in topo:
187                     self._metadata = topo['metadata']
188             except yaml.YAMLError as exc:
189                 raise RuntimeError("Couldn't read the Auto config file {}.".format(self._autoconfig_filename, exc))
190
191         systemfile = self._rootdir + self._metadata['system_config_file']
192         if os.path.isfile(systemfile):
193             with open(systemfile, 'r') as sysstream:
194                 try:
195                     systopo = yaml.load(sysstream)
196                     if 'nodes' in systopo:
197                         self._nodes = systopo['nodes']
198                 except yaml.YAMLError as sysexc:
199                     raise RuntimeError("Couldn't read the System config file {}.".format(systemfile, sysexc))
200         else:
201             # Get the nodes from Auto Config
202             if 'nodes' in topo:
203                 self._nodes = topo['nodes']
204
205         # Set the root directory in all the nodes
206         for i in self._nodes.items():
207             node = i[1]
208             node['rootdir'] = self._rootdir
209
210     def updateconfig(self):
211         """
212         Update the testbed configuration, given the auto configuration file.
213         We will write the system configuration file with the current node
214         information
215
216         """
217
218         # Initialize the yaml data
219         ydata = {'metadata': self._metadata, 'nodes': self._nodes}
220
221         # Write the system config file
222         filename = self._rootdir + self._metadata['system_config_file']
223         with open(filename, 'w') as yamlfile:
224             yaml.dump(ydata, yamlfile, default_flow_style=False)
225
226     def _update_auto_config(self):
227         """
228         Write the auto configuration file with the new configuration data,
229         input from the user.
230
231         """
232
233         # Initialize the yaml data
234         nodes = {}
235         with open(self._autoconfig_filename, 'r') as stream:
236             try:
237                 ydata = yaml.load(stream)
238                 if 'nodes' in ydata:
239                     nodes = ydata['nodes']
240             except yaml.YAMLError as exc:
241                 print exc
242                 return
243
244         for i in nodes.items():
245             key = i[0]
246             node = i[1]
247
248             # Interfaces
249             node['interfaces'] = {}
250             for item in self._nodes[key]['interfaces'].items():
251                 port = item[0]
252                 interface = item[1]
253
254                 node['interfaces'][port] = {}
255                 node['interfaces'][port]['pci_address'] = \
256                     interface['pci_address']
257                 if 'mac_address' in interface:
258                     node['interfaces'][port]['mac_address'] = \
259                         interface['mac_address']
260
261             if 'total_other_cpus' in self._nodes[key]['cpu']:
262                 node['cpu']['total_other_cpus'] = \
263                     self._nodes[key]['cpu']['total_other_cpus']
264             if 'total_vpp_cpus' in self._nodes[key]['cpu']:
265                 node['cpu']['total_vpp_cpus'] = \
266                     self._nodes[key]['cpu']['total_vpp_cpus']
267             if 'reserve_vpp_main_core' in self._nodes[key]['cpu']:
268                 node['cpu']['reserve_vpp_main_core'] = \
269                     self._nodes[key]['cpu']['reserve_vpp_main_core']
270
271             # TCP
272             if 'active_open_sessions' in self._nodes[key]['tcp']:
273                 node['tcp']['active_open_sessions'] = \
274                     self._nodes[key]['tcp']['active_open_sessions']
275             if 'passive_open_sessions' in self._nodes[key]['tcp']:
276                 node['tcp']['passive_open_sessions'] = \
277                     self._nodes[key]['tcp']['passive_open_sessions']
278
279             # Huge pages
280             node['hugepages']['total'] = self._nodes[key]['hugepages']['total']
281
282         # Write the auto config config file
283         with open(self._autoconfig_filename, 'w') as yamlfile:
284             yaml.dump(ydata, yamlfile, default_flow_style=False)
285
286     def apply_huge_pages(self):
287         """
288         Apply the huge page config
289
290         """
291
292         for i in self._nodes.items():
293             node = i[1]
294
295             hpg = VppHugePageUtil(node)
296             hpg.hugepages_dryrun_apply()
297
298     @staticmethod
299     def _apply_vpp_unix(node):
300         """
301         Apply the VPP Unix config
302
303         :param node: Node dictionary with cpuinfo.
304         :type node: dict
305         """
306
307         unix = '  nodaemon\n'
308         if 'unix' not in node['vpp']:
309             return ''
310
311         unixv = node['vpp']['unix']
312         if 'interactive' in unixv:
313             interactive = unixv['interactive']
314             if interactive is True:
315                 unix = '  interactive\n'
316
317         return unix.rstrip('\n')
318
319     @staticmethod
320     def _apply_vpp_cpu(node):
321         """
322         Apply the VPP cpu config
323
324         :param node: Node dictionary with cpuinfo.
325         :type node: dict
326         """
327
328         # Get main core
329         cpu = '\n'
330         vpp_main_core = node['cpu']['vpp_main_core']
331         if vpp_main_core is not 0:
332             cpu += '  main-core {}\n'.format(vpp_main_core)
333
334         # Get workers
335         vpp_workers = node['cpu']['vpp_workers']
336         vpp_worker_len = len(vpp_workers)
337         if vpp_worker_len > 0:
338             vpp_worker_str = ''
339             for i, worker in enumerate(vpp_workers):
340                 if i > 0:
341                     vpp_worker_str += ','
342                 if worker[0] == worker[1]:
343                     vpp_worker_str += "{}".format(worker[0])
344                 else:
345                     vpp_worker_str += "{}-{}".format(worker[0], worker[1])
346
347             cpu += '  corelist-workers {}\n'.format(vpp_worker_str)
348
349         return cpu
350
351     @staticmethod
352     def _apply_vpp_devices(node):
353         """
354         Apply VPP PCI Device configuration to vpp startup.
355
356         :param node: Node dictionary with cpuinfo.
357         :type node: dict
358         """
359
360         devices = ''
361         ports_per_numa = node['cpu']['ports_per_numa']
362         total_mbufs = node['cpu']['total_mbufs']
363
364         for item in ports_per_numa.items():
365             value = item[1]
366             interfaces = value['interfaces']
367
368             # if 0 was specified for the number of vpp workers, use 1 queue
369             num_rx_queues = None
370             num_tx_queues = None
371             if 'rx_queues' in value:
372                 num_rx_queues = value['rx_queues']
373             if 'tx_queues' in value:
374                 num_tx_queues = value['tx_queues']
375
376             num_rx_desc = None
377             num_tx_desc = None
378
379             # Create the devices string
380             for interface in interfaces:
381                 pci_address = interface['pci_address']
382                 pci_address = pci_address.lstrip("'").rstrip("'")
383                 devices += '\n'
384                 devices += '  dev {} {{ \n'.format(pci_address)
385                 if num_rx_queues:
386                     devices += '    num-rx-queues {}\n'.format(num_rx_queues)
387                 else:
388                     devices += '    num-rx-queues {}\n'.format(1)
389                 if num_tx_queues:
390                     devices += '    num-tx-queues {}\n'.format(num_tx_queues)
391                 if num_rx_desc:
392                     devices += '    num-rx-desc {}\n'.format(num_rx_desc)
393                 if num_tx_desc:
394                     devices += '    num-tx-desc {}\n'.format(num_tx_desc)
395                 devices += '  }'
396
397         if total_mbufs is not 0:
398             devices += '\n  num-mbufs {}'.format(total_mbufs)
399
400         return devices
401
402     @staticmethod
403     def _calc_vpp_workers(node, vpp_workers, numa_node,
404                           other_cpus_end, total_vpp_workers,
405                           reserve_vpp_main_core):
406         """
407         Calculate the VPP worker information
408
409         :param node: Node dictionary
410         :param vpp_workers: List of VPP workers
411         :param numa_node: Numa node
412         :param other_cpus_end: The end of the cpus allocated for cores
413         other than vpp
414         :param total_vpp_workers: The number of vpp workers needed
415         :param reserve_vpp_main_core: Is there a core needed for
416         the vpp main core
417         :type node: dict
418         :type numa_node: int
419         :type other_cpus_end: int
420         :type total_vpp_workers: int
421         :type reserve_vpp_main_core: bool
422         :returns: Is a core still needed for the vpp main core
423         :rtype: bool
424         """
425
426         # Can we fit the workers in one of these slices
427         cpus = node['cpu']['cpus_per_node'][numa_node]
428         for cpu in cpus:
429             start = cpu[0]
430             end = cpu[1]
431             if start <= other_cpus_end:
432                 start = other_cpus_end + 1
433
434             if reserve_vpp_main_core:
435                 start += 1
436
437             workers_end = start + total_vpp_workers - 1
438             if workers_end <= end:
439                 if reserve_vpp_main_core:
440                     node['cpu']['vpp_main_core'] = start - 1
441                 reserve_vpp_main_core = False
442                 if total_vpp_workers:
443                     vpp_workers.append((start, workers_end))
444                 break
445
446         # We still need to reserve the main core
447         if reserve_vpp_main_core:
448             node['cpu']['vpp_main_core'] = other_cpus_end + 1
449
450         return reserve_vpp_main_core
451
452     @staticmethod
453     def _calc_desc_and_queues(total_numa_nodes,
454                               total_ports_per_numa,
455                               total_vpp_cpus,
456                               ports_per_numa_value):
457         """
458         Calculate the number of descriptors and queues
459
460         :param total_numa_nodes: The total number of numa nodes
461         :param total_ports_per_numa: The total number of ports for this
462         numa node
463         :param total_vpp_cpus: The total number of cpus to allocate for vpp
464         :param ports_per_numa_value: The value from the ports_per_numa
465         dictionary
466         :type total_numa_nodes: int
467         :type total_ports_per_numa: int
468         :type total_vpp_cpus: int
469         :type ports_per_numa_value: dict
470         :returns The total number of message buffers
471         :returns: The total number of vpp workers
472         :rtype: int
473         :rtype: int
474         """
475
476         # Get the total vpp workers
477         total_vpp_workers = total_vpp_cpus
478         ports_per_numa_value['total_vpp_workers'] = total_vpp_workers
479
480         # Get the number of rx queues
481         rx_queues = max(1, total_vpp_workers)
482         tx_queues = total_vpp_workers * total_numa_nodes + 1
483
484         # Get the descriptor entries
485         desc_entries = 1024
486         ports_per_numa_value['rx_queues'] = rx_queues
487         total_mbufs = (((rx_queues * desc_entries) +
488                         (tx_queues * desc_entries)) *
489                        total_ports_per_numa)
490         total_mbufs = total_mbufs
491
492         return total_mbufs, total_vpp_workers
493
494     @staticmethod
495     def _create_ports_per_numa(node, interfaces):
496         """
497         Create a dictionary or ports per numa node
498         :param node: Node dictionary
499         :param interfaces: All the interfaces to be used by vpp
500         :type node: dict
501         :type interfaces: dict
502         :returns: The ports per numa dictionary
503         :rtype: dict
504         """
505
506         # Make a list of ports by numa node
507         ports_per_numa = {}
508         for item in interfaces.items():
509             i = item[1]
510             if i['numa_node'] not in ports_per_numa:
511                 ports_per_numa[i['numa_node']] = {'interfaces': []}
512                 ports_per_numa[i['numa_node']]['interfaces'].append(i)
513             else:
514                 ports_per_numa[i['numa_node']]['interfaces'].append(i)
515         node['cpu']['ports_per_numa'] = ports_per_numa
516
517         return ports_per_numa
518
519     def calculate_cpu_parameters(self):
520         """
521         Calculate the cpu configuration.
522
523         """
524
525         # Calculate the cpu parameters, needed for the
526         # vpp_startup and grub configuration
527         for i in self._nodes.items():
528             node = i[1]
529
530             # get total number of nic ports
531             interfaces = node['interfaces']
532
533             # Make a list of ports by numa node
534             ports_per_numa = self._create_ports_per_numa(node, interfaces)
535
536             # Get the number of cpus to skip, we never use the first cpu
537             other_cpus_start = 1
538             other_cpus_end = other_cpus_start + \
539                 node['cpu']['total_other_cpus'] - 1
540             other_workers = None
541             if other_cpus_end is not 0:
542                 other_workers = (other_cpus_start, other_cpus_end)
543             node['cpu']['other_workers'] = other_workers
544
545             # Allocate the VPP main core and workers
546             vpp_workers = []
547             reserve_vpp_main_core = node['cpu']['reserve_vpp_main_core']
548             total_vpp_cpus = node['cpu']['total_vpp_cpus']
549
550             # If total_vpp_cpus is 0 or is less than the numa nodes with ports
551             #  then we shouldn't get workers
552             total_with_main = total_vpp_cpus
553             if reserve_vpp_main_core:
554                 total_with_main += 1
555             total_mbufs = 0
556             if total_with_main is not 0:
557                 for item in ports_per_numa.items():
558                     numa_node = item[0]
559                     value = item[1]
560
561                     # Get the number of descriptors and queues
562                     mbufs, total_vpp_workers = self._calc_desc_and_queues(
563                         len(ports_per_numa),
564                         len(value['interfaces']), total_vpp_cpus, value)
565                     total_mbufs += mbufs
566
567                     # Get the VPP workers
568                     reserve_vpp_main_core = self._calc_vpp_workers(
569                         node, vpp_workers, numa_node, other_cpus_end,
570                         total_vpp_workers, reserve_vpp_main_core)
571
572                 total_mbufs *= 2.5
573                 total_mbufs = int(total_mbufs)
574             else:
575                 total_mbufs = 0
576
577             # Save the info
578             node['cpu']['vpp_workers'] = vpp_workers
579             node['cpu']['total_mbufs'] = total_mbufs
580
581         # Write the config
582         self.updateconfig()
583
584     @staticmethod
585     def _apply_vpp_tcp(node):
586         """
587         Apply the VPP Unix config
588
589         :param node: Node dictionary with cpuinfo.
590         :type node: dict
591         """
592
593         active_open_sessions = node['tcp']['active_open_sessions']
594         aos = int(active_open_sessions)
595
596         passive_open_sessions = node['tcp']['passive_open_sessions']
597         pos = int(passive_open_sessions)
598
599         # Generate the api-segment gid vpp sheit in any case
600         if (aos + pos) == 0:
601             tcp = "api-segment {\n"
602             tcp = tcp + "  gid vpp\n"
603             tcp = tcp + "}\n"
604             return tcp.rstrip('\n')
605
606         tcp = "# TCP stack-related configuration parameters\n"
607         tcp = tcp + "# expecting {:d} client sessions, {:d} server sessions\n\n".format(aos, pos)
608         tcp = tcp + "heapsize 4g\n\n"
609         tcp = tcp + "api-segment {\n"
610         tcp = tcp + "  global-size 2000M\n"
611         tcp = tcp + "  api-size 1G\n"
612         tcp = tcp + "}\n\n"
613
614         tcp = tcp + "session {\n"
615         tcp = tcp + "  event-queue-length " + "{:d}".format(aos + pos) + "\n"
616         tcp = tcp + "  preallocated-sessions " + "{:d}".format(aos + pos) + "\n"
617         tcp = tcp + "  v4-session-table-buckets " + "{:d}".format((aos + pos) / 4) + "\n"
618         tcp = tcp + "  v4-session-table-memory 3g\n"
619         if aos > 0:
620             tcp = tcp + "  v4-halfopen-table-buckets " + \
621                   "{:d}".format((aos + pos) / 4) + "\n"
622             tcp = tcp + "  v4-halfopen-table-memory 3g\n"
623         tcp = tcp + "}\n\n"
624
625         tcp = tcp + "tcp {\n"
626         tcp = tcp + "  preallocated-connections " + "{:d}".format(aos + pos) + "\n"
627         if aos > 0:
628             tcp = tcp + "  preallocated-half-open-connections " + "{:d}".format(aos) + "\n"
629             tcp = tcp + "  local-endpoints-table-buckets " + "{:d}".format((aos + pos) / 4) + "\n"
630             tcp = tcp + "  local-endpoints-table-memory 3g\n"
631         tcp = tcp + "}\n\n"
632
633         return tcp.rstrip('\n')
634
635     def apply_vpp_startup(self):
636         """
637         Apply the vpp startup configration
638
639         """
640
641         # Apply the VPP startup configruation
642         for i in self._nodes.items():
643             node = i[1]
644
645             # Get the startup file
646             rootdir = node['rootdir']
647             sfile = rootdir + node['vpp']['startup_config_file']
648
649             # Get the devices
650             devices = self._apply_vpp_devices(node)
651
652             # Get the CPU config
653             cpu = self._apply_vpp_cpu(node)
654
655             # Get the unix config
656             unix = self._apply_vpp_unix(node)
657
658             # Get the TCP configuration, if any
659             tcp = self._apply_vpp_tcp(node)
660
661             # Make a backup if needed
662             self._autoconfig_backup_file(sfile)
663
664             # Get the template
665             tfile = sfile + '.template'
666             (ret, stdout, stderr) = \
667                 VPPUtil.exec_command('cat {}'.format(tfile))
668             if ret != 0:
669                 raise RuntimeError('Executing cat command failed to node {}'.
670                                    format(node['host']))
671             startup = stdout.format(unix=unix,
672                                     cpu=cpu,
673                                     devices=devices,
674                                     tcp=tcp)
675
676             (ret, stdout, stderr) = \
677                 VPPUtil.exec_command('rm {}'.format(sfile))
678             if ret != 0:
679                 logging.debug(stderr)
680
681             cmd = "sudo cat > {0} << EOF\n{1}\n".format(sfile, startup)
682             (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
683             if ret != 0:
684                 raise RuntimeError('Writing config failed node {}'.
685                                    format(node['host']))
686
687     def apply_grub_cmdline(self):
688         """
689         Apply the grub cmdline
690
691         """
692
693         for i in self._nodes.items():
694             node = i[1]
695
696             # Get the isolated CPUs
697             other_workers = node['cpu']['other_workers']
698             vpp_workers = node['cpu']['vpp_workers']
699             vpp_main_core = node['cpu']['vpp_main_core']
700             all_workers = []
701             if other_workers is not None:
702                 all_workers = [other_workers]
703             if vpp_main_core is not 0:
704                 all_workers += [(vpp_main_core, vpp_main_core)]
705             all_workers += vpp_workers
706             isolated_cpus = ''
707             for idx, worker in enumerate(all_workers):
708                 if worker is None:
709                     continue
710                 if idx > 0:
711                     isolated_cpus += ','
712                 if worker[0] == worker[1]:
713                     isolated_cpus += "{}".format(worker[0])
714                 else:
715                     isolated_cpus += "{}-{}".format(worker[0], worker[1])
716
717             vppgrb = VppGrubUtil(node)
718             current_cmdline = vppgrb.get_current_cmdline()
719             if 'grub' not in node:
720                 node['grub'] = {}
721             node['grub']['current_cmdline'] = current_cmdline
722             node['grub']['default_cmdline'] = \
723                 vppgrb.apply_cmdline(node, isolated_cpus)
724
725         self.updateconfig()
726
727     def get_hugepages(self):
728         """
729         Get the hugepage configuration
730
731         """
732
733         for i in self._nodes.items():
734             node = i[1]
735
736             hpg = VppHugePageUtil(node)
737             max_map_count, shmmax = hpg.get_huge_page_config()
738             node['hugepages']['max_map_count'] = max_map_count
739             node['hugepages']['shmax'] = shmmax
740             total, free, size, memtotal, memfree = hpg.get_actual_huge_pages()
741             node['hugepages']['actual_total'] = total
742             node['hugepages']['free'] = free
743             node['hugepages']['size'] = size
744             node['hugepages']['memtotal'] = memtotal
745             node['hugepages']['memfree'] = memfree
746
747         self.updateconfig()
748
749     def get_grub(self):
750         """
751         Get the grub configuration
752
753         """
754
755         for i in self._nodes.items():
756             node = i[1]
757
758             vppgrb = VppGrubUtil(node)
759             current_cmdline = vppgrb.get_current_cmdline()
760             default_cmdline = vppgrb.get_default_cmdline()
761
762             # Get the total number of isolated CPUs
763             current_iso_cpus = 0
764             iso_cpur = re.findall(r'isolcpus=[\w+\-,]+', current_cmdline)
765             iso_cpurl = len(iso_cpur)
766             if iso_cpurl > 0:
767                 iso_cpu_str = iso_cpur[0]
768                 iso_cpu_str = iso_cpu_str.split('=')[1]
769                 iso_cpul = iso_cpu_str.split(',')
770                 for iso_cpu in iso_cpul:
771                     isocpuspl = iso_cpu.split('-')
772                     if len(isocpuspl) is 1:
773                         current_iso_cpus += 1
774                     else:
775                         first = int(isocpuspl[0])
776                         second = int(isocpuspl[1])
777                         if first == second:
778                             current_iso_cpus += 1
779                         else:
780                             current_iso_cpus += second - first
781
782             if 'grub' not in node:
783                 node['grub'] = {}
784             node['grub']['current_cmdline'] = current_cmdline
785             node['grub']['default_cmdline'] = default_cmdline
786             node['grub']['current_iso_cpus'] = current_iso_cpus
787
788         self.updateconfig()
789
790     @staticmethod
791     def _get_device(node):
792         """
793         Get the device configuration for a single node
794
795         :param node: Node dictionary with cpuinfo.
796         :type node: dict
797
798         """
799
800         vpp = VppPCIUtil(node)
801         vpp.get_all_devices()
802
803         # Save the device information
804         node['devices'] = {}
805         node['devices']['dpdk_devices'] = vpp.get_dpdk_devices()
806         node['devices']['kernel_devices'] = vpp.get_kernel_devices()
807         node['devices']['other_devices'] = vpp.get_other_devices()
808         node['devices']['linkup_devices'] = vpp.get_link_up_devices()
809
810     def get_devices_per_node(self):
811         """
812         Get the device configuration for all the nodes
813
814         """
815
816         for i in self._nodes.items():
817             node = i[1]
818             # Update the interface data
819
820             self._get_device(node)
821
822         self.updateconfig()
823
824     @staticmethod
825     def get_cpu_layout(node):
826         """
827         Get the cpu layout
828
829         using lscpu -p get the cpu layout.
830         Returns a list with each item representing a single cpu.
831
832         :param node: Node dictionary.
833         :type node: dict
834         :returns: The cpu layout
835         :rtype: list
836         """
837
838         cmd = 'lscpu -p'
839         (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
840         if ret != 0:
841             raise RuntimeError('{} failed on node {} {}'.
842                                format(cmd, node['host'], stderr))
843
844         pcpus = []
845         lines = stdout.split('\n')
846         for line in lines:
847             if line == '' or line[0] == '#':
848                 continue
849             linesplit = line.split(',')
850             layout = {'cpu': linesplit[0], 'core': linesplit[1],
851                       'socket': linesplit[2], 'node': linesplit[3]}
852
853             # cpu, core, socket, node
854             pcpus.append(layout)
855
856         return pcpus
857
858     def get_cpu(self):
859         """
860         Get the cpu configuration
861
862         """
863
864         # Get the CPU layout
865         CpuUtils.get_cpu_layout_from_all_nodes(self._nodes)
866
867         for i in self._nodes.items():
868             node = i[1]
869
870             # Get the cpu layout
871             layout = self.get_cpu_layout(node)
872             node['cpu']['layout'] = layout
873
874             cpuinfo = node['cpuinfo']
875             smt_enabled = CpuUtils.is_smt_enabled(cpuinfo)
876             node['cpu']['smt_enabled'] = smt_enabled
877
878             # We don't want to write the cpuinfo
879             node['cpuinfo'] = ""
880
881         # Write the config
882         self.updateconfig()
883
884     def discover(self):
885         """
886         Get the current system configuration.
887
888         """
889
890         # Get the Huge Page configuration
891         self.get_hugepages()
892
893         # Get the device configuration
894         self.get_devices_per_node()
895
896         # Get the CPU configuration
897         self.get_cpu()
898
899         # Get the current grub cmdline
900         self.get_grub()
901
902     def _modify_cpu_questions(self, node, total_cpus, numa_nodes):
903         """
904         Ask the user questions related to the cpu configuration.
905
906         :param node: Node dictionary
907         :param total_cpus: The total number of cpus in the system
908         :param numa_nodes: The list of numa nodes in the system
909         :type node: dict
910         :type total_cpus: int
911         :type numa_nodes: list
912         """
913
914         print "\nYour system has {} core(s) and {} Numa Nodes.". \
915             format(total_cpus, len(numa_nodes))
916         print "To begin, we suggest not reserving any cores for VPP",
917         print "or other processes."
918         print "Then to improve performance try reserving cores as needed. "
919
920         max_other_cores = total_cpus / 2
921         question = '\nHow many core(s) do you want to reserve for processes \
922 other than VPP? [0-{}][0]? '.format(str(max_other_cores))
923         total_other_cpus = self._ask_user_range(question, 0, max_other_cores,
924                                                 0)
925         node['cpu']['total_other_cpus'] = total_other_cpus
926
927         max_vpp_cpus = 4
928         total_vpp_cpus = 0
929         if max_vpp_cpus > 0:
930             question = "How many core(s) shall we reserve for VPP workers[0-{}][0]? ". \
931                 format(max_vpp_cpus)
932             total_vpp_cpus = self._ask_user_range(question, 0, max_vpp_cpus, 0)
933             node['cpu']['total_vpp_cpus'] = total_vpp_cpus
934
935         max_main_cpus = max_vpp_cpus - total_vpp_cpus
936         reserve_vpp_main_core = False
937         if max_main_cpus > 0:
938             question = "Should we reserve 1 core for the VPP Main thread? "
939             question += "[y/N]? "
940             answer = self._ask_user_yn(question, 'n')
941             if answer == 'y':
942                 reserve_vpp_main_core = True
943             node['cpu']['reserve_vpp_main_core'] = reserve_vpp_main_core
944             node['cpu']['vpp_main_core'] = 0
945
946     def modify_cpu(self):
947         """
948         Modify the cpu configuration, asking for the user for the values.
949
950         """
951
952         # Get the CPU layout
953         CpuUtils.get_cpu_layout_from_all_nodes(self._nodes)
954
955         for i in self._nodes.items():
956             node = i[1]
957             total_cpus = 0
958             total_cpus_per_slice = 0
959             cpus_per_node = {}
960             numa_nodes = []
961             cores = []
962             cpu_layout = self.get_cpu_layout(node)
963
964             # Assume the number of cpus per slice is always the same as the
965             # first slice
966             first_node = '0'
967             for cpu in cpu_layout:
968                 if cpu['node'] != first_node:
969                     break
970                 total_cpus_per_slice += 1
971
972             # Get the total number of cpus, cores, and numa nodes from the
973             # cpu layout
974             for cpul in cpu_layout:
975                 numa_node = cpul['node']
976                 core = cpul['core']
977                 cpu = cpul['cpu']
978                 total_cpus += 1
979
980                 if numa_node not in cpus_per_node:
981                     cpus_per_node[numa_node] = []
982                 cpuperslice = int(cpu) % total_cpus_per_slice
983                 if cpuperslice == 0:
984                     cpus_per_node[numa_node].append((int(cpu), int(cpu) +
985                                                      total_cpus_per_slice - 1))
986                 if numa_node not in numa_nodes:
987                     numa_nodes.append(numa_node)
988                 if core not in cores:
989                     cores.append(core)
990             node['cpu']['cpus_per_node'] = cpus_per_node
991
992             # Ask the user some questions
993             self._modify_cpu_questions(node, total_cpus, numa_nodes)
994
995             # Populate the interfaces with the numa node
996             ikeys = node['interfaces'].keys()
997             VPPUtil.get_interfaces_numa_node(node, *tuple(ikeys))
998
999             # We don't want to write the cpuinfo
1000             node['cpuinfo'] = ""
1001
1002         # Write the configs
1003         self._update_auto_config()
1004         self.updateconfig()
1005
1006     def _modify_other_devices(self, node,
1007                               other_devices, kernel_devices, dpdk_devices):
1008         """
1009         Modify the devices configuration, asking for the user for the values.
1010
1011         """
1012
1013         odevices_len = len(other_devices)
1014         if odevices_len > 0:
1015             print "\nThese device(s) are currently NOT being used",
1016             print "by VPP or the OS.\n"
1017             VppPCIUtil.show_vpp_devices(other_devices, show_interfaces=False)
1018             question = "\nWould you like to give any of these devices"
1019             question += " back to the OS [Y/n]? "
1020             answer = self._ask_user_yn(question, 'Y')
1021             if answer == 'y':
1022                 vppd = {}
1023                 for dit in other_devices.items():
1024                     dvid = dit[0]
1025                     device = dit[1]
1026                     question = "Would you like to use device {} for". \
1027                         format(dvid)
1028                     question += " the OS [y/N]? "
1029                     answer = self._ask_user_yn(question, 'n')
1030                     if answer == 'y':
1031                         driver = device['unused'][0]
1032                         VppPCIUtil.bind_vpp_device(node, driver, dvid)
1033                         vppd[dvid] = device
1034                 for dit in vppd.items():
1035                     dvid = dit[0]
1036                     device = dit[1]
1037                     kernel_devices[dvid] = device
1038                     del other_devices[dvid]
1039
1040         odevices_len = len(other_devices)
1041         if odevices_len > 0:
1042             print "\nThese device(s) are still NOT being used ",
1043             print "by VPP or the OS.\n"
1044             VppPCIUtil.show_vpp_devices(other_devices, show_interfaces=False)
1045             question = "\nWould you like use any of these for VPP [y/N]? "
1046             answer = self._ask_user_yn(question, 'N')
1047             if answer == 'y':
1048                 vppd = {}
1049                 for dit in other_devices.items():
1050                     dvid = dit[0]
1051                     device = dit[1]
1052                     question = "Would you like to use device {} ".format(dvid)
1053                     question += "for VPP [y/N]? "
1054                     answer = self._ask_user_yn(question, 'n')
1055                     if answer == 'y':
1056                         vppd[dvid] = device
1057                 for dit in vppd.items():
1058                     dvid = dit[0]
1059                     device = dit[1]
1060                     dpdk_devices[dvid] = device
1061                     del other_devices[dvid]
1062
1063     def modify_devices(self):
1064         """
1065         Modify the devices configuration, asking for the user for the values.
1066
1067         """
1068
1069         for i in self._nodes.items():
1070             node = i[1]
1071             devices = node['devices']
1072             other_devices = devices['other_devices']
1073             kernel_devices = devices['kernel_devices']
1074             dpdk_devices = devices['dpdk_devices']
1075
1076             if other_devices:
1077                 self._modify_other_devices(node, other_devices,
1078                                            kernel_devices, dpdk_devices)
1079
1080                 # Get the devices again for this node
1081                 self._get_device(node)
1082                 devices = node['devices']
1083                 kernel_devices = devices['kernel_devices']
1084                 dpdk_devices = devices['dpdk_devices']
1085
1086             klen = len(kernel_devices)
1087             if klen > 0:
1088                 print "\nThese devices have kernel interfaces, but",
1089                 print "appear to be safe to use with VPP.\n"
1090                 VppPCIUtil.show_vpp_devices(kernel_devices)
1091                 question = "\nWould you like to use any of these "
1092                 question += "device(s) for VPP [y/N]? "
1093                 answer = self._ask_user_yn(question, 'n')
1094                 if answer == 'y':
1095                     vppd = {}
1096                     for dit in kernel_devices.items():
1097                         dvid = dit[0]
1098                         device = dit[1]
1099                         question = "Would you like to use device {} ". \
1100                             format(dvid)
1101                         question += "for VPP [y/N]? "
1102                         answer = self._ask_user_yn(question, 'n')
1103                         if answer == 'y':
1104                             vppd[dvid] = device
1105                     for dit in vppd.items():
1106                         dvid = dit[0]
1107                         device = dit[1]
1108                         dpdk_devices[dvid] = device
1109                         del kernel_devices[dvid]
1110
1111             dlen = len(dpdk_devices)
1112             if dlen > 0:
1113                 print "\nThese device(s) will be used by VPP.\n"
1114                 VppPCIUtil.show_vpp_devices(dpdk_devices, show_interfaces=False)
1115                 question = "\nWould you like to remove any of "
1116                 question += "these device(s) [y/N]? "
1117                 answer = self._ask_user_yn(question, 'n')
1118                 if answer == 'y':
1119                     vppd = {}
1120                     for dit in dpdk_devices.items():
1121                         dvid = dit[0]
1122                         device = dit[1]
1123                         question = "Would you like to remove {} [y/N]? ". \
1124                             format(dvid)
1125                         answer = self._ask_user_yn(question, 'n')
1126                         if answer == 'y':
1127                             vppd[dvid] = device
1128                     for dit in vppd.items():
1129                         dvid = dit[0]
1130                         device = dit[1]
1131                         driver = device['unused'][0]
1132                         VppPCIUtil.bind_vpp_device(node, driver, dvid)
1133                         kernel_devices[dvid] = device
1134                         del dpdk_devices[dvid]
1135
1136             interfaces = {}
1137             for dit in dpdk_devices.items():
1138                 dvid = dit[0]
1139                 device = dit[1]
1140                 VppPCIUtil.vpp_create_interface(interfaces, dvid, device)
1141             node['interfaces'] = interfaces
1142
1143             print "\nThese device(s) will be used by VPP, please",
1144             print "rerun this option if this is incorrect.\n"
1145             VppPCIUtil.show_vpp_devices(dpdk_devices, show_interfaces=False)
1146
1147         self._update_auto_config()
1148         self.updateconfig()
1149
1150     def modify_huge_pages(self):
1151         """
1152         Modify the huge page configuration, asking for the user for the values.
1153
1154         """
1155
1156         for i in self._nodes.items():
1157             node = i[1]
1158
1159             total = node['hugepages']['actual_total']
1160             free = node['hugepages']['free']
1161             size = node['hugepages']['size']
1162             memfree = node['hugepages']['memfree'].split(' ')[0]
1163             hugesize = int(size.split(' ')[0])
1164             # The max number of huge pages should be no more than
1165             # 70% of total free memory
1166             maxpages = (int(memfree) * MAX_PERCENT_FOR_HUGE_PAGES / 100) / hugesize
1167             print "\nThere currently {} {} huge pages free.". \
1168                 format(free, size)
1169             question = "Do you want to reconfigure the number of "
1170             question += "huge pages [y/N]? "
1171             answer = self._ask_user_yn(question, 'n')
1172             if answer == 'n':
1173                 node['hugepages']['total'] = total
1174                 continue
1175
1176             print "\nThere currently a total of {} huge pages.". \
1177                 format(total)
1178             question = \
1179                 "How many huge pages do you want [{} - {}][{}]? ". \
1180                 format(MIN_TOTAL_HUGE_PAGES, maxpages, MIN_TOTAL_HUGE_PAGES)
1181             answer = self._ask_user_range(question, 1024, maxpages, 1024)
1182             node['hugepages']['total'] = str(answer)
1183
1184         # Update auto-config.yaml
1185         self._update_auto_config()
1186
1187         # Rediscover just the hugepages
1188         self.get_hugepages()
1189
1190     def get_tcp_params(self):
1191         """
1192         Get the tcp configuration
1193
1194         """
1195         # maybe nothing to do here?
1196         self.updateconfig()
1197
1198     def acquire_tcp_params(self):
1199         """
1200         Ask the user for TCP stack configuration parameters
1201
1202         """
1203
1204         for i in self._nodes.items():
1205             node = i[1]
1206
1207             question = "\nHow many active-open / tcp client sessions are expected "
1208             question = question + "[0-10000000][0]? "
1209             answer = self._ask_user_range(question, 0, 10000000, 0)
1210             # Less than 10K is equivalent to 0
1211             if int(answer) < 10000:
1212                 answer = 0
1213             node['tcp']['active_open_sessions'] = answer
1214
1215             question = "How many passive-open / tcp server sessions are expected "
1216             question = question + "[0-10000000][0]? "
1217             answer = self._ask_user_range(question, 0, 10000000, 0)
1218             # Less than 10K is equivalent to 0
1219             if int(answer) < 10000:
1220                 answer = 0
1221             node['tcp']['passive_open_sessions'] = answer
1222
1223         # Update auto-config.yaml
1224         self._update_auto_config()
1225
1226         # Rediscover tcp parameters
1227         self.get_tcp_params()
1228
1229     @staticmethod
1230     def patch_qemu(node):
1231         """
1232         Patch qemu with the correct patches.
1233
1234         :param node: Node dictionary
1235         :type node: dict
1236         """
1237
1238         print '\nWe are patching the node "{}":\n'.format(node['host'])
1239         QemuUtils.build_qemu(node, force_install=True, apply_patch=True)
1240
1241     @staticmethod
1242     def cpu_info(node):
1243         """
1244         print the CPU information
1245
1246         """
1247
1248         cpu = CpuUtils.get_cpu_info_per_node(node)
1249
1250         item = 'Model name'
1251         if item in cpu:
1252             print "{:>20}:    {}".format(item, cpu[item])
1253         item = 'CPU(s)'
1254         if item in cpu:
1255             print "{:>20}:    {}".format(item, cpu[item])
1256         item = 'Thread(s) per core'
1257         if item in cpu:
1258             print "{:>20}:    {}".format(item, cpu[item])
1259         item = 'Core(s) per socket'
1260         if item in cpu:
1261             print "{:>20}:    {}".format(item, cpu[item])
1262         item = 'Socket(s)'
1263         if item in cpu:
1264             print "{:>20}:    {}".format(item, cpu[item])
1265         item = 'NUMA node(s)'
1266         numa_nodes = 0
1267         if item in cpu:
1268             numa_nodes = int(cpu[item])
1269         for i in xrange(0, numa_nodes):
1270             item = "NUMA node{} CPU(s)".format(i)
1271             print "{:>20}:    {}".format(item, cpu[item])
1272         item = 'CPU max MHz'
1273         if item in cpu:
1274             print "{:>20}:    {}".format(item, cpu[item])
1275         item = 'CPU min MHz'
1276         if item in cpu:
1277             print "{:>20}:    {}".format(item, cpu[item])
1278
1279         if node['cpu']['smt_enabled']:
1280             smt = 'Enabled'
1281         else:
1282             smt = 'Disabled'
1283         print "{:>20}:    {}".format('SMT', smt)
1284
1285         # VPP Threads
1286         print "\nVPP Threads: (Name: Cpu Number)"
1287         vpp_processes = cpu['vpp_processes']
1288         for i in vpp_processes.items():
1289             print "  {:10}: {:4}".format(i[0], i[1])
1290
1291     @staticmethod
1292     def device_info(node):
1293         """
1294         Show the device information.
1295
1296         """
1297
1298         if 'cpu' in node and 'total_mbufs' in node['cpu']:
1299             total_mbufs = node['cpu']['total_mbufs']
1300             if total_mbufs is not 0:
1301                 print "Total Number of Buffers: {}".format(total_mbufs)
1302
1303         vpp = VppPCIUtil(node)
1304         vpp.get_all_devices()
1305         linkup_devs = vpp.get_link_up_devices()
1306         if len(linkup_devs):
1307             print ("\nDevices with link up (can not be used with VPP):")
1308             vpp.show_vpp_devices(linkup_devs, show_header=False)
1309             # for dev in linkup_devs:
1310             #    print ("    " + dev)
1311         kernel_devs = vpp.get_kernel_devices()
1312         if len(kernel_devs):
1313             print ("\nDevices bound to kernel drivers:")
1314             vpp.show_vpp_devices(kernel_devs, show_header=False)
1315         else:
1316             print ("\nNo devices bound to kernel drivers")
1317
1318         dpdk_devs = vpp.get_dpdk_devices()
1319         if len(dpdk_devs):
1320             print ("\nDevices bound to DPDK drivers:")
1321             vpp.show_vpp_devices(dpdk_devs, show_interfaces=True,
1322                                  show_header=False)
1323         else:
1324             print ("\nNo devices bound to DPDK drivers")
1325
1326         vpputl = VPPUtil()
1327         interfaces = vpputl.get_hardware(node)
1328         if interfaces == {}:
1329             return
1330
1331         print ("\nDevices in use by VPP:")
1332
1333         if len(interfaces.items()) < 2:
1334             print ("None")
1335             return
1336
1337         print "{:30} {:6} {:4} {:7} {:4} {:7}". \
1338             format('Name', 'Socket', 'RXQs',
1339                    'RXDescs', 'TXQs', 'TXDescs')
1340         for intf in sorted(interfaces.items()):
1341             name = intf[0]
1342             value = intf[1]
1343             if name == 'local0':
1344                 continue
1345             socket = rx_qs = rx_ds = tx_qs = tx_ds = ''
1346             if 'cpu socket' in value:
1347                 socket = int(value['cpu socket'])
1348             if 'rx queues' in value:
1349                 rx_qs = int(value['rx queues'])
1350             if 'rx descs' in value:
1351                 rx_ds = int(value['rx descs'])
1352             if 'tx queues' in value:
1353                 tx_qs = int(value['tx queues'])
1354             if 'tx descs' in value:
1355                 tx_ds = int(value['tx descs'])
1356
1357             print ("{:30} {:>6} {:>4} {:>7} {:>4} {:>7}".
1358                    format(name, socket, rx_qs, rx_ds, tx_qs, tx_ds))
1359
1360     @staticmethod
1361     def hugepage_info(node):
1362         """
1363         Show the huge page information.
1364
1365         """
1366
1367         hpg = VppHugePageUtil(node)
1368         hpg.show_huge_pages()
1369
1370     @staticmethod
1371     def min_system_resources(node):
1372         """
1373         Check the system for basic minimum resources, return true if
1374         there is enough.
1375
1376         :returns: boolean
1377         :rtype: dict
1378         """
1379
1380         min_sys_res = True
1381
1382         # CPUs
1383         if 'layout' in node['cpu']:
1384             total_cpus = len(node['cpu']['layout'])
1385             if total_cpus < 2:
1386                 print "\nThere is only {} CPU(s) available on this system.".format(total_cpus)
1387                 print "This is not enough to run VPP."
1388                 min_sys_res = False
1389
1390         # System Memory
1391         if 'free' in node['hugepages'] and \
1392                 'memfree' in node['hugepages'] and \
1393                 'size' in node['hugepages']:
1394             free = node['hugepages']['free']
1395             memfree = float(node['hugepages']['memfree'].split(' ')[0])
1396             hugesize = float(node['hugepages']['size'].split(' ')[0])
1397
1398             memhugepages = MIN_TOTAL_HUGE_PAGES * hugesize
1399             percentmemhugepages = (memhugepages / memfree) * 100
1400             if free is '0' and \
1401                     percentmemhugepages > MAX_PERCENT_FOR_HUGE_PAGES:
1402                 print "\nThe System has only {} of free memory.".format(int(memfree))
1403                 print "You will not be able to allocate enough Huge Pages for VPP."
1404                 min_sys_res = False
1405
1406         return min_sys_res
1407
1408     def sys_info(self):
1409         """
1410         Print the system information
1411
1412         """
1413
1414         for i in self._nodes.items():
1415             print "\n=============================="
1416             name = i[0]
1417             node = i[1]
1418
1419             print "NODE: {}\n".format(name)
1420
1421             # CPU
1422             print "CPU:"
1423             self.cpu_info(node)
1424
1425             # Grub
1426             print "\nGrub Command Line:"
1427             if 'grub' in node:
1428                 print \
1429                     "  Current: {}".format(
1430                         node['grub']['current_cmdline'])
1431                 print \
1432                     "  Configured: {}".format(
1433                         node['grub']['default_cmdline'])
1434
1435             # Huge Pages
1436             print "\nHuge Pages:"
1437             self.hugepage_info(node)
1438
1439             # Devices
1440             print "\nDevices:"
1441             self.device_info(node)
1442
1443             # Status
1444             print "\nVPP Service Status:"
1445             state, errors = VPPUtil.status(node)
1446             print "  {}".format(state)
1447             for e in errors:
1448                 print "  {}".format(e)
1449
1450             # Minimum system resources
1451             self.min_system_resources(node)
1452
1453             print "\n=============================="
1454
1455     def _ipv4_interface_setup_questions(self, node):
1456         """
1457         Ask the user some questions and get a list of interfaces
1458         and IPv4 addresses associated with those interfaces
1459
1460         :param node: Node dictionary.
1461         :type node: dict
1462         :returns: A list or interfaces with ip addresses
1463         :rtype: dict
1464         """
1465
1466         vpputl = VPPUtil()
1467         interfaces = vpputl.get_hardware(node)
1468         if interfaces == {}:
1469             return
1470
1471         interfaces_with_ip = []
1472         for intf in sorted(interfaces.items()):
1473             name = intf[0]
1474             if name == 'local0':
1475                 continue
1476
1477             question = "Would you like add address to interface {} [Y/n]? ".format(name)
1478             answer = self._ask_user_yn(question, 'y')
1479             if answer == 'y':
1480                 address = {}
1481                 addr = self._ask_user_ipv4()
1482                 address['name'] = name
1483                 address['addr'] = addr
1484                 interfaces_with_ip.append(address)
1485
1486         return interfaces_with_ip
1487
1488     def ipv4_interface_setup(self):
1489         """
1490         After asking the user some questions, get a list of interfaces
1491         and IPv4 addresses associated with those interfaces
1492
1493         """
1494
1495         for i in self._nodes.items():
1496             node = i[1]
1497
1498             # Show the current interfaces with IP addresses
1499             current_ints = VPPUtil.get_int_ip(node)
1500             if current_ints is not {}:
1501                 print ("\nThese are the current interfaces with IP addresses:")
1502                 for items in sorted(current_ints.items()):
1503                     name = items[0]
1504                     value = items[1]
1505                     if 'address' not in value:
1506                         address = 'Not Set'
1507                     else:
1508                         address = value['address']
1509                     print ("{:30} {:20} {:10}".format(name, address, value['state']))
1510                 question = "\nWould you like to keep this configuration [Y/n]? "
1511                 answer = self._ask_user_yn(question, 'y')
1512                 if answer == 'y':
1513                     continue
1514             else:
1515                 print ("\nThere are currently no interfaces with IP addresses.")
1516
1517             # Create a script that add the ip addresses to the interfaces
1518             # and brings the interfaces up
1519             ints_with_addrs = self._ipv4_interface_setup_questions(node)
1520             content = ''
1521             for ints in ints_with_addrs:
1522                 name = ints['name']
1523                 addr = ints['addr']
1524                 setipstr = 'set int ip address {} {}\n'.format(name, addr)
1525                 setintupstr = 'set int state {} up\n'.format(name)
1526                 content += setipstr + setintupstr
1527
1528             # Write the content to the script
1529             rootdir = node['rootdir']
1530             filename = rootdir + '/vpp/vpp-config/scripts/set_int_ipv4_and_up'
1531             with open(filename, 'w+') as sfile:
1532                 sfile.write(content)
1533
1534             # Execute the script
1535             cmd = 'vppctl exec {}'.format(filename)
1536             (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
1537             if ret != 0:
1538                 logging.debug(stderr)
1539
1540             print("\nA script as been created at {}".format(filename))
1541             print("This script can be run using the following:")
1542             print("vppctl exec {}\n".format(filename))
1543
1544     def _create_vints_questions(self, node):
1545         """
1546         Ask the user some questions and get a list of interfaces
1547         and IPv4 addresses associated with those interfaces
1548
1549         :param node: Node dictionary.
1550         :type node: dict
1551         :returns: A list or interfaces with ip addresses
1552         :rtype: list
1553         """
1554
1555         vpputl = VPPUtil()
1556         interfaces = vpputl.get_hardware(node)
1557         if interfaces == {}:
1558             return []
1559
1560         # First delete all the Virtual interfaces
1561         for intf in sorted(interfaces.items()):
1562             name = intf[0]
1563             if name[:7] == 'Virtual':
1564                 cmd = 'vppctl delete vhost-user {}'.format(name)
1565                 (ret, stdout, stderr) = vpputl.exec_command(cmd)
1566                 if ret != 0:
1567                     logging.debug('{} failed on node {} {}'.format(
1568                         cmd, node['host'], stderr))
1569
1570         # Create a virtual interface, for each interface the user wants to use
1571         interfaces = vpputl.get_hardware(node)
1572         if interfaces == {}:
1573             return []
1574         interfaces_with_virtual_interfaces = []
1575         inum = 1
1576         for intf in sorted(interfaces.items()):
1577             name = intf[0]
1578             if name == 'local0':
1579                 continue
1580
1581             question = "Would you like connect this interface {} to the VM [Y/n]? ".format(name)
1582             answer = self._ask_user_yn(question, 'y')
1583             if answer == 'y':
1584                 sockfilename = '/tmp/sock{}.sock'.format(inum)
1585                 if os.path.exists(sockfilename):
1586                     os.remove(sockfilename)
1587                 cmd = 'vppctl create vhost-user socket {} server'.format(sockfilename)
1588                 (ret, stdout, stderr) = vpputl.exec_command(cmd)
1589                 if ret != 0:
1590                     raise RuntimeError("Create vhost failed on node {} {}."
1591                                        .format(node['host'], stderr))
1592                 vintname = stdout.rstrip('\r\n')
1593
1594                 interface = {'name': name, 'virtualinterface': '{}'.format(vintname),
1595                              'bridge': '{}'.format(inum)}
1596                 inum += 1
1597                 interfaces_with_virtual_interfaces.append(interface)
1598
1599         return interfaces_with_virtual_interfaces
1600
1601     def create_and_bridge_virtual_interfaces(self):
1602         """
1603         After asking the user some questions, create a VM and connect the interfaces
1604         to VPP interfaces
1605
1606         """
1607
1608         for i in self._nodes.items():
1609             node = i[1]
1610
1611             # Show the current bridge and interface configuration
1612             print "\nThis the current bridge configuration:"
1613             VPPUtil.show_bridge(node)
1614             question = "\nWould you like to keep this configuration [Y/n]? "
1615             answer = self._ask_user_yn(question, 'y')
1616             if answer == 'y':
1617                 continue
1618
1619             # Create a script that builds a bridge configuration with physical interfaces
1620             # and virtual interfaces
1621             ints_with_vints = self._create_vints_questions(node)
1622             content = ''
1623             for intf in ints_with_vints:
1624                 vhoststr = 'comment { The following command creates the socket }\n'
1625                 vhoststr += 'comment { and returns a virtual interface }\n'
1626                 vhoststr += 'comment {{ create vhost-user socket /tmp/sock{}.sock server }}\n'. \
1627                     format(intf['bridge'])
1628
1629                 setintdnstr = 'set interface state {} down\n'.format(intf['name'])
1630
1631                 setintbrstr = 'set interface l2 bridge {} {}\n'.format(intf['name'], intf['bridge'])
1632                 setvintbrstr = 'set interface l2 bridge {} {}\n'.format(intf['virtualinterface'], intf['bridge'])
1633
1634                 # set interface state VirtualEthernet/0/0/0 up
1635                 setintvststr = 'set interface state {} up\n'.format(intf['virtualinterface'])
1636
1637                 # set interface state VirtualEthernet/0/0/0 down
1638                 setintupstr = 'set interface state {} up\n'.format(intf['name'])
1639
1640                 content += vhoststr + setintdnstr + setintbrstr + setvintbrstr + setintvststr + setintupstr
1641
1642             # Write the content to the script
1643             rootdir = node['rootdir']
1644             filename = rootdir + '/vpp/vpp-config/scripts/create_vms_and_connect_to_vpp'
1645             with open(filename, 'w+') as sfile:
1646                 sfile.write(content)
1647
1648             # Execute the script
1649             cmd = 'vppctl exec {}'.format(filename)
1650             (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
1651             if ret != 0:
1652                 logging.debug(stderr)
1653
1654             print("\nA script as been created at {}".format(filename))
1655             print("This script can be run using the following:")
1656             print("vppctl exec {}\n".format(filename))