diff options
Diffstat (limited to 'scripts/dpdk_setup_ports.py')
-rwxr-xr-x | scripts/dpdk_setup_ports.py | 834 |
1 files changed, 834 insertions, 0 deletions
diff --git a/scripts/dpdk_setup_ports.py b/scripts/dpdk_setup_ports.py new file mode 100755 index 00000000..f85dae5d --- /dev/null +++ b/scripts/dpdk_setup_ports.py @@ -0,0 +1,834 @@ +#! /usr/bin/python +# hhaim +import sys +import os +python_ver = 'python%s' % sys.version_info[0] +sys.path.append(os.path.join('external_libs', 'pyyaml-3.11', python_ver)) +import yaml +import dpdk_nic_bind +import re +import argparse +import copy +import shlex +import traceback +from collections import defaultdict, OrderedDict +from distutils.util import strtobool +import getpass + +class ConfigCreator(object): + mandatory_interface_fields = ['Slot_str', 'Device_str', 'NUMA'] + _2hex_re = '[\da-fA-F]{2}' + mac_re = re.compile('^({0}:){{5}}{0}$'.format(_2hex_re)) + + # cpu_topology - dict: physical processor -> physical core -> logical processing unit (thread) + # interfaces - array of dicts per interface, should include "mandatory_interface_fields" values + def __init__(self, cpu_topology, interfaces, include_lcores = [], exclude_lcores = [], only_first_thread = False, zmq_rpc_port = None, zmq_pub_port = None, prefix = None, ignore_numa = False): + self.cpu_topology = copy.deepcopy(cpu_topology) + self.interfaces = copy.deepcopy(interfaces) + del cpu_topology + del interfaces + assert isinstance(self.cpu_topology, dict), 'Type of cpu_topology should be dict, got: %s' % type(self.cpu_topology) + assert len(self.cpu_topology.keys()) > 0, 'cpu_topology should contain at least one processor' + assert isinstance(self.interfaces, list), 'Type of interfaces should be list, got: %s' % type(list) + assert len(self.interfaces) % 2 == 0, 'Should be even number of interfaces, got: %s' % len(self.interfaces) + assert len(self.interfaces) >= 2, 'Should be at least two interfaces, got: %s' % len(self.interfaces) + assert isinstance(include_lcores, list), 'include_lcores should be list, got: %s' % type(include_lcores) + assert isinstance(exclude_lcores, list), 'exclude_lcores should be list, got: %s' % type(exclude_lcores) + assert len(self.interfaces) >= 2, 'Should be at least two interfaces, got: %s' % len(self.interfaces) + if only_first_thread: + for cores in self.cpu_topology.values(): + for core in cores.keys(): + cores[core] = cores[core][:1] + include_lcores = [int(x) for x in include_lcores] + exclude_lcores = [int(x) for x in exclude_lcores] + self.has_zero_lcore = False + for numa, cores in self.cpu_topology.items(): + for core, lcores in cores.items(): + for lcore in copy.copy(lcores): + if include_lcores and lcore not in include_lcores: + cores[core].remove(lcore) + if exclude_lcores and lcore in exclude_lcores: + cores[core].remove(lcore) + if 0 in lcores: + self.has_zero_lcore = True + cores[core].remove(0) + zero_lcore_numa = numa + zero_lcore_core = core + zero_lcore_siblings = cores[core] + if self.has_zero_lcore: + del self.cpu_topology[zero_lcore_numa][zero_lcore_core] + self.cpu_topology[zero_lcore_numa][zero_lcore_core] = zero_lcore_siblings + for interface in self.interfaces: + for mandatory_interface_field in ConfigCreator.mandatory_interface_fields: + if mandatory_interface_field not in interface: + raise DpdkSetup("Expected '%s' field in interface dictionary, got: %s" % (mandatory_interface_field, interface)) + Device_str = self._verify_devices_same_type(self.interfaces) + if '40Gb' in Device_str: + self.speed = 40 + else: + self.speed = 10 + lcores_per_numa = OrderedDict() + system_lcores = int(self.has_zero_lcore) + for numa, core in self.cpu_topology.items(): + for lcores in core.values(): + if numa not in lcores_per_numa: + lcores_per_numa[numa] = [] + lcores_per_numa[numa].extend(lcores) + system_lcores += len(lcores) + minimum_required_lcores = len(self.interfaces) / 2 + 2 + if system_lcores < minimum_required_lcores: + raise DpdkSetup('Your system should have at least %s cores for %s interfaces, and it has: %s.' % + (minimum_required_lcores, len(self.interfaces), system_lcores + (0 if self.has_zero_lcore else 1))) + interfaces_per_numa = defaultdict(int) + for i in range(0, len(self.interfaces), 2): + if self.interfaces[i]['NUMA'] != self.interfaces[i+1]['NUMA'] and not ignore_numa: + raise DpdkSetup('NUMA of each pair of interfaces should be the same. Got NUMA %s for client interface %s, NUMA %s for server interface %s' % + (self.interfaces[i]['NUMA'], self.interfaces[i]['Slot_str'], self.interfaces[i+1]['NUMA'], self.interfaces[i+1]['Slot_str'])) + interfaces_per_numa[self.interfaces[i]['NUMA']] += 2 + self.lcores_per_numa = lcores_per_numa + self.interfaces_per_numa = interfaces_per_numa + self.prefix = prefix + self.zmq_pub_port = zmq_pub_port + self.zmq_rpc_port = zmq_rpc_port + self.ignore_numa = ignore_numa + + @staticmethod + def verify_mac(mac_string): + if not ConfigCreator.mac_re.match(mac_string): + raise DpdkSetup('MAC address should be in format of 12:34:56:78:9a:bc, got: %s' % mac_string) + return mac_string.lower() + + @staticmethod + def _exit_if_bad_ip(ip): + if not ConfigCreator._verify_ip(ip): + raise DpdkSetup("Got bad IP %s" % ip) + + @staticmethod + def _verify_ip(ip): + a = ip.split('.') + if len(a) != 4: + return False + for x in a: + if not x.isdigit(): + return False + i = int(x) + if i < 0 or i > 255: + return False + return True + + @staticmethod + def _verify_devices_same_type(interfaces_list): + Device_str = interfaces_list[0]['Device_str'] + for interface in interfaces_list: + if Device_str != interface['Device_str']: + raise DpdkSetup('Interfaces should be of same type, got:\n\t* %s\n\t* %s' % (Device_str, interface['Device_str'])) + return Device_str + + def create_config(self, filename = None, print_config = False): + config_str = '### Config file generated by dpdk_setup_ports.py ###\n\n' + config_str += '- port_limit: %s\n' % len(self.interfaces) + config_str += ' version: 2\n' + config_str += " interfaces: ['%s']\n" % "', '".join([interface['Slot_str'] for interface in self.interfaces]) + if self.speed > 10: + config_str += ' port_bandwidth_gb: %s\n' % self.speed + if self.prefix: + config_str += ' prefix: %s\n' % self.prefix + if self.zmq_pub_port: + config_str += ' zmq_pub_port: %s\n' % self.zmq_pub_port + if self.zmq_rpc_port: + config_str += ' zmq_rpc_port: %s\n' % self.zmq_rpc_port + config_str += ' port_info:\n' + for index, interface in enumerate(self.interfaces): + if 'ip' in interface: + self._exit_if_bad_ip(interface['ip']) + self._exit_if_bad_ip(interface['def_gw']) + config_str += ' '*6 + '- ip: %s\n' % interface['ip'] + config_str += ' '*8 + 'default_gw: %s\n' % interface['def_gw'] + else: + config_str += ' '*6 + '- dest_mac: %s' % self.verify_mac(interface['dest_mac']) + if interface.get('loopback_dest'): + config_str += " # MAC OF LOOPBACK TO IT'S DUAL INTERFACE\n" + else: + config_str += '\n' + config_str += ' '*8 + 'src_mac: %s\n' % self.verify_mac(interface['src_mac']) + if index % 2: + config_str += '\n' # dual if barrier + if not self.ignore_numa: + config_str += ' platform:\n' + if len(self.interfaces_per_numa.keys()) == 1 and -1 in self.interfaces_per_numa: # VM, use any cores, 1 core per dual_if + lcores_pool = sorted([lcore for lcores in self.lcores_per_numa.values() for lcore in lcores]) + config_str += ' '*6 + 'master_thread_id: %s\n' % (0 if self.has_zero_lcore else lcores_pool.pop()) + config_str += ' '*6 + 'latency_thread_id: %s\n' % lcores_pool.pop(0) + lcores_per_dual_if = int(len(lcores_pool) / len(self.interfaces)) + config_str += ' '*6 + 'dual_if:\n' + for i in range(0, len(self.interfaces), 2): + lcores_for_this_dual_if = [str(lcores_pool.pop(0)) for _ in range(lcores_per_dual_if)] + config_str += ' '*8 + '- socket: 0\n' + config_str += ' '*10 + 'threads: [%s]\n\n' % ','.join(lcores_for_this_dual_if) + else: + # we will take common minimum among all NUMAs, to satisfy all + lcores_per_dual_if = 99 + extra_lcores = 1 if self.has_zero_lcore else 2 + # worst case 3 iterations, to ensure master and "rx" have cores left + while (lcores_per_dual_if * sum(self.interfaces_per_numa.values()) / 2) + extra_lcores > sum([len(lcores) for lcores in self.lcores_per_numa.values()]): + lcores_per_dual_if -= 1 + for numa, cores in self.lcores_per_numa.items(): + if not self.interfaces_per_numa[numa]: + continue + lcores_per_dual_if = min(lcores_per_dual_if, int(2 * len(cores) / self.interfaces_per_numa[numa])) + lcores_pool = copy.deepcopy(self.lcores_per_numa) + # first, allocate lcores for dual_if section + dual_if_section = ' '*6 + 'dual_if:\n' + for i in range(0, len(self.interfaces), 2): + numa = self.interfaces[i]['NUMA'] + dual_if_section += ' '*8 + '- socket: %s\n' % numa + lcores_for_this_dual_if = [str(lcores_pool[numa].pop(0)) for _ in range(lcores_per_dual_if)] + if not lcores_for_this_dual_if: + raise DpdkSetup('Not enough cores at NUMA %s. This NUMA has %s processing units and %s interfaces.' % (numa, len(self.lcores_per_numa[numa]), self.interfaces_per_numa[numa])) + dual_if_section += ' '*10 + 'threads: [%s]\n\n' % ','.join(lcores_for_this_dual_if) + # take the cores left to master and rx + lcores_pool_left = [lcore for lcores in lcores_pool.values() for lcore in lcores] + config_str += ' '*6 + 'master_thread_id: %s\n' % (0 if self.has_zero_lcore else lcores_pool_left.pop(0)) + config_str += ' '*6 + 'latency_thread_id: %s\n' % lcores_pool_left.pop(0) + # add the dual_if section + config_str += dual_if_section + + # verify config is correct YAML format + try: + yaml.safe_load(config_str) + except Exception as e: + raise DpdkSetup('Could not create correct yaml config.\nGenerated YAML:\n%s\nEncountered error:\n%s' % (config_str, e)) + + if print_config: + print(config_str) + if filename: + if os.path.exists(filename): + if not dpdk_nic_bind.confirm('File %s already exist, overwrite? (y/N)' % filename): + print('Skipping.') + return config_str + with open(filename, 'w') as f: + f.write(config_str) + print('Saved.') + return config_str + + +class map_driver(object): + args=None; + cfg_file='/etc/trex_cfg.yaml' + parent_cfg = None + dump_interfaces = None + +class DpdkSetup(Exception): + pass + +class CIfMap: + + def __init__(self, cfg_file): + self.m_cfg_file =cfg_file; + self.m_cfg_dict={}; + self.m_devices={}; + + def dump_error (self,err): + s="""%s +From this TRex version a configuration file must exist in /etc/ folder " +The name of the configuration file should be /etc/trex_cfg.yaml " +The minimum configuration file should include something like this +- version : 2 # version 2 of the configuration file + interfaces : ["03:00.0","03:00.1","13:00.1","13:00.0"] # list of the interfaces to bind run ./dpdk_nic_bind.py --status to see the list + port_limit : 2 # number of ports to use valid is 2,4,6,8,10,12 + +example of already bind devices + +$ ./dpdk_nic_bind.py --status + +Network devices using DPDK-compatible driver +============================================ +0000:03:00.0 '82599ES 10-Gigabit SFI/SFP+ Network Connection' drv=igb_uio unused= +0000:03:00.1 '82599ES 10-Gigabit SFI/SFP+ Network Connection' drv=igb_uio unused= +0000:13:00.0 '82599ES 10-Gigabit SFI/SFP+ Network Connection' drv=igb_uio unused= +0000:13:00.1 '82599ES 10-Gigabit SFI/SFP+ Network Connection' drv=igb_uio unused= + +Network devices using kernel driver +=================================== +0000:02:00.0 '82545EM Gigabit Ethernet Controller (Copper)' if=eth2 drv=e1000 unused=igb_uio *Active* + +Other network devices +===================== + + + """ % (err); + return s; + + + def raise_error (self,err): + s= self.dump_error (err) + raise DpdkSetup(s) + + def load_config_file (self): + + fcfg=self.m_cfg_file + + if not os.path.isfile(fcfg) : + self.raise_error ("There is no valid configuration file %s " % fcfg) + + try: + stream = open(fcfg, 'r') + self.m_cfg_dict= yaml.safe_load(stream) + except Exception as e: + print(e); + raise e + + stream.close(); + cfg_dict = self.m_cfg_dict[0] + if 'version' not in cfg_dict: + self.raise_error ("Configuration file %s is old, should include version field\n" % fcfg ) + + if int(cfg_dict['version'])<2 : + self.raise_error ("Configuration file %s is old, should include version field with value greater than 2\n" % fcfg) + + if 'interfaces' not in self.m_cfg_dict[0]: + self.raise_error ("Configuration file %s is old, should include interfaces field even number of elemets" % fcfg) + + if_list=self.m_cfg_dict[0]['interfaces'] + l=len(if_list); + if (l>20): + self.raise_error ("Configuration file %s should include interfaces field with maximum of number of elemets" % (fcfg,l)) + if ((l % 2)==1): + self.raise_error ("Configuration file %s should include even number of interfaces " % (fcfg,l)) + if 'port_limit' in cfg_dict and cfg_dict['port_limit'] > len(if_list): + self.raise_error ('Error: port_limit should not be higher than number of interfaces in config file: %s\n' % fcfg) + + + def do_bind_one (self,key): + cmd='%s dpdk_nic_bind.py --bind=igb_uio %s ' % (sys.executable, key) + print(cmd) + res=os.system(cmd); + if res!=0: + raise DpdkSetup('') + + + + def pci_name_to_full_name (self,pci_name): + c='[0-9A-Fa-f]'; + sp='[:]' + s_short=c+c+sp+c+c+'[.]'+c; + s_full=c+c+c+c+sp+s_short + re_full = re.compile(s_full) + re_short = re.compile(s_short) + + if re_short.match(pci_name): + return '0000:'+pci_name + + if re_full.match(pci_name): + return pci_name + + err=" %s is not a valid pci address \n" %pci_name; + raise DpdkSetup(err) + + + def run_dpdk_lspci (self): + dpdk_nic_bind.get_nic_details() + self.m_devices= dpdk_nic_bind.devices + + def do_run (self): + self.run_dpdk_lspci () + if map_driver.dump_interfaces is None or (map_driver.dump_interfaces == [] and map_driver.parent_cfg): + self.load_config_file() + if_list=self.m_cfg_dict[0]['interfaces'] + else: + if_list = map_driver.dump_interfaces + if not if_list: + for dev in self.m_devices.values(): + if dev.get('Driver_str') in dpdk_nic_bind.dpdk_drivers: + if_list.append(dev['Slot']) + + if_list = list(map(self.pci_name_to_full_name, if_list)) + for key in if_list: + if key not in self.m_devices: + err=" %s does not exist " %key; + raise DpdkSetup(err) + + + if 'Driver_str' in self.m_devices[key]: + if self.m_devices[key]['Driver_str'] not in dpdk_nic_bind.dpdk_drivers : + self.do_bind_one (key) + else: + self.do_bind_one (key) + + if if_list and map_driver.args.parent and dpdk_nic_bind.get_igb_uio_usage(): + pid = dpdk_nic_bind.get_pid_using_pci(if_list) + if pid: + cmdline = dpdk_nic_bind.read_pid_cmdline(pid) + print('Some or all of given interfaces are in use by following process:\npid: %s, cmd: %s' % (pid, cmdline)) + if not dpdk_nic_bind.confirm('Ignore and proceed (y/N):'): + sys.exit(1) + else: + print('WARNING: Some other program is using DPDK driver.\nIf it is TRex and you did not configure it for dual run, current command will fail.') + + def do_return_to_linux(self): + if not self.m_devices: + self.run_dpdk_lspci() + dpdk_interfaces = [] + for device in self.m_devices.values(): + if device.get('Driver_str') in dpdk_nic_bind.dpdk_drivers: + dpdk_interfaces.append(device['Slot']) + if not dpdk_interfaces: + print('No DPDK bound interfaces.') + return + if dpdk_nic_bind.get_igb_uio_usage(): + pid = dpdk_nic_bind.get_pid_using_pci(dpdk_interfaces) + if pid: + cmdline = dpdk_nic_bind.read_pid_cmdline(pid) + print('DPDK interfaces are in use. Unbinding them might cause following process to hang:\npid: %s, cmd: %s' % (pid, cmdline)) + if not dpdk_nic_bind.confirm('Confirm (y/N):'): + return + drivers_table = { + 'rte_ixgbe_pmd': 'ixgbe', + 'rte_igb_pmd': 'igb', + 'rte_i40e_pmd': 'i40e', + 'rte_em_pmd': 'e1000', + 'rte_vmxnet3_pmd': 'vmxnet3', + 'rte_virtio_pmd': 'virtio-pci', + 'rte_enic_pmd': 'enic', + } + for pci, info in dpdk_nic_bind.get_info_from_trex(dpdk_interfaces).items(): + if pci not in self.m_devices: + raise DpdkSetup('Internal error: PCI %s is not found among devices' % pci) + dev = self.m_devices[pci] + if info['TRex_Driver'] not in drivers_table: + print('Got unknown driver %s, description: %s' % (info['TRex_Driver'], dev['Device_str'])) + else: + print('Returning to Linux %s' % pci) + dpdk_nic_bind.bind_one(pci, drivers_table[info['TRex_Driver']], False) + + def _get_cpu_topology(self): + cpu_topology_file = '/proc/cpuinfo' + # physical processor -> physical core -> logical processing units (threads) + cpu_topology = OrderedDict() + if not os.path.exists(cpu_topology_file): + raise DpdkSetup('File with CPU topology (%s) does not exist.' % cpu_topology_file) + with open(cpu_topology_file) as f: + for lcore in f.read().split('\n\n'): + if not lcore: + continue + lcore_dict = OrderedDict() + for line in lcore.split('\n'): + key, val = line.split(':', 1) + lcore_dict[key.strip()] = val.strip() + if 'processor' not in lcore_dict: + continue + numa = int(lcore_dict.get('physical id', -1)) + if numa not in cpu_topology: + cpu_topology[numa] = OrderedDict() + core = int(lcore_dict.get('core id', lcore_dict['processor'])) + if core not in cpu_topology[numa]: + cpu_topology[numa][core] = [] + cpu_topology[numa][core].append(int(lcore_dict['processor'])) + if not cpu_topology: + raise DpdkSetup('Could not determine CPU topology from %s' % cpu_topology_file) + return cpu_topology + + # input: list of different descriptions of interfaces: index, pci, name etc. + # Binds to dpdk wanted interfaces, not bound to any driver. + # output: list of maps of devices in dpdk_* format (self.m_devices.values()) + def _get_wanted_interfaces(self, input_interfaces, get_macs = True): + if type(input_interfaces) is not list: + raise DpdkSetup('type of input interfaces should be list') + if not len(input_interfaces): + raise DpdkSetup('Please specify interfaces to use in the config') + if len(input_interfaces) % 2: + raise DpdkSetup('Please specify even number of interfaces') + wanted_interfaces = [] + sorted_pci = sorted(self.m_devices.keys()) + for interface in input_interfaces: + dev = None + try: + interface = int(interface) + if interface < 0 or interface >= len(sorted_pci): + raise DpdkSetup('Index of an interfaces should be in range 0:%s' % (len(sorted_pci) - 1)) + dev = self.m_devices[sorted_pci[interface]] + except ValueError: + for d in self.m_devices.values(): + if interface in (d['Interface'], d['Slot'], d['Slot_str']): + dev = d + break + if not dev: + raise DpdkSetup('Could not find information about this interface: %s' % interface) + if dev in wanted_interfaces: + raise DpdkSetup('Interface %s is specified twice' % interface) + dev['Interface_argv'] = interface + wanted_interfaces.append(dev) + + if get_macs: + unbound = [] + dpdk_bound = [] + for interface in wanted_interfaces: + if 'Driver_str' not in interface: + unbound.append(interface['Slot']) + elif interface.get('Driver_str') in dpdk_nic_bind.dpdk_drivers: + dpdk_bound.append(interface['Slot']) + if unbound or dpdk_bound: + for pci, info in dpdk_nic_bind.get_info_from_trex(unbound + dpdk_bound).items(): + if pci not in self.m_devices: + raise DpdkSetup('Internal error: PCI %s is not found among devices' % pci) + self.m_devices[pci].update(info) + + return wanted_interfaces + + def do_create(self): + + ips = map_driver.args.ips + def_gws = map_driver.args.def_gws + dest_macs = map_driver.args.dest_macs + if map_driver.args.force_macs: + ip_config = False + if ips: + raise DpdkSetup("If using --force-macs, should not specify ips") + if def_gws: + raise DpdkSetup("If using --force-macs, should not specify default gateways") + elif ips: + ip_config = True + if not def_gws: + raise DpdkSetup("If specifying ips, must specify also def-gws") + if dest_macs: + raise DpdkSetup("If specifying ips, should not specify dest--macs") + if len(ips) != len(def_gws) or len(ips) != len(map_driver.args.create_interfaces): + raise DpdkSetup("Number of given IPs should equal number of given def-gws and number of interfaces") + else: + if dest_macs: + ip_config = False + else: + ip_config = True + + # gather info about NICS from dpdk_nic_bind.py + if not self.m_devices: + self.run_dpdk_lspci() + wanted_interfaces = self._get_wanted_interfaces(map_driver.args.create_interfaces, get_macs = not ip_config) + + for i, interface in enumerate(wanted_interfaces): + dual_index = i + 1 - (i % 2) * 2 + if ip_config: + if isinstance(ips, list) and len(ips) > i: + interface['ip'] = ips[i] + else: + interface['ip'] = ".".join(list(str(i+1))*4) + if isinstance(def_gws, list) and len(def_gws) > i: + interface['def_gw'] = def_gws[i] + else: + interface['def_gw'] = ".".join(list(str(dual_index+1))*4) + else: + dual_if = wanted_interfaces[dual_index] + if 'MAC' not in interface: + raise DpdkSetup('Could not determine MAC of interface: %s. Please verify with -t flag.' % interface['Interface_argv']) + if 'MAC' not in dual_if: + raise DpdkSetup('Could not determine MAC of interface: %s. Please verify with -t flag.' % dual_if['Interface_argv']) + interface['src_mac'] = interface['MAC'] + if isinstance(dest_macs, list) and len(dest_macs) > i: + interface['dest_mac'] = dest_macs[i] + else: + interface['dest_mac'] = dual_if['MAC'] + interface['loopback_dest'] = True + + config = ConfigCreator(self._get_cpu_topology(), wanted_interfaces, include_lcores = map_driver.args.create_include, exclude_lcores = map_driver.args.create_exclude, + only_first_thread = map_driver.args.no_ht, ignore_numa = map_driver.args.ignore_numa, + prefix = map_driver.args.prefix, zmq_rpc_port = map_driver.args.zmq_rpc_port, zmq_pub_port = map_driver.args.zmq_pub_port) + config.create_config(filename = map_driver.args.o, print_config = map_driver.args.dump) + + def do_interactive_create(self): + ignore_numa = False + cpu_topology = self._get_cpu_topology() + total_lcores = sum([len(lcores) for cores in cpu_topology.values() for lcores in cores.values()]) + if total_lcores < 1: + raise DpdkSetup('Script could not determine number of cores of the system, exiting.') + elif total_lcores < 2: + if dpdk_nic_bind.confirm("You only have 1 core and can't run TRex at all. Ignore and continue? (y/N): "): + ignore_numa = True + else: + sys.exit(1) + elif total_lcores < 3: + if dpdk_nic_bind.confirm("You only have 2 cores and will be able to run only stateful without latency checks.\nIgnore and continue? (y/N): "): + ignore_numa = True + else: + sys.exit(1) + + if map_driver.args.force_macs: + ip_based = False + elif dpdk_nic_bind.confirm("By default, IP based configuration file will be created. Do you want to use MAC based config? (y/N)"): + ip_based = False + else: + ip_based = True + ip_addr_digit = 1 + + if not self.m_devices: + self.run_dpdk_lspci() + dpdk_nic_bind.show_table(get_macs = not ip_based) + print('Please choose even number of interfaces from the list above, either by ID , PCI or Linux IF') + print('Stateful will use order of interfaces: Client1 Server1 Client2 Server2 etc. for flows.') + print('Stateless can be in any order.') + numa = None + for dev in self.m_devices.values(): + if numa is None: + numa = dev['NUMA'] + elif numa != dev['NUMA']: + print('For performance, try to choose each pair of interfaces to be on the same NUMA.') + break + while True: + try: + input = dpdk_nic_bind.read_line('Enter list of interfaces separated by space (for example: 1 3) : ') + create_interfaces = input.replace(',', ' ').replace(';', ' ').split() + wanted_interfaces = self._get_wanted_interfaces(create_interfaces) + ConfigCreator._verify_devices_same_type(wanted_interfaces) + except Exception as e: + print(e) + continue + break + print('') + + for interface in wanted_interfaces: + if interface['Active']: + print('Interface %s is active. Using it by TRex might close ssh connections etc.' % interface['Interface_argv']) + if not dpdk_nic_bind.confirm('Ignore and continue? (y/N): '): + sys.exit(1) + + for i, interface in enumerate(wanted_interfaces): + if not ip_based: + if 'MAC' not in interface: + raise DpdkSetup('Could not determine MAC of interface: %s. Please verify with -t flag.' % interface['Interface_argv']) + interface['src_mac'] = interface['MAC'] + dual_index = i + 1 - (i % 2) * 2 + dual_int = wanted_interfaces[dual_index] + if not ignore_numa and interface['NUMA'] != dual_int['NUMA']: + print('NUMA is different at pair of interfaces: %s and %s. It will reduce performance.' % (interface['Interface_argv'], dual_int['Interface_argv'])) + if dpdk_nic_bind.confirm('Ignore and continue? (y/N): '): + ignore_numa = True + print('') + else: + return + + if ip_based: + if ip_addr_digit % 2 == 0: + dual_ip_digit = ip_addr_digit - 1 + else: + dual_ip_digit = ip_addr_digit + 1 + ip = ".".join(list(str(ip_addr_digit))*4) + def_gw= ".".join(list(str(dual_ip_digit))*4) + ip_addr_digit += 1 + + print("For interface %s, assuming loopback to it's dual interface %s." % (interface['Interface_argv'], dual_int['Interface_argv'])) + if dpdk_nic_bind.confirm("Putting IP %s, default gw %s Change it?(y/N)." % (ip, def_gw)): + while True: + ip = dpdk_nic_bind.read_line('Please enter IP address for interface %s: ' % interface['Interface_argv']) + if not ConfigCreator._verify_ip(ip): + print ("Bad IP address format") + else: + break + while True: + def_gw = dpdk_nic_bind.read_line('Please enter default gateway for interface %s: ' % interface['Interface_argv']) + if not ConfigCreator._verify_ip(def_gw): + print ("Bad IP address format") + else: + break + wanted_interfaces[i]['ip'] = ip + wanted_interfaces[i]['def_gw'] = def_gw + else: + dest_mac = dual_int['MAC'] + loopback_dest = True + print("For interface %s, assuming loopback to it's dual interface %s." % (interface['Interface_argv'], dual_int['Interface_argv'])) + if dpdk_nic_bind.confirm("Destination MAC is %s. Change it to MAC of DUT? (y/N)." % dest_mac): + while True: + input_mac = dpdk_nic_bind.read_line('Please enter new destination MAC of interface %s: ' % interface['Interface_argv']) + try: + if input_mac: + ConfigCreator._convert_mac(input_mac) # verify format + dest_mac = input_mac + loopback_dest = False + else: + print('Leaving the loopback MAC.') + except Exception as e: + print(e) + continue + break + wanted_interfaces[i]['dest_mac'] = dest_mac + wanted_interfaces[i]['loopback_dest'] = loopback_dest + + config = ConfigCreator(cpu_topology, wanted_interfaces, include_lcores = map_driver.args.create_include, exclude_lcores = map_driver.args.create_exclude, + only_first_thread = map_driver.args.no_ht, ignore_numa = map_driver.args.ignore_numa or ignore_numa, + prefix = map_driver.args.prefix, zmq_rpc_port = map_driver.args.zmq_rpc_port, zmq_pub_port = map_driver.args.zmq_pub_port) + if dpdk_nic_bind.confirm('Print preview of generated config? (Y/n)', default = True): + config.create_config(print_config = True) + if dpdk_nic_bind.confirm('Save the config to file? (Y/n)', default = True): + print('Default filename is /etc/trex_cfg.yaml') + filename = dpdk_nic_bind.read_line('Press ENTER to confirm or enter new file: ') + if not filename: + filename = '/etc/trex_cfg.yaml' + config.create_config(filename = filename) + + +def parse_parent_cfg (parent_cfg): + parent_parser = argparse.ArgumentParser(add_help = False) + parent_parser.add_argument('-?', '-h', '--help', dest = 'help', action = 'store_true') + parent_parser.add_argument('--cfg', default='') + parent_parser.add_argument('--dump-interfaces', nargs='*', default=None) + args, _ = parent_parser.parse_known_args(shlex.split(parent_cfg)) + if args.help: + sys.exit(0) + return (args.cfg, args.dump_interfaces) + +def process_options (): + parser = argparse.ArgumentParser(usage=""" + +Examples: +--------- + +To return to Linux the DPDK bound interfaces (for ifconfig etc.) + sudo ./dpdk_set_ports.py -l + +To create TRex config file using interactive mode + sudo ./dpdk_set_ports.py -l + +To create a default config file (example1) + sudo ./dpdk_setup_ports.py -c 02:00.0 02:00.1 -o /etc/trex_cfg.yaml + +To create a default config file (example2) + sudo ./dpdk_setup_ports.py -c eth1 eth2 --dest-macs 11:11:11:11:11:11 22:22:22:22:22:22 --dump + +To show interfaces status + sudo ./dpdk_set_ports.py -s + +To see more detailed info on interfaces (table): + sudo ./dpdk_set_ports.py -t + + """, + description=" unbind dpdk interfaces ", + epilog=" written by hhaim"); + + parser.add_argument("-l", "--linux", action='store_true', + help=""" Return all DPDK interfaces to Linux driver """, + ) + + parser.add_argument("--cfg", + help=""" configuration file name """, + ) + + parser.add_argument("--parent", + help=argparse.SUPPRESS + ) + + parser.add_argument('--dump-pci-description', help=argparse.SUPPRESS, dest='dump_pci_desc', action='store_true') + + parser.add_argument("-i", "--interactive", action='store_true', + help=""" Create TRex config in interactive mode """, + ) + + parser.add_argument("-c", "--create", nargs='*', default=None, dest='create_interfaces', metavar='<interface>', + help="""Try to create a configuration file by specifying needed interfaces by PCI address or Linux names: eth1 etc.""", + ) + + parser.add_argument("--ci", "--cores-include", nargs='*', default=[], dest='create_include', metavar='<cores>', + help="""White list of cores to use. Make sure there is enough for each NUMA.""", + ) + + parser.add_argument("--ce", "--cores-exclude", nargs='*', default=[], dest='create_exclude', metavar='<cores>', + help="""Black list of cores to exclude. Make sure there will be enough for each NUMA.""", + ) + + parser.add_argument("--dump", default=False, action='store_true', + help="""Dump created config to screen.""", + ) + + parser.add_argument("--no-ht", default=False, dest='no_ht', action='store_true', + help="""Use only one thread of each Core in created config yaml (No Hyper-Threading).""", + ) + + parser.add_argument("--dest-macs", nargs='*', default=[], action='store', + help="""Destination MACs to be used in created yaml file. Without them, will assume loopback (0<->1, 2<->3 etc.)""", + ) + + parser.add_argument("--force-macs", default=False, action='store_true', + help="""Use MACs in created config file.""", + ) + + parser.add_argument("--ips", nargs='*', default=[], action='store', + help="""IP addresses to be used in created yaml file. Without them, will assume loopback (0<->1, 2<->3 etc.)""", + ) + + parser.add_argument("--def-gws", nargs='*', default=[], action='store', + help="""Default gateways to be used in created yaml file. Without them, will assume loopback (0<->1, 2<->3 etc.)""", + ) + + parser.add_argument("-o", default=None, action='store', metavar='PATH', + help="""Output the config to this file.""", + ) + + parser.add_argument("--prefix", default=None, action='store', + help="""Advanced option: prefix to be used in TRex config in case of parallel instances.""", + ) + + parser.add_argument("--zmq-pub-port", default=None, action='store', + help="""Advanced option: ZMQ Publisher port to be used in TRex config in case of parallel instances.""", + ) + + parser.add_argument("--zmq-rpc-port", default=None, action='store', + help="""Advanced option: ZMQ RPC port to be used in TRex config in case of parallel instances.""", + ) + + parser.add_argument("--ignore-numa", default=False, action='store_true', + help="""Advanced option: Ignore NUMAs for config creation. Use this option only if you have to, as it will reduce performance.""", + ) + + parser.add_argument("-s", "--show", action='store_true', + help=""" show the status """, + ) + + parser.add_argument("-t", "--table", action='store_true', + help=""" show table with NICs info """, + ) + + parser.add_argument('--version', action='version', + version="0.2" ) + + map_driver.args = parser.parse_args(); + if map_driver.args.parent : + map_driver.parent_cfg, map_driver.dump_interfaces = parse_parent_cfg (map_driver.args.parent) + if map_driver.parent_cfg != '': + map_driver.cfg_file = map_driver.parent_cfg; + if map_driver.args.cfg : + map_driver.cfg_file = map_driver.args.cfg; + +def main (): + try: + if getpass.getuser() != 'root': + raise DpdkSetup('Please run this program as root/with sudo') + + process_options () + + if map_driver.args.show: + dpdk_nic_bind.show_status() + return + + if map_driver.args.table: + dpdk_nic_bind.show_table() + return + + if map_driver.args.dump_pci_desc: + dpdk_nic_bind.dump_pci_description() + return + + obj =CIfMap(map_driver.cfg_file); + + if map_driver.args.create_interfaces is not None: + obj.do_create(); + elif map_driver.args.interactive: + obj.do_interactive_create(); + elif map_driver.args.linux: + obj.do_return_to_linux(); + else: + obj.do_run(); + print('') + except DpdkSetup as e: + print(e) + exit(-1) + +if __name__ == '__main__': + main() + |