From 11be4868b77d49174afafcd515d1037325796d04 Mon Sep 17 00:00:00 2001 From: Christian Trautman Date: Thu, 17 May 2018 17:46:29 -0400 Subject: Xena_Flow_Fix: Fixes multistream of greater than 64k values 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 --- docs/testing/user/configguide/trafficgen.rst | 8 ++ tools/pkt_gen/xena/XenaDriver.py | 150 +++++++++++++++++++-------- tools/pkt_gen/xena/json/xena_json.py | 106 ++++++++++++++----- tools/pkt_gen/xena/xena.py | 18 +++- 4 files changed, 211 insertions(+), 71 deletions(-) diff --git a/docs/testing/user/configguide/trafficgen.rst b/docs/testing/user/configguide/trafficgen.rst index 42141c59..2636626a 100644 --- a/docs/testing/user/configguide/trafficgen.rst +++ b/docs/testing/user/configguide/trafficgen.rst @@ -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 ------- diff --git a/tools/pkt_gen/xena/XenaDriver.py b/tools/pkt_gen/xena/XenaDriver.py index cdc82838..ac9cef1c 100644 --- a/tools/pkt_gen/xena/XenaDriver.py +++ b/tools/pkt_gen/xena/XenaDriver.py @@ -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): """ diff --git a/tools/pkt_gen/xena/json/xena_json.py b/tools/pkt_gen/xena/json/xena_json.py index df2aa55f..e56b4125 100644 --- a/tools/pkt_gen/xena/json/xena_json.py +++ b/tools/pkt_gen/xena/json/xena_json.py @@ -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) diff --git a/tools/pkt_gen/xena/xena.py b/tools/pkt_gen/xena/xena.py index 33864079..3adc8294 100755 --- a/tools/pkt_gen/xena/xena.py +++ b/tools/pkt_gen/xena/xena.py @@ -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) -- cgit 1.2.3-korg