3 # Copyright (c) 2016 Cisco and/or its affiliates.
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at:
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
16 """VPP Configuration Main Entry"""
24 from vpplib.AutoConfig import AutoConfig
25 from vpplib.VPPUtil import VPPUtil
27 VPP_DRYRUNDIR = '/vpp/vpp-config/dryrun'
28 VPP_AUTO_CONFIGURATION_FILE = '/vpp/vpp-config/configs/auto-config.yaml'
29 VPP_HUGE_PAGE_FILE = '/vpp/vpp-config/dryrun/sysctl.d/80-vpp.conf'
30 VPP_STARTUP_FILE = '/vpp/vpp-config/dryrun/vpp/startup.conf'
31 VPP_GRUB_FILE = '/vpp/vpp-config/dryrun/default/grub'
32 VPP_REAL_HUGE_PAGE_FILE = '/etc/sysctl.d/80-vpp.conf'
33 VPP_REAL_STARTUP_FILE = '/etc/vpp/startup.conf'
34 VPP_REAL_GRUB_FILE = '/etc/default/grub'
39 def autoconfig_yn(question, default):
41 Ask the user a yes or no question.
43 :param question: The text of the question
44 :param default: Value to be returned if '\n' is entered
45 :type question: string
51 default = default.lower()
53 while not input_valid:
54 answer = raw_input(question)
57 if re.findall(r'[YyNn]', answer):
59 answer = answer[0].lower()
61 print "Please answer Y, N or Return."
66 def autoconfig_cp(node, src, dst):
68 Copies a file, saving the original if needed.
70 :param node: Node dictionary with cpuinfo.
71 :param src: Source File
72 :param dst: Destination file
76 :raises RuntimeError: If command fails
79 # If the destination file exist, create a copy if one does not already
82 (ret, stdout, stderr) = VPPUtil.exec_command('ls {}'.format(dst))
84 cmd = 'cp {} {}'.format(dst, ofile)
85 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
87 raise RuntimeError('{} failed on node {} {} {}'.
93 # Copy the source file
94 cmd = 'cp {} {}'.format(src, os.path.dirname(dst))
95 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
97 raise RuntimeError('{} failed on node {} {}'.
98 format(cmd, node['host'], stderr))
101 def autoconfig_diff(node, src, dst):
103 Returns the diffs of 2 files.
105 :param node: Node dictionary with cpuinfo.
106 :param src: Source File
107 :param dst: Destination file
113 :raises RuntimeError: If command fails
116 # Diff the files and return the output
117 cmd = "diff {} {}".format(src, dst)
118 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
120 raise RuntimeError('{} failed on node {} {} {}'.
129 def autoconfig_show_system():
131 Shows the system information.
135 acfg = AutoConfig(rootdir, VPP_AUTO_CONFIGURATION_FILE)
142 def autoconfig_hugepage_apply(node, ask_questions=True):
144 Apply the huge page configuration.
145 :param node: The node structure
147 :param ask_questions: When True ask the user questions
148 :type ask_questions: bool
149 :returns: -1 if the caller should return, 0 if not
154 diffs = autoconfig_diff(node, VPP_REAL_HUGE_PAGE_FILE, rootdir + VPP_HUGE_PAGE_FILE)
156 print "These are the changes we will apply to"
157 print "the huge page file ({}).\n".format(VPP_REAL_HUGE_PAGE_FILE)
160 answer = autoconfig_yn("\nAre you sure you want to apply these changes [Y/n]? ", 'y')
165 autoconfig_cp(node, rootdir + VPP_HUGE_PAGE_FILE, VPP_REAL_HUGE_PAGE_FILE)
166 cmd = "sysctl -p {}".format(VPP_REAL_HUGE_PAGE_FILE)
167 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
169 raise RuntimeError('{} failed on node {} {} {}'.
170 format(cmd, node['host'], stdout, stderr))
172 print '\nThere are no changes to the huge page configuration.'
177 def autoconfig_vpp_apply(node, ask_questions=True):
179 Apply the vpp configuration.
181 :param node: The node structure
183 :param ask_questions: When True ask the user questions
184 :type ask_questions: bool
185 :returns: -1 if the caller should return, 0 if not
190 diffs = autoconfig_diff(node, VPP_REAL_STARTUP_FILE, rootdir + VPP_STARTUP_FILE)
192 print "These are the changes we will apply to"
193 print "the VPP startup file ({}).\n".format(VPP_REAL_STARTUP_FILE)
196 answer = autoconfig_yn("\nAre you sure you want to apply these changes [Y/n]? ", 'y')
200 # Copy the VPP startup
201 autoconfig_cp(node, rootdir + VPP_STARTUP_FILE, VPP_REAL_STARTUP_FILE)
203 print '\nThere are no changes to VPP startup.'
208 def autoconfig_grub_apply(node, ask_questions=True):
210 Apply the grub configuration.
212 :param node: The node structure
214 :param ask_questions: When True ask the user questions
215 :type ask_questions: bool
216 :returns: -1 if the caller should return, 0 if not
221 print "\nThe configured grub cmdline looks like this:"
222 configured_cmdline = node['grub']['default_cmdline']
223 current_cmdline = node['grub']['current_cmdline']
224 print configured_cmdline
225 print "\nThe current boot cmdline looks like this:"
226 print current_cmdline
228 question = "\nDo you want to keep the current boot cmdline [Y/n]? "
229 answer = autoconfig_yn(question, 'y')
233 node['grub']['keep_cmdline'] = False
236 diffs = autoconfig_diff(node, VPP_REAL_GRUB_FILE, rootdir + VPP_GRUB_FILE)
238 print "These are the changes we will apply to"
239 print "the GRUB file ({}).\n".format(VPP_REAL_GRUB_FILE)
242 answer = autoconfig_yn("\nAre you sure you want to apply these changes [y/N]? ", 'n')
246 # Copy and update grub
247 autoconfig_cp(node, rootdir + VPP_GRUB_FILE, VPP_REAL_GRUB_FILE)
248 distro = VPPUtil.get_linux_distro()
249 if distro[0] == 'Ubuntu':
252 cmd = "grub2-mkconfig -o /boot/grub2/grub.cfg"
254 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
256 raise RuntimeError('{} failed on node {} {} {}'.
257 format(cmd, node['host'], stdout, stderr))
259 print "There have been changes to the GRUB config a",
260 print "reboot will be required."
263 print '\nThere are no changes to the GRUB config.'
268 def autoconfig_apply(ask_questions=True):
270 Apply the configuration.
272 Show the diff of the dryrun file and the actual configuration file
273 Copy the files from the dryrun directory to the actual file.
274 Peform the system function
276 :param ask_questions: When true ask the user questions
277 :type ask_questions: bool
282 pkgs = vutil.get_installed_vpp_pkgs()
284 print "\nVPP is not installed, Install VPP with option 4."
287 acfg = AutoConfig(rootdir, VPP_AUTO_CONFIGURATION_FILE)
290 print "\nWe are now going to configure your system(s).\n"
291 answer = autoconfig_yn("Are you sure you want to do this [Y/n]? ", 'y')
295 nodes = acfg.get_nodes()
296 for i in nodes.items():
299 # Check the system resources
300 if not acfg.min_system_resources(node):
307 ret = autoconfig_hugepage_apply(node, ask_questions)
312 ret = autoconfig_vpp_apply(node, ask_questions)
317 ret = autoconfig_grub_apply(node, ask_questions)
319 # We can still start VPP, even if we haven't configured grub
323 # Everything is configured start vpp
327 def autoconfig_dryrun(ask_questions=True):
329 Execute the dryrun function.
331 :param ask_questions: When true ask the user for paraameters
332 :type ask_questions: bool
337 pkgs = vutil.get_installed_vpp_pkgs()
339 print "\nVPP is not installed, please install VPP."
342 acfg = AutoConfig(rootdir, VPP_AUTO_CONFIGURATION_FILE, clean=True)
344 # Stop VPP on each node
345 nodes = acfg.get_nodes()
346 for i in nodes.items():
353 # Check the system resources
354 nodes = acfg.get_nodes()
355 for i in nodes.items():
357 if not acfg.min_system_resources(node):
362 acfg.modify_devices()
364 acfg.update_interfaces_config()
367 acfg.modify_cpu(ask_questions)
369 # Calculate the cpu parameters
370 acfg.calculate_cpu_parameters()
372 # Acquire TCP stack parameters
374 acfg.acquire_tcp_params()
377 acfg.apply_vpp_startup()
379 # Apply the grub configuration
380 acfg.apply_grub_cmdline()
384 acfg.modify_huge_pages()
385 acfg.apply_huge_pages()
388 def autoconfig_install():
390 Install or Uninstall VPP.
394 # Since these commands will take a while, we
395 # want to see the progress
396 logger = logging.getLogger()
398 acfg = AutoConfig(rootdir, VPP_AUTO_CONFIGURATION_FILE)
401 nodes = acfg.get_nodes()
402 for i in nodes.items():
405 pkgs = vutil.get_installed_vpp_pkgs()
408 print "\nThese packages are installed on node {}" \
409 .format(node['host'])
410 print "{:25} {}".format("Name", "Version")
413 print "{:25} {}".format(
414 pkg['name'], pkg['version'])
416 print "{}".format(pkg['name'])
418 question = "\nDo you want to uninstall these "
419 question += "packages [y/N]? "
420 answer = autoconfig_yn(question, 'n')
422 logger.setLevel(logging.INFO)
423 vutil.uninstall_vpp(node)
425 print "\nThere are no VPP packages on node {}." \
426 .format(node['host'])
427 question = "Do you want to install VPP [Y/n]? "
428 answer = autoconfig_yn(question, 'y')
430 logger.setLevel(logging.INFO)
431 vutil.install_vpp(node)
433 # Set the logging level back
434 logger.setLevel(logging.ERROR)
437 def autoconfig_patch_qemu():
439 Patch the correct qemu version that is needed for openstack
443 # Since these commands will take a while, we
444 # want to see the progress
445 logger = logging.getLogger()
447 acfg = AutoConfig(rootdir, VPP_AUTO_CONFIGURATION_FILE)
449 nodes = acfg.get_nodes()
450 for i in nodes.items():
453 logger.setLevel(logging.INFO)
454 acfg.patch_qemu(node)
457 def autoconfig_ipv4_setup():
459 Setup IPv4 interfaces
463 acfg = AutoConfig(rootdir, VPP_AUTO_CONFIGURATION_FILE)
464 acfg.ipv4_interface_setup()
467 def autoconfig_create_iperf_vm():
469 Setup IPv4 interfaces
473 acfg = AutoConfig(rootdir, VPP_AUTO_CONFIGURATION_FILE)
474 acfg.destroy_iperf_vm('iperf-server')
475 acfg.create_and_bridge_iperf_virtual_interface()
476 acfg.create_iperf_vm('iperf-server')
479 def autoconfig_not_implemented():
481 This feature is not implemented
485 print "\nThis Feature is not implemented yet...."
488 def autoconfig_basic_test_menu():
490 The auto configuration basic test menu
494 basic_menu_text = '\nWhat would you like to do?\n\n\
495 1) List/Create Simple IPv4 Setup\n\
496 2) Create an iperf VM and Connect to VPP an interface\n\
497 9 or q) Back to main menu.'
499 print "{}".format(basic_menu_text)
503 while not input_valid:
504 answer = raw_input("\nCommand: ")
506 print "Please enter only 1 character."
508 if re.findall(r'[Qq1-29]', answer):
510 answer = answer[0].lower()
512 print "Please enter a character between 1 and 2 or 9."
520 def autoconfig_basic_test():
522 The auto configuration basic test menu
526 pkgs = vutil.get_installed_vpp_pkgs()
528 print "\nVPP is not installed, install VPP with option 4."
533 answer = autoconfig_basic_test_menu()
535 autoconfig_ipv4_setup()
537 autoconfig_create_iperf_vm()
538 elif answer == '9' or answer == 'q':
541 autoconfig_not_implemented()
544 def autoconfig_main_menu():
546 The auto configuration main menu
550 main_menu_text = '\nWhat would you like to do?\n\n\
551 1) Show basic system information\n\
552 2) Dry Run (Will save the configuration files in {}/vpp/vpp-config/dryrun for inspection)\n\
553 and user input in {}/vpp/vpp-config/configs/auto-config.yaml\n\
554 3) Full configuration (WARNING: This will change the system configuration)\n\
555 4) List/Install/Uninstall VPP.\n\
556 5) Execute some basic tests.\n\
557 9 or q) Quit'.format(rootdir, rootdir)
559 # 5) Dry Run from {}/vpp/vpp-config/auto-config.yaml (will not ask questions).\n\
560 # 6) Install QEMU patch (Needed when running openstack).\n\
562 print "{}".format(main_menu_text)
566 while not input_valid:
567 answer = raw_input("\nCommand: ")
569 print "Please enter only 1 character."
571 if re.findall(r'[Qq1-79]', answer):
573 answer = answer[0].lower()
575 print "Please enter a character between 1 and 5 or 9."
582 def autoconfig_main():
584 The auto configuration main entry point
593 answer = autoconfig_main_menu()
595 autoconfig_show_system()
603 autoconfig_basic_test()
604 elif answer == '9' or answer == 'q':
607 autoconfig_not_implemented()
610 def autoconfig_setup(ask_questions=True):
612 The auto configuration setup function.
614 We will copy the configuration files to the dryrun directory.
620 distro = VPPUtil.get_linux_distro()
621 if distro[0] == 'Ubuntu':
622 rootdir = '/usr/local'
626 # If there is a system configuration file use that, if not use the initial auto-config file
627 filename = rootdir + VPP_AUTO_CONFIGURATION_FILE
628 if os.path.isfile(filename) is True:
629 acfg = AutoConfig(rootdir, VPP_AUTO_CONFIGURATION_FILE)
631 raise RuntimeError('The Auto configuration file does not exist {}'.
635 print "\nWelcome to the VPP system configuration utility"
637 print "\nThese are the files we will modify:"
638 print " /etc/vpp/startup.conf"
639 print " /etc/sysctl.d/80-vpp.conf"
640 print " /etc/default/grub"
642 print "\nBefore we change them, we'll create working copies in {}".format(rootdir + VPP_DRYRUNDIR)
643 print "Please inspect them carefully before applying the actual configuration (option 3)!"
645 nodes = acfg.get_nodes()
646 for i in nodes.items():
649 if (os.path.isfile(rootdir + VPP_STARTUP_FILE) is not True) and \
650 (os.path.isfile(VPP_REAL_STARTUP_FILE) is True):
651 autoconfig_cp(node, VPP_REAL_STARTUP_FILE, '{}'.format(rootdir + VPP_STARTUP_FILE))
652 if (os.path.isfile(rootdir + VPP_HUGE_PAGE_FILE) is not True) and \
653 (os.path.isfile(VPP_REAL_HUGE_PAGE_FILE) is True):
654 autoconfig_cp(node, VPP_REAL_HUGE_PAGE_FILE, '{}'.format(rootdir + VPP_HUGE_PAGE_FILE))
655 if (os.path.isfile(rootdir + VPP_GRUB_FILE) is not True) and \
656 (os.path.isfile(VPP_REAL_GRUB_FILE) is True):
657 autoconfig_cp(node, VPP_REAL_GRUB_FILE, '{}'.format(rootdir + VPP_GRUB_FILE))
659 # Be sure the uio_pci_generic driver is installed
660 cmd = 'modprobe uio_pci_generic'
661 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
663 logging.warning('{} failed on node {} {}'. format(cmd, node['host'], stderr))
666 # noinspection PyUnresolvedReferences
667 def execute_with_args(args):
669 Execute the configuration utility with agruments.
671 :param args: The Command line arguments
676 autoconfig_setup(ask_questions=False)
678 # Execute the command
680 autoconfig_show_system()
682 autoconfig_dryrun(ask_questions=False)
684 autoconfig_apply(ask_questions=False)
686 autoconfig_not_implemented()
691 The vpp configuration utility main entry point.
696 if not os.geteuid() == 0:
697 sys.exit('\nPlease run the VPP Configuration Utility as root.')
699 if len(sys.argv) > 1 and ((sys.argv[1] == '-d') or (sys.argv[1] == '--debug')):
700 logging.basicConfig(level=logging.DEBUG)
702 logging.basicConfig(level=logging.ERROR)
704 # If no arguments were entered, ask the user questions to get the main parameters
705 if len(sys.argv) == 1:
708 elif len(sys.argv) == 2 and (sys.argv[1] == '-d' or sys.argv[1] == '--debug'):
712 # There were arguments specified, so execute the utility using command line arguments
713 description = 'The VPP configuration utility allows the user to configure VPP in a simple and safe manner. \
714 The utility takes input from the user or the speficfied .yaml file. The user should then examine these files \
715 to be sure they are correct and then actually apply the configuration. When run without arguments the utility run \
716 in an interactive mode'
718 main_parser = argparse.ArgumentParser(prog='arg-test', description=description,
719 epilog='See "%(prog)s help COMMAND" for help on a specific command.')
720 main_parser.add_argument('--apply', '-a', action='store_true', help='Apply the cofiguration.')
721 main_parser.add_argument('--dry-run', '-dr', action='store_true', help='Create the dryrun configuration files.')
722 main_parser.add_argument('--show', '-s', action='store_true', help='Shows basic system information')
723 main_parser.add_argument('--debug', '-d', action='count', help='Print debug output (multiple levels)')
725 args = main_parser.parse_args()
727 return execute_with_args(args)
730 if __name__ == '__main__':