From ddecfb3d9f5960b69d964f9e870a6ded78a52f1f Mon Sep 17 00:00:00 2001 From: John DeNisco Date: Wed, 15 Nov 2017 08:50:57 -0500 Subject: Add a non interactive mode Change-Id: I2ebcb1acb43b4316e3dd48e83909d710dbef4e2f Signed-off-by: John DeNisco --- extras/vpp_config/data/auto-config.yaml | 25 ++-- extras/vpp_config/scripts/vpp-config | 12 +- extras/vpp_config/setup.py | 2 +- extras/vpp_config/vpp_config.py | 248 ++++++++++++++++++++------------ extras/vpp_config/vpplib/AutoConfig.py | 65 +++++++-- 5 files changed, 221 insertions(+), 131 deletions(-) (limited to 'extras') diff --git a/extras/vpp_config/data/auto-config.yaml b/extras/vpp_config/data/auto-config.yaml index 4a74de21882..83e3aab8470 100644 --- a/extras/vpp_config/data/auto-config.yaml +++ b/extras/vpp_config/data/auto-config.yaml @@ -1,23 +1,14 @@ -metadata: - system_config_file: /vpp/vpp-config/configs/system-config.yaml - version: 0.1 +metadata: {system_config_file: /vpp/vpp-config/configs/system-config.yaml, version: 0.1} nodes: DUT1: - cpu: - grub_config_file: /vpp/vpp-config/dryrun/default/grub - reserve_vpp_main_core: true - total_other_cpus: 0 - total_vpp_cpus: 2 + cpu: {grub_config_file: /vpp/vpp-config/dryrun/default/grub, reserve_vpp_main_core: false, + total_other_cpus: 0, total_vpp_cpus: 0} host: localhost - hugepages: - hugepage_config_file: /vpp/vpp-config/dryrun/sysctl.d/80-vpp.conf - total: '1024' - interfaces: - tcp: - active_open_sessions: 0 - passive_open_sessions: 0 + hugepages: {hugepage_config_file: /vpp/vpp-config/dryrun/sysctl.d/80-vpp.conf, + total: '1024'} + interfaces: {} + tcp: {active_open_sessions: 0, passive_open_sessions: 0} type: DUT vpp: startup_config_file: /vpp/vpp-config/dryrun/vpp/startup.conf - unix: - interactive: false + unix: {interactive: false} diff --git a/extras/vpp_config/scripts/vpp-config b/extras/vpp_config/scripts/vpp-config index 8f5314bca72..7cb27fc20e8 100755 --- a/extras/vpp_config/scripts/vpp-config +++ b/extras/vpp_config/scripts/vpp-config @@ -17,14 +17,6 @@ import os import sys -import vpp_config as vppcfg +import vpp_config as vpp -# Check for root -if not os.geteuid() == 0: - sys.exit('\nPlease run the VPP Configuration Utility as root.') - -# Setup -vppcfg.autoconfig_setup() - -# Main menu -vppcfg.autoconfig_main() +vpp.config_main() diff --git a/extras/vpp_config/setup.py b/extras/vpp_config/setup.py index 32c376d295c..892a60dff83 100644 --- a/extras/vpp_config/setup.py +++ b/extras/vpp_config/setup.py @@ -1,7 +1,7 @@ from setuptools import setup setup(name="vpp_config", - version="17.10.5", + version="17.10.6", author="John DeNisco", author_email="jdenisco@cisco.com", description="VPP Configuration Utility", diff --git a/extras/vpp_config/vpp_config.py b/extras/vpp_config/vpp_config.py index da455c178a4..69e7de706e5 100755 --- a/extras/vpp_config/vpp_config.py +++ b/extras/vpp_config/vpp_config.py @@ -19,6 +19,7 @@ import re import os import sys import logging +import argparse from vpplib.AutoConfig import AutoConfig from vpplib.VPPUtil import VPPUtil @@ -138,11 +139,13 @@ def autoconfig_show_system(): acfg.sys_info() -def autoconfig_hugepage_apply(node): +def autoconfig_hugepage_apply(node, ask_questions=True): """ Apply the huge page configuration. :param node: The node structure :type node: dict + :param ask_questions: When True ask the user questions + :type ask_questions: bool :returns: -1 if the caller should return, 0 if not :rtype: int @@ -153,11 +156,10 @@ def autoconfig_hugepage_apply(node): print "These are the changes we will apply to" print "the huge page file ({}).\n".format(VPP_REAL_HUGE_PAGE_FILE) print diffs - answer = autoconfig_yn( - "\nAre you sure you want to apply these changes [Y/n]? ", - 'y') - if answer == 'n': - return -1 + if ask_questions: + answer = autoconfig_yn("\nAre you sure you want to apply these changes [Y/n]? ", 'y') + if answer == 'n': + return -1 # Copy and sysctl autoconfig_cp(node, rootdir + VPP_HUGE_PAGE_FILE, VPP_REAL_HUGE_PAGE_FILE) @@ -172,33 +174,28 @@ def autoconfig_hugepage_apply(node): return 0 -def autoconfig_vpp_apply(node): +def autoconfig_vpp_apply(node, ask_questions=True): """ Apply the vpp configuration. :param node: The node structure :type node: dict + :param ask_questions: When True ask the user questions + :type ask_questions: bool :returns: -1 if the caller should return, 0 if not :rtype: int """ - cmd = "service vpp stop" - (ret, stdout, stderr) = VPPUtil.exec_command(cmd) - if ret != 0: - raise RuntimeError('{} failed on node {} {} {}'. - format(cmd, node['host'], stdout, stderr)) - diffs = autoconfig_diff(node, VPP_REAL_STARTUP_FILE, rootdir + VPP_STARTUP_FILE) if diffs != '': print "These are the changes we will apply to" print "the VPP startup file ({}).\n".format(VPP_REAL_STARTUP_FILE) print diffs - answer = autoconfig_yn( - "\nAre you sure you want to apply these changes [Y/n]? ", - 'y') - if answer == 'n': - return -1 + if ask_questions: + answer = autoconfig_yn("\nAre you sure you want to apply these changes [Y/n]? ", 'y') + if answer == 'n': + return -1 # Copy the VPP startup autoconfig_cp(node, rootdir + VPP_STARTUP_FILE, VPP_REAL_STARTUP_FILE) @@ -208,63 +205,67 @@ def autoconfig_vpp_apply(node): return 0 -def autoconfig_grub_apply(node): +def autoconfig_grub_apply(node, ask_questions=True): """ Apply the grub configuration. :param node: The node structure :type node: dict + :param ask_questions: When True ask the user questions + :type ask_questions: bool :returns: -1 if the caller should return, 0 if not :rtype: int """ + print "\nThe configured grub cmdline looks like this:" configured_cmdline = node['grub']['default_cmdline'] current_cmdline = node['grub']['current_cmdline'] print configured_cmdline print "\nThe current boot cmdline looks like this:" print current_cmdline - question = "\nDo you want to keep the current boot cmdline [Y/n]? " - answer = autoconfig_yn(question, 'y') - if answer == 'n': - node['grub']['keep_cmdline'] = False - - # Diff the file - diffs = autoconfig_diff(node, VPP_REAL_GRUB_FILE, rootdir + VPP_GRUB_FILE) - if diffs != '': - print "These are the changes we will apply to" - print "the GRUB file ({}).\n".format(VPP_REAL_GRUB_FILE) - print diffs - answer = autoconfig_yn( - "\nAre you sure you want to apply these changes [y/N]? ", - 'n') + if ask_questions: + question = "\nDo you want to keep the current boot cmdline [Y/n]? " + answer = autoconfig_yn(question, 'y') + if answer == 'y': + return + + node['grub']['keep_cmdline'] = False + + # Diff the file + diffs = autoconfig_diff(node, VPP_REAL_GRUB_FILE, rootdir + VPP_GRUB_FILE) + if diffs != '': + print "These are the changes we will apply to" + print "the GRUB file ({}).\n".format(VPP_REAL_GRUB_FILE) + print diffs + if ask_questions: + answer = autoconfig_yn("\nAre you sure you want to apply these changes [y/N]? ", 'n') if answer == 'n': return -1 - # Copy and update grub - autoconfig_cp(node, rootdir + VPP_GRUB_FILE, VPP_REAL_GRUB_FILE) - distro = VPPUtil.get_linux_distro() - if distro[0] == 'Ubuntu': - cmd = "update-grub" - else: - cmd = "grub2-mkconfig -o /boot/grub2/grub.cfg" - (ret, stdout, stderr) = VPPUtil.exec_command(cmd) - if ret != 0: - raise RuntimeError('{} failed on node {} {} {}'. - format(cmd, - node['host'], - stdout, - stderr)) - print "There have been changes to the GRUB config a", - print "reboot will be required." - return -1 + # Copy and update grub + autoconfig_cp(node, rootdir + VPP_GRUB_FILE, VPP_REAL_GRUB_FILE) + distro = VPPUtil.get_linux_distro() + if distro[0] == 'Ubuntu': + cmd = "update-grub" else: - print '\nThere are no changes to the GRUB config.' + cmd = "grub2-mkconfig -o /boot/grub2/grub.cfg" + + (ret, stdout, stderr) = VPPUtil.exec_command(cmd) + if ret != 0: + raise RuntimeError('{} failed on node {} {} {}'. + format(cmd, node['host'], stdout, stderr)) + + print "There have been changes to the GRUB config a", + print "reboot will be required." + return -1 + else: + print '\nThere are no changes to the GRUB config.' return 0 -def autoconfig_apply(): +def autoconfig_apply(ask_questions=True): """ Apply the configuration. @@ -272,6 +273,9 @@ def autoconfig_apply(): Copy the files from the dryrun directory to the actual file. Peform the system function + :param ask_questions: When true ask the user questions + :type ask_questions: bool + """ vutil = VPPUtil() @@ -282,10 +286,11 @@ def autoconfig_apply(): acfg = AutoConfig(rootdir, VPP_AUTO_CONFIGURATION_FILE) - print "\nWe are now going to configure your system(s).\n" - answer = autoconfig_yn("Are you sure you want to do this [Y/n]? ", 'y') - if answer == 'n': - return + if ask_questions: + print "\nWe are now going to configure your system(s).\n" + answer = autoconfig_yn("Are you sure you want to do this [Y/n]? ", 'y') + if answer == 'n': + return nodes = acfg.get_nodes() for i in nodes.items(): @@ -295,42 +300,45 @@ def autoconfig_apply(): if not acfg.min_system_resources(node): return + # Stop VPP + VPPUtil.stop(node) + # Huge Pages - ret = autoconfig_hugepage_apply(node) + ret = autoconfig_hugepage_apply(node, ask_questions) if ret != 0: return # VPP - ret = autoconfig_vpp_apply(node) + ret = autoconfig_vpp_apply(node, ask_questions) if ret != 0: return # Grub - ret = autoconfig_grub_apply(node) + ret = autoconfig_grub_apply(node, ask_questions) if ret != 0: + # We can still start VPP, even if we haven't configured grub + VPPUtil.start(node) return # Everything is configured start vpp - cmd = "service vpp start" - (ret, stdout, stderr) = VPPUtil.exec_command(cmd) - if ret != 0: - raise RuntimeError('{} failed on node {} {} {}'. - format(cmd, node['host'], stdout, stderr)) + VPPUtil.start(node) - -def autoconfig_dryrun(): +def autoconfig_dryrun(ask_questions=True): """ Execute the dryrun function. + :param ask_questions: When true ask the user for paraameters + :type ask_questions: bool + """ vutil = VPPUtil() pkgs = vutil.get_installed_vpp_pkgs() if len(pkgs) == 0: - print "\nVPP is not installed, install VPP with option 4." + print "\nVPP is not installed, please install VPP." return - acfg = AutoConfig(rootdir, VPP_AUTO_CONFIGURATION_FILE) + acfg = AutoConfig(rootdir, VPP_AUTO_CONFIGURATION_FILE, clean=True) # Stop VPP on each node nodes = acfg.get_nodes() @@ -349,16 +357,20 @@ def autoconfig_dryrun(): return # Modify the devices - acfg.modify_devices() + if ask_questions: + acfg.modify_devices() + else: + acfg.update_interfaces_config() # Modify CPU - acfg.modify_cpu() + acfg.modify_cpu(ask_questions) # Calculate the cpu parameters acfg.calculate_cpu_parameters() # Acquire TCP stack parameters - acfg.acquire_tcp_params() + if ask_questions: + acfg.acquire_tcp_params() # Apply the startup acfg.apply_vpp_startup() @@ -367,7 +379,8 @@ def autoconfig_dryrun(): acfg.apply_grub_cmdline() # Huge Pages - acfg.modify_huge_pages() + if ask_questions: + acfg.modify_huge_pages() acfg.apply_huge_pages() @@ -475,14 +488,15 @@ def autoconfig_basic_test_menu(): """ -# basic_menu_text = '\nWhat would you like to do?\n\n\ -# 1) List/Create Simple IPv4 Setup\n\ -# 2) List/Create Create VM and Connect to VPP interfaces\n\ -# 9 or q) Back to main menu.' - basic_menu_text = '\nWhat would you like to do?\n\n\ 1) List/Create Simple IPv4 Setup\n\ 9 or q) Back to main menu.' + + # 1) List/Create Simple IPv4 Setup\n\ + # 2) List/Create Create VM and Connect to VPP interfaces\n\ + # 9 or q) Back to main menu.' + + print "{}".format(basic_menu_text) input_valid = False @@ -521,7 +535,7 @@ def autoconfig_basic_test(): if answer == '1': autoconfig_ipv4_setup() # elif answer == '2': - # autoconfig_create_vm() + # autoconfig_create_vm() elif answer == '9' or answer == 'q': return else: @@ -572,6 +586,9 @@ def autoconfig_main(): """ + # Setup + autoconfig_setup() + answer = '' while answer != 'q': answer = autoconfig_main_menu() @@ -591,7 +608,7 @@ def autoconfig_main(): autoconfig_not_implemented() -def autoconfig_setup(): +def autoconfig_setup(ask_questions=True): """ The auto configuration setup function. @@ -617,15 +634,16 @@ def autoconfig_setup(): raise RuntimeError('The Auto configuration file does not exist {}'. format(filename)) - print "\nWelcome to the VPP system configuration utility" + if ask_questions: + print "\nWelcome to the VPP system configuration utility" - print "\nThese are the files we will modify:" - print " /etc/vpp/startup.conf" - print " /etc/sysctl.d/80-vpp.conf" - print " /etc/default/grub" + print "\nThese are the files we will modify:" + print " /etc/vpp/startup.conf" + print " /etc/sysctl.d/80-vpp.conf" + print " /etc/default/grub" - print "\nBefore we change them, we'll create working copies in {}".format(rootdir + VPP_DRYRUNDIR) - print "Please inspect them carefully before applying the actual configuration (option 3)!" + print "\nBefore we change them, we'll create working copies in {}".format(rootdir + VPP_DRYRUNDIR) + print "Please inspect them carefully before applying the actual configuration (option 3)!" nodes = acfg.get_nodes() for i in nodes.items(): @@ -642,14 +660,62 @@ def autoconfig_setup(): autoconfig_cp(node, VPP_REAL_GRUB_FILE, '{}'.format(rootdir + VPP_GRUB_FILE)) -if __name__ == '__main__': +def execute_with_args(args): + """ + Execute the configuration utility with agruments. + + :param args: The Command line arguments + :type args: tuple + """ + + # Setup + autoconfig_setup(ask_questions=False) + + # Execute the command + if args.show: + autoconfig_show_system() + elif args.dry_run: + autoconfig_dryrun(ask_questions=False) + elif args.apply: + autoconfig_apply(ask_questions=False) + else: + autoconfig_not_implemented() + + +def config_main(): + """ + The vpp configuration utility main entry point. + + """ # Check for root if not os.geteuid() == 0: sys.exit('\nPlease run the VPP Configuration Utility as root.') - # Setup - autoconfig_setup() + # If no arguments were entered, ask the user questions to get the main parameters + if len(sys.argv) == 1: + # Main menu + autoconfig_main() + return + + # There were arguments specified, so execute the utility using command line arguments + description = 'The VPP configuration utility allows the user to configure VPP in a simple and safe manner. \ +The utility takes input from the user or the speficfied .yaml file. The user should then examine these files \ +to be sure they are correct and then actually apply the configuration. When run without arguments the utility run \ +in an interactive mode' + + main_parser = argparse.ArgumentParser(prog='arg-test', description=description, + epilog='See "%(prog)s help COMMAND" for help on a specific command.') + main_parser.add_argument('--apply', '-a', action='store_true', help='Apply the cofiguration.') + main_parser.add_argument('--dry-run', '-dr', action='store_true', help='Create the dryrun configuration files.') + main_parser.add_argument('--show', '-s', action='store_true', help='Shows basic system information') + main_parser.add_argument('--debug', '-d', action='count', help='Print debug output (multiple levels)') + + args = main_parser.parse_args() + + return execute_with_args(args) + + +if __name__ == '__main__': - # Main menu - autoconfig_main() + config_main() diff --git a/extras/vpp_config/vpplib/AutoConfig.py b/extras/vpp_config/vpplib/AutoConfig.py index 7b7d7a7be8f..b995f417e6b 100644 --- a/extras/vpp_config/vpplib/AutoConfig.py +++ b/extras/vpp_config/vpplib/AutoConfig.py @@ -37,14 +37,16 @@ MAX_PERCENT_FOR_HUGE_PAGES = 70 class AutoConfig(object): """Auto Configuration Tools""" - def __init__(self, rootdir, filename): + def __init__(self, rootdir, filename, clean=False): """ The Auto Configure class. :param rootdir: The root directory for all the auto configuration files :param filename: The autoconfiguration file + :param clean: When set initialize the nodes from the auto-config file :type rootdir: str :type filename: str + :type clean: bool """ self._autoconfig_filename = rootdir + filename self._rootdir = rootdir @@ -52,6 +54,7 @@ class AutoConfig(object): self._nodes = {} self._vpp_devices_node = {} self._hugepage_config = "" + self._clean = clean self._loadconfig() def get_nodes(self): @@ -84,6 +87,7 @@ class AutoConfig(object): if ret != 0: logging.debug(stderr) + # noinspection PyBroadException @staticmethod def _ask_user_ipv4(): """ @@ -189,7 +193,7 @@ class AutoConfig(object): raise RuntimeError("Couldn't read the Auto config file {}.".format(self._autoconfig_filename, exc)) systemfile = self._rootdir + self._metadata['system_config_file'] - if os.path.isfile(systemfile): + if self._clean is False and os.path.isfile(systemfile): with open(systemfile, 'r') as sysstream: try: systopo = yaml.load(sysstream) @@ -221,7 +225,7 @@ class AutoConfig(object): # Write the system config file filename = self._rootdir + self._metadata['system_config_file'] with open(filename, 'w') as yamlfile: - yaml.dump(ydata, yamlfile, default_flow_style=False) + yaml.dump(ydata, yamlfile) def _update_auto_config(self): """ @@ -252,8 +256,8 @@ class AutoConfig(object): interface = item[1] node['interfaces'][port] = {} - node['interfaces'][port]['pci_address'] = \ - interface['pci_address'] + addr = '{}'.format(interface['pci_address']) + node['interfaces'][port]['pci_address'] = addr if 'mac_address' in interface: node['interfaces'][port]['mac_address'] = \ interface['mac_address'] @@ -281,7 +285,7 @@ class AutoConfig(object): # Write the auto config config file with open(self._autoconfig_filename, 'w') as yamlfile: - yaml.dump(ydata, yamlfile, default_flow_style=False) + yaml.dump(ydata, yamlfile) def apply_huge_pages(self): """ @@ -327,7 +331,10 @@ class AutoConfig(object): # Get main core cpu = '\n' - vpp_main_core = node['cpu']['vpp_main_core'] + if 'vpp_main_core' in node['cpu']: + vpp_main_core = node['cpu']['vpp_main_core'] + else: + vpp_main_core = 0 if vpp_main_core is not 0: cpu += ' main-core {}\n'.format(vpp_main_core) @@ -696,7 +703,10 @@ class AutoConfig(object): # Get the isolated CPUs other_workers = node['cpu']['other_workers'] vpp_workers = node['cpu']['vpp_workers'] - vpp_main_core = node['cpu']['vpp_main_core'] + if 'vpp_main_core' in node['cpu']: + vpp_main_core = node['cpu']['vpp_main_core'] + else: + vpp_main_core = 0 all_workers = [] if other_workers is not None: all_workers = [other_workers] @@ -943,10 +953,12 @@ other than VPP? [0-{}][0]? '.format(str(max_other_cores)) node['cpu']['reserve_vpp_main_core'] = reserve_vpp_main_core node['cpu']['vpp_main_core'] = 0 - def modify_cpu(self): + def modify_cpu(self, ask_questions=True): """ Modify the cpu configuration, asking for the user for the values. + :param ask_questions: When true ask the user for config parameters + """ # Get the CPU layout @@ -990,11 +1002,13 @@ other than VPP? [0-{}][0]? '.format(str(max_other_cores)) node['cpu']['cpus_per_node'] = cpus_per_node # Ask the user some questions - self._modify_cpu_questions(node, total_cpus, numa_nodes) + if ask_questions: + self._modify_cpu_questions(node, total_cpus, numa_nodes) # Populate the interfaces with the numa node - ikeys = node['interfaces'].keys() - VPPUtil.get_interfaces_numa_node(node, *tuple(ikeys)) + if 'interfaces' in node: + ikeys = node['interfaces'].keys() + VPPUtil.get_interfaces_numa_node(node, *tuple(ikeys)) # We don't want to write the cpuinfo node['cpuinfo'] = "" @@ -1060,6 +1074,33 @@ other than VPP? [0-{}][0]? '.format(str(max_other_cores)) dpdk_devices[dvid] = device del other_devices[dvid] + def update_interfaces_config(self): + """ + Modify the interfaces directly from the config file. + + """ + + for i in self._nodes.items(): + node = i[1] + devices = node['devices'] + all_devices = devices['other_devices'] + all_devices.update(devices['dpdk_devices']) + all_devices.update(devices['kernel_devices']) + + current_ifcs = {} + interfaces = {} + if 'interfaces' in node: + current_ifcs = node['interfaces'] + if current_ifcs: + for ifc in current_ifcs.values(): + dvid = ifc['pci_address'] + if dvid in all_devices: + VppPCIUtil.vpp_create_interface(interfaces, dvid, + all_devices[dvid]) + node['interfaces'] = interfaces + + self.updateconfig() + def modify_devices(self): """ Modify the devices configuration, asking for the user for the values. -- cgit 1.2.3-korg