diff options
Diffstat (limited to 'yardstick/network_services/vnf_generic')
5 files changed, 739 insertions, 14 deletions
diff --git a/yardstick/network_services/vnf_generic/vnf/prox_helpers.py b/yardstick/network_services/vnf_generic/vnf/prox_helpers.py index e9d83623b..cd3035ef8 100644 --- a/yardstick/network_services/vnf_generic/vnf/prox_helpers.py +++ b/yardstick/network_services/vnf_generic/vnf/prox_helpers.py @@ -561,6 +561,41 @@ class ProxSocketHelper(object): tsc = int(ret[3]) return rx, tx, drop, tsc + def irq_core_stats(self, cores_tasks): + """ get IRQ stats per core""" + + stat = {} + core = 0 + task = 0 + for core, task in cores_tasks: + self.put_command("stats task.core({}).task({}).max_irq,task.core({}).task({}).irq(0)," + "task.core({}).task({}).irq(1),task.core({}).task({}).irq(2)," + "task.core({}).task({}).irq(3),task.core({}).task({}).irq(4)," + "task.core({}).task({}).irq(5),task.core({}).task({}).irq(6)," + "task.core({}).task({}).irq(7),task.core({}).task({}).irq(8)," + "task.core({}).task({}).irq(9),task.core({}).task({}).irq(10)," + "task.core({}).task({}).irq(11),task.core({}).task({}).irq(12)" + "\n".format(core, task, core, task, core, task, core, task, + core, task, core, task, core, task, core, task, + core, task, core, task, core, task, core, task, + core, task, core, task)) + in_data_str = self.get_data().split(",") + ret = [try_int(s, 0) for s in in_data_str] + key = "core_" + str(core) + try: + stat[key] = {"cpu": core, "max_irq": ret[0], "bucket_0" : ret[1], + "bucket_1" : ret[2], "bucket_2" : ret[3], + "bucket_3" : ret[4], "bucket_4" : ret[5], + "bucket_5" : ret[6], "bucket_6" : ret[7], + "bucket_7" : ret[8], "bucket_8" : ret[9], + "bucket_9" : ret[10], "bucket_10" : ret[11], + "bucket_11" : ret[12], "bucket_12" : ret[13], + "overflow": ret[10] + ret[11] + ret[12] + ret[13]} + except (KeyError, IndexError): + LOG.error("Corrupted PACKET %s", in_data_str) + + return stat + def multi_port_stats(self, ports): """get counter values from all ports at once""" @@ -754,7 +789,6 @@ class ProxSocketHelper(object): self.put_command("quit_force\n") time.sleep(3) - _LOCAL_OBJECT = object() @@ -836,6 +870,30 @@ class ProxDpdkVnfSetupEnvHelper(DpdkVnfSetupEnvHelper): file_str[1] = self.additional_files[base_name] return '"'.join(file_str) + def _make_core_list(self, inputStr): + + my_input = inputStr.split("core ", 1)[1] + ok_list = set() + + substrs = [x.strip() for x in my_input.split(',')] + for i in substrs: + try: + ok_list.add(int(i)) + + except ValueError: + try: + substr = [int(k.strip()) for k in i.split('-')] + if len(substr) > 1: + startstr = substr[0] + endstr = substr[len(substr) - 1] + for z in range(startstr, endstr + 1): + ok_list.add(z) + except ValueError: + LOG.error("Error in cores list ... resuming ") + return ok_list + + return ok_list + def generate_prox_config_file(self, config_path): sections = [] prox_config = ConfigParser(config_path, sections) @@ -855,6 +913,18 @@ class ProxDpdkVnfSetupEnvHelper(DpdkVnfSetupEnvHelper): if section_data[0] == "mac": section_data[1] = "hardware" + # adjust for range of cores + new_sections = [] + for section_name, section in sections: + if section_name.startswith('core') and section_name.find('$') == -1: + core_list = self._make_core_list(section_name) + for core in core_list: + new_sections.append(["core " + str(core), section]) + else: + new_sections.append([section_name, section]) + + sections = new_sections + # search for dst mac for _, section in sections: for section_data in section: @@ -2067,3 +2137,15 @@ class ProxlwAFTRProfileHelper(ProxProfileHelper): data_helper.latency = self.get_latency() return data_helper.result_tuple, data_helper.samples + + +class ProxIrqProfileHelper(ProxProfileHelper): + + __prox_profile_type__ = "IRQ Query" + + def __init__(self, resource_helper): + super(ProxIrqProfileHelper, self).__init__(resource_helper) + self._cores_tuple = None + self._ports_tuple = None + self.step_delta = 5 + self.step_time = 0.5 diff --git a/yardstick/network_services/vnf_generic/vnf/prox_irq.py b/yardstick/network_services/vnf_generic/vnf/prox_irq.py new file mode 100644 index 000000000..dda26b0fe --- /dev/null +++ b/yardstick/network_services/vnf_generic/vnf/prox_irq.py @@ -0,0 +1,200 @@ +# Copyright (c) 2018 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import errno +import logging +import copy +import time + +from yardstick.common.process import check_if_process_failed +from yardstick.network_services.utils import get_nsb_option +from yardstick.network_services.vnf_generic.vnf.prox_vnf import ProxApproxVnf +from yardstick.network_services.vnf_generic.vnf.sample_vnf import SampleVNFTrafficGen +from yardstick.benchmark.contexts.base import Context +from yardstick.network_services.vnf_generic.vnf.prox_helpers import CoreSocketTuple +LOG = logging.getLogger(__name__) + + +class ProxIrq(SampleVNFTrafficGen): + + def __init__(self, name, vnfd, task_id, setup_env_helper_type=None, + resource_helper_type=None): + vnfd_cpy = copy.deepcopy(vnfd) + super(ProxIrq, self).__init__(name, vnfd_cpy, task_id) + + self._vnf_wrapper = ProxApproxVnf( + name, vnfd, task_id, setup_env_helper_type, resource_helper_type) + self.bin_path = get_nsb_option('bin_path', '') + self.name = self._vnf_wrapper.name + self.ssh_helper = self._vnf_wrapper.ssh_helper + self.setup_helper = self._vnf_wrapper.setup_helper + self.resource_helper = self._vnf_wrapper.resource_helper + self.scenario_helper = self._vnf_wrapper.scenario_helper + self.irq_cores = None + + def terminate(self): + self._vnf_wrapper.terminate() + super(ProxIrq, self).terminate() + + def instantiate(self, scenario_cfg, context_cfg): + self._vnf_wrapper.instantiate(scenario_cfg, context_cfg) + self._tg_process = self._vnf_wrapper._vnf_process + + def wait_for_instantiate(self): + self._vnf_wrapper.wait_for_instantiate() + + def get_irq_cores(self): + cores = [] + mode = "irq" + + for section_name, section in self.setup_helper.prox_config_data: + if not section_name.startswith("core"): + continue + irq_mode = task_present = False + task_present_task = 0 + for key, value in section: + if key == "mode" and value == mode: + irq_mode = True + if key == "task": + task_present = True + task_present_task = int(value) + + if irq_mode: + if not task_present: + task_present_task = 0 + core_tuple = CoreSocketTuple(section_name) + core = core_tuple.core_id + cores.append((core, task_present_task)) + + return cores + +class ProxIrqVNF(ProxIrq, SampleVNFTrafficGen): + + APP_NAME = 'ProxIrqVNF' + + def __init__(self, name, vnfd, task_id, setup_env_helper_type=None, + resource_helper_type=None): + ProxIrq.__init__(self, name, vnfd, task_id, setup_env_helper_type, + resource_helper_type) + + self.start_test_time = None + self.end_test_time = None + + def vnf_execute(self, cmd, *args, **kwargs): + ignore_errors = kwargs.pop("_ignore_errors", False) + try: + return self.resource_helper.execute(cmd, *args, **kwargs) + except OSError as e: + if e.errno in {errno.EPIPE, errno.ESHUTDOWN, errno.ECONNRESET}: + if ignore_errors: + LOG.debug("ignoring vnf_execute exception %s for command %s", e, cmd) + else: + raise + else: + raise + + def collect_kpi(self): + # check if the tg processes have exited + physical_node = Context.get_physical_node_from_server( + self.scenario_helper.nodes[self.name]) + + result = {"physical_node": physical_node} + for proc in (self._tg_process, self._traffic_process): + check_if_process_failed(proc) + + if self.resource_helper is None: + return result + + if self.irq_cores is None: + self.setup_helper.build_config_file() + self.irq_cores = self.get_irq_cores() + + data = self.vnf_execute('irq_core_stats', self.irq_cores) + new_data = copy.deepcopy(data) + + self.end_test_time = time.time() + self.vnf_execute('reset_stats') + + if self.start_test_time is None: + new_data = {} + else: + test_time = self.end_test_time - self.start_test_time + for index, item in data.items(): + for counter, value in item.items(): + if counter.startswith("bucket_")or \ + counter.startswith("overflow"): + if value is 0: + del new_data[index][counter] + else: + new_data[index][counter] = float(value) / test_time + + self.start_test_time = time.time() + + result["collect_stats"] = new_data + LOG.debug("%s collect KPIs %s", self.APP_NAME, result) + + return result + +class ProxIrqGen(ProxIrq, SampleVNFTrafficGen): + + APP_NAME = 'ProxIrqGen' + + def __init__(self, name, vnfd, task_id, setup_env_helper_type=None, + resource_helper_type=None): + ProxIrq.__init__(self, name, vnfd, task_id, setup_env_helper_type, + resource_helper_type) + self.start_test_time = None + self.end_test_time = None + + def collect_kpi(self): + # check if the tg processes have exited + physical_node = Context.get_physical_node_from_server( + self.scenario_helper.nodes[self.name]) + + result = {"physical_node": physical_node} + for proc in (self._tg_process, self._traffic_process): + check_if_process_failed(proc) + + if self.resource_helper is None: + return result + + if self.irq_cores is None: + self.setup_helper.build_config_file() + self.irq_cores = self.get_irq_cores() + + data = self.resource_helper.sut.irq_core_stats(self.irq_cores) + new_data = copy.deepcopy(data) + + self.end_test_time = time.time() + self.resource_helper.sut.reset_stats() + + if self.start_test_time is None: + new_data = {} + else: + test_time = self.end_test_time - self.start_test_time + for index, item in data.items(): + for counter, value in item.items(): + if counter.startswith("bucket_") or \ + counter.startswith("overflow"): + if value is 0: + del new_data[index][counter] + else: + new_data[index][counter] = float(value) / test_time + + self.start_test_time = time.time() + + result["collect_stats"] = new_data + LOG.debug("%s collect KPIs %s", self.APP_NAME, result) + + return result diff --git a/yardstick/network_services/vnf_generic/vnf/sample_vnf.py b/yardstick/network_services/vnf_generic/vnf/sample_vnf.py index 673344f4e..8833b88f2 100644 --- a/yardstick/network_services/vnf_generic/vnf/sample_vnf.py +++ b/yardstick/network_services/vnf_generic/vnf/sample_vnf.py @@ -180,7 +180,7 @@ class DpdkVnfSetupEnvHelper(SetupEnvHelper): """No actions/rules (flows) by default""" return None - def _build_pipeline_kwargs(self, cfg_file=None): + def _build_pipeline_kwargs(self, cfg_file=None, script=None): tool_path = self.ssh_helper.provision_tool(tool_file=self.APP_NAME) # count the number of actual ports in the list of pairs # remove duplicate ports @@ -201,7 +201,7 @@ class DpdkVnfSetupEnvHelper(SetupEnvHelper): self.pipeline_kwargs = { 'cfg_file': cfg_file if cfg_file else self.CFG_CONFIG, - 'script': self.CFG_SCRIPT, + 'script': script if script else self.CFG_SCRIPT, 'port_mask_hex': ports_mask_hex, 'tool_path': tool_path, 'hwlb': hwlb, @@ -898,6 +898,8 @@ class SampleVNFTrafficGen(GenericTrafficGen): self.scenario_helper.nodes[self.name] ) + self.resource_helper.context_cfg = context_cfg + self.resource_helper.setup() # must generate_cfg after DPDK bind because we need port number self.resource_helper.generate_cfg() diff --git a/yardstick/network_services/vnf_generic/vnf/tg_rfc2544_ixia.py b/yardstick/network_services/vnf_generic/vnf/tg_rfc2544_ixia.py index 89f8194c0..4fbbf6a40 100644 --- a/yardstick/network_services/vnf_generic/vnf/tg_rfc2544_ixia.py +++ b/yardstick/network_services/vnf_generic/vnf/tg_rfc2544_ixia.py @@ -1,4 +1,4 @@ -# Copyright (c) 2016-2017 Intel Corporation +# Copyright (c) 2016-2018 Intel Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,9 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. +import ipaddress import logging +import six from yardstick.common import utils +from yardstick.common import exceptions from yardstick.network_services.libs.ixia_libs.ixnet import ixnet_api from yardstick.network_services.vnf_generic.vnf.sample_vnf import SampleVNFTrafficGen from yardstick.network_services.vnf_generic.vnf.sample_vnf import ClientResourceHelper @@ -25,6 +28,409 @@ LOG = logging.getLogger(__name__) WAIT_AFTER_CFG_LOAD = 10 WAIT_FOR_TRAFFIC = 30 +WAIT_PROTOCOLS_STARTED = 360 + + +class IxiaBasicScenario(object): + """Ixia Basic scenario for flow from port to port""" + + def __init__(self, client, context_cfg, ixia_cfg): + + self.client = client + self.context_cfg = context_cfg + self.ixia_cfg = ixia_cfg + + self._uplink_vports = None + self._downlink_vports = None + + def apply_config(self): + pass + + def run_protocols(self): + pass + + def stop_protocols(self): + pass + + def create_traffic_model(self, traffic_profile=None): + # pylint: disable=unused-argument + vports = self.client.get_vports() + self._uplink_vports = vports[::2] + self._downlink_vports = vports[1::2] + self.client.create_traffic_model(self._uplink_vports, + self._downlink_vports) + + +class IxiaL3Scenario(IxiaBasicScenario): + """Ixia scenario for L3 flow between static ip's""" + + def _add_static_ips(self): + vports = self.client.get_vports() + uplink_intf_vport = [(self.client.get_static_interface(vport), vport) + for vport in vports[::2]] + downlink_intf_vport = [(self.client.get_static_interface(vport), vport) + for vport in vports[1::2]] + + for index in range(len(uplink_intf_vport)): + intf, vport = uplink_intf_vport[index] + try: + iprange = self.ixia_cfg['flow'].get('src_ip')[index] + start_ip = utils.get_ip_range_start(iprange) + count = utils.get_ip_range_count(iprange) + self.client.add_static_ipv4(intf, vport, start_ip, count, '32') + except IndexError: + raise exceptions.IncorrectFlowOption( + option="src_ip", link="uplink_{}".format(index)) + + intf, vport = downlink_intf_vport[index] + try: + iprange = self.ixia_cfg['flow'].get('dst_ip')[index] + start_ip = utils.get_ip_range_start(iprange) + count = utils.get_ip_range_count(iprange) + self.client.add_static_ipv4(intf, vport, start_ip, count, '32') + except IndexError: + raise exceptions.IncorrectFlowOption( + option="dst_ip", link="downlink_{}".format(index)) + + def _add_interfaces(self): + vports = self.client.get_vports() + uplink_vports = (vport for vport in vports[::2]) + downlink_vports = (vport for vport in vports[1::2]) + + ix_node = next(node for _, node in self.context_cfg['nodes'].items() + if node['role'] == 'IxNet') + + for intf in ix_node['interfaces'].values(): + ip = intf.get('local_ip') + mac = intf.get('local_mac') + gateway = None + try: + gateway = next(route.get('gateway') + for route in ix_node.get('routing_table') + if route.get('if') == intf.get('ifname')) + except StopIteration: + LOG.debug("Gateway not provided") + + if 'uplink' in intf.get('vld_id'): + self.client.add_interface(next(uplink_vports), + ip, mac, gateway) + else: + self.client.add_interface(next(downlink_vports), + ip, mac, gateway) + + def apply_config(self): + self._add_interfaces() + self._add_static_ips() + + def create_traffic_model(self, traffic_profile=None): + # pylint: disable=unused-argument + vports = self.client.get_vports() + self._uplink_vports = vports[::2] + self._downlink_vports = vports[1::2] + + uplink_endpoints = [port + '/protocols/static' + for port in self._uplink_vports] + downlink_endpoints = [port + '/protocols/static' + for port in self._downlink_vports] + + self.client.create_ipv4_traffic_model(uplink_endpoints, + downlink_endpoints) + + +class IxiaPppoeClientScenario(object): + def __init__(self, client, context_cfg, ixia_cfg): + + self.client = client + + self._uplink_vports = None + self._downlink_vports = None + + self._access_topologies = [] + self._core_topologies = [] + + self._context_cfg = context_cfg + self._ixia_cfg = ixia_cfg + self.protocols = [] + self.device_groups = [] + + def apply_config(self): + vports = self.client.get_vports() + self._uplink_vports = vports[::2] + self._downlink_vports = vports[1::2] + self._fill_ixia_config() + self._apply_access_network_config() + self._apply_core_network_config() + + def create_traffic_model(self, traffic_profile): + endpoints_id_pairs = self._get_endpoints_src_dst_id_pairs( + traffic_profile.full_profile) + endpoints_obj_pairs = \ + self._get_endpoints_src_dst_obj_pairs(endpoints_id_pairs) + uplink_endpoints = endpoints_obj_pairs[::2] + downlink_endpoints = endpoints_obj_pairs[1::2] + self.client.create_ipv4_traffic_model(uplink_endpoints, + downlink_endpoints) + + def run_protocols(self): + LOG.info('PPPoE Scenario - Start Protocols') + self.client.start_protocols() + utils.wait_until_true( + lambda: self.client.is_protocols_running(self.protocols), + timeout=WAIT_PROTOCOLS_STARTED, sleep=2) + + def stop_protocols(self): + LOG.info('PPPoE Scenario - Stop Protocols') + self.client.stop_protocols() + + def _get_intf_addr(self, intf): + """Retrieve interface IP address and mask + + :param intf: could be the string which represents IP address + with mask (e.g 192.168.10.2/24) or a dictionary with the host + name and the port (e.g. {'tg__0': 'xe1'}) + :return: (tuple) pair of ip address and mask + """ + if isinstance(intf, six.string_types): + ip, mask = tuple(intf.split('/')) + return ip, int(mask) + + node_name, intf_name = next(iter(intf.items())) + node = self._context_cfg["nodes"].get(node_name, {}) + interface = node.get("interfaces", {})[intf_name] + ip = interface["local_ip"] + mask = interface["netmask"] + ipaddr = ipaddress.ip_network(six.text_type('{}/{}'.format(ip, mask)), + strict=False) + return ip, ipaddr.prefixlen + + @staticmethod + def _get_endpoints_src_dst_id_pairs(flows_params): + """Get list of flows src/dst port pairs + + Create list of flows src/dst port pairs based on traffic profile + flows data. Each uplink/downlink pair in traffic profile represents + specific flows between the pair of ports. + + Example ('port' key represents port on which flow will be created): + + Input flows data: + uplink_0: + ipv4: + id: 1 + port: xe0 + downlink_0: + ipv4: + id: 2 + port: xe1 + uplink_1: + ipv4: + id: 3 + port: xe2 + downlink_1: + ipv4: + id: 4 + port: xe3 + + Result list: ['xe0', 'xe1', 'xe2', 'xe3'] + + Result list means that the following flows pairs will be created: + - uplink 0: port xe0 <-> port xe1 + - downlink 0: port xe1 <-> port xe0 + - uplink 1: port xe2 <-> port xe3 + - downlink 1: port xe3 <-> port xe2 + + :param flows_params: ordered dict of traffic profile flows params + :return: (list) list of flows src/dst ports + """ + if len(flows_params) % 2: + raise RuntimeError('Number of uplink/downlink pairs' + ' in traffic profile is not equal') + endpoint_pairs = [] + for flow in flows_params: + port = flows_params[flow]['ipv4'].get('port') + if port is None: + continue + endpoint_pairs.append(port) + return endpoint_pairs + + def _get_endpoints_src_dst_obj_pairs(self, endpoints_id_pairs): + """Create list of uplink/downlink device groups pairs + + Based on traffic profile options, create list of uplink/downlink + device groups pairs between which flow groups will be created: + + 1. In case uplink/downlink flows in traffic profile doesn't have + specified 'port' key, flows will be created between each device + group on access port and device group on corresponding core port. + E.g.: + Device groups created on access port xe0: dg1, dg2, dg3 + Device groups created on core port xe1: dg4 + Flows will be created between: + dg1 -> dg4 + dg4 -> dg1 + dg2 -> dg4 + dg4 -> dg2 + dg3 -> dg4 + dg4 -> dg3 + + 2. In case uplink/downlink flows in traffic profile have specified + 'port' key, flows will be created between device groups on this + port. + E.g., for the following traffic profile + uplink_0: + port: xe0 + downlink_0: + port: xe1 + uplink_1: + port: xe0 + downlink_0: + port: xe3 + Flows will be created between: + Port xe0 (dg1) -> Port xe1 (dg1) + Port xe1 (dg1) -> Port xe0 (dg1) + Port xe0 (dg2) -> Port xe3 (dg1) + Port xe3 (dg3) -> Port xe0 (dg1) + + :param endpoints_id_pairs: (list) List of uplink/downlink flows ports + pairs + :return: (list) list of uplink/downlink device groups descriptors pairs + """ + pppoe = self._ixia_cfg['pppoe_client'] + sessions_per_port = pppoe['sessions_per_port'] + sessions_per_svlan = pppoe['sessions_per_svlan'] + svlan_count = int(sessions_per_port / sessions_per_svlan) + + uplink_ports = [p['tg__0'] for p in self._ixia_cfg['flow']['src_ip']] + downlink_ports = [p['tg__0'] for p in self._ixia_cfg['flow']['dst_ip']] + uplink_port_topology_map = zip(uplink_ports, self._access_topologies) + downlink_port_topology_map = zip(downlink_ports, self._core_topologies) + + port_to_dev_group_mapping = {} + for port, topology in uplink_port_topology_map: + topology_dgs = self.client.get_topology_device_groups(topology) + port_to_dev_group_mapping[port] = topology_dgs + for port, topology in downlink_port_topology_map: + topology_dgs = self.client.get_topology_device_groups(topology) + port_to_dev_group_mapping[port] = topology_dgs + + uplink_endpoints = endpoints_id_pairs[::2] + downlink_endpoints = endpoints_id_pairs[1::2] + + uplink_dev_groups = [] + group_up = [uplink_endpoints[i:i + svlan_count] + for i in range(0, len(uplink_endpoints), svlan_count)] + + for group in group_up: + for i, port in enumerate(group): + uplink_dev_groups.append(port_to_dev_group_mapping[port][i]) + + downlink_dev_groups = [] + for port in downlink_endpoints: + downlink_dev_groups.append(port_to_dev_group_mapping[port][0]) + + endpoint_obj_pairs = [] + [endpoint_obj_pairs.extend([up, down]) + for up, down in zip(uplink_dev_groups, downlink_dev_groups)] + + if not endpoint_obj_pairs: + for up, down in zip(uplink_ports, downlink_ports): + uplink_dev_groups = port_to_dev_group_mapping[up] + downlink_dev_groups = \ + port_to_dev_group_mapping[down] * len(uplink_dev_groups) + [endpoint_obj_pairs.extend(list(i)) + for i in zip(uplink_dev_groups, downlink_dev_groups)] + return endpoint_obj_pairs + + def _fill_ixia_config(self): + pppoe = self._ixia_cfg["pppoe_client"] + ipv4 = self._ixia_cfg["ipv4_client"] + + _ip = [self._get_intf_addr(intf)[0] for intf in pppoe["ip"]] + self._ixia_cfg["pppoe_client"]["ip"] = _ip + + _ip = [self._get_intf_addr(intf)[0] for intf in ipv4["gateway_ip"]] + self._ixia_cfg["ipv4_client"]["gateway_ip"] = _ip + + addrs = [self._get_intf_addr(intf) for intf in ipv4["ip"]] + _ip = [addr[0] for addr in addrs] + _prefix = [addr[1] for addr in addrs] + + self._ixia_cfg["ipv4_client"]["ip"] = _ip + self._ixia_cfg["ipv4_client"]["prefix"] = _prefix + + def _apply_access_network_config(self): + pppoe = self._ixia_cfg["pppoe_client"] + sessions_per_port = pppoe['sessions_per_port'] + sessions_per_svlan = pppoe['sessions_per_svlan'] + svlan_count = int(sessions_per_port / sessions_per_svlan) + + # add topology per uplink port (access network) + for access_tp_id, vport in enumerate(self._uplink_vports): + name = 'Topology access {}'.format(access_tp_id) + tp = self.client.add_topology(name, vport) + self._access_topologies.append(tp) + # add device group per svlan + for dg_id in range(svlan_count): + s_vlan_id = int(pppoe['s_vlan']) + dg_id + access_tp_id * svlan_count + s_vlan = ixnet_api.Vlan(vlan_id=s_vlan_id) + c_vlan = ixnet_api.Vlan(vlan_id=pppoe['c_vlan'], vlan_id_step=1) + name = 'SVLAN {}'.format(s_vlan_id) + dg = self.client.add_device_group(tp, name, sessions_per_svlan) + self.device_groups.append(dg) + # add ethernet layer to device group + ethernet = self.client.add_ethernet(dg, 'Ethernet') + self.protocols.append(ethernet) + self.client.add_vlans(ethernet, [s_vlan, c_vlan]) + # add ppp over ethernet + if 'pap_user' in pppoe: + ppp = self.client.add_pppox_client(ethernet, 'pap', + pppoe['pap_user'], + pppoe['pap_password']) + else: + ppp = self.client.add_pppox_client(ethernet, 'chap', + pppoe['chap_user'], + pppoe['chap_password']) + self.protocols.append(ppp) + + def _apply_core_network_config(self): + ipv4 = self._ixia_cfg["ipv4_client"] + sessions_per_port = ipv4['sessions_per_port'] + sessions_per_vlan = ipv4['sessions_per_vlan'] + vlan_count = int(sessions_per_port / sessions_per_vlan) + + # add topology per downlink port (core network) + for core_tp_id, vport in enumerate(self._downlink_vports): + name = 'Topology core {}'.format(core_tp_id) + tp = self.client.add_topology(name, vport) + self._core_topologies.append(tp) + # add device group per vlan + for dg_id in range(vlan_count): + name = 'Core port {}'.format(core_tp_id) + dg = self.client.add_device_group(tp, name, sessions_per_vlan) + self.device_groups.append(dg) + # add ethernet layer to device group + ethernet = self.client.add_ethernet(dg, 'Ethernet') + self.protocols.append(ethernet) + if 'vlan' in ipv4: + vlan_id = int(ipv4['vlan']) + dg_id + core_tp_id * vlan_count + vlan = ixnet_api.Vlan(vlan_id=vlan_id) + self.client.add_vlans(ethernet, [vlan]) + # add ipv4 layer + gw_ip = ipv4['gateway_ip'][core_tp_id] + # use gw addr to generate ip addr from the same network + ip_addr = ipaddress.IPv4Address(gw_ip) + 1 + ipv4_obj = self.client.add_ipv4(ethernet, name='ipv4', + addr=ip_addr, + addr_step='0.0.0.1', + prefix=ipv4['prefix'][core_tp_id], + gateway=gw_ip) + self.protocols.append(ipv4_obj) + if ipv4.get("bgp"): + bgp_peer_obj = self.client.add_bgp(ipv4_obj, + dut_ip=ipv4["bgp"]["dut_ip"], + local_as=ipv4["bgp"]["as_number"], + bgp_type=ipv4["bgp"].get("bgp_type")) + self.protocols.append(bgp_peer_obj) class IxiaRfc2544Helper(Rfc2544ResourceHelper): @@ -41,6 +447,12 @@ class IxiaResourceHelper(ClientResourceHelper): super(IxiaResourceHelper, self).__init__(setup_helper) self.scenario_helper = setup_helper.scenario_helper + self._ixia_scenarios = { + "IxiaBasic": IxiaBasicScenario, + "IxiaL3": IxiaL3Scenario, + "IxiaPppoeClient": IxiaPppoeClientScenario, + } + self.client = ixnet_api.IxNextgen() if rfc_helper_type is None: @@ -49,6 +461,8 @@ class IxiaResourceHelper(ClientResourceHelper): self.rfc_helper = rfc_helper_type(self.scenario_helper) self.uplink_ports = None self.downlink_ports = None + self.context_cfg = None + self._ix_scenario = None self._connect() def _connect(self, client=None): @@ -57,7 +471,12 @@ class IxiaResourceHelper(ClientResourceHelper): def get_stats(self, *args, **kwargs): return self.client.get_statistics() + def setup(self): + super(IxiaResourceHelper, self).setup() + self._init_ix_scenario() + def stop_collect(self): + self._ix_scenario.stop_protocols() self._terminated.value = 1 def generate_samples(self, ports, duration): @@ -92,14 +511,24 @@ class IxiaResourceHelper(ClientResourceHelper): return samples - def _initialize_client(self): + def _init_ix_scenario(self): + ixia_config = self.scenario_helper.scenario_cfg.get('ixia_config', 'IxiaBasic') + + if ixia_config in self._ixia_scenarios: + scenario_type = self._ixia_scenarios[ixia_config] + + self._ix_scenario = scenario_type(self.client, self.context_cfg, + self.scenario_helper.scenario_cfg['options']) + else: + raise RuntimeError( + "IXIA config type '{}' not supported".format(ixia_config)) + + def _initialize_client(self, traffic_profile): """Initialize the IXIA IxNetwork client and configure the server""" self.client.clear_config() self.client.assign_ports() - vports = self.client.get_vports() - uplink_vports = vports[::2] - downlink_vports = vports[1::2] - self.client.create_traffic_model(uplink_vports, downlink_vports) + self._ix_scenario.apply_config() + self._ix_scenario.create_traffic_model(traffic_profile) def run_traffic(self, traffic_profile, *args): if self._terminated.value: @@ -111,7 +540,8 @@ class IxiaResourceHelper(ClientResourceHelper): default = "00:00:00:00:00:00" self._build_ports() - self._initialize_client() + traffic_profile.update_traffic_profile(self) + self._initialize_client(traffic_profile) mac = {} for port_name in self.vnfd_helper.port_pairs.all_ports: @@ -123,6 +553,8 @@ class IxiaResourceHelper(ClientResourceHelper): mac["src_mac_{}".format(port_num)] = virt_intf.get("local_mac", default) mac["dst_mac_{}".format(port_num)] = virt_intf.get("dst_mac", default) + self._ix_scenario.run_protocols() + try: while not self._terminated.value: first_run = traffic_profile.execute_traffic( @@ -144,6 +576,7 @@ class IxiaResourceHelper(ClientResourceHelper): except Exception: # pylint: disable=broad-except LOG.exception('Run Traffic terminated') + self._ix_scenario.stop_protocols() self._terminated.value = 1 def collect_kpi(self): diff --git a/yardstick/network_services/vnf_generic/vnf/vpe_vnf.py b/yardstick/network_services/vnf_generic/vnf/vpe_vnf.py index 349ef7888..dd3221386 100644 --- a/yardstick/network_services/vnf_generic/vnf/vpe_vnf.py +++ b/yardstick/network_services/vnf_generic/vnf/vpe_vnf.py @@ -106,6 +106,7 @@ class VpeApproxSetupEnvHelper(DpdkVnfSetupEnvHelper): action_bulk_file = vnf_cfg.get('action_bulk_file', '/tmp/action_bulk_512.txt') full_tm_profile_file = vnf_cfg.get('full_tm_profile_file', '/tmp/full_tm_profile_10G.cfg') config_file = vnf_cfg.get('file', '/tmp/vpe_config') + script_file = vnf_cfg.get('script_file', None) vpe_vars = { "bin_path": self.ssh_helper.bin_path, "socket": self.socket, @@ -113,8 +114,16 @@ class VpeApproxSetupEnvHelper(DpdkVnfSetupEnvHelper): self._build_vnf_ports() vpe_conf = ConfigCreate(self.vnfd_helper, self.socket) + if script_file is None: + # autogenerate vpe_script if not given + vpe_script = vpe_conf.generate_vpe_script(self.vnfd_helper.interfaces) + script_file = self.CFG_SCRIPT + else: + with utils.open_relative_file(script_file, task_path) as handle: + vpe_script = handle.read() + config_basename = posixpath.basename(config_file) - script_basename = posixpath.basename(self.CFG_SCRIPT) + script_basename = posixpath.basename(script_file) with utils.open_relative_file(action_bulk_file, task_path) as handle: action_bulk = handle.read() @@ -125,8 +134,6 @@ class VpeApproxSetupEnvHelper(DpdkVnfSetupEnvHelper): with utils.open_relative_file(config_file, task_path) as handle: vpe_config = handle.read() - # vpe_script needs to be autogenerated - vpe_script = vpe_conf.generate_vpe_script(self.vnfd_helper.interfaces) # upload the 4 config files to the target server self.ssh_helper.upload_config_file(config_basename, vpe_config.format(**vpe_vars)) self.ssh_helper.upload_config_file(script_basename, vpe_script.format(**vpe_vars)) @@ -138,7 +145,8 @@ class VpeApproxSetupEnvHelper(DpdkVnfSetupEnvHelper): LOG.info("Provision and start the %s", self.APP_NAME) LOG.info(config_file) LOG.info(self.CFG_SCRIPT) - self._build_pipeline_kwargs(cfg_file='/tmp/' + config_basename) + self._build_pipeline_kwargs(cfg_file='/tmp/' + config_basename, + script='/tmp/' + script_basename) return self.PIPELINE_COMMAND.format(**self.pipeline_kwargs) |