From 391d020a1acaeca1831c9f43d6c98b3ca4b57ee8 Mon Sep 17 00:00:00 2001
From: Myron Sosyak <myronx.sosyak@intel.com>
Date: Wed, 8 Aug 2018 11:46:46 +0100
Subject: Add QinQ support for IXIA traffic profile

Implemented logic for parsing and applying QinQ in IXIA traffic profile.
For QinQ configuration add QinQ section to outerl2 section.
For example:
...
  outerl2:
    QinQ:
      S-VLAN:
        id: 128
        priority: 0
        cfi: 0
      C-VLAN:
        id: 512
        priority: 0
        cfi: 0
...

JIRA: YARDSTICK-1370

Change-Id: I29caa9eb7dd038e7de66faab316f03c7c29a742f
Signed-off-by: Myron Sosyak <myronx.sosyak@intel.com>
---
 .../libs/ixia_libs/ixnet/ixnet_api.py              | 54 ++++++++++++++++++++--
 .../traffic_profile/ixia_rfc2544.py                |  1 +
 .../libs/ixia_libs/test_ixnet_api.py               | 43 ++++++++++++++++-
 3 files changed, 92 insertions(+), 6 deletions(-)

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 06c6b0bcb..8274ff9ce 100644
--- a/yardstick/network_services/libs/ixia_libs/ixnet/ixnet_api.py
+++ b/yardstick/network_services/libs/ixia_libs/ixnet/ixnet_api.py
@@ -34,6 +34,13 @@ PROTO_UDP = 'udp'
 PROTO_TCP = 'tcp'
 PROTO_VLAN = 'vlan'
 
+SINGLE_VALUE = "singleValue"
+
+S_VLAN = 0
+C_VLAN = 1
+
+ETHER_TYPE_802_1ad = '0x88a8'
+
 IP_VERSION_4_MASK = 24
 IP_VERSION_6_MASK = 64
 
@@ -367,10 +374,28 @@ class IxNextgen(object):  # pragma: no cover
                 traffic_param['outer_l2']['framesize'])
             srcmac = str(traffic_param.get('srcmac', '00:00:00:00:00:01'))
             dstmac = str(traffic_param.get('dstmac', '00:00:00:00:00:02'))
-            # NOTE(ralonsoh): add QinQ tagging when
-            # traffic_param['outer_l2']['QinQ'] exists.
-            # s_vlan = traffic_param['outer_l2']['QinQ']['S-VLAN']
-            # c_vlan = traffic_param['outer_l2']['QinQ']['C-VLAN']
+
+            if traffic_param['outer_l2']['QinQ']:
+                s_vlan = traffic_param['outer_l2']['QinQ']['S-VLAN']
+                c_vlan = traffic_param['outer_l2']['QinQ']['C-VLAN']
+
+                field_descriptor = self._get_field_in_stack_item(
+                    self._get_stack_item(fg_id, PROTO_ETHERNET)[0],
+                    'etherType')
+
+                self.ixnet.setMultiAttribute(field_descriptor,
+                                             '-auto', 'false',
+                                             '-singleValue', ETHER_TYPE_802_1ad,
+                                             '-fieldValue', ETHER_TYPE_802_1ad,
+                                             '-valueType', SINGLE_VALUE)
+
+                self._append_procotol_to_stack(
+                    PROTO_VLAN, config_element + '/stack:"ethernet-1"')
+                self._append_procotol_to_stack(
+                    PROTO_VLAN, config_element + '/stack:"ethernet-1"')
+
+                self._update_vlan_tag(fg_id, s_vlan, S_VLAN)
+                self._update_vlan_tag(fg_id, c_vlan, C_VLAN)
 
             self.ixnet.setMultiAttribute(
                 config_element + '/transmissionControl',
@@ -391,6 +416,27 @@ class IxNextgen(object):  # pragma: no cover
                 self._get_stack_item(fg_id, PROTO_ETHERNET)[0],
                 'sourceAddress', srcmac)
 
