From a3db0782d4c069733fa2e3ac1763efd4499b1de7 Mon Sep 17 00:00:00 2001 From: John DeNisco Date: Tue, 17 Oct 2017 11:07:22 -0400 Subject: Initial commit for phase 2, Add some simple validation. Change-Id: I5b1d5600cdef4b05cc7c2f1cddb60aed2cc49ac2 Signed-off-by: John DeNisco --- extras/vpp_config/setup.py | 2 +- extras/vpp_config/vpp_config.py | 68 ++++++++++++++++- extras/vpp_config/vpplib/AutoConfig.py | 132 +++++++++++++++++++++++++++++++-- extras/vpp_config/vpplib/VPPUtil.py | 41 +++++++++- 4 files changed, 235 insertions(+), 8 deletions(-) (limited to 'extras') diff --git a/extras/vpp_config/setup.py b/extras/vpp_config/setup.py index 3444e685f58..8d2a3968762 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.1", + version="17.10.3", 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 2e644185236..b8b49a032b4 100755 --- a/extras/vpp_config/vpp_config.py +++ b/extras/vpp_config/vpp_config.py @@ -440,6 +440,16 @@ def autoconfig_patch_qemu(): acfg.patch_qemu(node) +def autoconfig_ipv4_setup(): + """ + Setup IPv4 interfaces + + """ + + acfg = AutoConfig(rootdir, VPP_AUTO_CONFIGURATION_FILE) + acfg.ipv4_interface_setup() + + def autoconfig_not_implemented(): """ This feature is not implemented @@ -449,6 +459,59 @@ def autoconfig_not_implemented(): print "\nThis Feature is not implemented yet...." +def autoconfig_basic_test_menu(): + """ + The auto configuration basic test 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.' + + print "{}".format(basic_menu_text) + + input_valid = False + answer = '' + while not input_valid: + answer = raw_input("\nCommand: ") + if len(answer) > 1: + print "Please enter only 1 character." + continue + if re.findall(r'[Qq1-29]', answer): + input_valid = True + answer = answer[0].lower() + else: + print "Please enter a character between 1 and 2 or 9." + + if answer == '9': + answer = 'q' + + return answer + + +def autoconfig_basic_test(): + """ + The auto configuration basic test menu + + """ + vutil = VPPUtil() + pkgs = vutil.get_installed_vpp_pkgs() + if len(pkgs) == 0: + print "\nVPP is not installed, install VPP with option 4." + return + + answer = '' + while answer != 'q': + answer = autoconfig_basic_test_menu() + if answer == '1': + autoconfig_ipv4_setup() + elif answer == '9' or answer == 'q': + return + else: + autoconfig_not_implemented() + + def autoconfig_main_menu(): """ The auto configuration main menu @@ -461,6 +524,7 @@ def autoconfig_main_menu(): and user input in {}/vpp/vpp-config/configs/auto-config.yaml\n\ 3) Full configuration (WARNING: This will change the system configuration)\n\ 4) List/Install/Uninstall VPP.\n\ +5) Execute some basic tests.\n\ 9 or q) Quit'.format(rootdir, rootdir) # 5) Dry Run from {}/vpp/vpp-config/auto-config.yaml (will not ask questions).\n\ @@ -479,7 +543,7 @@ def autoconfig_main_menu(): input_valid = True answer = answer[0].lower() else: - print "Please enter a character between 1 and 7 or 9." + print "Please enter a character between 1 and 5 or 9." if answer == '9': answer = 'q' @@ -503,6 +567,8 @@ def autoconfig_main(): autoconfig_apply() elif answer == '4': autoconfig_install() + elif answer == '5': + autoconfig_basic_test() elif answer == '9' or answer == 'q': return else: diff --git a/extras/vpp_config/vpplib/AutoConfig.py b/extras/vpp_config/vpplib/AutoConfig.py index 49c7d54257c..ab943e0ef21 100644 --- a/extras/vpp_config/vpplib/AutoConfig.py +++ b/extras/vpp_config/vpplib/AutoConfig.py @@ -17,6 +17,7 @@ import logging import os import re import yaml +import ipaddress from vpplib.VPPUtil import VPPUtil from vpplib.VppPCIUtil import VppPCIUtil @@ -83,6 +84,36 @@ class AutoConfig(object): if ret != 0: logging.debug(stderr) + @staticmethod + def _ask_user_ipv4(): + """ + Asks the user for a number within a range. + default is returned if return is entered. + + :returns: IP address and prefix len + :rtype: tuple + """ + + while True: + answer = raw_input("Please enter the IPv4 Address [n.n.n.n]: ") + try: + ipaddr = ipaddress.ip_address(u'{}'.format(answer)) + except: + print "Please enter a valid IPv4 address." + continue + + answer = raw_input("Please enter the netmask [n.n.n.n]: ") + try: + netmask = ipaddress.ip_address(u'{}'.format(answer)) + pl = ipaddress.ip_network(u'0.0.0.0/{}'.format(netmask)) + plen = pl.exploded.split('/')[1] + break + except: + print "Please enter a valid IPv4 address and netmask." + continue + + return ipaddr, plen + @staticmethod def _ask_user_range(question, first, last, default): """ @@ -1150,7 +1181,7 @@ other than VPP? [0-{}][0]? '.format(str(max_other_cores)) print "\nThere currently a total of {} huge pages.". \ format(total) question = \ - "How many huge pages do you want [{} - {}][{}]? ".\ + "How many huge pages do you want [{} - {}][{}]? ". \ format(MIN_TOTAL_HUGE_PAGES, maxpages, MIN_TOTAL_HUGE_PAGES) answer = self._ask_user_range(question, 1024, maxpages, 1024) node['hugepages']['total'] = str(answer) @@ -1363,10 +1394,10 @@ other than VPP? [0-{}][0]? '.format(str(max_other_cores)) # System Memory if 'free' in node['hugepages'] and \ - 'memfree' in node['hugepages'] and \ - 'size' in node['hugepages']: - free = node['hugepages']['free'] - memfree = float(node['hugepages']['memfree'].split(' ')[0]) + 'memfree' in node['hugepages'] and \ + 'size' in node['hugepages']: + free = node['hugepages']['free'] + memfree = float(node['hugepages']['memfree'].split(' ')[0]) hugesize = float(node['hugepages']['size'].split(' ')[0]) memhugepages = MIN_TOTAL_HUGE_PAGES * hugesize @@ -1425,3 +1456,94 @@ other than VPP? [0-{}][0]? '.format(str(max_other_cores)) self.min_system_resources(node) print "\n==============================" + + def _ipv4_interface_setup_questions(self, node): + """ + Ask the user some questions and get a list of interfaces + and IPv4 addresses associated with those interfaces + + :param node: Node dictionary. + :type node: dict + :returns: A list or interfaces with ip addresses + :rtype: dict + """ + + vpputl = VPPUtil() + interfaces = vpputl.get_hardware(node) + if interfaces == {}: + return + + interfaces_with_ip = [] + for intf in sorted(interfaces.items()): + name = intf[0] + if name == 'local0': + continue + + question = "Would you like an address to interface {} [Y/n]? ".format(name) + answer = self._ask_user_yn(question, 'y') + if answer == 'y': + address = {} + addr, plen = self._ask_user_ipv4() + address['name'] = name + address['addr'] = addr + address['plen'] = plen + interfaces_with_ip.append(address) + + return interfaces_with_ip + + def ipv4_interface_setup(self): + """ + After asking the user some questions, get a list of interfaces + and IPv4 addresses associated with those interfaces + + """ + + for i in self._nodes.items(): + node = i[1] + + # Show the current interfaces with IP addresses + current_ints = VPPUtil.get_int_ip(node) + if current_ints is not {}: + print ("\nThese are the current interfaces with IP addresses:") + for items in sorted(current_ints.items()): + name = items[0] + value = items[1] + if 'address' not in value: + address = 'Not Set' + else: + address = value['address'] + print ("{:30} {:20} {:10}".format(name, address, value['state'])) + question = "\nWould you like to keep this configuration [Y/n]? " + answer = self._ask_user_yn(question, 'y') + if answer == 'y': + continue + else: + print ("\nThere are currently no interfaces with IP addresses.") + + # Create a script that add the ip addresses to the interfaces + # and brings the interfaces up + ints_with_addrs = self._ipv4_interface_setup_questions(node) + content = '' + for ints in ints_with_addrs: + name = ints['name'] + addr = ints['addr'] + plen = ints['plen'] + setipstr = 'set int ip address {} {}/{}\n'.format(name, addr, plen) + setintupstr = 'set int state {} up\n'.format(name) + content += setipstr + setintupstr + + # Write the content to the script + rootdir = node['rootdir'] + filename = rootdir + '/vpp/vpp-config/scripts/set_int_ipv4_and_up' + with open(filename, 'w+') as sfile: + sfile.write(content) + + # Execute the script + cmd = 'vppctl exec {}'.format(filename) + (ret, stdout, stderr) = VPPUtil.exec_command(cmd) + if ret != 0: + logging.debug(stderr) + + print("\nA script as been created at {}".format(filename)) + print("This script can be run using the following:") + print("vppctl exec {}\n".format(filename)) \ No newline at end of file diff --git a/extras/vpp_config/vpplib/VPPUtil.py b/extras/vpp_config/vpplib/VPPUtil.py index 350b7759a03..f042e80bd8f 100644 --- a/extras/vpp_config/vpplib/VPPUtil.py +++ b/extras/vpp_config/vpplib/VPPUtil.py @@ -372,6 +372,45 @@ class VPPUtil(object): for _, value in def_setting_tb_displayed.items(): self.exec_command('vppctl sh {}'.format(value)) + @staticmethod + def get_int_ip(node): + """ + Get the VPP interfaces and IP addresses + + :param node: VPP node. + :type node: dict + :returns: Dictionary containing VPP interfaces and IP addresses + :rtype: dictionary + """ + interfaces = {} + cmd = 'vppctl show int addr' + (ret, stdout, stderr) = VPPUtil.exec_command(cmd) + if ret != 0: + return interfaces + + lines = stdout.split('\n') + if len(lines[0]) is not 0: + if lines[0].split(' ')[0] == 'FileNotFoundError': + return interfaces + + for line in lines: + if len(line) is 0: + continue + + # If the first character is not whitespace + # create a new interface + if len(re.findall(r'\s', line[0])) is 0: + spl = line.split() + name = spl[0] + if name == 'local0': + continue + interfaces[name] = {} + interfaces[name]['state'] = spl[1].lstrip('(').rstrip('):\r') + else: + interfaces[name]['address'] = line.lstrip(' ').rstrip('\r') + + return interfaces + @staticmethod def get_hardware(node): """ @@ -380,7 +419,7 @@ class VPPUtil(object): :param node: VPP node. :type node: dict - :returns: Dictionary containing improtant VPP information + :returns: Dictionary containing VPP hardware information :rtype: dictionary """ -- cgit 1.2.3-korg