From: Rodolfo Alonso Hernandez Date: Wed, 11 Apr 2018 17:32:52 +0000 (+0100) Subject: Improve IXIA IxNetwork library and traffic profile (3) X-Git-Tag: opnfv-7.0.0~263 X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F35%2F55435%2F10;p=yardstick.git Improve IXIA IxNetwork library and traffic profile (3) This patch modifies IP packet parameters. "IxNextgen.update_ip_packet" modifies the L3 packet according to the test case and setup the IP addresses. JIRA: YARDSTICK-1116 Change-Id: I46ff75ab1989d0e6f5cc876418a015386717e06f Signed-off-by: Rodolfo Alonso Hernandez Signed-off-by: Emma Foley --- 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 70d0eeed8..a8464e936 100644 --- a/yardstick/network_services/libs/ixia_libs/ixnet/ixnet_api.py +++ b/yardstick/network_services/libs/ixia_libs/ixnet/ixnet_api.py @@ -23,12 +23,17 @@ log = logging.getLogger(__name__) IP_VERSION_4 = 4 IP_VERSION_6 = 6 + +PROTO_ETHERNET = 'ethernet' PROTO_IPV4 = 'ipv4' PROTO_IPV6 = 'ipv6' PROTO_UDP = 'udp' PROTO_TCP = 'tcp' PROTO_VLAN = 'vlan' +IP_VERSION_4_MASK = '0.0.0.255' +IP_VERSION_6_MASK = '0:0:0:0:0:0:0:ff' + # NOTE(ralonsoh): this pragma will be removed in the last patch of this series class IxNextgen(object): # pragma: no cover @@ -51,17 +56,6 @@ class IxNextgen(object): # pragma: no cover "Store-Forward_Max_latency_ns": 'Store-Forward Max Latency (ns)', } - RANDOM_MASK_MAP = { - IP_VERSION_4: '0.0.0.255', - IP_VERSION_6: '0:0:0:0:0:0:0:ff', - } - - MODE_SEEDS_MAP = { - 0: ('uplink', ['256', '2048']), - } - - MODE_SEEDS_DEFAULT = 'downlink', ['2048', '256'] - @staticmethod def get_config(tg_cfg): card = [] @@ -119,6 +113,34 @@ class IxNextgen(object): # pragma: no cover flow_group_name): return traffic_item + '/configElement:' + flow_group_name + def _get_stack_item(self, flow_group_name, protocol_name): + """Return the stack item given the flow group name and the proto name + + :param flow_group_name: (str) flow group name + :param protocol_name: (str) protocol name, referred to PROTO_* + constants + :return: list of stack item descriptors + """ + celement = self._get_config_element_by_flow_group_name(flow_group_name) + if not celement: + raise exceptions.IxNetworkFlowNotPresent( + flow_group=flow_group_name) + stack_items = self.ixnet.getList(celement, 'stack') + return [s_i for s_i in stack_items if protocol_name in s_i] + + def _get_field_in_stack_item(self, stack_item, field_name): + """Return the field in a stack item given the name + + :param stack_item: (str) stack item descriptor + :param field_name: (str) field name + :return: (str) field descriptor + """ + fields = self.ixnet.getList(stack_item, 'field') + for field in (field for field in fields if field_name in field): + return field + raise exceptions.IxNetworkFieldNotPresentInStackItem( + field_name=field_name, stack_item=stack_item) + @staticmethod def _parse_framesize(framesize): """Parse "framesize" config param. to return a list of weighted pairs @@ -272,19 +294,6 @@ class IxNextgen(object): # pragma: no cover self._create_flow_groups() self._setup_config_elements() - def _get_field_in_stack_item(self, stack_item, field_name): - """List all fields in a stack item an return t - - :param stack_item: (str) stack item descriptor - :param field_name: (str) field name - :return: (str) field descriptor - """ - fields = self.ixnet.getList(stack_item, 'field') - for field in (field for field in fields if field_name in field): - return field - raise exceptions.IxNetworkFieldNotPresentInStackItem( - field_name=field_name, stack_item=stack_item) - def _update_frame_mac(self, ethernet_descriptor, field, mac_address): """Set the MAC address in a config element stack Ethernet field @@ -321,11 +330,10 @@ class IxNextgen(object): # pragma: no cover the injection parameter for each flow group. """ for traffic_param in traffic.values(): - config_element = self._get_config_element_by_flow_group_name( - str(traffic_param['id'])) + fg_id = str(traffic_param['id']) + config_element = self._get_config_element_by_flow_group_name(fg_id) if not config_element: - raise exceptions.IxNetworkFlowNotPresent( - flow_group=traffic_param['id']) + raise exceptions.IxNetworkFlowNotPresent(flow_group=fg_id) type = traffic_param.get('traffic_type', 'fixedDuration') duration = traffic_param.get('duration', 30) @@ -352,56 +360,61 @@ class IxNextgen(object): # pragma: no cover self.ixnet.commit() self._update_frame_mac( - config_element + '/stack:"ethernet-1"', + self._get_stack_item(fg_id, PROTO_ETHERNET)[0], 'destinationAddress', dstmac) self._update_frame_mac( - config_element + '/stack:"ethernet-1"', + self._get_stack_item(fg_id, PROTO_ETHERNET)[0], 'sourceAddress', srcmac) - def set_random_ip_multi_attribute(self, ipv4, seed, fixed_bits, random_mask, l3_count): - self.ixnet.setMultiAttribute( - ipv4, - '-seed', str(seed), - '-fixedBits', str(fixed_bits), - '-randomMask', str(random_mask), - '-valueType', 'random', - '-countValue', str(l3_count)) + def _update_ipv4_address(self, ip_descriptor, field, ip_address, seed, + mask, count): + """Set the IPv4 address in a config element stack IP field + + :param ip_descriptor: (str) IP descriptor, e.g.: + /traffic/trafficItem:1/configElement:1/stack:"ipv4-2" + :param field: (str) field name, e.g.: scrIp, dstIp + :param ip_address: (str) IP address + :param seed: (int) seed length + :param mask: (str) IP address mask + :param count: (int) number of random IPs to generate + """ + field_descriptor = self._get_field_in_stack_item(ip_descriptor, + field) + self.ixnet.setMultiAttribute(field_descriptor, + '-seed', seed, + '-fixedBits', ip_address, + '-randomMask', mask, + '-valueType', 'random', + '-countValue', count) + self.ixnet.commit() - # NOTE(ralonsoh): to be updated in next patchset - # pragma: no cover - def set_random_ip_multi_attributes(self, ip, version, seeds, l3): - try: - random_mask = self.RANDOM_MASK_MAP[version] - except KeyError: - raise ValueError('Unknown version %s' % version) - - l3_count = l3['count'] - if 'srcIp' in ip: - fixed_bits = l3['srcip4'] - self.set_random_ip_multi_attribute(ip, seeds[0], fixed_bits, random_mask, l3_count) - if 'dstIp' in ip: - fixed_bits = l3['dstip4'] - self.set_random_ip_multi_attribute(ip, seeds[1], fixed_bits, random_mask, l3_count) + def update_ip_packet(self, traffic): + """Update the IP packet - # NOTE(ralonsoh): to be updated in next patchset - # pragma: no cover - def add_ip_header(self, params, version): - for _, ep, i in self.iter_over_get_lists('/traffic', 'trafficItem', "configElement", 1): - iter1 = (v['outer_l3'] for v in params.values() if str(v['id']) == str(i)) - try: - l3 = next(iter1, {}) - seeds = self.MODE_SEEDS_MAP.get(i, self.MODE_SEEDS_DEFAULT)[1] - except (KeyError, IndexError): - continue - - for _, ip_bits, _ in self.iter_over_get_lists(ep, 'stack', 'field'): - self.set_random_ip_multi_attributes(ip_bits, version, seeds, l3) + NOTE: Only IPv4 is currently supported. + :param traffic: list of traffic elements; each traffic element contains + the injection parameter for each flow group. + """ + # NOTE(ralonsoh): L4 configuration is not set. + for traffic_param in traffic.values(): + fg_id = str(traffic_param['id']) + if not self._get_config_element_by_flow_group_name(fg_id): + raise exceptions.IxNetworkFlowNotPresent(flow_group=fg_id) - self.ixnet.commit() + count = traffic_param['outer_l3']['count'] + srcip4 = str(traffic_param['outer_l3']['srcip4']) + dstip4 = str(traffic_param['outer_l3']['dstip4']) + + self._update_ipv4_address( + self._get_stack_item(fg_id, PROTO_IPV4)[0], + 'srcIp', srcip4, 1, IP_VERSION_4_MASK, count) + self._update_ipv4_address( + self._get_stack_item(fg_id, PROTO_IPV4)[0], + 'dstIp', dstip4, 1, IP_VERSION_4_MASK, count) def _build_stats_map(self, view_obj, name_map): return {data_yardstick: self.ixnet.execute( - 'getColumnValues', view_obj, data_ixia) + 'getColumnValues', view_obj, data_ixia) for data_yardstick, data_ixia in name_map.items()} def get_statistics(self): @@ -421,9 +434,7 @@ class IxNextgen(object): # pragma: no cover self.LATENCY_NAME_MAP)) return stats - # NOTE(ralonsoh): to be updated in next patchset - # pragma: no cover - def ix_start_traffic(self): + def start_traffic(self): tis = self.ixnet.getList('/traffic', 'trafficItem') for ti in tis: self.ixnet.execute('generate', [ti]) diff --git a/yardstick/network_services/traffic_profile/ixia_rfc2544.py b/yardstick/network_services/traffic_profile/ixia_rfc2544.py index ec354c8df..453c58622 100644 --- a/yardstick/network_services/traffic_profile/ixia_rfc2544.py +++ b/yardstick/network_services/traffic_profile/ixia_rfc2544.py @@ -84,8 +84,8 @@ class IXIARFC2544Profile(TrexProfile): if key.startswith((self.UPLINK, self.DOWNLINK)): value["iload"] = str(self.rate) ixia_obj.update_frame(traffic) - ixia_obj.add_ip_header(traffic, 4) - ixia_obj.ix_start_traffic() + ixia_obj.update_ip_packet(traffic) + ixia_obj.start_traffic() self.tmp_drop = 0 self.tmp_throughput = 0 diff --git a/yardstick/tests/unit/network_services/libs/ixia_libs/test_ixnet_api.py b/yardstick/tests/unit/network_services/libs/ixia_libs/test_ixnet_api.py index 5bc2d8b16..460728c98 100644 --- a/yardstick/tests/unit/network_services/libs/ixia_libs/test_ixnet_api.py +++ b/yardstick/tests/unit/network_services/libs/ixia_libs/test_ixnet_api.py @@ -33,6 +33,7 @@ TRAFFIC_PARAMETERS = { 'framesize': {'64B': '25', '256B': '75'} }, 'outer_l3': { + 'count': 512, 'dscp': 0, 'dstip4': '152.16.40.20', 'proto': 'udp', @@ -163,6 +164,40 @@ class TestIxNextgen(unittest.TestCase): 'flow_group_01') self.assertIsNone(output) + def test__get_stack_item(self): + ixnet_gen = ixnet_api.IxNextgen() + ixnet_gen._ixnet = self.ixnet + ixnet_gen._ixnet.getList.return_value = ['tcp1', 'tcp2', 'udp'] + with mock.patch.object( + ixnet_gen, '_get_config_element_by_flow_group_name') as \ + mock_get_cfg_element: + mock_get_cfg_element.return_value = 'cfg_element' + output = ixnet_gen._get_stack_item(mock.ANY, ixnet_api.PROTO_TCP) + self.assertEqual(['tcp1', 'tcp2'], output) + + def test__get_stack_item_no_config_element(self): + ixnet_gen = ixnet_api.IxNextgen() + ixnet_gen._ixnet = self.ixnet + with mock.patch.object( + ixnet_gen, '_get_config_element_by_flow_group_name', + return_value=None): + with self.assertRaises(exceptions.IxNetworkFlowNotPresent): + ixnet_gen._get_stack_item(mock.ANY, mock.ANY) + + def test__get_field_in_stack_item(self): + ixnet_gen = ixnet_api.IxNextgen() + ixnet_gen._ixnet = self.ixnet + ixnet_gen._ixnet.getList.return_value = ['field1', 'field2'] + output = ixnet_gen._get_field_in_stack_item(mock.ANY, 'field2') + self.assertEqual('field2', output) + + def test__get_field_in_stack_item_no_field_present(self): + ixnet_gen = ixnet_api.IxNextgen() + ixnet_gen._ixnet = self.ixnet + ixnet_gen._ixnet.getList.return_value = ['field1', 'field2'] + with self.assertRaises(exceptions.IxNetworkFieldNotPresentInStackItem): + ixnet_gen._get_field_in_stack_item(mock.ANY, 'field3') + def test__parse_framesize(self): ixnet_gen = ixnet_api.IxNextgen() ixnet_gen._ixnet = self.ixnet @@ -336,20 +371,6 @@ class TestIxNextgen(unittest.TestCase): mock__create_flow_groups.assert_called_once() mock__setup_config_elements.assert_called_once() - def test__get_field_in_stack_item(self): - ixnet_gen = ixnet_api.IxNextgen() - ixnet_gen._ixnet = self.ixnet - ixnet_gen._ixnet.getList.return_value = ['field1', 'field2'] - output = ixnet_gen._get_field_in_stack_item(mock.ANY, 'field2') - self.assertEqual('field2', output) - - def test__get_field_in_stack_item_field_not_present(self): - ixnet_gen = ixnet_api.IxNextgen() - ixnet_gen._ixnet = self.ixnet - ixnet_gen._ixnet.getList.return_value = ['field1', 'field2'] - with self.assertRaises(exceptions.IxNetworkFieldNotPresentInStackItem): - ixnet_gen._get_field_in_stack_item(mock.ANY, 'field3') - def test__update_frame_mac(self): ixnet_gen = ixnet_api.IxNextgen() ixnet_gen._ixnet = self.ixnet @@ -370,7 +391,11 @@ class TestIxNextgen(unittest.TestCase): ixnet_gen, '_get_config_element_by_flow_group_name', return_value='cfg_element'), \ mock.patch.object(ixnet_gen, '_update_frame_mac') as \ - mock_update_frame: + mock_update_frame, \ + mock.patch.object(ixnet_gen, '_get_stack_item') as \ + mock_get_stack_item: + mock_get_stack_item.side_effect = [['item1'], ['item2'], + ['item3'], ['item4']] ixnet_gen.update_frame(TRAFFIC_PARAMETERS) self.assertEqual(6, len(ixnet_gen.ixnet.setMultiAttribute.mock_calls)) @@ -385,14 +410,6 @@ class TestIxNextgen(unittest.TestCase): with self.assertRaises(exceptions.IxNetworkFlowNotPresent): ixnet_gen.update_frame(TRAFFIC_PARAMETERS) - def test_set_random_ip_multi_attribute(self): - ixnet_gen = ixnet_api.IxNextgen() - ixnet_gen._ixnet = self.ixnet - ixnet_gen.set_random_ip_multi_attribute('ipv4', 20, 30, 40, 100) - ixnet_gen.ixnet.setMultiAttribute.assert_called_once_with( - 'ipv4', '-seed', '20', '-fixedBits', '30', '-randomMask', '40', - '-valueType', 'random', '-countValue', '100') - def test_get_statistics(self): ixnet_gen = ixnet_api.IxNextgen() port_statistics = '::ixNet::OBJ-/statistics/view:"Port Statistics"' @@ -404,3 +421,36 @@ class TestIxNextgen(unittest.TestCase): mock_build_stats.assert_has_calls([ mock.call(port_statistics, ixnet_gen.PORT_STATS_NAME_MAP), mock.call(flow_statistics, ixnet_gen.LATENCY_NAME_MAP)]) + + def test__update_ipv4_address(self): + ixnet_gen = ixnet_api.IxNextgen() + ixnet_gen._ixnet = self.ixnet + with mock.patch.object(ixnet_gen, '_get_field_in_stack_item', + return_value='field_desc'): + ixnet_gen._update_ipv4_address(mock.ANY, mock.ANY, '192.168.1.1', + 100, '255.255.255.0', 25) + ixnet_gen.ixnet.setMultiAttribute.assert_called_once_with( + 'field_desc', '-seed', 100, '-fixedBits', '192.168.1.1', + '-randomMask', '255.255.255.0', '-valueType', 'random', + '-countValue', 25) + + def test_update_ip_packet(self): + ixnet_gen = ixnet_api.IxNextgen() + ixnet_gen._ixnet = self.ixnet + with mock.patch.object(ixnet_gen, '_update_ipv4_address') as \ + mock_update_add, \ + mock.patch.object(ixnet_gen, '_get_stack_item'), \ + mock.patch.object(ixnet_gen, + '_get_config_element_by_flow_group_name', return_value='celm'): + ixnet_gen.update_ip_packet(TRAFFIC_PARAMETERS) + + self.assertEqual(4, len(mock_update_add.mock_calls)) + + def test_update_ip_packet_exception_no_config_element(self): + ixnet_gen = ixnet_api.IxNextgen() + ixnet_gen._ixnet = self.ixnet + with mock.patch.object(ixnet_gen, + '_get_config_element_by_flow_group_name', + return_value=None): + with self.assertRaises(exceptions.IxNetworkFlowNotPresent): + ixnet_gen.update_ip_packet(TRAFFIC_PARAMETERS)