Merge "Fixed document for Grafana Port usage"
[yardstick.git] / yardstick / network_services / traffic_profile / traffic_profile.py
index 7bbe892..3b19ff9 100644 (file)
@@ -19,6 +19,7 @@ import socket
 import logging
 from random import SystemRandom
 import six
+import ipaddress
 
 from yardstick.network_services.traffic_profile.base import TrafficProfile
 from trex_stl_lib.trex_stl_client import STLStream
@@ -33,203 +34,100 @@ from trex_stl_lib.trex_stl_packet_builder_scapy import STLScVmRaw
 from trex_stl_lib.trex_stl_packet_builder_scapy import STLVmFixIpv4
 from trex_stl_lib import api as Pkt
 
+SRC = 'src'
+DST = 'dst'
+ETHERNET = 'Ethernet'
+IP = 'IP'
+IPv6 = 'IPv6'
+UDP = 'UDP'
+DSCP = 'DSCP'
+SRC_PORT = 'sport'
+DST_PORT = 'dport'
+TYPE_OF_SERVICE = 'tos'
 
-class TrexProfile(TrafficProfile):
-    """ This class handles Trex Traffic profile generation and execution """
+LOG = logging.getLogger(__name__)
 
-    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.ip_packet = None
-        self.ip6_packet = None
-        self.udp_packet = None
-        self.udp_dport = ''
-        self.udp_sport = ''
-        self.qinq_packet = None
-        self.qinq = False
-        self.vm_flow_vars = []
-        self.packets = []
-        self.ether_packet = []
 
-    def execute(self, traffic_generator):
-        """ Generate the stream and run traffic on the given ports """
-        pass
-
-    def _set_ether_fields(self, **kwargs):
-        """ set ethernet protocol fields """
-        if not self.ether_packet:
-            self.ether_packet = Pkt.Ether()
-            for key, value in six.iteritems(kwargs):
-                setattr(self.ether_packet, key, value)
-
-    def _set_ip_fields(self, **kwargs):
-        """ set l3 ipv4 protocol fields """
-
-        if not self.ip_packet:
-            self.ip_packet = Pkt.IP()
-        for key in kwargs:
-            setattr(self.ip_packet, key, kwargs[key])
-
-    def _set_ip6_fields(self, **kwargs):
-        """ set l3 ipv6 protocol fields """
-        if not self.ip6_packet:
-            self.ip6_packet = Pkt.IPv6()
-        for key in kwargs:
-            setattr(self.ip6_packet, key, kwargs[key])
-
-    def _set_udp_fields(self, **kwargs):
-        """ set l4 udp ports fields """
-        if not self.udp_packet:
-            self.udp_packet = Pkt.UDP()
-        for key in kwargs:
-            setattr(self.udp_packet, key, kwargs[key])
-
-    def set_src_mac(self, src_mac):
-        """ set source mac address fields """
-        src_macs = src_mac.split('-')
-        min_value = src_macs[0]
-        if len(src_macs) == 1:
-            src_mac = min_value
-            self._set_ether_fields(src=src_mac)
-        else:
-            stl_vm_flow_var = STLVmFlowVar(name="mac_src",
-                                           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_src',
-                                                pkt_offset='Ether.src')
-            self.vm_flow_vars.append(stl_vm_wr_flow_var)
+class TrexProfile(TrafficProfile):
+    """ This class handles Trex Traffic profile generation and execution """
 
