X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=extras%2Fvpp_config%2Fvpplib%2FAutoConfig.py;h=8c052a0277368f9bef159ce96c8cd50e9762c9b8;hb=582eac5c30e78f65ba0127aed2cb7442f4122fd2;hp=7b7d7a7be8f606bf85e32b3a875b3e56918cb924;hpb=c6b2a206b6a27bd136adb856cf51828bf91d892d;p=vpp.git diff --git a/extras/vpp_config/vpplib/AutoConfig.py b/extras/vpp_config/vpplib/AutoConfig.py index 7b7d7a7be8f..8c052a02773 100644 --- a/extras/vpp_config/vpplib/AutoConfig.py +++ b/extras/vpp_config/vpplib/AutoConfig.py @@ -12,12 +12,14 @@ # limitations under the License. """Library that supports Auto Configuration.""" +from __future__ import absolute_import, division, print_function import logging import os import re +from ipaddress import ip_address + import yaml -from netaddr import IPAddress from vpplib.VPPUtil import VPPUtil from vpplib.VppPCIUtil import VppPCIUtil @@ -26,6 +28,12 @@ from vpplib.CpuUtils import CpuUtils from vpplib.VppGrubUtil import VppGrubUtil from vpplib.QemuUtils import QemuUtils +# Python2/3 compatible +try: + input = raw_input # noqa +except NameError: + pass + __all__ = ["AutoConfig"] # Constants @@ -33,18 +41,24 @@ MIN_SYSTEM_CPUS = 2 MIN_TOTAL_HUGE_PAGES = 1024 MAX_PERCENT_FOR_HUGE_PAGES = 70 +IPERFVM_XML = 'configs/iperf-vm.xml' +IPERFVM_IMAGE = 'images/xenial-mod.img' +IPERFVM_ISO = 'configs/cloud-config.iso' + 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,7 +66,9 @@ class AutoConfig(object): self._nodes = {} self._vpp_devices_node = {} self._hugepage_config = "" + self._clean = clean self._loadconfig() + self._sockfilename = "" def get_nodes(self): """ @@ -84,6 +100,7 @@ class AutoConfig(object): if ret != 0: logging.debug(stderr) + # noinspection PyBroadException @staticmethod def _ask_user_ipv4(): """ @@ -95,19 +112,18 @@ class AutoConfig(object): """ while True: - answer = raw_input("Please enter the IPv4 Address [n.n.n.n/n]: ") + answer = input("Please enter the IPv4 Address [n.n.n.n/n]: ") try: ipinput = answer.split('/') - ipaddr = IPAddress(ipinput[0]) + ipaddr = ip_address(ipinput[0]) if len(ipinput) > 1: plen = answer.split('/')[1] else: - answer = raw_input("Please enter the netmask [n.n.n.n]: ") - plen = IPAddress(answer).netmask_bits() + answer = input("Please enter the netmask [n.n.n.n]: ") + plen = ip_address(answer).netmask_bits() return '{}/{}'.format(ipaddr, plen) - except: - print "Please enter a valid IPv4 address." - continue + except ValueError: + print("Please enter a valid IPv4 address.") @staticmethod def _ask_user_range(question, first, last, default): @@ -128,7 +144,7 @@ class AutoConfig(object): """ while True: - answer = raw_input(question) + answer = input(question) if answer == '': answer = default break @@ -136,11 +152,11 @@ class AutoConfig(object): if int(answer) in range(first, last + 1): break else: - print "Please a value between {} and {} or Return.". \ - format(first, last) + print("Please a value between {} and {} or Return.". + format(first, last)) else: - print "Please a number between {} and {} or Return.". \ - format(first, last) + print("Please a number between {} and {} or Return.". + format(first, last)) return int(answer) @@ -161,14 +177,14 @@ class AutoConfig(object): default = default.lower() answer = '' while not input_valid: - answer = raw_input(question) + answer = input(question) if answer == '': answer = default if re.findall(r'[YyNn]', answer): input_valid = True answer = answer[0].lower() else: - print "Please answer Y, N or Return." + print("Please answer Y, N or Return.") return answer @@ -186,17 +202,21 @@ class AutoConfig(object): if 'metadata' in topo: self._metadata = topo['metadata'] except yaml.YAMLError as exc: - raise RuntimeError("Couldn't read the Auto config file {}.".format(self._autoconfig_filename, exc)) + 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) if 'nodes' in systopo: self._nodes = systopo['nodes'] except yaml.YAMLError as sysexc: - raise RuntimeError("Couldn't read the System config file {}.".format(systemfile, sysexc)) + raise RuntimeError( + "Couldn't read the System config file {}.".format( + systemfile, sysexc)) else: # Get the nodes from Auto Config if 'nodes' in topo: @@ -221,7 +241,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): """ @@ -238,7 +258,7 @@ class AutoConfig(object): if 'nodes' in ydata: nodes = ydata['nodes'] except yaml.YAMLError as exc: - print exc + print(exc) return for i in nodes.items(): @@ -252,8 +272,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 +301,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,8 +347,11 @@ class AutoConfig(object): # Get main core cpu = '\n' - vpp_main_core = node['cpu']['vpp_main_core'] - if vpp_main_core is not 0: + if 'vpp_main_core' in node['cpu']: + vpp_main_core = node['cpu']['vpp_main_core'] + else: + vpp_main_core = 0 + if vpp_main_core != 0: cpu += ' main-core {}\n'.format(vpp_main_core) # Get workers @@ -394,14 +417,16 @@ class AutoConfig(object): devices += ' num-tx-desc {}\n'.format(num_tx_desc) devices += ' }' - if total_mbufs is not 0: + # If the total mbufs is not 0 or less than the default, set num-bufs + logging.debug("Total mbufs: {}".format(total_mbufs)) + if total_mbufs != 0 and total_mbufs > 16384: devices += '\n num-mbufs {}'.format(total_mbufs) return devices @staticmethod - def _calc_vpp_workers(node, vpp_workers, numa_node, - other_cpus_end, total_vpp_workers, + def _calc_vpp_workers(node, vpp_workers, numa_node, other_cpus_end, + total_vpp_workers, reserve_vpp_main_core): """ Calculate the VPP worker information @@ -435,6 +460,7 @@ class AutoConfig(object): start += 1 workers_end = start + total_vpp_workers - 1 + if workers_end <= end: if reserve_vpp_main_core: node['cpu']['vpp_main_core'] = start - 1 @@ -452,7 +478,7 @@ class AutoConfig(object): @staticmethod def _calc_desc_and_queues(total_numa_nodes, total_ports_per_numa, - total_vpp_cpus, + total_rx_queues, ports_per_numa_value): """ Calculate the number of descriptors and queues @@ -460,26 +486,20 @@ class AutoConfig(object): :param total_numa_nodes: The total number of numa nodes :param total_ports_per_numa: The total number of ports for this numa node - :param total_vpp_cpus: The total number of cpus to allocate for vpp + :param total_rx_queues: The total number of rx queues / port :param ports_per_numa_value: The value from the ports_per_numa dictionary :type total_numa_nodes: int :type total_ports_per_numa: int - :type total_vpp_cpus: int + :type total_rx_queues: int :type ports_per_numa_value: dict :returns The total number of message buffers - :returns: The total number of vpp workers - :rtype: int :rtype: int """ - # Get the total vpp workers - total_vpp_workers = total_vpp_cpus - ports_per_numa_value['total_vpp_workers'] = total_vpp_workers - # Get the number of rx queues - rx_queues = max(1, total_vpp_workers) - tx_queues = total_vpp_workers * total_numa_nodes + 1 + rx_queues = max(1, total_rx_queues) + tx_queues = rx_queues * total_numa_nodes + 1 # Get the descriptor entries desc_entries = 1024 @@ -489,7 +509,7 @@ class AutoConfig(object): total_ports_per_numa) total_mbufs = total_mbufs - return total_mbufs, total_vpp_workers + return total_mbufs @staticmethod def _create_ports_per_numa(node, interfaces): @@ -538,7 +558,7 @@ class AutoConfig(object): other_cpus_end = other_cpus_start + \ node['cpu']['total_other_cpus'] - 1 other_workers = None - if other_cpus_end is not 0: + if other_cpus_end != 0: other_workers = (other_cpus_start, other_cpus_end) node['cpu']['other_workers'] = other_workers @@ -546,28 +566,33 @@ class AutoConfig(object): vpp_workers = [] reserve_vpp_main_core = node['cpu']['reserve_vpp_main_core'] total_vpp_cpus = node['cpu']['total_vpp_cpus'] + total_rx_queues = node['cpu']['total_rx_queues'] # If total_vpp_cpus is 0 or is less than the numa nodes with ports # then we shouldn't get workers - total_with_main = total_vpp_cpus + total_workers_node = 0 + if len(ports_per_numa): + total_workers_node = total_vpp_cpus // len(ports_per_numa) + total_main = 0 if reserve_vpp_main_core: - total_with_main += 1 + total_main = 1 total_mbufs = 0 - if total_with_main is not 0: + if total_main + total_workers_node != 0: for item in ports_per_numa.items(): numa_node = item[0] value = item[1] # Get the number of descriptors and queues - mbufs, total_vpp_workers = self._calc_desc_and_queues( + mbufs = self._calc_desc_and_queues( len(ports_per_numa), - len(value['interfaces']), total_vpp_cpus, value) + len(value['interfaces']), total_rx_queues, value) total_mbufs += mbufs # Get the VPP workers reserve_vpp_main_core = self._calc_vpp_workers( - node, vpp_workers, numa_node, other_cpus_end, - total_vpp_workers, reserve_vpp_main_core) + node, vpp_workers, numa_node, + other_cpus_end, total_workers_node, + reserve_vpp_main_core) total_mbufs *= 2.5 total_mbufs = int(total_mbufs) @@ -598,36 +623,43 @@ class AutoConfig(object): # Generate the api-segment gid vpp sheit in any case if (aos + pos) == 0: - tcp = "api-segment {\n" - tcp = tcp + " gid vpp\n" - tcp = tcp + "}\n" + tcp = '\n'.join([ + "api-segment {", + " gid vpp", + "}" + ]) return tcp.rstrip('\n') - tcp = "# TCP stack-related configuration parameters\n" - tcp = tcp + "# expecting {:d} client sessions, {:d} server sessions\n\n".format(aos, pos) - tcp = tcp + "heapsize 4g\n\n" - tcp = tcp + "api-segment {\n" - tcp = tcp + " global-size 2000M\n" - tcp = tcp + " api-size 1G\n" - tcp = tcp + "}\n\n" - - tcp = tcp + "session {\n" - tcp = tcp + " event-queue-length " + "{:d}".format(aos + pos) + "\n" - tcp = tcp + " preallocated-sessions " + "{:d}".format(aos + pos) + "\n" - tcp = tcp + " v4-session-table-buckets " + "{:d}".format((aos + pos) / 4) + "\n" - tcp = tcp + " v4-session-table-memory 3g\n" + tcp = '\n'.join([ + "# TCP stack-related configuration parameters", + "# expecting {:d} client sessions, {:d} server sessions\n".format( + aos, pos), + "heapsize 4g\n", + "api-segment {", + " global-size 2000M", + " api-size 1G", + "}\n", + + "session {", + " event-queue-length {:d}".format(aos + pos), + " preallocated-sessions {:d}".format(aos + pos), + " v4-session-table-buckets {:d}".format((aos + pos) // 4), + " v4-session-table-memory 3g\n" + ]) if aos > 0: - tcp = tcp + " v4-halfopen-table-buckets " + \ - "{:d}".format((aos + pos) / 4) + "\n" + tcp = tcp + " v4-halfopen-table-buckets {:d}".format( + (aos + pos) // 4) + "\n" tcp = tcp + " v4-halfopen-table-memory 3g\n" + tcp = tcp + " local-endpoints-table-buckets {:d}".format( + (aos + pos) // 4) + "\n" + tcp = tcp + " local-endpoints-table-memory 3g\n" tcp = tcp + "}\n\n" tcp = tcp + "tcp {\n" - tcp = tcp + " preallocated-connections " + "{:d}".format(aos + pos) + "\n" + tcp = tcp + " preallocated-connections {:d}".format(aos + pos) + "\n" if aos > 0: - tcp = tcp + " preallocated-half-open-connections " + "{:d}".format(aos) + "\n" - tcp = tcp + " local-endpoints-table-buckets " + "{:d}".format((aos + pos) / 4) + "\n" - tcp = tcp + " local-endpoints-table-memory 3g\n" + tcp = tcp + " preallocated-half-open-connections {:d}".format( + aos) + "\n" tcp = tcp + "}\n\n" return tcp.rstrip('\n') @@ -696,11 +728,14 @@ 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] - if vpp_main_core is not 0: + if vpp_main_core != 0: all_workers += [(vpp_main_core, vpp_main_core)] all_workers += vpp_workers isolated_cpus = '' @@ -769,7 +804,7 @@ class AutoConfig(object): iso_cpul = iso_cpu_str.split(',') for iso_cpu in iso_cpul: isocpuspl = iso_cpu.split('-') - if len(isocpuspl) is 1: + if len(isocpuspl) == 1: current_iso_cpus += 1 else: first = int(isocpuspl[0]) @@ -911,28 +946,33 @@ class AutoConfig(object): :type numa_nodes: list """ - print "\nYour system has {} core(s) and {} Numa Nodes.". \ - format(total_cpus, len(numa_nodes)) - print "To begin, we suggest not reserving any cores for VPP", - print "or other processes." - print "Then to improve performance try reserving cores as needed. " + print("\nYour system has {} core(s) and {} Numa Nodes.". + format(total_cpus, len(numa_nodes))) + print("To begin, we suggest not reserving any cores for " + "VPP or other processes.") + print("Then to improve performance start reserving cores and " + "adding queues as needed.") - max_other_cores = total_cpus / 2 - question = '\nHow many core(s) do you want to reserve for processes \ -other than VPP? [0-{}][0]? '.format(str(max_other_cores)) - total_other_cpus = self._ask_user_range(question, 0, max_other_cores, - 0) - node['cpu']['total_other_cpus'] = total_other_cpus - - max_vpp_cpus = 4 + # Leave 1 for the general system + total_cpus -= 1 + max_vpp_cpus = min(total_cpus, 4) total_vpp_cpus = 0 if max_vpp_cpus > 0: - question = "How many core(s) shall we reserve for VPP workers[0-{}][0]? ". \ - format(max_vpp_cpus) + question = "\nHow many core(s) shall we reserve for " \ + "VPP [0-{}][0]? ".format(max_vpp_cpus) total_vpp_cpus = self._ask_user_range(question, 0, max_vpp_cpus, 0) node['cpu']['total_vpp_cpus'] = total_vpp_cpus - max_main_cpus = max_vpp_cpus - total_vpp_cpus + total_other_cpus = 0 + max_other_cores = total_cpus - total_vpp_cpus + if max_other_cores > 0: + question = 'How many core(s) do you want to reserve for ' \ + 'processes other than VPP? [0-{}][0]? '. \ + format(str(max_other_cores)) + total_other_cpus = self._ask_user_range(question, 0, max_other_cores, 0) + node['cpu']['total_other_cpus'] = total_other_cpus + + max_main_cpus = total_cpus - total_vpp_cpus - total_other_cpus reserve_vpp_main_core = False if max_main_cpus > 0: question = "Should we reserve 1 core for the VPP Main thread? " @@ -943,10 +983,17 @@ 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): + question = "How many RX queues per port shall we use for " \ + "VPP [1-4][1]? ".format(max_vpp_cpus) + total_rx_queues = self._ask_user_range(question, 1, 4, 1) + node['cpu']['total_rx_queues'] = total_rx_queues + + 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 +1037,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 and total_cpus >= 4: + 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'] = "" @@ -1012,8 +1061,8 @@ other than VPP? [0-{}][0]? '.format(str(max_other_cores)) odevices_len = len(other_devices) if odevices_len > 0: - print "\nThese device(s) are currently NOT being used", - print "by VPP or the OS.\n" + print("\nThese device(s) are currently NOT being used " + "by VPP or the OS.\n") VppPCIUtil.show_vpp_devices(other_devices, show_interfaces=False) question = "\nWould you like to give any of these devices" question += " back to the OS [Y/n]? " @@ -1028,9 +1077,17 @@ other than VPP? [0-{}][0]? '.format(str(max_other_cores)) question += " the OS [y/N]? " answer = self._ask_user_yn(question, 'n') if answer == 'y': - driver = device['unused'][0] - VppPCIUtil.bind_vpp_device(node, driver, dvid) - vppd[dvid] = device + if 'unused' in device and len( + device['unused']) != 0 and \ + device['unused'][0] != '': + driver = device['unused'][0] + ret = VppPCIUtil.bind_vpp_device( + node, driver, dvid) + if ret: + logging.debug( + 'Could not bind device {}'.format(dvid)) + else: + vppd[dvid] = device for dit in vppd.items(): dvid = dit[0] device = dit[1] @@ -1039,8 +1096,8 @@ other than VPP? [0-{}][0]? '.format(str(max_other_cores)) odevices_len = len(other_devices) if odevices_len > 0: - print "\nThese device(s) are still NOT being used ", - print "by VPP or the OS.\n" + print("\nThese device(s) are still NOT being used " + "by VPP or the OS.\n") VppPCIUtil.show_vpp_devices(other_devices, show_interfaces=False) question = "\nWould you like use any of these for VPP [y/N]? " answer = self._ask_user_yn(question, 'N') @@ -1057,8 +1114,46 @@ other than VPP? [0-{}][0]? '.format(str(max_other_cores)) for dit in vppd.items(): dvid = dit[0] device = dit[1] - dpdk_devices[dvid] = device - del other_devices[dvid] + if 'unused' in device and len(device['unused']) != 0 and \ + device['unused'][0] != '': + driver = device['unused'][0] + logging.debug( + 'Binding device {} to driver {}'.format(dvid, + driver)) + ret = VppPCIUtil.bind_vpp_device(node, driver, dvid) + if ret: + logging.debug( + 'Could not bind device {}'.format(dvid)) + else: + 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): """ @@ -1085,19 +1180,17 @@ other than VPP? [0-{}][0]? '.format(str(max_other_cores)) klen = len(kernel_devices) if klen > 0: - print "\nThese devices have kernel interfaces, but", - print "appear to be safe to use with VPP.\n" + print("\nThese devices are safe to be used with VPP.\n") VppPCIUtil.show_vpp_devices(kernel_devices) - question = "\nWould you like to use any of these " - question += "device(s) for VPP [y/N]? " + question = "\nWould you like to use any of these " \ + "device(s) for VPP [y/N]? " answer = self._ask_user_yn(question, 'n') if answer == 'y': vppd = {} for dit in kernel_devices.items(): dvid = dit[0] device = dit[1] - question = "Would you like to use device {} ". \ - format(dvid) + question = "Would you like to use device {} ".format(dvid) question += "for VPP [y/N]? " answer = self._ask_user_yn(question, 'n') if answer == 'y': @@ -1105,18 +1198,30 @@ other than VPP? [0-{}][0]? '.format(str(max_other_cores)) for dit in vppd.items(): dvid = dit[0] device = dit[1] + if 'unused' in device and len( + device['unused']) != 0 and device['unused'][ + 0] != '': + driver = device['unused'][0] + question = "Would you like to bind the driver {} for {} [y/N]? ".format(driver, dvid) + answer = self._ask_user_yn(question, 'n') + if answer == 'y': + logging.debug('Binding device {} to driver {}'.format(dvid, driver)) + ret = VppPCIUtil.bind_vpp_device(node, driver, dvid) + if ret: + logging.debug('Could not bind device {}'.format(dvid)) dpdk_devices[dvid] = device del kernel_devices[dvid] dlen = len(dpdk_devices) if dlen > 0: - print "\nThese device(s) will be used by VPP.\n" - VppPCIUtil.show_vpp_devices(dpdk_devices, show_interfaces=False) + print("\nThese device(s) are already using DPDK.\n") + VppPCIUtil.show_vpp_devices(dpdk_devices, + show_interfaces=False) question = "\nWould you like to remove any of " question += "these device(s) [y/N]? " answer = self._ask_user_yn(question, 'n') if answer == 'y': - vppd = {} + vppdl = {} for dit in dpdk_devices.items(): dvid = dit[0] device = dit[1] @@ -1124,14 +1229,25 @@ other than VPP? [0-{}][0]? '.format(str(max_other_cores)) format(dvid) answer = self._ask_user_yn(question, 'n') if answer == 'y': - vppd[dvid] = device - for dit in vppd.items(): + vppdl[dvid] = device + for dit in vppdl.items(): dvid = dit[0] device = dit[1] - driver = device['unused'][0] - VppPCIUtil.bind_vpp_device(node, driver, dvid) - kernel_devices[dvid] = device - del dpdk_devices[dvid] + if 'unused' in device and len( + device['unused']) != 0 and device['unused'][ + 0] != '': + driver = device['unused'][0] + logging.debug( + 'Binding device {} to driver {}'.format( + dvid, driver)) + ret = VppPCIUtil.bind_vpp_device(node, driver, + dvid) + if ret: + logging.debug( + 'Could not bind device {}'.format(dvid)) + else: + kernel_devices[dvid] = device + del dpdk_devices[dvid] interfaces = {} for dit in dpdk_devices.items(): @@ -1140,10 +1256,6 @@ other than VPP? [0-{}][0]? '.format(str(max_other_cores)) VppPCIUtil.vpp_create_interface(interfaces, dvid, device) node['interfaces'] = interfaces - print "\nThese device(s) will be used by VPP, please", - print "rerun this option if this is incorrect.\n" - VppPCIUtil.show_vpp_devices(dpdk_devices, show_interfaces=False) - self._update_auto_config() self.updateconfig() @@ -1163,20 +1275,20 @@ other than VPP? [0-{}][0]? '.format(str(max_other_cores)) hugesize = int(size.split(' ')[0]) # The max number of huge pages should be no more than # 70% of total free memory - maxpages = (int(memfree) * MAX_PERCENT_FOR_HUGE_PAGES / 100) / hugesize - print "\nThere currently {} {} huge pages free.". \ - format(free, size) - question = "Do you want to reconfigure the number of " - question += "huge pages [y/N]? " + maxpages = (int(memfree) * MAX_PERCENT_FOR_HUGE_PAGES // 100) // \ + hugesize + print("\nThere currently {} {} huge pages free.".format( + free, size)) + question = "Do you want to reconfigure the number of " \ + "huge pages [y/N]? " answer = self._ask_user_yn(question, 'n') if answer == 'n': node['hugepages']['total'] = total continue - print "\nThere currently a total of {} huge pages.". \ - format(total) - question = \ - "How many huge pages do you want [{} - {}][{}]? ". \ + print("\nThere currently a total of {} huge pages.". + format(total)) + question = "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) @@ -1204,16 +1316,16 @@ other than VPP? [0-{}][0]? '.format(str(max_other_cores)) for i in self._nodes.items(): node = i[1] - question = "\nHow many active-open / tcp client sessions are expected " - question = question + "[0-10000000][0]? " + question = "\nHow many active-open / tcp client sessions are " \ + "expected [0-10000000][0]? " answer = self._ask_user_range(question, 0, 10000000, 0) # Less than 10K is equivalent to 0 if int(answer) < 10000: answer = 0 node['tcp']['active_open_sessions'] = answer - question = "How many passive-open / tcp server sessions are expected " - question = question + "[0-10000000][0]? " + question = "How many passive-open / tcp server sessions are " \ + "expected [0-10000000][0]? " answer = self._ask_user_range(question, 0, 10000000, 0) # Less than 10K is equivalent to 0 if int(answer) < 10000: @@ -1235,7 +1347,7 @@ other than VPP? [0-{}][0]? '.format(str(max_other_cores)) :type node: dict """ - print '\nWe are patching the node "{}":\n'.format(node['host']) + print('\nWe are patching the node "{}":\n'.format(node['host'])) QemuUtils.build_qemu(node, force_install=True, apply_patch=True) @staticmethod @@ -1249,44 +1361,44 @@ other than VPP? [0-{}][0]? '.format(str(max_other_cores)) item = 'Model name' if item in cpu: - print "{:>20}: {}".format(item, cpu[item]) + print("{:>20}: {}".format(item, cpu[item])) item = 'CPU(s)' if item in cpu: - print "{:>20}: {}".format(item, cpu[item]) + print("{:>20}: {}".format(item, cpu[item])) item = 'Thread(s) per core' if item in cpu: - print "{:>20}: {}".format(item, cpu[item]) + print("{:>20}: {}".format(item, cpu[item])) item = 'Core(s) per socket' if item in cpu: - print "{:>20}: {}".format(item, cpu[item]) + print("{:>20}: {}".format(item, cpu[item])) item = 'Socket(s)' if item in cpu: - print "{:>20}: {}".format(item, cpu[item]) + print("{:>20}: {}".format(item, cpu[item])) item = 'NUMA node(s)' numa_nodes = 0 if item in cpu: numa_nodes = int(cpu[item]) - for i in xrange(0, numa_nodes): + for i in range(0, numa_nodes): item = "NUMA node{} CPU(s)".format(i) - print "{:>20}: {}".format(item, cpu[item]) + print("{:>20}: {}".format(item, cpu[item])) item = 'CPU max MHz' if item in cpu: - print "{:>20}: {}".format(item, cpu[item]) + print("{:>20}: {}".format(item, cpu[item])) item = 'CPU min MHz' if item in cpu: - print "{:>20}: {}".format(item, cpu[item]) + print("{:>20}: {}".format(item, cpu[item])) if node['cpu']['smt_enabled']: smt = 'Enabled' else: smt = 'Disabled' - print "{:>20}: {}".format('SMT', smt) + print("{:>20}: {}".format('SMT', smt)) # VPP Threads - print "\nVPP Threads: (Name: Cpu Number)" + print("\nVPP Threads: (Name: Cpu Number)") vpp_processes = cpu['vpp_processes'] for i in vpp_processes.items(): - print " {:10}: {:4}".format(i[0], i[1]) + print(" {:10}: {:4}".format(i[0], i[1])) @staticmethod def device_info(node): @@ -1297,54 +1409,62 @@ other than VPP? [0-{}][0]? '.format(str(max_other_cores)) if 'cpu' in node and 'total_mbufs' in node['cpu']: total_mbufs = node['cpu']['total_mbufs'] - if total_mbufs is not 0: - print "Total Number of Buffers: {}".format(total_mbufs) + if total_mbufs != 0: + print("Total Number of Buffers: {}".format(total_mbufs)) vpp = VppPCIUtil(node) vpp.get_all_devices() linkup_devs = vpp.get_link_up_devices() if len(linkup_devs): - print ("\nDevices with link up (can not be used with VPP):") + print("\nDevices with link up (can not be used with VPP):") vpp.show_vpp_devices(linkup_devs, show_header=False) # for dev in linkup_devs: # print (" " + dev) kernel_devs = vpp.get_kernel_devices() if len(kernel_devs): - print ("\nDevices bound to kernel drivers:") + print("\nDevices bound to kernel drivers:") vpp.show_vpp_devices(kernel_devs, show_header=False) else: - print ("\nNo devices bound to kernel drivers") + print("\nNo devices bound to kernel drivers") dpdk_devs = vpp.get_dpdk_devices() if len(dpdk_devs): - print ("\nDevices bound to DPDK drivers:") + print("\nDevices bound to DPDK drivers:") vpp.show_vpp_devices(dpdk_devs, show_interfaces=True, show_header=False) else: - print ("\nNo devices bound to DPDK drivers") + print("\nNo devices bound to DPDK drivers") + + other_devs = vpp.get_other_devices() + if len(other_devs): + print("\nDevices not bound to Kernel or DPDK drivers:") + vpp.show_vpp_devices(other_devs, show_interfaces=True, + show_header=False) + else: + print("\nNo devices not bound to Kernel or DPDK drivers") vpputl = VPPUtil() interfaces = vpputl.get_hardware(node) if interfaces == {}: return - print ("\nDevices in use by VPP:") + print("\nDevices in use by VPP:") if len(interfaces.items()) < 2: - print ("None") + print("None") return - print "{:30} {:6} {:4} {:7} {:4} {:7}". \ - format('Name', 'Socket', 'RXQs', - 'RXDescs', 'TXQs', 'TXDescs') + print("{:30} {:4} {:4} {:7} {:4} {:7}". + format('Name', 'Numa', 'RXQs', + 'RXDescs', 'TXQs', 'TXDescs')) for intf in sorted(interfaces.items()): name = intf[0] value = intf[1] if name == 'local0': continue - socket = rx_qs = rx_ds = tx_qs = tx_ds = '' - if 'cpu socket' in value: - socket = int(value['cpu socket']) + numa = rx_qs = rx_ds = tx_qs = tx_ds = '' + if 'numa' in value: + numa = int(value['numa']) if 'rx queues' in value: rx_qs = int(value['rx queues']) if 'rx descs' in value: @@ -1354,8 +1474,8 @@ other than VPP? [0-{}][0]? '.format(str(max_other_cores)) if 'tx descs' in value: tx_ds = int(value['tx descs']) - print ("{:30} {:>6} {:>4} {:>7} {:>4} {:>7}". - format(name, socket, rx_qs, rx_ds, tx_qs, tx_ds)) + print("{:30} {:>4} {:>4} {:>7} {:>4} {:>7}". + format(name, numa, rx_qs, rx_ds, tx_qs, tx_ds)) @staticmethod def hugepage_info(node): @@ -1367,6 +1487,18 @@ other than VPP? [0-{}][0]? '.format(str(max_other_cores)) hpg = VppHugePageUtil(node) hpg.show_huge_pages() + @staticmethod + def has_interfaces(node): + """ + Check for interfaces, return tru if there is at least one + + :returns: boolean + """ + if 'interfaces' in node and len(node['interfaces']): + return True + else: + return False + @staticmethod def min_system_resources(node): """ @@ -1374,7 +1506,6 @@ other than VPP? [0-{}][0]? '.format(str(max_other_cores)) there is enough. :returns: boolean - :rtype: dict """ min_sys_res = True @@ -1383,8 +1514,8 @@ other than VPP? [0-{}][0]? '.format(str(max_other_cores)) if 'layout' in node['cpu']: total_cpus = len(node['cpu']['layout']) if total_cpus < 2: - print "\nThere is only {} CPU(s) available on this system.".format(total_cpus) - print "This is not enough to run VPP." + print("\nThere is only {} CPU(s) available on this system. " + "This is not enough to run VPP.".format(total_cpus)) min_sys_res = False # System Memory @@ -1399,8 +1530,12 @@ other than VPP? [0-{}][0]? '.format(str(max_other_cores)) percentmemhugepages = (memhugepages / memfree) * 100 if free is '0' and \ percentmemhugepages > MAX_PERCENT_FOR_HUGE_PAGES: - print "\nThe System has only {} of free memory.".format(int(memfree)) - print "You will not be able to allocate enough Huge Pages for VPP." + print( + "\nThe System has only {} of free memory. You will not " + "be able to allocate enough Huge Pages for VPP.".format( + int( + memfree)) + ) min_sys_res = False return min_sys_res @@ -1412,45 +1547,43 @@ other than VPP? [0-{}][0]? '.format(str(max_other_cores)) """ for i in self._nodes.items(): - print "\n==============================" + print("\n==============================") name = i[0] node = i[1] - print "NODE: {}\n".format(name) + print("NODE: {}\n".format(name)) # CPU - print "CPU:" + print("CPU:") self.cpu_info(node) # Grub - print "\nGrub Command Line:" + print("\nGrub Command Line:") if 'grub' in node: - print \ - " Current: {}".format( - node['grub']['current_cmdline']) - print \ - " Configured: {}".format( - node['grub']['default_cmdline']) + print(" Current: {}".format( + node['grub']['current_cmdline'])) + print(" Configured: {}".format( + node['grub']['default_cmdline'])) # Huge Pages - print "\nHuge Pages:" + print("\nHuge Pages:") self.hugepage_info(node) # Devices - print "\nDevices:" + print("\nDevices:") self.device_info(node) # Status - print "\nVPP Service Status:" + print("\nVPP Service Status:") state, errors = VPPUtil.status(node) - print " {}".format(state) + print(" {}".format(state)) for e in errors: - print " {}".format(e) + print(" {}".format(e)) # Minimum system resources self.min_system_resources(node) - print "\n==============================" + print("\n==============================") def _ipv4_interface_setup_questions(self, node): """ @@ -1474,7 +1607,8 @@ other than VPP? [0-{}][0]? '.format(str(max_other_cores)) if name == 'local0': continue - question = "Would you like add address to interface {} [Y/n]? ".format(name) + question = "Would you like add address to " \ + "interface {} [Y/n]? ".format(name) answer = self._ask_user_yn(question, 'y') if answer == 'y': address = {} @@ -1497,8 +1631,8 @@ other than VPP? [0-{}][0]? '.format(str(max_other_cores)) # 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:") + if current_ints != {}: + print("\nThese are the current interfaces with IP addresses:") for items in sorted(current_ints.items()): name = items[0] value = items[1] @@ -1506,13 +1640,16 @@ other than VPP? [0-{}][0]? '.format(str(max_other_cores)) 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]? " + 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.") + 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 @@ -1578,20 +1715,32 @@ other than VPP? [0-{}][0]? '.format(str(max_other_cores)) if name == 'local0': continue - question = "Would you like connect this interface {} to the VM [Y/n]? ".format(name) + question = "Would you like connect this interface {} to " \ + "the VM [Y/n]? ".format(name) answer = self._ask_user_yn(question, 'y') if answer == 'y': - sockfilename = '/tmp/sock{}.sock'.format(inum) + sockfilename = '/var/run/vpp/{}.sock'.format( + name.replace('/', '_')) if os.path.exists(sockfilename): os.remove(sockfilename) - cmd = 'vppctl create vhost-user socket {} server'.format(sockfilename) + cmd = 'vppctl create vhost-user socket {} server'.format( + sockfilename) (ret, stdout, stderr) = vpputl.exec_command(cmd) if ret != 0: - raise RuntimeError("Create vhost failed on node {} {}." - .format(node['host'], stderr)) + raise RuntimeError( + "Couldn't execute the command {}, {}.".format(cmd, + stderr)) vintname = stdout.rstrip('\r\n') - interface = {'name': name, 'virtualinterface': '{}'.format(vintname), + cmd = 'chmod 777 {}'.format(sockfilename) + (ret, stdout, stderr) = vpputl.exec_command(cmd) + if ret != 0: + raise RuntimeError( + "Couldn't execute the command {}, {}.".format(cmd, + stderr)) + + interface = {'name': name, + 'virtualinterface': '{}'.format(vintname), 'bridge': '{}'.format(inum)} inum += 1 interfaces_with_virtual_interfaces.append(interface) @@ -1600,8 +1749,8 @@ other than VPP? [0-{}][0]? '.format(str(max_other_cores)) def create_and_bridge_virtual_interfaces(self): """ - After asking the user some questions, create a VM and connect the interfaces - to VPP interfaces + After asking the user some questions, create a VM and connect + the interfaces to VPP interfaces """ @@ -1609,39 +1758,189 @@ other than VPP? [0-{}][0]? '.format(str(max_other_cores)) node = i[1] # Show the current bridge and interface configuration - print "\nThis the current bridge configuration:" + print("\nThis the current bridge configuration:") VPPUtil.show_bridge(node) question = "\nWould you like to keep this configuration [Y/n]? " answer = self._ask_user_yn(question, 'y') if answer == 'y': continue - # Create a script that builds a bridge configuration with physical interfaces - # and virtual interfaces + # Create a script that builds a bridge configuration with + # physical interfaces and virtual interfaces ints_with_vints = self._create_vints_questions(node) content = '' for intf in ints_with_vints: - vhoststr = 'comment { The following command creates the socket }\n' - vhoststr += 'comment { and returns a virtual interface }\n' - vhoststr += 'comment {{ create vhost-user socket /tmp/sock{}.sock server }}\n'. \ - format(intf['bridge']) + vhoststr = '\n'.join([ + 'comment { The following command creates the socket }', + 'comment { and returns a virtual interface }', + 'comment {{ create vhost-user socket ' + '/var/run/vpp/sock{}.sock server }}\n'.format( + intf['bridge']) + ]) + + setintdnstr = 'set interface state {} down\n'.format( + intf['name']) + + setintbrstr = 'set interface l2 bridge {} {}\n'.format( + intf['name'], intf['bridge']) + setvintbrstr = 'set interface l2 bridge {} {}\n'.format( + intf['virtualinterface'], intf['bridge']) - setintdnstr = 'set interface state {} down\n'.format(intf['name']) + # set interface state VirtualEthernet/0/0/0 up + setintvststr = 'set interface state {} up\n'.format( + intf['virtualinterface']) + + # set interface state VirtualEthernet/0/0/0 down + setintupstr = 'set interface state {} up\n'.format( + intf['name']) + + content += vhoststr + setintdnstr + setintbrstr + \ + setvintbrstr + setintvststr + setintupstr + + # Write the content to the script + rootdir = node['rootdir'] + filename = rootdir + \ + '/vpp/vpp-config/scripts/create_vms_and_connect_to_vpp' + with open(filename, 'w+') as sfile: + sfile.write(content) - setintbrstr = 'set interface l2 bridge {} {}\n'.format(intf['name'], intf['bridge']) - setvintbrstr = 'set interface l2 bridge {} {}\n'.format(intf['virtualinterface'], intf['bridge']) + # 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)) + + def _iperf_vm_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: list + """ + + vpputl = VPPUtil() + interfaces = vpputl.get_hardware(node) + if interfaces == {}: + return [] + + # First delete all the Virtual interfaces + for intf in sorted(interfaces.items()): + name = intf[0] + if name[:7] == 'Virtual': + cmd = 'vppctl delete vhost-user {}'.format(name) + (ret, stdout, stderr) = vpputl.exec_command(cmd) + if ret != 0: + logging.debug('{} failed on node {} {}'.format( + cmd, node['host'], stderr)) + + # Create a virtual interface, for each interface the user wants to use + interfaces = vpputl.get_hardware(node) + if interfaces == {}: + return [] + interfaces_with_virtual_interfaces = [] + inum = 1 + + while True: + print('\nPlease pick one interface to connect to the iperf VM.') + for intf in sorted(interfaces.items()): + name = intf[0] + if name == 'local0': + continue + + question = "Would you like connect this interface {} to " \ + "the VM [y/N]? ".format(name) + answer = self._ask_user_yn(question, 'n') + if answer == 'y': + self._sockfilename = '/var/run/vpp/{}.sock'.format( + name.replace('/', '_')) + if os.path.exists(self._sockfilename): + os.remove(self._sockfilename) + cmd = 'vppctl create vhost-user socket {} server'.format( + self._sockfilename) + (ret, stdout, stderr) = vpputl.exec_command(cmd) + if ret != 0: + raise RuntimeError( + "Couldn't execute the command {}, {}.".format( + cmd, stderr)) + vintname = stdout.rstrip('\r\n') + + cmd = 'chmod 777 {}'.format(self._sockfilename) + (ret, stdout, stderr) = vpputl.exec_command(cmd) + if ret != 0: + raise RuntimeError( + "Couldn't execute the command {}, {}.".format( + cmd, stderr)) + + interface = {'name': name, + 'virtualinterface': '{}'.format(vintname), + 'bridge': '{}'.format(inum)} + inum += 1 + interfaces_with_virtual_interfaces.append(interface) + return interfaces_with_virtual_interfaces + + def create_and_bridge_iperf_virtual_interface(self): + """ + After asking the user some questions, and create and bridge a + virtual interface to be used with iperf VM + + """ + + for i in self._nodes.items(): + node = i[1] + + # Show the current bridge and interface configuration + print("\nThis the current bridge configuration:") + ifaces = VPPUtil.show_bridge(node) + question = "\nWould you like to keep this configuration [Y/n]? " + answer = self._ask_user_yn(question, 'y') + if answer == 'y': + self._sockfilename = '/var/run/vpp/{}.sock'.format( + ifaces[0]['name'].replace('/', '_')) + if os.path.exists(self._sockfilename): + continue + + # Create a script that builds a bridge configuration with + # physical interfaces and virtual interfaces + ints_with_vints = self._iperf_vm_questions(node) + content = '' + for intf in ints_with_vints: + vhoststr = '\n'.join([ + 'comment { The following command creates the socket }', + 'comment { and returns a virtual interface }', + 'comment {{ create vhost-user socket ' + '/var/run/vpp/sock{}.sock server }}\n'.format( + intf['bridge']) + ]) + + setintdnstr = 'set interface state {} down\n'.format( + intf['name']) + + setintbrstr = 'set interface l2 bridge {} {}\n'.format( + intf['name'], intf['bridge']) + setvintbrstr = 'set interface l2 bridge {} {}\n'.format( + intf['virtualinterface'], intf['bridge']) # set interface state VirtualEthernet/0/0/0 up - setintvststr = 'set interface state {} up\n'.format(intf['virtualinterface']) + setintvststr = 'set interface state {} up\n'.format( + intf['virtualinterface']) # set interface state VirtualEthernet/0/0/0 down - setintupstr = 'set interface state {} up\n'.format(intf['name']) + setintupstr = 'set interface state {} up\n'.format( + intf['name']) - content += vhoststr + setintdnstr + setintbrstr + setvintbrstr + setintvststr + setintupstr + content += vhoststr + setintdnstr + setintbrstr + \ + setvintbrstr + setintvststr + setintupstr # Write the content to the script rootdir = node['rootdir'] - filename = rootdir + '/vpp/vpp-config/scripts/create_vms_and_connect_to_vpp' + filename = rootdir + '/vpp/vpp-config/scripts/create_iperf_vm' with open(filename, 'w+') as sfile: sfile.write(content) @@ -1654,3 +1953,72 @@ other than VPP? [0-{}][0]? '.format(str(max_other_cores)) print("\nA script as been created at {}".format(filename)) print("This script can be run using the following:") print("vppctl exec {}\n".format(filename)) + + @staticmethod + def destroy_iperf_vm(name): + """ + After asking the user some questions, create a VM and connect + the interfaces to VPP interfaces + + :param name: The name of the VM to be be destroyed + :type name: str + """ + + cmd = 'virsh list' + (ret, stdout, stderr) = VPPUtil.exec_command(cmd) + if ret != 0: + logging.debug(stderr) + raise RuntimeError( + "Couldn't execute the command {} : {}".format(cmd, stderr)) + + if re.findall(name, stdout): + cmd = 'virsh destroy {}'.format(name) + (ret, stdout, stderr) = VPPUtil.exec_command(cmd) + if ret != 0: + logging.debug(stderr) + raise RuntimeError( + "Couldn't execute the command {} : {}".format( + cmd, stderr)) + + def create_iperf_vm(self, vmname): + """ + After asking the user some questions, create a VM and connect + the interfaces to VPP interfaces + + """ + + # Read the iperf VM template file + distro = VPPUtil.get_linux_distro() + if distro[0] == 'Ubuntu': + tfilename = \ + '{}/vpp/vpp-config/configs/iperf-ubuntu.xml.template'.format( + self._rootdir) + else: + tfilename = \ + '{}/vpp/vpp-config/configs/iperf-centos.xml.template'.format( + self._rootdir) + + with open(tfilename, 'r') as tfile: + tcontents = tfile.read() + tfile.close() + + # Add the variables + imagename = '{}/vpp/vpp-config/{}'.format( + self._rootdir, IPERFVM_IMAGE) + isoname = '{}/vpp/vpp-config/{}'.format(self._rootdir, IPERFVM_ISO) + tcontents = tcontents.format(vmname=vmname, imagename=imagename, + isoname=isoname, + vhostsocketname=self._sockfilename) + + # Write the xml + ifilename = '{}/vpp/vpp-config/{}'.format(self._rootdir, IPERFVM_XML) + with open(ifilename, 'w+') as ifile: + ifile.write(tcontents) + ifile.close() + + cmd = 'virsh create {}'.format(ifilename) + (ret, stdout, stderr) = VPPUtil.exec_command(cmd) + if ret != 0: + logging.debug(stderr) + raise RuntimeError( + "Couldn't execute the command {} : {}".format(cmd, stderr))