def __init__(self, deployment, pod_class): """Sets up the POD infrastructure based on deployment scenario :param pod_class: The POD class to be used. """ # reset POD ID counter for each testcase IPod.reset_pod_counter() pod_number = 0 # setup controller with requested number of pods self._logger = logging.getLogger(__name__) self._pod_class = pod_class self._deployment = deployment.lower() self._pods = [] single_pods = ['pcp', 'c2c'] two_pods = ['pccp', 'c2c2c'] if any(item in self._deployment for item in single_pods): pod_number = 1 elif any(item in self._deployment for item in two_pods): pod_number = 2 print("POD COUNTING DONE") settings.setValue('POD_COUNT', pod_number) # we will have single controller for all pods if pod_number: self._pods.append(pod_class()) self._logger.debug('Initializing the pod')
def terminate_task(pid, signal='-15', sleep=10, logger=None): """Terminate process with given pid Function will sent given signal to the process. In case that process will not terminate within given sleep interval and signal was not SIGKILL, then process will be killed by SIGKILL. :param pid: Process ID to terminate :param signal: Signal to be sent to the process :param sleep: Maximum delay in seconds after signal is sent :param logger: Logger to write details to """ if systeminfo.pid_isalive(pid): run_task(['sudo', 'kill', signal, str(pid)], logger) logger.debug('Wait for process %s to terminate after signal %s', pid, signal) for dummy in range(sleep): time.sleep(1) if not systeminfo.pid_isalive(pid): break if signal.lstrip('-').upper() not in ( '9', 'KILL', 'SIGKILL') and systeminfo.pid_isalive(pid): terminate_task(pid, '-9', sleep, logger) pids = settings.getValue('_EXECUTED_PIDS') if pid in pids: pids.remove(pid) settings.setValue('_EXECUTED_PIDS', pids)
def terminate_all_tasks(logger): """Terminate all processes executed by vsperf, just for case they were not terminated by standard means. """ pids = settings.getValue('_EXECUTED_PIDS') if pids: logger.debug('Following processes will be terminated: %s', pids) for pid in pids: terminate_task_subtree(pid, logger=logger) settings.setValue('_EXECUTED_PIDS', [])
def __init__(self, deployment, vnf_class, extra_vnfs): """Sets up the VNF infrastructure based on deployment scenario :param vnf_class: The VNF class to be used. :param extra_vnfs: The number of VNFs not involved in given deployment scenario. It will be used to correctly expand configuration values and initialize shared dirs. This parameter is used in case, that additional VNFs are executed by TestSteps. """ # reset VNF ID counter for each testcase IVnf.reset_vnf_counter() # setup controller with requested number of VNFs self._logger = logging.getLogger(__name__) self._vnf_class = vnf_class self._deployment = deployment.lower() self._vnfs = [] if self._deployment == 'pvp': vm_number = 1 elif (self._deployment.startswith('pvvp') or self._deployment.startswith('pvpv')): if len(self._deployment) > 4: vm_number = int(self._deployment[4:]) else: vm_number = 2 else: # VnfController is created for all deployments, including deployments # without VNFs like p2p vm_number = 0 if vm_number + extra_vnfs > 0: self._logger.debug('Check configuration for %s guests.', vm_number + extra_vnfs) settings.check_vm_settings(vm_number + extra_vnfs) # enforce that GUEST_NIC_NR is 1 or even number of NICs updated = False nics_nr = settings.getValue('GUEST_NICS_NR') for index, value in enumerate(nics_nr): if value > 1 and value % 2: updated = True nics_nr[index] = int(value / 2) * 2 if updated: settings.setValue('GUEST_NICS_NR', nics_nr) self._logger.warning( 'Odd number of NICs was detected. Configuration ' 'was updated to GUEST_NICS_NR = %s', settings.getValue('GUEST_NICS_NR')) if vm_number: self._vnfs = [vnf_class() for _ in range(vm_number)] self._logger.debug('__init__ ' + str(len(self._vnfs)) + ' VNF[s] with ' + ' '.join(map(str, self._vnfs)))
def _update_settings(self, param, value): """ Check value of given configuration parameter In case that new value is different, then testcase specific settings is updated and original value stored :param param: Name of parameter inside settings :param value: Disired parameter value """ orig_value = S.getValue(param) if orig_value != value: self._settings_original[param] = copy.deepcopy(orig_value) S.setValue(param, value)
def main(): """Main function. """ #args = parse_arguments() # define the timestamp to be used by logs and results date = datetime.datetime.fromtimestamp(time.time()) timestamp = date.strftime('%Y-%m-%d_%H-%M-%S') S.setValue('LOG_TIMESTAMP', timestamp) # configure settings S.load_from_dir(os.path.join(_CURR_DIR, 'conf')) openstack_params = utils.pack_openstack_params() print(openstack_params) output = act() print(output)
def run(self): """Run the test All setup and teardown through controllers is included. """ # prepare test execution environment self.run_initialize() try: with self._vswitch_ctl, self._loadgen: with self._vnf_ctl, self._collector: if not self._vswitch_none: self._add_flows() self._versions += self._vswitch_ctl.get_vswitch( ).get_version() with self._traffic_ctl: # execute test based on TestSteps definition if needed... if self.step_run(): # ...and continue with traffic generation, but keep # in mind, that clean deployment does not configure # OVS nor executes the traffic if self.deployment != 'clean': self._traffic_ctl.send_traffic(self._traffic) # dump vswitch flows before they are affected by VNF termination if not self._vswitch_none: self._vswitch_ctl.dump_vswitch_flows() # garbage collection for case that TestSteps modify existing deployment self.step_stop_vnfs() finally: # tear down test execution environment and log results self.run_finalize() self._testcase_run_time = time.strftime( "%H:%M:%S", time.gmtime(time.time() - self._testcase_start_time)) logging.info("Testcase execution time: " + self._testcase_run_time) # report test results self.run_report() # restore original settings for key in self._settings_original: S.setValue(key, self._settings_original[key])
def update_vsperf_configuration(agents): """ Create Configuration file for VSPERF. """ tgen = S.getValue('TRAFFICGEN') east_chassis_ip = agents[0]['public_ip'] # east_data_ip = agents[0]['private_ip'] if len(agents) == 2: west_chassis_ip = agents[1]['public_ip'] # west_data_ip = agents[1]['private_ip'] else: west_chassis_ip = east_chassis_ip # west_data_ip = east_chassis_ip if "TestCenter" in tgen: S.setValue('TRAFFICGEN_STC_EAST_CHASSIS_ADDR', east_chassis_ip) S.setValue('TRAFFICGEN_STC_WEST_CHASSIS_ADDR', west_chassis_ip) if "Ix" in tgen: S.setValue("TRAFFICGEN_EAST_IXIA_HOST", east_chassis_ip) S.setValue("TRAFFICGEN_WEST_IXIA_HOST", west_chassis_ip)
def _create_throughput_result(root): """ Create the results based off the output xml file from the Xena2544.exe execution :param root: root dictionary from xml import :return: Results Ordered dictionary based off ResultsConstants """ # get the test type from the report file test_type = root[0][1].get('TestType') # set the version from the report file settings.setValue('XENA_VERSION', root[0][0][1].get('GeneratedBy')) if test_type == 'Throughput': results = OrderedDict() results[ResultsConstants.THROUGHPUT_RX_FPS] = float( root[0][1][0][0].get('PortRxPps')) + float( root[0][1][0][1].get('PortRxPps')) results[ResultsConstants.THROUGHPUT_RX_MBPS] = (float( root[0][1][0][0].get('PortRxBpsL1')) + float( root[0][1][0][1].get('PortRxBpsL1')))/ 1000000 results[ResultsConstants.THROUGHPUT_RX_PERCENT] = ( 100 - float(root[0][1][0].get('TotalLossRatioPcnt'))) * float( root[0][1][0].get('TotalTxRatePcnt'))/100 results[ResultsConstants.TX_RATE_FPS] = root[0][1][0].get( 'TotalTxRateFps') results[ResultsConstants.TX_RATE_MBPS] = float( root[0][1][0].get('TotalTxRateBpsL1')) / 1000000 results[ResultsConstants.TX_RATE_PERCENT] = root[0][1][0].get( 'TotalTxRatePcnt') try: results[ResultsConstants.MIN_LATENCY_NS] = float( root[0][1][0][0].get('MinLatency')) * 1000 except ValueError: # Stats for latency returned as N/A so just post them results[ResultsConstants.MIN_LATENCY_NS] = root[0][1][0][0].get( 'MinLatency') try: results[ResultsConstants.MAX_LATENCY_NS] = float( root[0][1][0][0].get('MaxLatency')) * 1000 except ValueError: # Stats for latency returned as N/A so just post them results[ResultsConstants.MAX_LATENCY_NS] = root[0][1][0][0].get( 'MaxLatency') try: results[ResultsConstants.AVG_LATENCY_NS] = float( root[0][1][0][0].get('AvgLatency')) * 1000 except ValueError: # Stats for latency returned as N/A so just post them results[ResultsConstants.AVG_LATENCY_NS] = root[0][1][0][0].get( 'AvgLatency') elif test_type == 'Back2Back': results = OrderedDict() # Just mimic what Ixia does and only return the b2b frame count. # This may change later once its decided the common results stats # to be returned should be. results[ResultsConstants.B2B_FRAMES] = root[0][1][0][0].get( 'TotalTxBurstFrames') else: raise NotImplementedError('Unknown test type in report file.') return results
def send_rfc2544_throughput(self, traffic=None, tests=1, duration=20, lossrate=0.0): """Send traffic per RFC2544 throughput test specifications. See ITrafficGenerator for description """ self._duration = duration self._params.clear() self._params['traffic'] = self.traffic_defaults.copy() if traffic: self._params['traffic'] = merge_spec(self._params['traffic'], traffic) self._setup_json_config(tests, lossrate, '2544_throughput') self._start_xena_2544() self._wait_xena_2544_complete() root = ET.parse(os.path.join(_CURR_DIR, "xena2544-report.xml")).getroot() if settings.getValue('TRAFFICGEN_XENA_RFC2544_VERIFY'): # make sure we have a pass before even trying the verify. No need # to run verify on a failed iteration. root = ET.parse( os.path.join(_CURR_DIR, "xena2544-report.xml")).getroot() if root[0][1][0].get('TestState') == "FAIL": self._logger.info('Test failed, skipping verify') return Xena._create_throughput_result(root) # record the previous settings so we can revert to them if needed to # run the binary search again if the verify fails. old_tests = tests old_duration = self._duration old_min = settings.getValue('TRAFFICGEN_XENA_2544_TPUT_MIN_VALUE') # record the original values to restore after execution orig_min = settings.getValue('TRAFFICGEN_XENA_2544_TPUT_MIN_VALUE') orig_max = settings.getValue('TRAFFICGEN_XENA_2544_TPUT_MAX_VALUE') orig_init = settings.getValue('TRAFFICGEN_XENA_2544_TPUT_INIT_VALUE') for attempt in range( 1, settings.getValue( 'TRAFFICGEN_XENA_RFC2544_MAXIMUM_VERIFY_ATTEMPTS')+1): self._logger.info('Running verify attempt %s', attempt) # get the last pass tx rate from the binary search pass_rate = float(root[0][1][0].get('TotalTxRatePcnt')) # run a one pass rfc2544 with the pass rate to see if it passes # the verify duration settings.setValue( 'TRAFFICGEN_XENA_2544_TPUT_INIT_VALUE', pass_rate) settings.setValue( 'TRAFFICGEN_XENA_2544_TPUT_MIN_VALUE', pass_rate) settings.setValue( 'TRAFFICGEN_XENA_2544_TPUT_MAX_VALUE', pass_rate) self.start_rfc2544_throughput( traffic, 1, settings.getValue( 'TRAFFICGEN_XENA_RFC2544_VERIFY_DURATION'), lossrate) self.wait_rfc2544_throughput() root = ET.parse( os.path.join(_CURR_DIR, "xena2544-report.xml")).getroot() # If it passed, report the number of lost frames and exit the # loop if root[0][1][0].get('TestState') == "PASS": self._logger.info('Verify passed, packets lost = %s', root[0][1][0].get('TotalLossFrames')) break elif attempt < settings.getValue( 'TRAFFICGEN_XENA_RFC2544_MAXIMUM_VERIFY_ATTEMPTS'): self._logger.info( 'Verify failed, resuming binary search, packets lost = %s', root[0][1][0].get('TotalLossFrames')) settings.setValue( 'TRAFFICGEN_XENA_2544_TPUT_MAX_VALUE', pass_rate - float(settings.getValue( 'TRAFFICGEN_XENA_2544_TPUT_VALUE_RESOLUTION'))) if settings.getValue( 'TRAFFICGEN_XENA_RFC2544_BINARY_RESTART_SMART_SEARCH'): settings.setValue( 'TRAFFICGEN_XENA_2544_TPUT_INIT_VALUE', (pass_rate - float(old_min)) / 2) else: settings.setValue( 'TRAFFICGEN_XENA_2544_TPUT_INIT_VALUE', pass_rate - float(settings.getValue( 'TRAFFICGEN_XENA_2544_TPUT_VALUE_RESOLUTION'))) settings.setValue( 'TRAFFICGEN_XENA_2544_TPUT_MIN_VALUE', old_min) self._logger.debug( 'RFC2544 Initial rate: %s', settings.getValue('TRAFFICGEN_XENA_2544_TPUT_INIT_VALUE')) self._logger.debug( 'RFC2544 Maximum rate: %s', settings.getValue('TRAFFICGEN_XENA_2544_TPUT_MAX_VALUE')) self._logger.debug( 'RFC2544 Minimum rate: %s', settings.getValue('TRAFFICGEN_XENA_2544_TPUT_MIN_VALUE')) self._duration = old_duration self.start_rfc2544_throughput( traffic, old_tests, self._duration, lossrate) self.wait_rfc2544_throughput() root = ET.parse( os.path.join(_CURR_DIR, "xena2544-report.xml")).getroot() else: self._logger.error( 'Maximum number of verify attempts reached. Reporting last result') #restore original values settings.setValue('TRAFFICGEN_XENA_2544_TPUT_MIN_VALUE', orig_min) settings.setValue('TRAFFICGEN_XENA_2544_TPUT_MAX_VALUE', orig_max) settings.setValue('TRAFFICGEN_XENA_2544_TPUT_INIT_VALUE', orig_init) return Xena._create_throughput_result(root)
def _start_traffic_api(self, packet_limit): """ Start the Xena traffic using the socket API driver :param packet_limit: packet limit for stream, set to -1 for no limit :return: None """ if not self.xmanager: self._xsocket = XenaSocketDriver( settings.getValue('TRAFFICGEN_XENA_IP')) self.xmanager = XenaManager( self._xsocket, settings.getValue('TRAFFICGEN_XENA_USER'), settings.getValue('TRAFFICGEN_XENA_PASSWORD')) # for the report file version info ask the chassis directly for its # software versions settings.setValue('XENA_VERSION', 'XENA Socket API - {}'.format( self.xmanager.get_version())) if not self.xmanager.ports: self.xmanager.ports[0] = self.xmanager.add_module_port( settings.getValue('TRAFFICGEN_XENA_MODULE1'), settings.getValue('TRAFFICGEN_XENA_PORT1')) if not self.xmanager.ports[0].reserve_port(): self._logger.error( 'Unable to reserve port 0. Please release Xena Port') if len(self.xmanager.ports) < 2: self.xmanager.ports[1] = self.xmanager.add_module_port( settings.getValue('TRAFFICGEN_XENA_MODULE2'), settings.getValue('TRAFFICGEN_XENA_PORT2')) if not self.xmanager.ports[1].reserve_port(): self._logger.error( 'Unable to reserve port 1. Please release Xena Port') # Clear port configuration for a clean start self.xmanager.ports[0].reset_port() self.xmanager.ports[1].reset_port() if settings.getValue('TRAFFICGEN_XENA_CONT_PORT_LEARNING_ENABLED'): # turn on port learning self.xmanager.ports[0].set_port_learning(1) self.xmanager.ports[1].set_port_learning(1) sleep(settings.getValue('TRAFFICGEN_XENA_CONT_PORT_LEARNING_DURATION')) # turn off port learning self.xmanager.ports[0].set_port_learning(0) self.xmanager.ports[1].set_port_learning(0) sleep(1) self.xmanager.ports[0].clear_stats() self.xmanager.ports[1].clear_stats() # set the port IP from the conf file self.xmanager.ports[0].set_port_ip( settings.getValue('TRAFFICGEN_XENA_PORT0_IP'), settings.getValue('TRAFFICGEN_XENA_PORT0_CIDR'), settings.getValue('TRAFFICGEN_XENA_PORT0_GATEWAY')) self.xmanager.ports[1].set_port_ip( settings.getValue('TRAFFICGEN_XENA_PORT1_IP'), settings.getValue('TRAFFICGEN_XENA_PORT1_CIDR'), settings.getValue('TRAFFICGEN_XENA_PORT1_GATEWAY')) self.xmanager.ports[0].set_port_time_limit(self._duration) self.xmanager.ports[1].set_port_time_limit(self._duration) def setup_stream(stream, port, payload_id, flip_addr=False): """ Helper function to configure streams. :param stream: Stream object from XenaDriver module :param port: Port object from XenaDriver module :param payload_id: payload ID as int :param flip_addr: Boolean if the source and destination addresses should be flipped. :return: None """ stream.set_on() if packet_limit != -1: stream.set_packet_limit(packet_limit) port.set_port_arp_reply(is_on=True) port.set_port_arp_reply(is_on=True, ipv6=True) port.set_port_ping_reply(is_on=True) port.set_port_ping_reply(is_on=True, ipv6=True) stream.set_rate_fraction(int( 10000 * self._params['traffic']['frame_rate'])) stream.set_packet_header(self._build_packet_header( reverse=flip_addr)) stream.set_header_protocol( 'ETHERNET VLAN IP UDP' if self._params['traffic']['vlan'][ 'enabled'] else 'ETHERNET IP UDP') stream.set_packet_length( 'fixed', self._params['traffic']['l2']['framesize'], self._params['traffic']['l2']['framesize']) stream.set_packet_payload('incrementing', '0x00') stream.set_payload_id(payload_id) port.set_port_time_limit(self._duration * 1000000) if self._params['traffic']['l2']['framesize'] == 64: # set micro tpld port.micro_tpld_enable() if self._params['traffic']['multistream']: if self._params['traffic']['stream_type'] == 'L2': modobj = ModSet(mod_src_mac=True, mod_dst_mac=True) elif self._params['traffic']['stream_type'] == 'L3': modobj = ModSet(mod_src_ip=True, mod_dst_ip=True) elif self._params['traffic']['stream_type'] == 'L4': modobj = ModSet(mod_src_port=True, mod_dst_port=True) else: self._logger.error('Invalid segment for multistream. Using L2..') modobj = ModSet(mod_src_mac=True, mod_dst_mac=True) stream.enable_multistream( flows=self._params['traffic']['multistream'], mod_class=modobj) s1_p0 = self.xmanager.ports[0].add_stream() setup_stream(s1_p0, self.xmanager.ports[0], 0) if self._params['traffic']['bidir'] == 'True': s1_p1 = self.xmanager.ports[1].add_stream() setup_stream(s1_p1, self.xmanager.ports[1], 1, flip_addr=True) if not self.xmanager.ports[0].traffic_on(): self._logger.error( "Failure to start port 0. Check settings and retry.") if self._params['traffic']['bidir'] == 'True': if not self.xmanager.ports[1].traffic_on(): self._logger.error( "Failure to start port 1. Check settings and retry.") sleep(self._duration + 5) # the extra 5 seconds is to allow packets in flight to complete # getting results if self._params['traffic']['bidir'] == 'True': # need to aggregate out both ports stats and assign that data self.rx_stats = self.xmanager.ports[1].get_rx_stats() self.tx_stats = self.xmanager.ports[0].get_tx_stats() self.tx_stats.data = aggregate_stats( self.tx_stats.data, self.xmanager.ports[1].get_tx_stats().data) self.rx_stats.data = aggregate_stats( self.rx_stats.data, self.xmanager.ports[0].get_rx_stats().data) else: # no need to aggregate, just grab the appropriate port stats self.tx_stats = self.xmanager.ports[0].get_tx_stats() self.rx_stats = self.xmanager.ports[1].get_rx_stats() sleep(1)
def settings_update_paths(): """ Configure paths to OVS, DPDK and QEMU sources and binaries based on selected vswitch type and src/binary switch. Data are taken from PATHS dictionary and after their processing they are stored inside TOOLS. PATHS dictionary has specific section for 'vswitch', 'qemu' and 'dpdk' Following processing is done for every item: item 'type' - string, which defines the type of paths ('src' or 'bin') to be selected for a given section: 'src' means, that VSPERF will use OVS, DPDK or QEMU built from sources e.g. by execution of systems/build_base_machine.sh script during VSPERF installation 'bin' means, that VSPERF will use OVS, DPDK or QEMU binaries installed in the OS, e.g. via OS specific packaging system item 'path' - string with valid path; Its content is checked for existence, prefixed with section name and stored into TOOLS for later use e.g. TOOLS['dpdk_src'] or TOOLS['vswitch_src'] item 'modules' - list of strings; Every value from given list is checked for '.ko' suffix. In case it matches and it is not an absolute path to the module, then module name is prefixed with 'path' defined for the same section e.g. TOOLS['vswitch_modules'] = [ '/tmp/vsperf/src_vanilla/ovs/ovs/datapath/linux/openvswitch.ko'] all other items - string - if given string is a relative path and item 'path' is defined for a given section, then item content will be prefixed with content of the 'path'. Otherwise tool name will be searched within standard system directories. Also any OS filename wildcards will be expanded to the real path. At the end of processing, every absolute path is checked for its existence. In case that temporary path (i.e. path with '_tmp' suffix) doesn't exist, then log will be written and vsperf will continue. If any other path will not exist, then vsperf execution will be terminated with runtime error. Note: In case that 'bin' type is set for DPDK, then TOOLS['dpdk_src'] will be set to the value of PATHS['dpdk']['src']['path']. The reason is, that VSPERF uses downloaded DPDK sources to copy DPDK and testpmd into the GUEST, where testpmd is built. In case, that DPDK sources are not available, then vsperf will continue with test execution, but testpmd can't be used as a guest loopback. This is useful in case, that other guest loopback applications (e.g. buildin) are used by CI jobs, etc. """ # set dpdk and ovs paths according to VNF, VSWITCH and TRAFFICGEN selection paths = {} if S.getValue("mode") != 'trafficgen': # VSWITCH & (probably) VNF are needed vswitch_type = S.getValue('PATHS')['vswitch'][S.getValue( 'VSWITCH')]['type'] paths['vswitch'] = S.getValue('PATHS')['vswitch'][S.getValue( 'VSWITCH')][vswitch_type] paths['dpdk'] = S.getValue('PATHS')['dpdk'][S.getValue('PATHS')['dpdk'] ['type']] paths['qemu'] = S.getValue('PATHS')['qemu'][S.getValue('PATHS')['qemu'] ['type']] paths['paths'] = {} paths['paths']['ovs_var_tmp'] = S.getValue( 'PATHS')['vswitch']['ovs_var_tmp'] paths['paths']['ovs_etc_tmp'] = S.getValue( 'PATHS')['vswitch']['ovs_etc_tmp'] if S.getValue("mode") != 'trafficgen-off': # TRAFFCIGEN is required if S.getValue('TRAFFICGEN') in S.getValue('PATHS')['trafficgen']: tmp_trafficgen = S.getValue('PATHS')['trafficgen'][S.getValue( 'TRAFFICGEN')] paths['trafficgen'] = tmp_trafficgen[tmp_trafficgen['type']] tools = {} # pylint: disable=too-many-nested-blocks for path_class in paths: for tool in paths[path_class]: tmp_tool = paths[path_class][tool] # store valid path of given class into tools dict if tool == 'path': if os.path.isdir(tmp_tool): tools['{}_src'.format(path_class)] = tmp_tool continue else: raise RuntimeError( 'Path {} does not exist.'.format(tmp_tool)) # store list of modules of given class into tools dict if tool == 'modules': tmp_modules = [] for module in tmp_tool: # add path to the .ko modules and check it for existence if module.endswith('.ko') and not os.path.isabs(module): module = os.path.join(paths[path_class]['path'], module) if not os.path.exists(module): raise RuntimeError( 'Cannot locate modlue {}'.format(module)) tmp_modules.append(module) tools['{}_modules'.format(path_class)] = tmp_modules continue # if path to the tool is relative, then 'path' will be prefixed # in case that 'path' is not defined, then tool will be searched # within standard system paths if not os.path.isabs(tmp_tool): if 'path' in paths[path_class]: tmp_tool = os.path.join(paths[path_class]['path'], tmp_tool) elif shutil.which(tmp_tool): tmp_tool = shutil.which(tmp_tool) else: raise RuntimeError( 'Cannot locate tool {}'.format(tmp_tool)) # expand OS wildcards in paths if needed if glob.has_magic(tmp_tool): tmp_glob = glob.glob(tmp_tool) if not tmp_glob: raise RuntimeError( 'Path to the {} is not valid: {}.'.format( tool, tmp_tool)) elif len(tmp_glob) > 1: raise RuntimeError('Path to the {} is ambiguous {}'.format( tool, tmp_glob)) elif len(tmp_glob) == 1: tmp_tool = tmp_glob[0] elif not os.path.exists(tmp_tool): if tool.endswith('_tmp'): logging.getLogger().debug( 'Temporary path to the %s does not ' 'exist: %s', tool, tmp_tool) else: raise RuntimeError( 'Path to the {} is not valid: {}'.format( tool, tmp_tool)) tools[tool] = tmp_tool # ensure, that dpkg_src for bin will be set to downloaded DPDK sources, so it can # be copied to the guest share dir and used by GUEST to build and run testpmd # Validity of the path is not checked by purpose, so user can use VSPERF without # downloading DPDK sources. In that case guest loopback can't be set to 'testpmd' if S.getValue('PATHS')['dpdk']['type'] == 'bin': tools['dpdk_src'] = S.getValue('PATHS')['dpdk']['src']['path'] S.setValue('TOOLS', tools)
def __init__(self, test_cfg): """Pull out fields from test config :param test_cfg: A dictionary of string-value pairs describing the test configuration. Both the key and values strings use well-known values. :param results_dir: Where the csv formatted results are written. """ # make a local copy of test configuration to avoid modification of # original content used in vsperf main script cfg = copy.deepcopy(test_cfg) self._testcase_start_time = time.time() self._testcase_stop_time = self._testcase_start_time self._hugepages_mounted = False self._traffic_ctl = None self._vnf_ctl = None self._pod_ctl = None self._vswitch_ctl = None self._collector = None self._loadgen = None self._output_file = None self._tc_results = None self._settings_paths_modified = False self._testcast_run_time = None self._versions = [] self._k8s = False # initialization of step driven specific members self._step_check = False # by default don't check result for step driven testcases self._step_vnf_list = {} self._step_result = [] self._step_result_mapping = {} self._step_status = None self._step_send_traffic = False # indication if send_traffic was called within test steps self._vnf_list = [] self._testcase_run_time = None S.setValue('VSWITCH', cfg.get('vSwitch', S.getValue('VSWITCH'))) S.setValue('VNF', cfg.get('VNF', S.getValue('VNF'))) S.setValue('TRAFFICGEN', cfg.get('Trafficgen', S.getValue('TRAFFICGEN'))) S.setValue('TUNNEL_TYPE', cfg.get('Tunnel Type', S.getValue('TUNNEL_TYPE'))) test_params = copy.deepcopy(S.getValue('TEST_PARAMS')) tc_test_params = cfg.get('Parameters', S.getValue('TEST_PARAMS')) test_params = merge_spec(test_params, tc_test_params) # ensure that parameters from TC definition have the highest priority, see MAPPING_TC_CFG2CONF for (cfg_param, param) in MAPPING_TC_CFG2CONF.items(): if cfg_param in cfg and param in test_params: del test_params[param] S.setValue('TEST_PARAMS', test_params) S.check_test_params() # override all redefined GUEST_ values to have them expanded correctly tmp_test_params = copy.deepcopy(S.getValue('TEST_PARAMS')) for key in tmp_test_params: if key.startswith('GUEST_'): S.setValue(key, S.getValue(key)) S.getValue('TEST_PARAMS').pop(key) # update global settings functions.settings_update_paths() # set test parameters; CLI options take precedence to testcase settings self._logger = logging.getLogger(__name__) self.name = cfg['Name'] self.desc = cfg.get('Description', 'No description given.') self.test = cfg.get('TestSteps', None) # log testcase name and details tmp_desc = functions.format_description(self.desc, 50) self._logger.info( '############################################################') self._logger.info('# Test: %s', self.name) self._logger.info('# Details: %s', tmp_desc[0]) for i in range(1, len(tmp_desc)): self._logger.info('# %s', tmp_desc[i]) self._logger.info( '############################################################') bidirectional = S.getValue('TRAFFIC')['bidir'] if not isinstance(S.getValue('TRAFFIC')['bidir'], str): raise TypeError('Bi-dir value must be of type string') bidirectional = bidirectional.title() # Keep things consistent self.deployment = cfg['Deployment'] self._frame_mod = cfg.get('Frame Modification', None) self._tunnel_operation = cfg.get('Tunnel Operation', None) # check if test requires background load and which generator it uses self._load_cfg = cfg.get('Load', None) if self._frame_mod: self._frame_mod = self._frame_mod.lower() self._results_dir = S.getValue('RESULTS_PATH') # set traffic details, so they can be passed to vswitch and traffic ctls self._traffic = copy.deepcopy(S.getValue('TRAFFIC')) self._traffic.update({'bidir': bidirectional}) # Packet Forwarding mode self._vswitch_none = str( S.getValue('VSWITCH')).strip().lower() == 'none' # trafficgen configuration required for tests of tunneling protocols if self._tunnel_operation: self._traffic.update({'tunnel_type': S.getValue('TUNNEL_TYPE')}) self._traffic['l2'].update({ 'srcmac': S.getValue('TRAFFICGEN_PORT1_MAC'), 'dstmac': S.getValue('TRAFFICGEN_PORT2_MAC') }) self._traffic['l3'].update({ 'srcip': S.getValue('TRAFFICGEN_PORT1_IP'), 'dstip': S.getValue('TRAFFICGEN_PORT2_IP') }) if self._tunnel_operation == "decapsulation": self._traffic['l2'].update( S.getValue( S.getValue('TUNNEL_TYPE').upper() + '_FRAME_L2')) self._traffic['l3'].update( S.getValue( S.getValue('TUNNEL_TYPE').upper() + '_FRAME_L3')) self._traffic['l4'].update( S.getValue( S.getValue('TUNNEL_TYPE').upper() + '_FRAME_L4')) self._traffic['l2']['dstmac'] = S.getValue('NICS')[1]['mac'] elif len(S.getValue('NICS')) >= 2 and \ (S.getValue('NICS')[0]['type'] == 'vf' or S.getValue('NICS')[1]['type'] == 'vf'): mac1 = S.getValue('NICS')[0]['mac'] mac2 = S.getValue('NICS')[1]['mac'] if mac1 and mac2: self._traffic['l2'].update({'srcmac': mac2, 'dstmac': mac1}) else: self._logger.debug("MAC addresses can not be read") self._traffic = functions.check_traffic(self._traffic) # count how many VNFs are involved in TestSteps if self.test: for step in self.test: if step[0].startswith('vnf'): self._step_vnf_list[step[0]] = None # if llc allocation is required, initialize it. if S.getValue('LLC_ALLOCATION'): self._rmd = rmd.CacheAllocator()
def update_pids(pid): """update list of running pids, so they can be terminated at the end """ pids = settings.getValue('_EXECUTED_PIDS') pids.append(pid) settings.setValue('_EXECUTED_PIDS', pids)
def __init__(self, test_cfg): """Pull out fields from test config :param test_cfg: A dictionary of string-value pairs describing the test configuration. Both the key and values strings use well-known values. :param results_dir: Where the csv formatted results are written. """ # make a local copy of test configuration to avoid modification of # original content used in vsperf main script cfg = copy.deepcopy(test_cfg) self._testcase_start_time = time.time() self._hugepages_mounted = False self._traffic_ctl = None self._vnf_ctl = None self._vswitch_ctl = None self._collector = None self._loadgen = None self._output_file = None self._tc_results = None self._settings_original = {} self._settings_paths_modified = False self._testcast_run_time = None self._versions = [] # initialization of step driven specific members self._step_check = False # by default don't check result for step driven testcases self._step_vnf_list = {} self._step_result = [] self._step_status = None self._testcase_run_time = None # store all GUEST_ specific settings to keep original values before their expansion for key in S.__dict__: if key.startswith('GUEST_'): self._settings_original[key] = S.getValue(key) self._update_settings('VSWITCH', cfg.get('vSwitch', S.getValue('VSWITCH'))) self._update_settings('VNF', cfg.get('VNF', S.getValue('VNF'))) self._update_settings('TRAFFICGEN', cfg.get('Trafficgen', S.getValue('TRAFFICGEN'))) test_params = copy.deepcopy(S.getValue('TEST_PARAMS')) tc_test_params = cfg.get('Parameters', S.getValue('TEST_PARAMS')) test_params = merge_spec(test_params, tc_test_params) self._update_settings('TEST_PARAMS', test_params) S.check_test_params() # override all redefined GUEST_ values to have them expanded correctly tmp_test_params = copy.deepcopy(S.getValue('TEST_PARAMS')) for key in tmp_test_params: if key.startswith('GUEST_'): S.setValue(key, S.getValue(key)) S.getValue('TEST_PARAMS').pop(key) # update global settings functions.settings_update_paths() # set test parameters; CLI options take precedence to testcase settings self._logger = logging.getLogger(__name__) self.name = cfg['Name'] self.desc = cfg.get('Description', 'No description given.') self.test = cfg.get('TestSteps', None) bidirectional = S.getValue('TRAFFIC')['bidir'] if not isinstance(S.getValue('TRAFFIC')['bidir'], str): raise TypeError('Bi-dir value must be of type string') bidirectional = bidirectional.title() # Keep things consistent self.deployment = cfg['Deployment'] self._frame_mod = cfg.get('Frame Modification', None) self._tunnel_type = None self._tunnel_operation = None if self.deployment == 'op2p': self._tunnel_operation = cfg['Tunnel Operation'] if 'Tunnel Type' in cfg: self._tunnel_type = cfg['Tunnel Type'] self._tunnel_type = get_test_param('TUNNEL_TYPE', self._tunnel_type) # check if test requires background load and which generator it uses self._load_cfg = cfg.get('Load', None) if self._load_cfg and 'tool' in self._load_cfg: self._loadgen = self._load_cfg['tool'] else: # background load is not requested, so use dummy implementation self._loadgen = "Dummy" if self._frame_mod: self._frame_mod = self._frame_mod.lower() self._results_dir = S.getValue('RESULTS_PATH') # set traffic details, so they can be passed to vswitch and traffic ctls self._traffic = copy.deepcopy(S.getValue('TRAFFIC')) self._traffic.update({ 'bidir': bidirectional, 'tunnel_type': self._tunnel_type, }) self._traffic = functions.check_traffic(self._traffic) # Packet Forwarding mode self._vswitch_none = str( S.getValue('VSWITCH')).strip().lower() == 'none' # trafficgen configuration required for tests of tunneling protocols if self.deployment == "op2p": self._traffic['l2'].update({ 'srcmac': S.getValue('TRAFFICGEN_PORT1_MAC'), 'dstmac': S.getValue('TRAFFICGEN_PORT2_MAC') }) self._traffic['l3'].update({ 'srcip': S.getValue('TRAFFICGEN_PORT1_IP'), 'dstip': S.getValue('TRAFFICGEN_PORT2_IP') }) if self._tunnel_operation == "decapsulation": self._traffic['l2'] = S.getValue(self._tunnel_type.upper() + '_FRAME_L2') self._traffic['l3'] = S.getValue(self._tunnel_type.upper() + '_FRAME_L3') self._traffic['l4'] = S.getValue(self._tunnel_type.upper() + '_FRAME_L4') elif len(S.getValue('NICS')) and \ (S.getValue('NICS')[0]['type'] == 'vf' or S.getValue('NICS')[1]['type'] == 'vf'): mac1 = S.getValue('NICS')[0]['mac'] mac2 = S.getValue('NICS')[1]['mac'] if mac1 and mac2: self._traffic['l2'].update({'srcmac': mac2, 'dstmac': mac1}) else: self._logger.debug("MAC addresses can not be read") # count how many VNFs are involved in TestSteps if self.test: for step in self.test: if step[0].startswith('vnf'): self._step_vnf_list[step[0]] = None
def create(self): """ Creation Process """ print("Entering Create Function") config.load_kube_config(S.getValue('K8S_CONFIG_FILEPATH')) # create vswitchperf namespace api = client.CoreV1Api() namespace = 'default' pod_manifests = S.getValue('POD_MANIFEST_FILEPATH') pod_count = int(S.getValue('POD_COUNT')) if S.hasValue('POD_NAMESPACE'): namespace = S.getValue('POD_NAMESPACE') else: namespace = 'default' dep_pod_list = [] # sriov configmap if S.getValue('PLUGIN') == 'sriov': configmap = load_manifest(S.getValue('CONFIGMAP_FILEPATH')) self._sriov_config = configmap['metadata']['name'] self._sriov_config_ns = configmap['metadata']['namespace'] api.create_namespaced_config_map(self._sriov_config_ns, configmap) # create nad(network attachent definitions) group = 'k8s.cni.cncf.io' version = 'v1' kind_plural = 'network-attachment-definitions' api = client.CustomObjectsApi() assert pod_count <= len(pod_manifests) for nad_filepath in S.getValue('NETWORK_ATTACHMENT_FILEPATH'): nad_manifest = load_manifest(nad_filepath) try: response = api.create_namespaced_custom_object(group, version, namespace, kind_plural, nad_manifest) self._logger.info(str(response)) self._logger.info("Created Network Attachment Definition: %s", nad_filepath) except ApiException as err: raise Exception from err #create pod workloads api = client.CoreV1Api() for count in range(pod_count): dep_pod_info = {} pod_manifest = load_manifest(pod_manifests[count]) dep_pod_info['name'] = pod_manifest["metadata"]["name"] try: response = api.create_namespaced_pod(namespace, pod_manifest) self._logger.info(str(response)) self._logger.info("Created POD %d ...", self._number) except ApiException as err: raise Exception from err # Wait for the pod to start time.sleep(5) status = "Unknown" count = 0 while True: if count == 10: break try: response = api.read_namespaced_pod_status(dep_pod_info['name'], namespace) status = response.status.phase except ApiException as err: raise Exception from err if status in ("Running", "Failed", "Unknown"): break time.sleep(5) count = count + 1 # Now Get the Pod-IP try: response = api.read_namespaced_pod_status(dep_pod_info['name'], namespace) dep_pod_info['pod_ip'] = response.status.pod_ip except ApiException as err: raise Exception from err dep_pod_info['namespace'] = namespace cmd = ['cat', '/etc/podnetinfo/annotations'] output = util.execute_command(api, dep_pod_info, cmd) dep_pod_info['annotations'] = output dep_pod_list.append(dep_pod_info) S.setValue('POD_LIST',dep_pod_list) return dep_pod_list