-    def set_dst_mac(self, dst_mac):
-        """ set destination mac address fields """
-        dst_macs = dst_mac.split('-')
-        min_value = dst_macs[0]
-        if len(dst_macs) == 1:
-            dst_mac = min_value
-            self._set_ether_fields(dst=dst_mac)
-        else:
-            stl_vm_flow_var = STLVmFlowVar(name="mac_dst",
+    PROTO_MAP = {
+        ETHERNET: ('ether_packet', Pkt.Ether),
+        IP: ('ip_packet', Pkt.IP),
+        IPv6: ('ip6_packet', Pkt.IPv6),
+        UDP: ('udp_packet', Pkt.UDP),
+    }
+
+    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_dst',
-                                                pkt_offset='Ether.dst')
+            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)
-
-    def set_src_ip4(self, src_ip4, count=1):
-        """ set source ipv4 address fields """
-        src_ips = src_ip4.split('-')
-        min_value = src_ips[0]
-        max_value = src_ips[1] if len(src_ips) == 2 else src_ips[0]
-        if len(src_ips) == 1:
-            src_ip4 = min_value
-            self._set_ip_fields(src=src_ip4)
-        else:
-            stl_vm_flow_var = STLVmFlowVarRepeatableRandom(name="ip4_src",
-                                                           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_src',
-                                                pkt_offset='IP.src')
-            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)
-
-    def set_dst_ip4(self, dst_ip4, count=1):
-        """ set destination ipv4 address fields """
-        dst_ips = dst_ip4.split('-')
-        min_value = dst_ips[0]
-        max_value = dst_ips[1] if len(dst_ips) == 2 else dst_ips[0]
-        if len(dst_ips) == 1:
-            dst_ip4 = min_value
-            self._set_ip_fields(dst=dst_ip4)
-        else:
-            stl_vm_flow_var = STLVmFlowVarRepeatableRandom(name="dst_ip4",
+        return partial
+
+    def _ip_range_action_partial(self, direction, count=1):
+        # pylint: disable=unused-argument
+        def partial(min_value, max_value, count):
+            ip1 = int(ipaddress.IPv4Address(min_value))
+            ip2 = int(ipaddress.IPv4Address(max_value))
+            actual_count = (ip2 - ip1)
+            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='dst_ip4',
-                                                pkt_offset='IP.dst')
+            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 set_src_ip6(self, src_ip6):
-        """ set source ipv6 address fields """
-        src_ips = src_ip6.split('-')
-        min_value = src_ips[0]
-        max_value = src_ips[1] if len(src_ips) == 2 else src_ips[0]
-        src_ip6 = min_value
-        self._set_ip6_fields(src=src_ip6)
-        if len(src_ips) == 2:
-            min_value, max_value = \
-                self._get_start_end_ipv6(min_value, max_value)
-            stl_vm_flow_var = STLVmFlowVar(name="ip6_src",
-                                           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_src',
-                                                pkt_offset='IPv6.src',
-                                                offset_fixup=8)
-            self.vm_flow_vars.append(stl_vm_wr_flow_var)
-
-    def set_dst_ip6(self, dst_ip6):
-        """ set destination ipv6 address fields """
-        dst_ips = dst_ip6.split('-')
-        min_value = dst_ips[0]
-        max_value = dst_ips[1] if len(dst_ips) == 2 else dst_ips[0]
-        dst_ip6 = min_value
-        self._set_ip6_fields(dst=dst_ip6)
-        if len(dst_ips) == 2:
-            min_value, max_value = \
-                self._get_start_end_ipv6(min_value, max_value)
-            stl_vm_flow_var = STLVmFlowVar(name="dst_ip6",
+    def _ip6_range_action_partial(self, direction, _):
+        def partial(min_value, max_value, count):
+            # pylint: disable=unused-argument
+            min_value, max_value = self._get_start_end_ipv6(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='dst_ip6',
-                                                pkt_offset='IPv6.dst',
+            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 set_dscp(self, dscp):
-        """ set dscp for trex """
-        dscps = str(dscp).split('-')
-        min_value = int(dscps[0])
-        max_value = int(dscps[1]) if len(dscps) == 2 else int(dscps[0])
-        if len(dscps) == 1:
-            dscp = min_value
-            self._set_ip_fields(tos=dscp)
-        else:
+    def _dscp_range_action_partial(self, *_):
+        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,
@@ -240,55 +138,122 @@ class TrexProfile(TrafficProfile):
             stl_vm_wr_flow_var = STLVmWrFlowVar(fv_name='dscp',
                                                 pkt_offset='IP.tos')
             self.vm_flow_vars.append(stl_vm_wr_flow_var)
-
-    def set_src_port(self, src_port, count=1):
-        """ set packet source port """
-        src_ports = str(src_port).split('-')
-        min_value = int(src_ports[0])
-        if len(src_ports) == 1:
-            max_value = int(src_ports[0])
-            src_port = min_value
-            self._set_udp_fields(sport=src_port)
-        else:
-            max_value = int(src_ports[1])
-            stl_vm_flow_var = STLVmFlowVarRepeatableRandom(name="port_src",
+        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_src',
-                                                pkt_offset=self.udp_sport)
+            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._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 set_dst_port(self, dst_port, count=1):
-        """ set packet destnation port """
-        dst_ports = str(dst_port).split('-')
-        min_value = int(dst_ports[0])
-        if len(dst_ports) == 1:
-            max_value = int(dst_ports[0])
-            dst_port = min_value
-            self._set_udp_fields(dport=dst_port)
+    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:
-            max_value = int(dst_ports[1])
-            stl_vm_flow_var = \
-                STLVmFlowVarRepeatableRandom(name="port_dst",
-                                             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_dst',
-                                                pkt_offset=self.udp_dport)
-            self.vm_flow_vars.append(stl_vm_wr_flow_var)
+            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_ether_fields(**ether_params)
+        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])
@@ -309,69 +274,69 @@ class TrexProfile(TrafficProfile):
         """ set qinq in packet """
         self.set_svlan_cvlan(qinq['S-VLAN'], qinq['C-VLAN'])
 
-    def set_outer_l2_fields(self, outer_l2):
+    def _set_outer_l2_fields(self, outer_l2):
         """ setup outer l2 fields from traffic profile """
         ether_params = {'type': 0x800}
-        self._set_ether_fields(**ether_params)
+        self._set_proto_fields(ETHERNET, **ether_params)
         if 'srcmac' in outer_l2:
-            self.set_src_mac(outer_l2['srcmac'])
+            self._set_proto_addr(ETHERNET, SRC, outer_l2['srcmac'])
         if 'dstmac' in outer_l2:
