+ self._tc_results = self._append_results(self._traffic_ctl.get_results())
+ TestCase.write_result_to_file(self._tc_results, self._output_file)
+
+ def run(self):
+ """Run the test
+
+ All setup and teardown through controllers is included.
+ """
+ # prepare test execution environment
+ self.run_initialize()
+
+ with self._vswitch_ctl, self._loadgen:
+ with self._vnf_ctl, self._collector:
+ if not self._vswitch_none:
+ self._add_flows()
+
+ # run traffic generator if requested, otherwise wait for manual termination
+ if S.getValue('mode') == 'trafficgen-off':
+ time.sleep(2)
+ self._logger.debug("All is set. Please run traffic generator manually.")
+ input(os.linesep + "Press Enter to terminate vswitchperf..." + os.linesep + os.linesep)
+ else:
+ if S.getValue('mode') == 'trafficgen-pause':
+ time.sleep(2)
+ true_vals = ('yes', 'y', 'ye', None)
+ while True:
+ choice = input(os.linesep + 'Transmission paused, should'
+ ' transmission be resumed? ' + os.linesep).lower()
+ if not choice or choice not in true_vals:
+ print('Please respond with \'yes\' or \'y\' ', end='')
+ else:
+ break
+ with self._traffic_ctl:
+ 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()
+
+ # 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()
+
+ 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] = orig_value
+ S.setValue(param, value)
+
+ def _append_results(self, results):
+ """
+ Method appends mandatory Test Case results to list of dictionaries.
+
+ :param results: list of dictionaries which contains results from
+ traffic generator.
+
+ :returns: modified list of dictionaries.
+ """
+ for item in results:
+ item[ResultsConstants.ID] = self.name
+ item[ResultsConstants.DEPLOYMENT] = self.deployment
+ item[ResultsConstants.TRAFFIC_TYPE] = self._traffic['l3']['proto']
+ item[ResultsConstants.TEST_RUN_TIME] = self._testcase_run_time
+ if self._traffic['multistream']:
+ item[ResultsConstants.SCAL_STREAM_COUNT] = self._traffic['multistream']
+ item[ResultsConstants.SCAL_STREAM_TYPE] = self._traffic['stream_type']
+ item[ResultsConstants.SCAL_PRE_INSTALLED_FLOWS] = self._traffic['pre_installed_flows']
+ if self._vnf_ctl.get_vnfs_number():
+ item[ResultsConstants.GUEST_LOOPBACK] = ' '.join(S.getValue('GUEST_LOOPBACK'))
+ if self._tunnel_type:
+ item[ResultsConstants.TUNNEL_TYPE] = self._tunnel_type
+ return results
+
+ def _copy_fwd_tools_for_all_guests(self):
+ """Copy dpdk and l2fwd code to GUEST_SHARE_DIR[s] based on selected deployment.
+ """
+ # consider only VNFs involved in the test
+ for guest_dir in set(S.getValue('GUEST_SHARE_DIR')[:self._vnf_ctl.get_vnfs_number()]):
+ self._copy_fwd_tools_for_guest(guest_dir)
+
+ def _copy_fwd_tools_for_guest(self, guest_dir):
+ """Copy dpdk and l2fwd code to GUEST_SHARE_DIR of VM
+
+ :param index: Index of VM starting from 1 (i.e. 1st VM has index 1)
+ """
+ # remove shared dir if it exists to avoid issues with file consistency
+ if os.path.exists(guest_dir):
+ tasks.run_task(['rm', '-f', '-r', guest_dir], self._logger,
+ 'Removing content of shared directory...', True)
+
+ # directory to share files between host and guest
+ os.makedirs(guest_dir)
+
+ # copy sources into shared dir only if neccessary
+ guest_loopback = set(S.getValue('GUEST_LOOPBACK'))
+ if 'testpmd' in guest_loopback or 'l2fwd' in guest_loopback:
+ try:
+ tasks.run_task(['rsync', '-a', '-r', '-l', r'--exclude="\.git"',
+ os.path.join(S.getValue('RTE_SDK_USER'), ''),
+ os.path.join(guest_dir, 'DPDK')],
+ self._logger,
+ 'Copying DPDK to shared directory...',
+ True)
+ tasks.run_task(['rsync', '-a', '-r', '-l',
+ os.path.join(S.getValue('ROOT_DIR'), 'src/l2fwd/'),
+ os.path.join(guest_dir, 'l2fwd')],
+ self._logger,
+ 'Copying l2fwd to shared directory...',
+ True)
+ except subprocess.CalledProcessError:
+ self._logger.error('Unable to copy DPDK and l2fwd to shared directory')
+
+ def _mount_hugepages(self):
+ """Mount hugepages if usage of DPDK or Qemu is detected
+ """
+ # hugepages are needed by DPDK and Qemu
+ if not self._hugepages_mounted and \
+ (self.deployment.count('v') or \
+ S.getValue('VSWITCH').lower().count('dpdk') or \
+ self._vswitch_none or \
+ self.test and 'vnf' in [step[0][0:3] for step in self.test]):
+ hugepages.mount_hugepages()
+ self._hugepages_mounted = True
+
+ def _umount_hugepages(self):
+ """Umount hugepages if they were mounted before
+ """
+ if self._hugepages_mounted:
+ hugepages.umount_hugepages()
+ self._hugepages_mounted = False
+
+ def _check_for_enough_hugepages(self):
+ """Check to make sure enough hugepages are free to satisfy the
+ test environment.
+ """
+ hugepages_needed = 0
+ hugepage_size = hugepages.get_hugepage_size()
+ # get hugepage amounts per guest involved in the test
+ for guest in range(self._vnf_ctl.get_vnfs_number()):
+ hugepages_needed += math.ceil((int(S.getValue(
+ 'GUEST_MEMORY')[guest]) * 1000) / hugepage_size)
+
+ # get hugepage amounts for each socket on dpdk
+ sock0_mem, sock1_mem = 0, 0
+ if S.getValue('VSWITCH').lower().count('dpdk'):
+ # the import below needs to remain here and not put into the module
+ # imports because of an exception due to settings not yet loaded
+ from vswitches import ovs_dpdk_vhost
+ if ovs_dpdk_vhost.OvsDpdkVhost.old_dpdk_config():
+ match = re.search(
+ r'-socket-mem\s+(\d+),(\d+)',
+ ''.join(S.getValue('VSWITCHD_DPDK_ARGS')))
+ if match:
+ sock0_mem, sock1_mem = (int(match.group(1)) * 1024 / hugepage_size,
+ int(match.group(2)) * 1024 / hugepage_size)
+ else:
+ logging.info(
+ 'Could not parse socket memory config in dpdk params.')
+ else:
+ sock0_mem, sock1_mem = (
+ S.getValue(
+ 'VSWITCHD_DPDK_CONFIG')['dpdk-socket-mem'].split(','))
+ sock0_mem, sock1_mem = (int(sock0_mem) * 1024 / hugepage_size,
+ int(sock1_mem) * 1024 / hugepage_size)
+
+ # If hugepages needed, verify the amounts are free
+ if any([hugepages_needed, sock0_mem, sock1_mem]):
+ free_hugepages = hugepages.get_free_hugepages()
+ if hugepages_needed:
+ logging.info('Need %s hugepages free for guests',
+ hugepages_needed)
+ result1 = free_hugepages >= hugepages_needed
+ free_hugepages -= hugepages_needed
+ else:
+ result1 = True
+
+ if sock0_mem:
+ logging.info('Need %s hugepages free for dpdk socket 0',
+ sock0_mem)
+ result2 = hugepages.get_free_hugepages('0') >= sock0_mem
+ free_hugepages -= sock0_mem
+ else:
+ result2 = True
+
+ if sock1_mem:
+ logging.info('Need %s hugepages free for dpdk socket 1',
+ sock1_mem)
+ result3 = hugepages.get_free_hugepages('1') >= sock1_mem
+ free_hugepages -= sock1_mem
+ else:
+ result3 = True
+
+ logging.info('Need a total of {} total hugepages'.format(
+ hugepages_needed + sock1_mem + sock0_mem))
+
+ # The only drawback here is sometimes dpdk doesn't release
+ # its hugepages on a test failure. This could cause a test
+ # to fail when dpdk would be OK to start because it will just
+ # use the previously allocated hugepages.
+ result4 = True if free_hugepages >= 0 else False
+
+ return all([result1, result2, result3, result4])
+ else:
+ return True