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