-            self.set_dst_mac(outer_l2['dstmac'])
+            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):
+    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'] = outer_l3v4['proto']
+            ip_params['proto'] = socket.getprotobyname(outer_l3v4['proto'])
             if outer_l3v4['proto'] == 'tcp':
                 self.udp_packet = Pkt.TCP()
-                self.udp_dport = 'TCP.dport'
-                self.udp_sport = 'TCP.sport'
+                self.udp[DST_PORT] = 'TCP.dport'
+                self.udp[SRC_PORT] = 'TCP.sport'
                 tcp_params = {'flags': '', 'window': 0}
-                self._set_udp_fields(**tcp_params)
+                self._set_proto_fields(UDP, **tcp_params)
         if 'ttl' in outer_l3v4:
             ip_params['ttl'] = outer_l3v4['ttl']
-        self._set_ip_fields(**ip_params)
+        self._set_proto_fields(IP, **ip_params)
         if 'dscp' in outer_l3v4:
-            self.set_dscp(outer_l3v4['dscp'])
+            self._set_proto_addr(DSCP, TYPE_OF_SERVICE, outer_l3v4['dscp'])
         if 'srcip4' in outer_l3v4:
-            self.set_src_ip4(outer_l3v4['srcip4'], outer_l3v4['count'])
+            self._set_proto_addr(IP, SRC, outer_l3v4['srcip4'], outer_l3v4['count'])
         if 'dstip4' in outer_l3v4:
-            self.set_dst_ip4(outer_l3v4['dstip4'], outer_l3v4['count'])
+            self._set_proto_addr(IP, DST, outer_l3v4['dstip4'], outer_l3v4['count'])
 
-    def set_outer_l3v6_fields(self, outer_l3v6):
+    def _set_outer_l3v6_fields(self, outer_l3v6):
         """ setup outer l3v6 fields from traffic profile """
         ether_params = {'type': 0x86dd}
-        self._set_ether_fields(**ether_params)
+        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_dport = 'TCP.dport'
-                self.udp_sport = 'TCP.sport'
+                self.udp[DST_PORT] = 'TCP.dport'
+                self.udp[SRC_PORT] = 'TCP.sport'
                 tcp_params = {'flags': '', 'window': 0}
-                self._set_udp_fields(**tcp_params)
+                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_ip6_fields(**ip6_params)
+        self._set_proto_fields(IPv6, **ip6_params)
         if 'srcip6' in outer_l3v6:
-            self.set_src_ip6(outer_l3v6['srcip6'])
+            self._set_proto_addr(IPv6, SRC, outer_l3v6['srcip6'])
         if 'dstip6' in outer_l3v6:
-            self.set_dst_ip6(outer_l3v6['dstip6'])
+            self._set_proto_addr(IPv6, DST, outer_l3v6['dstip6'])
 
-    def set_outer_l4_fields(self, outer_l4):
+    def _set_outer_l4_fields(self, outer_l4):
         """ setup outer l4 fields from traffic profile """
         if 'srcport' in outer_l4:
-            self.set_src_port(outer_l4['srcport'], outer_l4['count'])
+            self._set_proto_addr(UDP, SRC_PORT, outer_l4['srcport'], outer_l4['count'])
         if 'dstport' in outer_l4:
-            self.set_dst_port(outer_l4['dstport'], outer_l4['count'])
+            self._set_proto_addr(UDP, DST_PORT, outer_l4['dstport'], outer_l4['count'])
 
     def generate_imix_data(self, packet_definition):
         """ generate packet size for a given traffic profile """
@@ -425,8 +390,8 @@ class TrexProfile(TrafficProfile):
         self.ip_packet = Pkt.IP()
         self.ip6_packet = None
         self.udp_packet = Pkt.UDP()
-        self.udp_dport = 'UDP.dport'
-        self.udp_sport = 'UDP.sport'
+        self.udp[DST_PORT] = 'UDP.dport'
+        self.udp[SRC_PORT] = 'UDP.sport'
         self.qinq = False
         self.vm_flow_vars = []
         outer_l2 = packet_definition.get('outer_l2', None)
@@ -434,13 +399,13 @@ class TrexProfile(TrafficProfile):
         outer_l3v6 = packet_definition.get('outer_l3v6', None)
         outer_l4 = packet_definition.get('outer_l4', None)
         if outer_l2:
-            self.set_outer_l2_fields(outer_l2)
+            self._set_outer_l2_fields(outer_l2)
         if outer_l3v4:
-            self.set_outer_l3v4_fields(outer_l3v4)
+            self._set_outer_l3v4_fields(outer_l3v4)
         if outer_l3v6:
-            self.set_outer_l3v6_fields(outer_l3v6)
+            self._set_outer_l3v6_fields(outer_l3v6)
         if outer_l4:
-            self.set_outer_l4_fields(outer_l4)
+            self._set_outer_l4_fields(outer_l4)
         self.trex_vm = STLScVmRaw(self.vm_flow_vars)
 
     def generate_packets(self):