+    def _update_vlan_tag(self, fg_id, params, vlan=0):
+        field_to_param_map = {
+            'vlanUserPriority': 'priority',
+            'cfi': 'cfi',
+            'vlanID': 'id'
+        }
+        for field, param in field_to_param_map.items():
+            value = params.get(param)
+            if value:
+                field_descriptor = self._get_field_in_stack_item(
+                    self._get_stack_item(fg_id, PROTO_VLAN)[vlan],
+                    field)
+
+                self.ixnet.setMultiAttribute(field_descriptor,
+                                             '-auto', 'false',
+                                             '-singleValue', value,
+                                             '-fieldValue', value,
+                                             '-valueType', SINGLE_VALUE)
+
+        self.ixnet.commit()
+
     def _update_ipv4_address(self, ip_descriptor, field, ip_address, seed,
                              mask, count):
         """Set the IPv4 address in a config element stack IP field
diff --git a/yardstick/network_services/traffic_profile/ixia_rfc2544.py b/yardstick/network_services/traffic_profile/ixia_rfc2544.py
index 2086273e6..26dc1fe04 100644
--- a/yardstick/network_services/traffic_profile/ixia_rfc2544.py
+++ b/yardstick/network_services/traffic_profile/ixia_rfc2544.py
@@ -88,6 +88,7 @@ class IXIARFC2544Profile(trex_traffic_profile.TrexProfile):
                     'outer_l2': {
                         'framesize': value['outer_l2']['framesize'],
                         'framesPerSecond': True,
+                        'QinQ': value['outer_l2'].get('QinQ'),
                         'srcmac': mac['src_mac_{}'.format(port_index)],
                         'dstmac': mac['dst_mac_{}'.format(port_index)],
                     },
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 5970ecae6..e078d70ad 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
@@ -16,6 +16,8 @@ import mock
 import IxNetwork
 import unittest
 
+from copy import deepcopy
+
 from yardstick.common import exceptions
 from yardstick.network_services.libs.ixia_libs.ixnet import ixnet_api
 
@@ -31,7 +33,8 @@ TRAFFIC_PARAMETERS = {
         'rate': 10000.5,
         'rate_unit': 'fps',
         'outer_l2': {
-            'framesize': {'64B': '25', '256B': '75'}
+            'framesize': {'64B': '25', '256B': '75'},
+            'QinQ': None
         },
         'outer_l3': {
             'count': 512,
@@ -61,7 +64,8 @@ TRAFFIC_PARAMETERS = {
         'rate': 75.2,
         'rate_unit': '%',
         'outer_l2': {
-            'framesize': {'128B': '35', '1024B': '65'}
+            'framesize': {'128B': '35', '1024B': '65'},
+            'QinQ': None
         },
         'outer_l3': {
             'count': 1024,
@@ -344,6 +348,41 @@ class TestIxNextgen(unittest.TestCase):
                       '-type', 'continuous', '-duration', 50)
         ])
 
+    def test_update_frame_qinq(self):
+        with mock.patch.object(self.ixnet_gen,
+                               '_get_config_element_by_flow_group_name',
+                               return_value='cfg_element'), \
+             mock.patch.object(self.ixnet_gen, '_update_frame_mac'),\
+             mock.patch.object(self.ixnet_gen, '_get_stack_item',
+                               return_value='item'), \
+             mock.patch.object(self.ixnet_gen, '_get_field_in_stack_item',
+                               return_value='field'):
+
+            traffic_parameters = deepcopy(TRAFFIC_PARAMETERS)
+            traffic_parameters[UPLINK]['outer_l2']['QinQ'] = {
+                'S-VLAN': {'id': 128,
+                           'priority': 1,
+                           'cfi': 0},
+                'C-VLAN': {'id': 512,
+                           'priority': 0,
+                           'cfi': 2}
+            }
+
+            self.ixnet_gen.update_frame(traffic_parameters, 50)
+
+        self.ixnet_gen.ixnet.setMultiAttribute.assert_has_calls([
+            mock.call('field', '-auto', 'false', '-singleValue', '0x88a8',
+                      '-fieldValue', '0x88a8', '-valueType', 'singleValue'),
+            mock.call('field', '-auto', 'false', '-singleValue', 1,
+                      '-fieldValue', 1, '-valueType', 'singleValue'),
+            mock.call('field', '-auto', 'false', '-singleValue', 128,
+                      '-fieldValue', 128, '-valueType', 'singleValue'),
+            mock.call('field', '-auto', 'false', '-singleValue', 512,
+                      '-fieldValue', 512, '-valueType', 'singleValue'),
+            mock.call('field', '-auto', 'false', '-singleValue', 2,
+                      '-fieldValue', 2, '-valueType', 'singleValue')
+        ], any_order=True)
+
     def test_update_frame_flow_not_present(self):
         with mock.patch.object(
                 self.ixnet_gen, '_get_config_element_by_flow_group_name',
-- 
cgit