Improve IXIA IxNetwork library and traffic profile (3) 35/55435/10
authorRodolfo Alonso Hernandez <rodolfo.alonso.hernandez@intel.com>
Wed, 11 Apr 2018 17:32:52 +0000 (18:32 +0100)
committerRodolfo Alonso Hernandez <rodolfo.alonso.hernandez@intel.com>
Tue, 12 Jun 2018 07:31:13 +0000 (08:31 +0100)
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 <rodolfo.alonso.hernandez@intel.com>
Signed-off-by: Emma Foley <emma.l.foley@intel.com>
yardstick/network_services/libs/ixia_libs/ixnet/ixnet_api.py
yardstick/network_services/traffic_profile/ixia_rfc2544.py
yardstick/tests/unit/network_services/libs/ixia_libs/test_ixnet_api.py

index 70d0eee..a8464e9 100644 (file)
@@ -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])
index ec354c8..453c586 100644 (file)
@@ -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
 
index 5bc2d8b..460728c 100644 (file)
@@ -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)