3 # Copyright (c) 2016 Cisco and/or its affiliates.
4 # Copyright (c) 2018 Vinci Consulting Corp. All rights reserved.
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at:
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 """VPP Configuration Main Entry"""
18 from __future__ import absolute_import, division, print_function
26 from vpplib.AutoConfig import AutoConfig
27 from vpplib.VPPUtil import VPPUtil
29 # Python2/3 compatible
31 input = raw_input # noqa
35 VPP_DRYRUNDIR = '/vpp/vpp-config/dryrun'
36 VPP_AUTO_CONFIGURATION_FILE = '/vpp/vpp-config/configs/auto-config.yaml'
37 VPP_HUGE_PAGE_FILE = '/vpp/vpp-config/dryrun/sysctl.d/80-vpp.conf'
38 VPP_STARTUP_FILE = '/vpp/vpp-config/dryrun/vpp/startup.conf'
39 VPP_GRUB_FILE = '/vpp/vpp-config/dryrun/default/grub'
40 VPP_REAL_HUGE_PAGE_FILE = '/etc/sysctl.d/80-vpp.conf'
41 VPP_REAL_STARTUP_FILE = '/etc/vpp/startup.conf'
42 VPP_REAL_GRUB_FILE = '/etc/default/grub'
47 def autoconfig_yn(question, default):
49 Ask the user a yes or no question.
51 :param question: The text of the question
52 :param default: Value to be returned if '\n' is entered
53 :type question: string
59 default = default.lower()
61 while not input_valid:
62 answer = input(question)
65 if re.findall(r'[YyNn]', answer):
67 answer = answer[0].lower()
69 print ("Please answer Y, N or Return.")
74 def autoconfig_cp(node, src, dst):
76 Copies a file, saving the original if needed.
78 :param node: Node dictionary with cpuinfo.
79 :param src: Source File
80 :param dst: Destination file
84 :raises RuntimeError: If command fails
87 # If the destination file exist, create a copy if one does not already
90 (ret, stdout, stderr) = VPPUtil.exec_command('ls {}'.format(dst))
92 cmd = 'cp {} {}'.format(dst, ofile)
93 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
95 raise RuntimeError('{} failed on node {} {} {}'.
101 # Copy the source file
102 cmd = 'cp {} {}'.format(src, os.path.dirname(dst))
103 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
105 raise RuntimeError('{} failed on node {} {}'.
106 format(cmd, node['host'], stderr))
109 def autoconfig_diff(node, src, dst):
111 Returns the diffs of 2 files.
113 :param node: Node dictionary with cpuinfo.
114 :param src: Source File
115 :param dst: Destination file
121 :raises RuntimeError: If command fails
124 # Diff the files and return the output
125 cmd = "diff {} {}".format(src, dst)
126 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
128 raise RuntimeError('{} failed on node {} {} {}'.
137 def autoconfig_show_system():
139 Shows the system information.
143 acfg = AutoConfig(rootdir, VPP_AUTO_CONFIGURATION_FILE)
150 def autoconfig_hugepage_apply(node, ask_questions=True):
152 Apply the huge page configuration.
153 :param node: The node structure
155 :param ask_questions: When True ask the user questions
156 :type ask_questions: bool
157 :returns: -1 if the caller should return, 0 if not
162 diffs = autoconfig_diff(node, VPP_REAL_HUGE_PAGE_FILE, rootdir + VPP_HUGE_PAGE_FILE)
164 print ("These are the changes we will apply to")
165 print ("the huge page file ({}).\n".format(VPP_REAL_HUGE_PAGE_FILE))
168 answer = autoconfig_yn("\nAre you sure you want to apply these changes [Y/n]? ", 'y')
173 autoconfig_cp(node, rootdir + VPP_HUGE_PAGE_FILE, VPP_REAL_HUGE_PAGE_FILE)
174 cmd = "sysctl -p {}".format(VPP_REAL_HUGE_PAGE_FILE)
175 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
177 raise RuntimeError('{} failed on node {} {} {}'.
178 format(cmd, node['host'], stdout, stderr))
180 print ('\nThere are no changes to the huge page configuration.')
185 def autoconfig_vpp_apply(node, ask_questions=True):
187 Apply the vpp configuration.
189 :param node: The node structure
191 :param ask_questions: When True ask the user questions
192 :type ask_questions: bool
193 :returns: -1 if the caller should return, 0 if not
198 diffs = autoconfig_diff(node, VPP_REAL_STARTUP_FILE, rootdir + VPP_STARTUP_FILE)
200 print ("These are the changes we will apply to")
201 print ("the VPP startup file ({}).\n".format(VPP_REAL_STARTUP_FILE))
204 answer = autoconfig_yn("\nAre you sure you want to apply these changes [Y/n]? ", 'y')
208 # Copy the VPP startup
209 autoconfig_cp(node, rootdir + VPP_STARTUP_FILE, VPP_REAL_STARTUP_FILE)
211 print ('\nThere are no changes to VPP startup.')
216 def autoconfig_grub_apply(node, ask_questions=True):
218 Apply the grub configuration.
220 :param node: The node structure
222 :param ask_questions: When True ask the user questions
223 :type ask_questions: bool
224 :returns: -1 if the caller should return, 0 if not
229 print ("\nThe configured grub cmdline looks like this:")
230 configured_cmdline = node['grub']['default_cmdline']
231 current_cmdline = node['grub']['current_cmdline']
232 print (configured_cmdline)
233 print ("\nThe current boot cmdline looks like this:")
234 print (current_cmdline)
236 question = "\nDo you want to keep the current boot cmdline [Y/n]? "
237 answer = autoconfig_yn(question, 'y')
241 node['grub']['keep_cmdline'] = False
244 diffs = autoconfig_diff(node, VPP_REAL_GRUB_FILE, rootdir + VPP_GRUB_FILE)
246 print ("These are the changes we will apply to")
247 print ("the GRUB file ({}).\n".format(VPP_REAL_GRUB_FILE))
250 answer = autoconfig_yn("\nAre you sure you want to apply these changes [y/N]? ", 'n')
254 # Copy and update grub
255 autoconfig_cp(node, rootdir + VPP_GRUB_FILE, VPP_REAL_GRUB_FILE)
256 distro = VPPUtil.get_linux_distro()
257 if distro[0] == 'Ubuntu':
260 cmd = "grub2-mkconfig -o /boot/grub2/grub.cfg"
262 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
264 raise RuntimeError('{} failed on node {} {} {}'.
265 format(cmd, node['host'], stdout, stderr))
267 print ("There have been changes to the GRUB config a", end=' ')
268 print ("reboot will be required.")
271 print ('\nThere are no changes to the GRUB config.')
276 def autoconfig_apply(ask_questions=True):
278 Apply the configuration.
280 Show the diff of the dryrun file and the actual configuration file
281 Copy the files from the dryrun directory to the actual file.
282 Peform the system function
284 :param ask_questions: When true ask the user questions
285 :type ask_questions: bool
290 pkgs = vutil.get_installed_vpp_pkgs()
292 print ("\nVPP is not installed, Install VPP with option 4.")
295 acfg = AutoConfig(rootdir, VPP_AUTO_CONFIGURATION_FILE)
298 print ("\nWe are now going to configure your system(s).\n")
299 answer = autoconfig_yn("Are you sure you want to do this [Y/n]? ", 'y')
303 nodes = acfg.get_nodes()
304 for i in nodes.items():
307 # Check the system resources
308 if not acfg.min_system_resources(node):
315 ret = autoconfig_hugepage_apply(node, ask_questions)
320 ret = autoconfig_vpp_apply(node, ask_questions)
325 ret = autoconfig_grub_apply(node, ask_questions)
327 # We can still start VPP, even if we haven't configured grub
331 # Everything is configured start vpp
335 def autoconfig_dryrun(ask_questions=True):
337 Execute the dryrun function.
339 :param ask_questions: When true ask the user for paraameters
340 :type ask_questions: bool
344 acfg = AutoConfig(rootdir, VPP_AUTO_CONFIGURATION_FILE, clean=True)
346 # Stop VPP on each node
347 nodes = acfg.get_nodes()
348 for i in nodes.items():
355 # Check the system resources
356 nodes = acfg.get_nodes()
357 for i in nodes.items():
359 if not acfg.min_system_resources(node):
364 acfg.modify_devices()
366 acfg.update_interfaces_config()
368 # If there are no interfaces, just return
369 for i in nodes.items():
371 if not acfg.has_interfaces(node):
372 print("\nThere are no VPP interfaces configured, please configure at least 1.")
376 acfg.modify_cpu(ask_questions)
378 # Calculate the cpu parameters
379 acfg.calculate_cpu_parameters()
381 # Acquire TCP stack parameters
383 acfg.acquire_tcp_params()
386 acfg.apply_vpp_startup()
388 # Apply the grub configuration
389 acfg.apply_grub_cmdline()
393 acfg.modify_huge_pages()
394 acfg.apply_huge_pages()
397 def autoconfig_install():
399 Install or Uninstall VPP.
403 # Since these commands will take a while, we
404 # want to see the progress
405 logger = logging.getLogger()
407 acfg = AutoConfig(rootdir, VPP_AUTO_CONFIGURATION_FILE)
410 nodes = acfg.get_nodes()
411 for i in nodes.items():
414 pkgs = vutil.get_installed_vpp_pkgs()
417 print ("\nThese packages are installed on node {}"
418 .format(node['host']))
419 print ("{:25} {}".format("Name", "Version"))
422 print ("{:25} {}".format(
423 pkg['name'], pkg['version']))
425 print ("{}".format(pkg['name']))
427 question = "\nDo you want to uninstall these "
428 question += "packages [y/N]? "
429 answer = autoconfig_yn(question, 'n')
431 logger.setLevel(logging.INFO)
432 vutil.uninstall_vpp(node)
434 print ("\nThere are no VPP packages on node {}."
435 .format(node['host']))
436 question = "Do you want to install VPP [Y/n]? "
437 answer = autoconfig_yn(question, 'y')
439 question = "Do you want to install the release version [Y/n]? "
440 answer = autoconfig_yn(question, 'y')
445 logger.setLevel(logging.INFO)
446 vutil.install_vpp(node, branch)
448 # Set the logging level back
449 logger.setLevel(logging.ERROR)
452 def autoconfig_patch_qemu():
454 Patch the correct qemu version that is needed for openstack
458 # Since these commands will take a while, we
459 # want to see the progress
460 logger = logging.getLogger()
462 acfg = AutoConfig(rootdir, VPP_AUTO_CONFIGURATION_FILE)
464 nodes = acfg.get_nodes()
465 for i in nodes.items():
468 logger.setLevel(logging.INFO)
469 acfg.patch_qemu(node)
472 def autoconfig_ipv4_setup():
474 Setup IPv4 interfaces
478 acfg = AutoConfig(rootdir, VPP_AUTO_CONFIGURATION_FILE)
479 acfg.ipv4_interface_setup()
482 def autoconfig_create_iperf_vm():
484 Setup IPv4 interfaces
488 acfg = AutoConfig(rootdir, VPP_AUTO_CONFIGURATION_FILE)
489 acfg.destroy_iperf_vm('iperf-server')
490 acfg.create_and_bridge_iperf_virtual_interface()
491 acfg.create_iperf_vm('iperf-server')
494 def autoconfig_not_implemented():
496 This feature is not implemented
500 print ("\nThis Feature is not implemented yet....")
503 def autoconfig_basic_test_menu():
505 The auto configuration basic test menu
509 basic_menu_text = '\nWhat would you like to do?\n\n\
510 1) List/Create Simple IPv4 Setup\n\
511 2) Create an iperf VM and Connect to VPP an interface\n\
512 9 or q) Back to main menu.'
514 print ("{}".format(basic_menu_text))
518 while not input_valid:
519 answer = input("\nCommand: ")
521 print ("Please enter only 1 character.")
523 if re.findall(r'[Qq1-29]', answer):
525 answer = answer[0].lower()
527 print ("Please enter a character between 1 and 2 or 9.")
535 def autoconfig_basic_test():
537 The auto configuration basic test menu
541 pkgs = vutil.get_installed_vpp_pkgs()
543 print ("\nVPP is not installed, install VPP with option 4.")
548 answer = autoconfig_basic_test_menu()
550 autoconfig_ipv4_setup()
552 autoconfig_create_iperf_vm()
553 elif answer == '9' or answer == 'q':
556 autoconfig_not_implemented()
559 def autoconfig_main_menu():
561 The auto configuration main menu
565 main_menu_text = '\nWhat would you like to do?\n\n\
566 1) Show basic system information\n\
567 2) Dry Run (Saves the configuration files in {}/vpp/vpp-config/dryrun.\n\
568 3) Full configuration (WARNING: This will change the system configuration)\n\
569 4) List/Install/Uninstall VPP.\n\
570 q) Quit'.format(rootdir, rootdir)
572 # 5) Dry Run from {}/vpp/vpp-config/auto-config.yaml (will not ask questions).\n\
573 # 6) Install QEMU patch (Needed when running openstack).\n\
575 print ("{}".format(main_menu_text))
579 while not input_valid:
580 answer = input("\nCommand: ")
582 print ("Please enter only 1 character.")
584 if re.findall(r'[Qq1-4]', answer):
586 answer = answer[0].lower()
588 print ("Please enter a character between 1 and 4 or q.")
593 def autoconfig_main():
595 The auto configuration main entry point
604 answer = autoconfig_main_menu()
606 autoconfig_show_system()
616 autoconfig_not_implemented()
619 def autoconfig_setup(ask_questions=True):
621 The auto configuration setup function.
623 We will copy the configuration files to the dryrun directory.
629 distro = VPPUtil.get_linux_distro()
630 if distro[0] == 'Ubuntu':
631 rootdir = '/usr/local'
635 # If there is a system configuration file use that, if not use the initial auto-config file
636 filename = rootdir + VPP_AUTO_CONFIGURATION_FILE
637 if os.path.isfile(filename) is True:
638 acfg = AutoConfig(rootdir, VPP_AUTO_CONFIGURATION_FILE)
640 raise RuntimeError('The Auto configuration file does not exist {}'.
644 print ("\nWelcome to the VPP system configuration utility")
646 print ("\nThese are the files we will modify:")
647 print (" /etc/vpp/startup.conf")
648 print (" /etc/sysctl.d/80-vpp.conf")
649 print (" /etc/default/grub")
652 "\nBefore we change them, we'll create working copies in "
653 "{}".format(rootdir + VPP_DRYRUNDIR))
655 "Please inspect them carefully before applying the actual "
656 "configuration (option 3)!")
658 nodes = acfg.get_nodes()
659 for i in nodes.items():
662 if (os.path.isfile(rootdir + VPP_STARTUP_FILE) is not True) and \
663 (os.path.isfile(VPP_REAL_STARTUP_FILE) is True):
664 autoconfig_cp(node, VPP_REAL_STARTUP_FILE, '{}'.format(rootdir + VPP_STARTUP_FILE))
665 if (os.path.isfile(rootdir + VPP_HUGE_PAGE_FILE) is not True) and \
666 (os.path.isfile(VPP_REAL_HUGE_PAGE_FILE) is True):
667 autoconfig_cp(node, VPP_REAL_HUGE_PAGE_FILE, '{}'.format(rootdir + VPP_HUGE_PAGE_FILE))
668 if (os.path.isfile(rootdir + VPP_GRUB_FILE) is not True) and \
669 (os.path.isfile(VPP_REAL_GRUB_FILE) is True):
670 autoconfig_cp(node, VPP_REAL_GRUB_FILE, '{}'.format(rootdir + VPP_GRUB_FILE))
672 # Be sure the uio_pci_generic driver is installed
673 cmd = 'modprobe uio_pci_generic'
674 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
676 logging.warning('{} failed on node {} {}'. format(cmd, node['host'], stderr))
679 # noinspection PyUnresolvedReferences
680 def execute_with_args(args):
682 Execute the configuration utility with agruments.
684 :param args: The Command line arguments
689 autoconfig_setup(ask_questions=False)
691 # Execute the command
693 autoconfig_show_system()
695 autoconfig_dryrun(ask_questions=False)
697 autoconfig_apply(ask_questions=False)
699 autoconfig_not_implemented()
704 The vpp configuration utility main entry point.
709 if not os.geteuid() == 0:
710 sys.exit('\nPlease run the VPP Configuration Utility as root.')
712 if len(sys.argv) > 1 and ((sys.argv[1] == '-d') or (
713 sys.argv[1] == '--debug')):
714 logging.basicConfig(level=logging.DEBUG)
716 logging.basicConfig(level=logging.ERROR)
718 # If no arguments were entered, ask the user questions to
719 # get the main parameters
720 if len(sys.argv) == 1:
723 elif len(sys.argv) == 2 and ((sys.argv[1] == '-d') or (
724 sys.argv[1] == '--debug')):
728 # There were arguments specified, so execute the utility using
729 # command line arguments
730 description = 'The VPP configuration utility allows the user to '
731 'configure VPP in a simple and safe manner. The utility takes input '
732 'from the user or the specified .yaml file. The user should then '
733 'examine these files to be sure they are correct and then actually '
734 'apply the configuration. When run without arguments the utility run '
735 'in an interactive mode'
737 main_parser = argparse.ArgumentParser(
739 description=description,
740 epilog='See "%(prog)s help COMMAND" for help on a specific command.')
741 main_parser.add_argument('--apply', '-a', action='store_true',
742 help='Apply the cofiguration.')
743 main_parser.add_argument('--dry-run', '-dr', action='store_true',
744 help='Create the dryrun configuration files.')
745 main_parser.add_argument('--show', '-s', action='store_true',
746 help='Shows basic system information')
747 main_parser.add_argument('--debug', '-d', action='count',
748 help='Print debug output (multiple levels)')
750 args = main_parser.parse_args()
752 return execute_with_args(args)
755 if __name__ == '__main__':