Xena_Flow_Fix: Fixes multistream of greater than 64k values 55/57555/10
authorChristian Trautman <ctrautma@redhat.com>
Thu, 17 May 2018 21:46:29 +0000 (17:46 -0400)
committerChristian Trautman <ctrautma@redhat.com>
Mon, 18 Jun 2018 11:22:40 +0000 (07:22 -0400)
Adds calculations to deal with values greater than 64k by doing
a square root of the multistream value and using two mods
to create the closet possible value that will work within the
current implementation of multistream in VSPerf.

JIRA: VSPERF-575

Change-Id: I9dab4bbac094a394a11ed74fe2cd88fbe7079fc7
Signed-off-by: Christian Trautman <ctrautma@redhat.com>
docs/testing/user/configguide/trafficgen.rst
tools/pkt_gen/xena/XenaDriver.py
tools/pkt_gen/xena/json/xena_json.py
tools/pkt_gen/xena/xena.py

index 42141c5..2636626 100644 (file)
@@ -682,6 +682,14 @@ or modify the length of the learning by modifying the following settings.
     TRAFFICGEN_XENA_CONT_PORT_LEARNING_ENABLED = False
     TRAFFICGEN_XENA_CONT_PORT_LEARNING_DURATION = 3
 
+Multistream Modifier
+~~~~~~~~~~~~~~~~~~~~
+
+Xena has a modifier maximum value or 64k in size. For this reason when specifying
+Multistream values of greater than 64k for Layer 2 or Layer 3 it will use two
+modifiers that may be modified to a value that can be square rooted to create the
+two modifiers. You will see a log notification for the new value that was calculated.
+
 MoonGen
 -------
 
index cdc8283..ac9cef1 100644 (file)
@@ -30,6 +30,7 @@ through socket commands and returning different statistics.
 """
 import locale
 import logging
+import math
 import socket
 import struct
 import sys
@@ -86,6 +87,26 @@ CMD_VERSION = 'c_versionno ?'
 _LOCALE = locale.getlocale()[1]
 _LOGGER = logging.getLogger(__name__)
 
+class ModSet(object):
+    """
+    Mod set attribute tracker
+    """
+    def __init__(self, **kwargs):
+        """ Constructor
+        All mods default to False
+        :param kwargs: Any class attribute can be set here.
+        """
+        self.mod_src_mac = False
+        self.mod_dst_mac = False
+        self.mod_src_ip = False
+        self.mod_dst_ip = False
+        self.mod_src_port = False
+        self.mod_dst_port = False
+
+        for (key, value) in kwargs.items():
+            if hasattr(self, key):
+                setattr(self, key, value)
+
 
 class SimpleSocket(object):
     """
@@ -639,57 +660,98 @@ class XenaStream(object):
         """
         return self._stream_id
 
-    def enable_multistream(self, flows, layer):
+    def enable_multistream(self, flows, mod_class):
         """
