diff options
Diffstat (limited to 'testcases/testcase.py')
-rw-r--r-- | testcases/testcase.py | 208 |
1 files changed, 142 insertions, 66 deletions
diff --git a/testcases/testcase.py b/testcases/testcase.py index 0effce75..5f5c9358 100644 --- a/testcases/testcase.py +++ b/testcases/testcase.py @@ -27,6 +27,7 @@ from core.loader import Loader from core.results.results_constants import ResultsConstants from tools import tasks from tools import hugepages +from tools import functions from tools.pkt_gen.trafficgen.trafficgenhelper import TRAFFIC_DEFAULTS from conf import settings as S from conf import get_test_param @@ -36,7 +37,7 @@ class TestCase(object): In this basic form runs RFC2544 throughput test """ - def __init__(self, cfg, results_dir): + def __init__(self, cfg): """Pull out fields from test config :param cfg: A dictionary of string-value pairs describing the test @@ -44,6 +45,7 @@ class TestCase(object): values. :param results_dir: Where the csv formatted results are written. """ + self._testcase_start_time = time.time() self._hugepages_mounted = False self._traffic_ctl = None self._vnf_ctl = None @@ -52,6 +54,27 @@ class TestCase(object): self._loadgen = None self._output_file = None self._tc_results = None + self.guest_loopback = [] + self._settings_original = {} + self._settings_paths_modified = False + self._testcast_run_time = None + + 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'))) + self._update_settings('TEST_PARAMS', cfg.get('Parameters', S.getValue('TEST_PARAMS'))) + + # update global settings + guest_loopback = get_test_param('guest_loopback', None) + if guest_loopback: + self._update_settings('GUEST_LOOPBACK', [guest_loopback for dummy in S.getValue('GUEST_LOOPBACK')]) + + if 'VSWITCH' in self._settings_original or 'VNF' in self._settings_original: + self._settings_original.update({ + 'RTE_SDK' : S.getValue('RTE_SDK'), + 'OVS_DIR' : S.getValue('OVS_DIR'), + }) + functions.settings_update_paths() # set test parameters; CLI options take precedence to testcase settings self._logger = logging.getLogger(__name__) @@ -61,6 +84,10 @@ class TestCase(object): bidirectional = cfg.get('biDirectional', TRAFFIC_DEFAULTS['bidir']) bidirectional = get_test_param('bidirectional', bidirectional) + if not isinstance(bidirectional, str): + raise TypeError( + 'Bi-dir value must be of type string in testcase configuration') + bidirectional = bidirectional.title() # Keep things consistent traffic_type = cfg.get('Traffic Type', TRAFFIC_DEFAULTS['traffic_type']) traffic_type = get_test_param('traffic_type', traffic_type) @@ -82,18 +109,11 @@ class TestCase(object): self._tunnel_type = get_test_param('tunnel_type', self._tunnel_type) - # identify guest loopback method, so it can be added into reports - self.guest_loopback = [] - if self.deployment in ['pvp', 'pvvp']: - guest_loopback = get_test_param('guest_loopback', None) - if guest_loopback: - self.guest_loopback.append(guest_loopback) - else: - if self.deployment == 'pvp': - self.guest_loopback.append(S.getValue('GUEST_LOOPBACK')[0]) - else: - self.guest_loopback = S.getValue('GUEST_LOOPBACK').copy() + if self.deployment == 'pvp': + self.guest_loopback.append(S.getValue('GUEST_LOOPBACK')[0]) + else: + self.guest_loopback = S.getValue('GUEST_LOOPBACK').copy() # read configuration of streams; CLI parameter takes precedence to # testcase definition @@ -114,7 +134,7 @@ class TestCase(object): if self._frame_mod: self._frame_mod = self._frame_mod.lower() - self._results_dir = results_dir + self._results_dir = S.getValue('RESULTS_PATH') # set traffic details, so they can be passed to vswitch and traffic ctls self._traffic = copy.deepcopy(TRAFFIC_DEFAULTS) @@ -127,27 +147,17 @@ class TestCase(object): 'pre_installed_flows' : pre_installed_flows, 'frame_rate': int(framerate)}) + # Packet Forwarding mode + self._vswitch_none = 'none' == S.getValue('VSWITCH').strip().lower() + # OVS Vanilla requires guest VM MAC address and IPs to work if 'linux_bridge' in self.guest_loopback: - self._traffic['l2'].update({'srcmac': S.getValue('GUEST_NET2_MAC')[0], - 'dstmac': S.getValue('GUEST_NET1_MAC')[0]}) + self._traffic['l2'].update({'srcmac': S.getValue('VANILLA_TGEN_PORT1_MAC'), + 'dstmac': S.getValue('VANILLA_TGEN_PORT2_MAC')}) self._traffic['l3'].update({'srcip': S.getValue('VANILLA_TGEN_PORT1_IP'), 'dstip': S.getValue('VANILLA_TGEN_PORT2_IP')}) - # Packet Forwarding mode - self._vswitch_none = 'none' == S.getValue('VSWITCH').strip().lower() - - def run_initialize(self): - """ Prepare test execution environment - """ - self._logger.debug(self.name) - - # mount hugepages if needed - self._mount_hugepages() - - # copy sources of l2 forwarding tools into VM shared dir if needed - self._copy_fwd_tools_for_guest() - + # trafficgen configuration required for tests of tunneling protocols if self.deployment == "op2p": self._traffic['l2'].update({'srcmac': S.getValue('TRAFFICGEN_PORT1_MAC'), @@ -163,7 +173,24 @@ class TestCase(object): 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 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") + def run_initialize(self): + """ Prepare test execution environment + """ + self._logger.debug(self.name) + + # mount hugepages if needed + self._mount_hugepages() + + # copy sources of l2 forwarding tools into VM shared dir if needed + self._copy_fwd_tools_for_all_guests() self._logger.debug("Controllers:") loader = Loader() @@ -177,6 +204,7 @@ class TestCase(object): if self._vswitch_none: self._vswitch_ctl = component_factory.create_pktfwd( + self.deployment, loader.get_pktfwd_class()) else: self._vswitch_ctl = component_factory.create_vswitch( @@ -203,6 +231,29 @@ class TestCase(object): # umount hugepages if mounted self._umount_hugepages() + # restore original settings + S.load_from_dict(self._settings_original) + + # cleanup any namespaces created + if os.path.isdir('/tmp/namespaces'): + import tools.namespace + namespace_list = os.listdir('/tmp/namespaces') + if len(namespace_list): + self._logger.info('Cleaning up namespaces') + for name in namespace_list: + tools.namespace.delete_namespace(name) + os.rmdir('/tmp/namespaces') + # cleanup any veth ports created + if os.path.isdir('/tmp/veth'): + import tools.veth + veth_list = os.listdir('/tmp/veth') + if len(veth_list): + self._logger.info('Cleaning up veth ports') + for eth in veth_list: + port1, port2 = eth.split('-') + tools.veth.del_veth_port(port1, port2) + os.rmdir('/tmp/veth') + def run_report(self): """ Report test results """ @@ -214,7 +265,7 @@ class TestCase(object): self._traffic_ctl.print_results() self._tc_results = self._append_results(self._traffic_ctl.get_results()) - TestCase._write_result_to_file(self._tc_results, self._output_file) + TestCase.write_result_to_file(self._tc_results, self._output_file) def run(self): """Run the test @@ -255,9 +306,25 @@ class TestCase(object): # 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. @@ -271,52 +338,60 @@ class TestCase(object): 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 len(self.guest_loopback): + if self.deployment in ['pvp', 'pvvp'] and len(self.guest_loopback): item[ResultsConstants.GUEST_LOOPBACK] = ' '.join(self.guest_loopback) if self._tunnel_type: item[ResultsConstants.TUNNEL_TYPE] = self._tunnel_type return results - def _copy_fwd_tools_for_guest(self): - """Copy dpdk and l2fwd code to GUEST_SHARE_DIR[s] for use by guests. + def _copy_fwd_tools_for_all_guests(self): + """Copy dpdk and l2fwd code to GUEST_SHARE_DIR[s] based on selected deployment. """ - counter = 0 - # method is executed only for pvp and pvvp, so let's count number of 'v' - while counter < self.deployment.count('v'): - guest_dir = S.getValue('GUEST_SHARE_DIR')[counter] - - # 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 - if 'testpmd' in self.guest_loopback or 'l2fwd' in self.guest_loopback: - try: - tasks.run_task(['rsync', '-a', '-r', '-l', r'--exclude="\.git"', - os.path.join(S.getValue('RTE_SDK'), ''), - 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') - + # data are copied only for pvp and pvvp, so let's count number of 'v' + counter = 1 + while counter <= self.deployment.count('v'): + self._copy_fwd_tools_for_guest(counter) counter += 1 + def _copy_fwd_tools_for_guest(self, index): + """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) + """ + guest_dir = S.getValue('GUEST_SHARE_DIR')[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 + if 'testpmd' in self.guest_loopback or 'l2fwd' in self.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 """ @@ -324,7 +399,8 @@ class TestCase(object): if not self._hugepages_mounted and \ (self.deployment.count('v') or \ S.getValue('VSWITCH').lower().count('dpdk') or \ - self._vswitch_none): + 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 @@ -336,7 +412,7 @@ class TestCase(object): self._hugepages_mounted = False @staticmethod - def _write_result_to_file(results, output): + def write_result_to_file(results, output): """Write list of dictionaries to a CSV file. Each element on list will create separate row in output file. |