diff options
Diffstat (limited to 'yardstick/network_services/traffic_profile/trex_traffic_profile.py')
-rw-r--r-- | yardstick/network_services/traffic_profile/trex_traffic_profile.py | 379 |
1 files changed, 379 insertions, 0 deletions
diff --git a/yardstick/network_services/traffic_profile/trex_traffic_profile.py b/yardstick/network_services/traffic_profile/trex_traffic_profile.py new file mode 100644 index 000000000..cf538d488 --- /dev/null +++ b/yardstick/network_services/traffic_profile/trex_traffic_profile.py @@ -0,0 +1,379 @@ +# Copyright (c) 2016-2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import struct +import socket +import logging +from random import SystemRandom +import ipaddress + +import six +from trex_stl_lib.trex_stl_packet_builder_scapy import STLVmWrFlowVar +from trex_stl_lib.trex_stl_packet_builder_scapy import STLVmFlowVarRepeatableRandom +from trex_stl_lib.trex_stl_packet_builder_scapy import STLVmFlowVar +from trex_stl_lib.trex_stl_packet_builder_scapy import STLVmFixIpv4 +from trex_stl_lib import api as Pkt + +from yardstick.common import exceptions as y_exc +from yardstick.network_services.traffic_profile import base + + +SRC = 'src' +DST = 'dst' +ETHERNET = 'Ethernet' +IP = 'IP' +IPv6 = 'IPv6' +UDP = 'UDP' +DSCP = 'DSCP' +SRC_PORT = 'sport' +DST_PORT = 'dport' +TYPE_OF_SERVICE = 'tos' + +LOG = logging.getLogger(__name__) + + +class TrexProfile(base.TrafficProfile): + """ This class handles Trex Traffic profile generation and execution """ + + PROTO_MAP = { + ETHERNET: ('ether_packet', Pkt.Ether), + IP: ('ip_packet', Pkt.IP), + IPv6: ('ip6_packet', Pkt.IPv6), + UDP: ('udp_packet', Pkt.UDP), + } + RATE_ROUND = 5 + + def _general_single_action_partial(self, protocol): + def f(field): + def partial(value): + kwargs = { + field: value + } + self._set_proto_fields(protocol, **kwargs) + return partial + return f + + def _ethernet_range_action_partial(self, direction, _): + def partial(min_value, max_value, count): + # pylint: disable=unused-argument + stl_vm_flow_var = STLVmFlowVar(name="mac_{}".format(direction), + min_value=1, + max_value=30, + size=4, + op='inc', + step=1) + self.vm_flow_vars.append(stl_vm_flow_var) + stl_vm_wr_flow_var = STLVmWrFlowVar( + fv_name='mac_{}'.format(direction), + pkt_offset='Ether.{}'.format(direction)) + self.vm_flow_vars.append(stl_vm_wr_flow_var) + return partial + + def _ip_range_action_partial(self, direction, count=1): + # pylint: disable=unused-argument + def partial(min_value, max_value, count): + _, _, actual_count = self._count_ip(min_value, max_value) + if not actual_count: + count = 1 + elif actual_count < int(count): + count = actual_count + + stl_vm_flow_var = STLVmFlowVarRepeatableRandom( + name="ip4_{}".format(direction), + min_value=min_value, + max_value=max_value, + size=4, + limit=int(count), + seed=0x1235) + self.vm_flow_vars.append(stl_vm_flow_var) + stl_vm_wr_flow_var = STLVmWrFlowVar( + fv_name='ip4_{}'.format(direction), + pkt_offset='IP.{}'.format(direction)) + self.vm_flow_vars.append(stl_vm_wr_flow_var) + stl_vm_fix_ipv4 = STLVmFixIpv4(offset="IP") + self.vm_flow_vars.append(stl_vm_fix_ipv4) + return partial + + def _ip6_range_action_partial(self, direction, _): + def partial(min_value, max_value, count): + # pylint: disable=unused-argument + min_value, max_value, _ = self._count_ip(min_value, max_value) + stl_vm_flow_var = STLVmFlowVar(name="ip6_{}".format(direction), + min_value=min_value, + max_value=max_value, + size=8, + op='random', + step=1) + self.vm_flow_vars.append(stl_vm_flow_var) + stl_vm_wr_flow_var = STLVmWrFlowVar( + fv_name='ip6_{}'.format(direction), + pkt_offset='IPv6.{}'.format(direction), + offset_fixup=8) + self.vm_flow_vars.append(stl_vm_wr_flow_var) + return partial + + def _dscp_range_action_partial(self, *args): + def partial(min_value, max_value, count): + # pylint: disable=unused-argument + stl_vm_flow_var = STLVmFlowVar(name="dscp", + min_value=min_value, + max_value=max_value, + size=2, + op='inc', + step=8) + self.vm_flow_vars.append(stl_vm_flow_var) + stl_vm_wr_flow_var = STLVmWrFlowVar(fv_name='dscp', + pkt_offset='IP.tos') + self.vm_flow_vars.append(stl_vm_wr_flow_var) + return partial + + def _udp_range_action_partial(self, field, count=1): + # pylint: disable=unused-argument + def partial(min_value, max_value, count): + actual_count = int(max_value) - int(min_value) + if not actual_count: + count = 1 + elif int(count) > actual_count: + count = actual_count + + stl_vm_flow_var = STLVmFlowVarRepeatableRandom( + name="port_{}".format(field), + min_value=min_value, + max_value=max_value, + size=2, + limit=int(count), + seed=0x1235) + self.vm_flow_vars.append(stl_vm_flow_var) + stl_vm_wr_flow_var = STLVmWrFlowVar( + fv_name='port_{}'.format(field), + pkt_offset=self.udp[field]) + self.vm_flow_vars.append(stl_vm_wr_flow_var) + return partial + + def __init__(self, yaml_data): + super(TrexProfile, self).__init__(yaml_data) + self.flows = 100 + self.pps = 100 + self.pg_id = 0 + self.first_run = True + self.streams = 1 + self.profile_data = [] + self.profile = None + self.base_pkt = None + self.fsize = None + self.trex_vm = None + self.vms = [] + self.rate = None + self.ether_packet = None + self.ip_packet = None + self.ip6_packet = None + self.udp_packet = None + self.udp = { + SRC_PORT: '', + DST_PORT: '', + } + self.qinq_packet = None + self.qinq = False + self.vm_flow_vars = [] + self.packets = [] + self.max_rate = 0 + self.min_rate = 0 + + self._map_proto_actions = { + # the tuple is (single value function, range value function, if the values should be + # converted to integer). + ETHERNET: (self._general_single_action_partial(ETHERNET), + self._ethernet_range_action_partial, + False, + ), + IP: (self._general_single_action_partial(IP), + self._ip_range_action_partial, + False, + ), + IPv6: (self._general_single_action_partial(IPv6), + self._ip6_range_action_partial, + False, + ), + DSCP: (self._general_single_action_partial(IP), + self._dscp_range_action_partial, + True, + ), + UDP: (self._general_single_action_partial(UDP), + self._udp_range_action_partial, + True, + ), + } + + def execute_traffic(self, traffic_generator): + """ Generate the stream and run traffic on the given ports """ + raise NotImplementedError() + + def _call_on_range(self, range, single_action, range_action, count=1, to_int=False): + def convert_to_int(val): + return int(val) if to_int else val + + range_iter = iter(str(range).split('-')) + min_value = convert_to_int(next(range_iter)) + try: + max_value = convert_to_int(next(range_iter)) + except StopIteration: + single_action(min_value) + else: + range_action(min_value=min_value, max_value=max_value, count=count) + + def _set_proto_addr(self, protocol, field, address, count=1): + single_action, range_action, to_int = self._map_proto_actions[protocol] + self._call_on_range(address, + single_action(field), + range_action(field, count), + count=count, + to_int=to_int, + ) + + def _set_proto_fields(self, protocol, **kwargs): + _attr_name, _class = self.PROTO_MAP[protocol] + + if not getattr(self, _attr_name): + setattr(self, _attr_name, _class()) + + _attr = getattr(self, _attr_name) + for key, value in six.iteritems(kwargs): + setattr(_attr, key, value) + + def set_svlan_cvlan(self, svlan, cvlan): + """ set svlan & cvlan """ + self.qinq = True + ether_params = {'type': 0x8100} + self._set_proto_fields(ETHERNET, **ether_params) + svlans = str(svlan['id']).split('-') + svlan_min = int(svlans[0]) + svlan_max = int(svlans[1]) if len(svlans) == 2 else int(svlans[0]) + if len(svlans) == 2: + svlan = self._get_random_value(svlan_min, svlan_max) + else: + svlan = svlan_min + cvlans = str(cvlan['id']).split('-') + cvlan_min = int(cvlans[0]) + cvlan_max = int(cvlans[1]) if len(cvlans) == 2 else int(cvlans[0]) + if len(cvlans) == 2: + cvlan = self._get_random_value(cvlan_min, cvlan_max) + else: + cvlan = cvlan_min + self.qinq_packet = Pkt.Dot1Q(vlan=svlan) / Pkt.Dot1Q(vlan=cvlan) + + def set_qinq(self, qinq): + """ set qinq in packet """ + self.set_svlan_cvlan(qinq['S-VLAN'], qinq['C-VLAN']) + + def _set_outer_l2_fields(self, outer_l2): + """ setup outer l2 fields from traffic profile """ + ether_params = {'type': 0x800} + self._set_proto_fields(ETHERNET, **ether_params) + if 'srcmac' in outer_l2: + self._set_proto_addr(ETHERNET, SRC, outer_l2['srcmac']) + if 'dstmac' in outer_l2: + self._set_proto_addr(ETHERNET, DST, outer_l2['dstmac']) + if 'QinQ' in outer_l2: + self.set_qinq(outer_l2['QinQ']) + + def _set_outer_l3v4_fields(self, outer_l3v4): + """ setup outer l3v4 fields from traffic profile """ + ip_params = {} + if 'proto' in outer_l3v4: + ip_params['proto'] = socket.getprotobyname(outer_l3v4['proto']) + if outer_l3v4['proto'] == 'tcp': + self.udp_packet = Pkt.TCP() + self.udp[DST_PORT] = 'TCP.dport' + self.udp[SRC_PORT] = 'TCP.sport' + tcp_params = {'flags': '', 'window': 0} + self._set_proto_fields(UDP, **tcp_params) + if 'ttl' in outer_l3v4: + ip_params['ttl'] = outer_l3v4['ttl'] + self._set_proto_fields(IP, **ip_params) + if 'dscp' in outer_l3v4: + self._set_proto_addr(DSCP, TYPE_OF_SERVICE, outer_l3v4['dscp']) + if 'srcip4' in outer_l3v4: + self._set_proto_addr(IP, SRC, outer_l3v4['srcip4'], outer_l3v4['count']) + if 'dstip4' in outer_l3v4: + self._set_proto_addr(IP, DST, outer_l3v4['dstip4'], outer_l3v4['count']) + + def _set_outer_l3v6_fields(self, outer_l3v6): + """ setup outer l3v6 fields from traffic profile """ + ether_params = {'type': 0x86dd} + self._set_proto_fields(ETHERNET, **ether_params) + ip6_params = {} + if 'proto' in outer_l3v6: + ip6_params['proto'] = outer_l3v6['proto'] + if outer_l3v6['proto'] == 'tcp': + self.udp_packet = Pkt.TCP() + self.udp[DST_PORT] = 'TCP.dport' + self.udp[SRC_PORT] = 'TCP.sport' + tcp_params = {'flags': '', 'window': 0} + self._set_proto_fields(UDP, **tcp_params) + if 'ttl' in outer_l3v6: + ip6_params['ttl'] = outer_l3v6['ttl'] + if 'tc' in outer_l3v6: + ip6_params['tc'] = outer_l3v6['tc'] + if 'hlim' in outer_l3v6: + ip6_params['hlim'] = outer_l3v6['hlim'] + self._set_proto_fields(IPv6, **ip6_params) + if 'srcip6' in outer_l3v6: + self._set_proto_addr(IPv6, SRC, outer_l3v6['srcip6']) + if 'dstip6' in outer_l3v6: + self._set_proto_addr(IPv6, DST, outer_l3v6['dstip6']) + + def _set_outer_l4_fields(self, outer_l4): + """ setup outer l4 fields from traffic profile """ + if 'srcport' in outer_l4: + self._set_proto_addr(UDP, SRC_PORT, outer_l4['srcport'], outer_l4['count']) + if 'dstport' in outer_l4: + self._set_proto_addr(UDP, DST_PORT, outer_l4['dstport'], outer_l4['count']) + + def _get_next_rate(self): + rate = round(float(self.max_rate + self.min_rate)/2.0, self.RATE_ROUND) + return rate + + def _get_framesize(self): + framesizes = [] + for traffickey, value in self.params.items(): + if not traffickey.startswith((self.UPLINK, self.DOWNLINK)): + continue + for _, data in value.items(): + framesize = data['outer_l2']['framesize'] + for size in (s for s, w in framesize.items() if int(w) != 0): + framesizes.append(size) + if len(set(framesizes)) == 0: + return '' + elif len(set(framesizes)) == 1: + return framesizes[0] + return 'IMIX' + + @classmethod + def _count_ip(cls, start_ip, end_ip): + start = ipaddress.ip_address(six.u(start_ip)) + end = ipaddress.ip_address(six.u(end_ip)) + if start.version == 4: + return start, end, int(end) - int(start) + elif start.version == 6: + if int(start) > int(end): + raise y_exc.IPv6RangeError(start_ip=str(start), + end_ip=str(end)) + _, lo1 = struct.unpack('!QQ', start.packed) + _, lo2 = struct.unpack('!QQ', end.packed) + return lo1, lo2, lo2 - lo1 + + @classmethod + def _get_random_value(cls, min_port, max_port): + cryptogen = SystemRandom() + return cryptogen.randrange(min_port, max_port) |