-        Basic implementation of multi stream. Enable multi stream by setting
-        modifiers on the stream
-        :param flows: Numbers of flows or end range
-        :param layer: layer to enable multi stream as str. Acceptable values
-        are L2, L3, or L4
+        Implementation of multi stream. Enable multi stream by setting
+        modifiers on the stream. If no mods are selected, src_ip mod will be used.
+        :param flows: Numbers of flows, Values greater than 65535 will square rooted
+                      to the closest value. Xena mods are limited to 4 bytes.
+        :param mod_class: ModSet object
         :return: True if success False otherwise
         """
         if not self._header_protocol:
             raise RuntimeError(
                 "Please set a protocol header before calling this method.")
-
-        # byte offsets for setting the modifier
-        offsets = {
-            'L2': [0, 6],
-            'L3': [32, 36] if 'VLAN' in self._header_protocol else [28, 32],
-            'L4': [38, 40] if 'VLAN' in self._header_protocol else [34, 36]
-        }
-
-        responses = list()
-        if layer in offsets.keys() and flows > 0:
-            command = make_port_command(
-                CMD_STREAM_MODIFIER_COUNT + ' [{}]'.format(self._stream_id) +
-                ' 2', self._xena_port)
-            responses.append(self._manager.driver.ask_verify(command))
-            command = make_port_command(
-                CMD_STREAM_MODIFIER + ' [{},0] {} 0xFFFF0000 INC 1'.format(
-                    self._stream_id, offsets[layer][0]), self._xena_port)
-            responses.append(self._manager.driver.ask_verify(command))
-            command = make_port_command(
-                CMD_STREAM_MODIFIER_RANGE + ' [{},0] 0 1 {}'.format(
-                    self._stream_id, flows), self._xena_port)
-            responses.append(self._manager.driver.ask_verify(command))
-            command = make_port_command(
-                CMD_STREAM_MODIFIER + ' [{},1] {} 0xFFFF0000 INC 1'.format(
-                    self._stream_id, offsets[layer][1]), self._xena_port)
-            responses.append(self._manager.driver.ask_verify(command))
-            command = make_port_command(
-                CMD_STREAM_MODIFIER_RANGE + ' [{},1] 0 1 {}'.format(
-                    self._stream_id, flows), self._xena_port)
-            responses.append(self._manager.driver.ask_verify(command))
-            return all(responses)  # return True if they all worked
-        elif flows < 1:
-            _LOGGER.warning(
-                'No flows specified in enable multistream. Bypassing...')
-            return False
+        # maximum value for a Xena modifier is 65535 (unsigned int). If flows
+        # is greater than 65535 we have to do two mods getting as close as we
+        # can with square rooting the flow count.
+        if flows > 4294836225:
+            _LOGGER.debug('Flow mods exceeds highest value, changing to 4294836225')
+            flows = 4294836225
+        if flows <= 65535:
+            mod1 = flows
+            mod2 = 0
         else:
-            raise NotImplementedError(
-                "Non-implemented stream layer in method enable multistream ",
-                "layer=", layer)
+            mod1, mod2 = int(math.sqrt(flows)), int(math.sqrt(flows))
+            _LOGGER.debug('Flow count modified to %s', mod1*mod2)
+        offset_list = list()
+        if not any([mod_class.mod_src_mac, mod_class.mod_dst_mac, mod_class.mod_src_ip,
+                    mod_class.mod_dst_ip, mod_class.mod_src_port, mod_class.mod_dst_port]):
+            # no mods were selected, default to src ip only
+            mod_class.mod_src_ip = True
+        if mod_class.mod_src_mac:
+            offset_list.append(3)
+        if mod_class.mod_dst_mac:
+            offset_list.append(9)
+        if mod_class.mod_src_ip:
+            offset_list.append(32 if 'VLAN' in self._header_protocol else 28)
+        if mod_class.mod_dst_ip:
+            offset_list.append(36 if 'VLAN' in self._header_protocol else 32)
+        if mod_class.mod_src_port:
+            offset_list.append(38 if 'VLAN' in self._header_protocol else 34)
+        if mod_class.mod_dst_port:
+            offset_list.append(40 if 'VLAN' in self._header_protocol else 36)
+        # calculate how many mods we have to do
+        countertotal = len(offset_list)
+        if mod2:
+            # to handle flows greater than 65535 we will need more mods for
+            # layer 2 and 3
+            for mod in [mod_class.mod_src_mac, mod_class.mod_dst_mac,
+                        mod_class.mod_src_ip, mod_class.mod_dst_ip]:
+                if mod:
+                    countertotal += 1
+        command = make_port_command(
+            CMD_STREAM_MODIFIER_COUNT + ' [{}]'.format(self._stream_id) +
+            ' {}'.format(countertotal), self._xena_port)
+        responses = list()
+        responses.append(self._manager.driver.ask_verify(command))
+        modcounter = 0
+        for offset in offset_list:
+            if (mod_class.mod_dst_port or mod_class.mod_src_port) and \
+                    (offset >= 38 if 'VLAN' in self._header_protocol else 34):
+                # only do a 1 mod for udp ports at max 65535
+                newmod1 = 65535 if flows >= 65535 else flows
+                command = make_port_command(
+                    CMD_STREAM_MODIFIER + ' [{},{}] {} 0xFFFF0000 INC 1'.format(
+                        self._stream_id, modcounter, offset), self._xena_port)
+                responses.append(self._manager.driver.ask_verify(command))
+                command = make_port_command(
+                    CMD_STREAM_MODIFIER_RANGE + ' [{},{}] 0 1 {}'.format(
+                        self._stream_id, modcounter, newmod1 - 1), self._xena_port)
+                responses.append(self._manager.driver.ask_verify(command))
+            else:
+                command = make_port_command(
+                    CMD_STREAM_MODIFIER + ' [{},{}] {} 0xFFFF0000 INC 1'.format(
+                        self._stream_id, modcounter, offset), self._xena_port)
+                responses.append(self._manager.driver.ask_verify(command))
+                command = make_port_command(
+                    CMD_STREAM_MODIFIER_RANGE + ' [{},{}] 0 1 {}'.format(
+                        self._stream_id, modcounter, mod1 - 1), self._xena_port)
+                responses.append(self._manager.driver.ask_verify(command))
+                # if we have a second modifier set the modifier to mod2 and to
+                # incremement once every full rotation of mod 1
+                if mod2:
+                    modcounter += 1
+                    command = make_port_command(
+                        CMD_STREAM_MODIFIER + ' [{},{}] {} 0xFFFF0000 INC {}'.format(
+                            self._stream_id, modcounter, offset-2, mod1), self._xena_port)
+                    responses.append(self._manager.driver.ask_verify(command))
+                    command = make_port_command(
+                        CMD_STREAM_MODIFIER_RANGE + ' [{},{}] 0 1 {}'.format(
+                            self._stream_id, modcounter, mod2), self._xena_port)
+                    responses.append(self._manager.driver.ask_verify(command))
+            modcounter += 1
+        return all(responses)  # return True if they all worked
 
     def get_stream_data(self):
         """
