From 63d966f9208664eaa8147c8f719d414418425a63 Mon Sep 17 00:00:00 2001 From: Oleksandr Naumets Date: Tue, 13 Nov 2018 12:42:08 +0000 Subject: Extend vBNG PPPoE test cases functionality Extended Ixia traffic generator with functionality to create traffic flows between device groups. JIRA: YARDSTICK-1521 Change-Id: Iaaa822f2f3da876629576d4101be29c9a65296d0 Signed-off-by: Oleksandr Naumets --- .../libs/ixia_libs/ixnet/ixnet_api.py | 12 +- .../traffic_profile/ixia_rfc2544.py | 34 ++++- .../vnf_generic/vnf/tg_rfc2544_ixia.py | 163 ++++++++++++++++++++- .../traffic_profile/test_ixia_rfc2544.py | 40 ++++- .../vnf_generic/vnf/test_tg_rfc2544_ixia.py | 112 +++++++++++++- 5 files changed, 341 insertions(+), 20 deletions(-) (limited to 'yardstick') diff --git a/yardstick/network_services/libs/ixia_libs/ixnet/ixnet_api.py b/yardstick/network_services/libs/ixia_libs/ixnet/ixnet_api.py index b5e4172a9..4e7434c68 100644 --- a/yardstick/network_services/libs/ixia_libs/ixnet/ixnet_api.py +++ b/yardstick/network_services/libs/ixia_libs/ixnet/ixnet_api.py @@ -216,6 +216,14 @@ class IxNextgen(object): # pragma: no cover """ return self.ixnet.getAttribute(proto, '-sessionStatus') + def get_topology_device_groups(self, topology): + """Get list of device groups in topology + + :param topology: (str) topology descriptor + :return: (list) list of device groups descriptors + """ + return self.ixnet.getList(topology, 'deviceGroup') + def is_traffic_running(self): """Returns true if traffic state == TRAFFIC_STATUS_STARTED""" return self._get_traffic_state() == TRAFFIC_STATUS_STARTED @@ -406,7 +414,7 @@ class IxNextgen(object): # pragma: no cover self._create_flow_groups(uplink_endpoints, downlink_endpoints) self._setup_config_elements() - def create_ipv4_traffic_model(self, uplink_topologies, downlink_topologies): + def create_ipv4_traffic_model(self, uplink_endpoints, downlink_endpoints): """Create a traffic item and the needed flow groups Each flow group inside the traffic item (only one is present) @@ -418,7 +426,7 @@ class IxNextgen(object): # pragma: no cover FlowGroup4: uplink2 <- downlink2 """ self._create_traffic_item('ipv4') - self._create_flow_groups(uplink_topologies, downlink_topologies) + self._create_flow_groups(uplink_endpoints, downlink_endpoints) self._setup_config_elements(False) def _update_frame_mac(self, ethernet_descriptor, field, mac_address): diff --git a/yardstick/network_services/traffic_profile/ixia_rfc2544.py b/yardstick/network_services/traffic_profile/ixia_rfc2544.py index 83d24a412..9328953a1 100644 --- a/yardstick/network_services/traffic_profile/ixia_rfc2544.py +++ b/yardstick/network_services/traffic_profile/ixia_rfc2544.py @@ -13,6 +13,7 @@ # limitations under the License. import logging +import collections from yardstick.common import utils from yardstick.network_services.traffic_profile import base as tp_base @@ -35,6 +36,7 @@ class IXIARFC2544Profile(trex_traffic_profile.TrexProfile): super(IXIARFC2544Profile, self).__init__(yaml_data) self.rate = self.config.frame_rate self.rate_unit = self.config.rate_unit + self.full_profile = {} def _get_ip_and_mask(self, ip_range): _ip_range = ip_range.split('-') @@ -164,9 +166,7 @@ class IXIARFC2544Profile(trex_traffic_profile.TrexProfile): first_run = self.first_run if self.first_run: self.first_run = False - self.full_profile = {} self.pg_id = 0 - self.update_traffic_profile(traffic_generator) self.max_rate = self.rate self.min_rate = 0.0 else: @@ -237,3 +237,33 @@ class IXIARFC2544Profile(trex_traffic_profile.TrexProfile): samples['latency_ns_max'] = latency_ns_max return completed, samples + + +class IXIARFC2544PppoeScenarioProfile(IXIARFC2544Profile): + """Class handles BNG PPPoE scenario tests traffic profile""" + + def __init__(self, yaml_data): + super(IXIARFC2544PppoeScenarioProfile, self).__init__(yaml_data) + self.full_profile = collections.OrderedDict() + + def _get_flow_groups_params(self): + flows_data = [key for key in self.params.keys() + if key.split('_')[0] in [self.UPLINK, self.DOWNLINK]] + for i in range(len(flows_data)): + uplink = '_'.join([self.UPLINK, str(i)]) + downlink = '_'.join([self.DOWNLINK, str(i)]) + if uplink in flows_data: + self.full_profile.update({uplink: self.params[uplink]}) + if downlink in flows_data: + self.full_profile.update({downlink: self.params[downlink]}) + + def update_traffic_profile(self, traffic_generator): + def port_generator(): + for vld_id, intfs in sorted(traffic_generator.networks.items()): + if not vld_id.startswith((self.UPLINK, self.DOWNLINK)): + continue + for intf in intfs: + yield traffic_generator.vnfd_helper.port_num(intf) + + self._get_flow_groups_params() + self.ports = [port for port in port_generator()] 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 4c13112be..1d37f8f6f 100644 --- a/yardstick/network_services/vnf_generic/vnf/tg_rfc2544_ixia.py +++ b/yardstick/network_services/vnf_generic/vnf/tg_rfc2544_ixia.py @@ -43,7 +43,8 @@ class IxiaBasicScenario(object): def apply_config(self): pass - def create_traffic_model(self): + 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] @@ -71,6 +72,7 @@ class IxiaPppoeClientScenario(object): self._context_cfg = context_cfg self._ixia_cfg = ixia_cfg self.protocols = [] + self.device_groups = [] def apply_config(self): vports = self.client.get_vports() @@ -80,9 +82,15 @@ class IxiaPppoeClientScenario(object): self._apply_access_network_config() self._apply_core_network_config() - def create_traffic_model(self): - self.client.create_ipv4_traffic_model(self._access_topologies, - self._core_topologies) + 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') @@ -116,6 +124,144 @@ class IxiaPppoeClientScenario(object): 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"] @@ -151,6 +297,7 @@ class IxiaPppoeClientScenario(object): 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) @@ -181,6 +328,7 @@ class IxiaPppoeClientScenario(object): 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) @@ -295,12 +443,12 @@ class IxiaResourceHelper(ClientResourceHelper): raise RuntimeError( "IXIA config type '{}' not supported".format(ixia_config)) - def _initialize_client(self): + def _initialize_client(self, traffic_profile): """Initialize the IXIA IxNetwork client and configure the server""" self.client.clear_config() self.client.assign_ports() self._ix_scenario.apply_config() - self._ix_scenario.create_traffic_model() + self._ix_scenario.create_traffic_model(traffic_profile) def run_traffic(self, traffic_profile, *args): if self._terminated.value: @@ -312,7 +460,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: diff --git a/yardstick/tests/unit/network_services/traffic_profile/test_ixia_rfc2544.py b/yardstick/tests/unit/network_services/traffic_profile/test_ixia_rfc2544.py index 5b39b6cd1..ef16676c7 100644 --- a/yardstick/tests/unit/network_services/traffic_profile/test_ixia_rfc2544.py +++ b/yardstick/tests/unit/network_services/traffic_profile/test_ixia_rfc2544.py @@ -16,6 +16,7 @@ import copy import mock import unittest +import collections from yardstick.network_services.traffic_profile import ixia_rfc2544 from yardstick.network_services.traffic_profile import trex_traffic_profile @@ -511,9 +512,7 @@ class TestIXIARFC2544Profile(unittest.TestCase): with mock.patch.object(rfc2544_profile, '_get_ixia_traffic_profile') \ as mock_get_tp, \ mock.patch.object(rfc2544_profile, '_ixia_traffic_generate') \ - as mock_tgenerate, \ - mock.patch.object(rfc2544_profile, 'update_traffic_profile') \ - as mock_update_tp: + as mock_tgenerate: mock_get_tp.return_value = 'fake_tprofile' output = rfc2544_profile.execute_traffic(mock.ANY, ixia_obj=mock.ANY) @@ -524,7 +523,6 @@ class TestIXIARFC2544Profile(unittest.TestCase): self.assertEqual(0, rfc2544_profile.min_rate) mock_get_tp.assert_called_once() mock_tgenerate.assert_called_once() - mock_update_tp.assert_called_once() def test_execute_traffic_not_first_run(self): rfc2544_profile = ixia_rfc2544.IXIARFC2544Profile(self.TRAFFIC_PROFILE) @@ -683,3 +681,37 @@ class TestIXIARFC2544Profile(unittest.TestCase): self.assertEqual(66.833, samples['RxThroughput']) self.assertEqual(0.099651, samples['DropPercentage']) self.assertEqual(33.45, rfc2544_profile.rate) + + +class TestIXIARFC2544PppoeScenarioProfile(unittest.TestCase): + + TRAFFIC_PROFILE = { + "schema": "nsb:traffic_profile:0.1", + "name": "fixed", + "description": "Fixed traffic profile to run UDP traffic", + "traffic_profile": { + "traffic_type": "FixedTraffic", + "frame_rate": 100}, + 'uplink_0': {'ipv4': {'port': 'xe0', 'id': 1}}, + 'downlink_0': {'ipv4': {'port': 'xe2', 'id': 2}}, + 'uplink_1': {'ipv4': {'port': 'xe1', 'id': 3}}, + 'downlink_1': {'ipv4': {'port': 'xe2', 'id': 4}} + } + + def setUp(self): + self.ixia_tp = ixia_rfc2544.IXIARFC2544PppoeScenarioProfile( + self.TRAFFIC_PROFILE) + + def test___init__(self): + self.assertIsInstance(self.ixia_tp.full_profile, + collections.OrderedDict) + + def test__get_flow_groups_params(self): + expected_tp = collections.OrderedDict([ + ('uplink_0', {'ipv4': {'id': 1, 'port': 'xe0'}}), + ('downlink_0', {'ipv4': {'id': 2, 'port': 'xe2'}}), + ('uplink_1', {'ipv4': {'id': 3, 'port': 'xe1'}}), + ('downlink_1', {'ipv4': {'id': 4, 'port': 'xe2'}})]) + + self.ixia_tp._get_flow_groups_params() + self.assertDictEqual(self.ixia_tp.full_profile, expected_tp) diff --git a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_rfc2544_ixia.py b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_rfc2544_ixia.py index e22398847..65bf56f1e 100644 --- a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_rfc2544_ixia.py +++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_rfc2544_ixia.py @@ -18,6 +18,7 @@ import mock import six import unittest import ipaddress +from collections import OrderedDict from yardstick.common import utils from yardstick.common import exceptions @@ -105,6 +106,7 @@ class TestIxiaResourceHelper(unittest.TestCase): ixia_rhelper.run_traffic(mock_tprofile) self.assertEqual('fake_samples', ixia_rhelper._queue.get()) + mock_tprofile.update_traffic_profile.assert_called_once() @mock.patch.object(tg_rfc2544_ixia, 'ixnet_api') @@ -524,12 +526,112 @@ class TestIxiaPppoeClientScenario(unittest.TestCase): mock_apply_core_net_cfg.assert_called_once() mock_apply_access_net_cfg.assert_called_once() - def test_create_traffic_model(self): - self.scenario._access_topologies = 'access' - self.scenario._core_topologies = 'core' - self.scenario.create_traffic_model() + @mock.patch.object(tg_rfc2544_ixia.IxiaPppoeClientScenario, + '_get_endpoints_src_dst_id_pairs') + @mock.patch.object(tg_rfc2544_ixia.IxiaPppoeClientScenario, + '_get_endpoints_src_dst_obj_pairs') + def test_create_traffic_model(self, mock_obj_pairs, mock_id_pairs): + uplink_endpoints = ['group1', 'group2'] + downlink_endpoints = ['group3', 'group3'] + mock_id_pairs.return_value = ['xe0', 'xe1', 'xe0', 'xe1'] + mock_obj_pairs.return_value = ['group1', 'group3', 'group2', 'group3'] + mock_tp = mock.Mock() + mock_tp.full_profile = {'uplink_0': 'data', + 'downlink_0': 'data', + 'uplink_1': 'data', + 'downlink_1': 'data' + } + self.scenario.create_traffic_model(mock_tp) + mock_id_pairs.assert_called_once_with(mock_tp.full_profile) + mock_obj_pairs.assert_called_once_with(['xe0', 'xe1', 'xe0', 'xe1']) self.scenario.client.create_ipv4_traffic_model.assert_called_once_with( - 'access', 'core') + uplink_endpoints, downlink_endpoints) + + def test__get_endpoints_src_dst_id_pairs(self): + full_tp = OrderedDict([ + ('uplink_0', {'ipv4': {'port': 'xe0'}}), + ('downlink_0', {'ipv4': {'port': 'xe1'}}), + ('uplink_1', {'ipv4': {'port': 'xe0'}}), + ('downlink_1', {'ipv4': {'port': 'xe3'}})]) + endpoints_src_dst_pairs = ['xe0', 'xe1', 'xe0', 'xe3'] + res = self.scenario._get_endpoints_src_dst_id_pairs(full_tp) + self.assertEqual(res, endpoints_src_dst_pairs) + + def test__get_endpoints_src_dst_id_pairs_wrong_flows_number(self): + full_tp = OrderedDict([ + ('uplink_0', {'ipv4': {'port': 'xe0'}}), + ('downlink_0', {'ipv4': {'port': 'xe1'}}), + ('uplink_1', {'ipv4': {'port': 'xe0'}})]) + with self.assertRaises(RuntimeError): + self.scenario._get_endpoints_src_dst_id_pairs(full_tp) + + def test__get_endpoints_src_dst_id_pairs_no_port_key(self): + full_tp = OrderedDict([ + ('uplink_0', {'ipv4': {'id': 1}}), + ('downlink_0', {'ipv4': {'id': 2}})]) + self.assertEqual( + self.scenario._get_endpoints_src_dst_id_pairs(full_tp), []) + + def test__get_endpoints_src_dst_obj_pairs_tp_with_port_key(self): + endpoints_id_pairs = ['xe0', 'xe1', + 'xe0', 'xe1', + 'xe0', 'xe3', + 'xe0', 'xe3'] + ixia_cfg = { + 'pppoe_client': { + 'sessions_per_port': 4, + 'sessions_per_svlan': 1 + }, + 'flow': { + 'src_ip': [{'tg__0': 'xe0'}, {'tg__0': 'xe2'}], + 'dst_ip': [{'tg__0': 'xe1'}, {'tg__0': 'xe3'}] + } + } + + expected_result = ['tp1_dg1', 'tp3_dg1', 'tp1_dg2', 'tp3_dg1', + 'tp1_dg3', 'tp4_dg1', 'tp1_dg4', 'tp4_dg1'] + + self.scenario._ixia_cfg = ixia_cfg + self.scenario._access_topologies = ['topology1', 'topology2'] + self.scenario._core_topologies = ['topology3', 'topology4'] + self.mock_IxNextgen.get_topology_device_groups.side_effect = \ + [['tp1_dg1', 'tp1_dg2', 'tp1_dg3', 'tp1_dg4'], + ['tp2_dg1', 'tp2_dg2', 'tp2_dg3', 'tp2_dg4'], + ['tp3_dg1'], + ['tp4_dg1']] + res = self.scenario._get_endpoints_src_dst_obj_pairs( + endpoints_id_pairs) + self.assertEqual(res, expected_result) + + def test__get_endpoints_src_dst_obj_pairs_default_flows_mapping(self): + endpoints_id_pairs = [] + ixia_cfg = { + 'pppoe_client': { + 'sessions_per_port': 4, + 'sessions_per_svlan': 1 + }, + 'flow': { + 'src_ip': [{'tg__0': 'xe0'}, {'tg__0': 'xe2'}], + 'dst_ip': [{'tg__0': 'xe1'}, {'tg__0': 'xe3'}] + } + } + + expected_result = ['tp1_dg1', 'tp3_dg1', 'tp1_dg2', 'tp3_dg1', + 'tp1_dg3', 'tp3_dg1', 'tp1_dg4', 'tp3_dg1', + 'tp2_dg1', 'tp4_dg1', 'tp2_dg2', 'tp4_dg1', + 'tp2_dg3', 'tp4_dg1', 'tp2_dg4', 'tp4_dg1'] + + self.scenario._ixia_cfg = ixia_cfg + self.scenario._access_topologies = ['topology1', 'topology2'] + self.scenario._core_topologies = ['topology3', 'topology4'] + self.mock_IxNextgen.get_topology_device_groups.side_effect = \ + [['tp1_dg1', 'tp1_dg2', 'tp1_dg3', 'tp1_dg4'], + ['tp2_dg1', 'tp2_dg2', 'tp2_dg3', 'tp2_dg4'], + ['tp3_dg1'], + ['tp4_dg1']] + res = self.scenario._get_endpoints_src_dst_obj_pairs( + endpoints_id_pairs) + self.assertEqual(res, expected_result) def test_run_protocols(self): self.scenario.client.is_protocols_running.return_value = True -- cgit 1.2.3-korg