index df2aa55..e56b412 100644 (file)
@@ -26,6 +26,7 @@ Xena JSON module
 from collections import OrderedDict
 import locale
 import logging
+import math
 import os
 
 from tools.pkt_gen.xena.json import json_utilities
@@ -71,30 +72,87 @@ class XenaJSON(object):
             3: ('Dest IP Addr', 'Src IP Addr'),
             4: ('Dest Port', 'Src Port')
         }
-        segments = [
-            {
-                "Offset": 0,
-                "Mask": "//8=",  # mask of 255/255
-                "Action": "INC",
-                "StartValue": 0,
-                "StopValue": stop_value,
-                "StepValue": 1,
-                "RepeatCount": 1,
-                "SegmentId": seg_uuid,
-                "FieldName": field_name[int(layer)][0]
-            },
-            {
-                "Offset": 0,
-                "Mask": "//8=",  # mask of 255/255
-                "Action": "INC",
-                "StartValue": 0,
-                "StopValue": stop_value,
-                "StepValue": 1,
-                "RepeatCount": 1,
-                "SegmentId": seg_uuid,
-                "FieldName": field_name[int(layer)][1]
-            }
-        ]
+
+        if stop_value > 4294836225:
+            _LOGGER.debug('Flow mods exceeds highest value, changing to 4294836225')
+            stop_value = 4294836225
+
+        if stop_value <= 65535 or layer == 4:
+            segments = [
+                {
+                    "Offset": 0 if layer == 4 else 2,
+                    "Mask": "//8=",  # mask of 255/255
+                    "Action": "INC",
+                    "StartValue": 0,
+                    "StopValue": stop_value - 1,
+                    "StepValue": 1,
+                    "RepeatCount": 1,
+                    "SegmentId": seg_uuid,
+                    "FieldName": field_name[int(layer)][0]
+                },
+                {
+                    "Offset": 0 if layer == 4 else 2,
+                    "Mask": "//8=",  # mask of 255/255
+                    "Action": "INC",
+                    "StartValue": 0,
+                    "StopValue": stop_value - 1,
+                    "StepValue": 1,
+                    "RepeatCount": 1,
+                    "SegmentId": seg_uuid,
+                    "FieldName": field_name[int(layer)][1]
+                }
+            ]
+        else:
+            stop_value = int(math.sqrt(stop_value))
+            _LOGGER.debug('Flow count modified to %s', stop_value * stop_value)
+            segments = [
+                {
+                    "Offset": 0 if layer == 3 else 1,
+                    "Mask": "//8=",  # mask of 255/255
+                    "Action": "INC",
+                    "StartValue": 0,
+                    "StopValue": stop_value - 1,
+                    "StepValue": 1,
+                    "RepeatCount": stop_value,
+                    "SegmentId": seg_uuid,
+                    "FieldName": field_name[int(layer)][0]
+                },
+                {
+                    "Offset": 2 if layer == 3 else 3,
+                    "Mask": "//8=",  # mask of 255/255
+                    "Action": "INC",
+                    "StartValue": 0,
+                    "StopValue": stop_value - 1,
+                    "StepValue": 1,
+                    "RepeatCount": 1,
+                    "SegmentId": seg_uuid,
+                    "FieldName": field_name[int(layer)][0]
+                },
+                {
+                    "Offset": 0 if layer == 3 else 1,
+                    "Mask": "//8=",  # mask of 255/255
+                    "Action": "INC",
+                    "StartValue": 0,
+                    "StopValue": stop_value - 1,
+                    "StepValue": 1,
+                    "RepeatCount": stop_value,
+                    "SegmentId": seg_uuid,
+                    "FieldName": field_name[int(layer)][1]
+                },
+                {
+                    "Offset": 2 if layer == 3 else 3,
+                    "Mask": "//8=",  # mask of 255/255
+                    "Action": "INC",
+                    "StartValue": 0,
+                    "StopValue": stop_value - 1,
+                    "StepValue": 1,
+                    "RepeatCount": 1,
+                    "SegmentId": seg_uuid,
+                    "FieldName": field_name[int(layer)][1]
+                }
+            ]
+
+
 
         self.json_data['StreamProfileHandler']['EntityList'][entity][
             'StreamConfig']['HwModifiers'] = (segments)
index 3386407..3adc829 100755 (executable)
@@ -39,6 +39,7 @@ from tools.pkt_gen.trafficgen.trafficgen import ITrafficGenerator
 from tools.pkt_gen.xena.XenaDriver import (
     aggregate_stats,
     line_percentage,
+    ModSet,
     XenaSocketDriver,
     XenaManager,
     )
@@ -283,6 +284,9 @@ class Xena(ITrafficGenerator):
                 j_file = XenaJSONBlocks()
             elif bonding_test:
                 j_file = XenaJSONPairs()
+            else: # just default to mesh config
+                self._logger.error('Invalid traffic type defaulting to Mesh config')
+                j_file = XenaJSONMesh()
 
             j_file.set_chassis_info(
                 settings.getValue('TRAFFICGEN_XENA_IP'),
@@ -346,7 +350,7 @@ class Xena(ITrafficGenerator):
                     id=self._params['traffic']['vlan']['cfi'],
                     prio=self._params['traffic']['vlan']['priority'])
             j_file.add_header_segments(
-                flows=self._params['traffic']['multistream'],
+                flows=self._params['traffic']['multistream'] - 1,
                 multistream_layer=self._params['traffic']['stream_type'])
 
             j_file.write_config(os.path.join(
@@ -454,9 +458,17 @@ class Xena(ITrafficGenerator):
                 port.micro_tpld_enable()
 
             if self._params['traffic']['multistream']:
+                if self._params['traffic']['stream_type'] == 'L2':
+                    modobj = ModSet(mod_src_mac=True, mod_dst_mac=True)
+                elif self._params['traffic']['stream_type'] == 'L3':
+                    modobj = ModSet(mod_src_ip=True, mod_dst_ip=True)
+                elif self._params['traffic']['stream_type'] == 'L4':
+                    modobj = ModSet(mod_src_port=True, mod_dst_port=True)
+                else:
+                    self._logger.error('Invalid segment for multistream. Using L2..')
+                    modobj = ModSet(mod_src_mac=True, mod_dst_mac=True)
                 stream.enable_multistream(
-                    flows=self._params['traffic']['multistream'],
-                    layer=self._params['traffic']['stream_type'])
+                    flows=self._params['traffic']['multistream'], mod_class=modobj)
 
         s1_p0 = self.xmanager.ports[0].add_stream()
         setup_stream(s1_p0, self.xmanager.ports[0], 0)