Merge "Re-define the framesize and flow dynamic define in testcase"
[yardstick.git] / yardstick / network_services / traffic_profile / traffic_profile.py
1 # Copyright (c) 2016-2017 Intel Corporation
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #      http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 """ Trex Traffic Profile definitions """
15
16 from __future__ import absolute_import
17 import struct
18 import socket
19 import logging
20 from random import SystemRandom
21 import six
22
23 from yardstick.network_services.traffic_profile.base import TrafficProfile
24 from stl.trex_stl_lib.trex_stl_client import STLStream
25 from stl.trex_stl_lib.trex_stl_streams import STLFlowLatencyStats
26 from stl.trex_stl_lib.trex_stl_streams import STLTXCont
27 from stl.trex_stl_lib.trex_stl_streams import STLProfile
28 from stl.trex_stl_lib.trex_stl_packet_builder_scapy import STLVmWrFlowVar
29 from stl.trex_stl_lib.trex_stl_packet_builder_scapy import STLVmFlowVarRepeatableRandom
30 from stl.trex_stl_lib.trex_stl_packet_builder_scapy import STLVmFlowVar
31 from stl.trex_stl_lib.trex_stl_packet_builder_scapy import STLPktBuilder
32 from stl.trex_stl_lib.trex_stl_packet_builder_scapy import STLScVmRaw
33 from stl.trex_stl_lib.trex_stl_packet_builder_scapy import STLVmFixIpv4
34 from stl.trex_stl_lib import api as Pkt
35
36
37 class TrexProfile(TrafficProfile):
38     """ This class handles Trex Traffic profile generation and execution """
39
40     def __init__(self, yaml_data):
41         super(TrexProfile, self).__init__(yaml_data)
42         self.flows = 100
43         self.pps = 100
44         self.pg_id = 0
45         self.first_run = True
46         self.streams = 1
47         self.profile_data = []
48         self.profile = None
49         self.base_pkt = None
50         self.fsize = None
51         self.trex_vm = None
52         self.vms = []
53         self.rate = None
54         self.ip_packet = None
55         self.ip6_packet = None
56         self.udp_packet = None
57         self.udp_dport = ''
58         self.udp_sport = ''
59         self.qinq_packet = None
60         self.qinq = False
61         self.vm_flow_vars = []
62         self.packets = []
63         self.ether_packet = []
64
65     def execute(self, traffic_generator):
66         """ Generate the stream and run traffic on the given ports """
67         pass
68
69     def _set_ether_fields(self, **kwargs):
70         """ set ethernet protocol fields """
71         if not self.ether_packet:
72             self.ether_packet = Pkt.Ether()
73             for key, value in six.iteritems(kwargs):
74                 setattr(self.ether_packet, key, value)
75
76     def _set_ip_fields(self, **kwargs):
77         """ set l3 ipv4 protocol fields """
78
79         if not self.ip_packet:
80             self.ip_packet = Pkt.IP()
81         for key in kwargs:
82             setattr(self.ip_packet, key, kwargs[key])
83
84     def _set_ip6_fields(self, **kwargs):
85         """ set l3 ipv6 protocol fields """
86         if not self.ip6_packet:
87             self.ip6_packet = Pkt.IPv6()
88         for key in kwargs:
89             setattr(self.ip6_packet, key, kwargs[key])
90
91     def _set_udp_fields(self, **kwargs):
92         """ set l4 udp ports fields """
93         if not self.udp_packet:
94             self.udp_packet = Pkt.UDP()
95         for key in kwargs:
96             setattr(self.udp_packet, key, kwargs[key])
97
98     def set_src_mac(self, src_mac):
99         """ set source mac address fields """
100         src_macs = src_mac.split('-')
101         min_value = src_macs[0]
102         if len(src_macs) == 1:
103             src_mac = min_value
104             self._set_ether_fields(src=src_mac)
105         else:
106             stl_vm_flow_var = STLVmFlowVar(name="mac_src",
107                                            min_value=1,
108                                            max_value=30,
109                                            size=4,
110                                            op='inc',
111                                            step=1)
112             self.vm_flow_vars.append(stl_vm_flow_var)
113             stl_vm_wr_flow_var = STLVmWrFlowVar(fv_name='mac_src',
114                                                 pkt_offset='Ether.src')
115             self.vm_flow_vars.append(stl_vm_wr_flow_var)
116
117     def set_dst_mac(self, dst_mac):
118         """ set destination mac address fields """
119         dst_macs = dst_mac.split('-')
120         min_value = dst_macs[0]
121         if len(dst_macs) == 1:
122             dst_mac = min_value
123             self._set_ether_fields(dst=dst_mac)
124         else:
125             stl_vm_flow_var = STLVmFlowVar(name="mac_dst",
126                                            min_value=1,
127                                            max_value=30,
128                                            size=4,
129                                            op='inc',
130                                            step=1)
131             self.vm_flow_vars.append(stl_vm_flow_var)
132             stl_vm_wr_flow_var = STLVmWrFlowVar(fv_name='mac_dst',
133                                                 pkt_offset='Ether.dst')
134             self.vm_flow_vars.append(stl_vm_wr_flow_var)
135
136     def set_src_ip4(self, src_ip4, count=1):
137         """ set source ipv4 address fields """
138         src_ips = src_ip4.split('-')
139         min_value = src_ips[0]
140         max_value = src_ips[1] if len(src_ips) == 2 else src_ips[0]
141         if len(src_ips) == 1:
142             src_ip4 = min_value
143             self._set_ip_fields(src=src_ip4)
144         else:
145             stl_vm_flow_var = \
146                 STLVmFlowVarRepeatableRandom(name="ip4_src",
147                                              min_value=min_value,
148                                              max_value=max_value,
149                                              size=4,
150                                              limit=int(count),
151                                              seed=0x1235)
152             self.vm_flow_vars.append(stl_vm_flow_var)
153             stl_vm_wr_flow_var = STLVmWrFlowVar(fv_name='ip4_src',
154                                                 pkt_offset='IP.src')
155             self.vm_flow_vars.append(stl_vm_wr_flow_var)
156             stl_vm_fix_ipv4 = STLVmFixIpv4(offset="IP")
157             self.vm_flow_vars.append(stl_vm_fix_ipv4)
158
159     def set_dst_ip4(self, dst_ip4, count=1):
160         """ set destination ipv4 address fields """
161         dst_ips = dst_ip4.split('-')
162         min_value = dst_ips[0]
163         max_value = dst_ips[1] if len(dst_ips) == 2 else dst_ips[0]
164         if len(dst_ips) == 1:
165             dst_ip4 = min_value
166             self._set_ip_fields(dst=dst_ip4)
167         else:
168             stl_vm_flow_var = \
169                 STLVmFlowVarRepeatableRandom(name="dst_ip4",
170                                              min_value=min_value,
171                                              max_value=max_value,
172                                              size=4,
173                                              limit=int(count),
174                                              seed=0x1235)
175             self.vm_flow_vars.append(stl_vm_flow_var)
176             stl_vm_wr_flow_var = STLVmWrFlowVar(fv_name='dst_ip4',
177                                                 pkt_offset='IP.dst')
178             self.vm_flow_vars.append(stl_vm_wr_flow_var)
179             stl_vm_fix_ipv4 = STLVmFixIpv4(offset="IP")
180             self.vm_flow_vars.append(stl_vm_fix_ipv4)
181
182     def set_src_ip6(self, src_ip6):
183         """ set source ipv6 address fields """
184         src_ips = src_ip6.split('-')
185         min_value = src_ips[0]
186         max_value = src_ips[1] if len(src_ips) == 2 else src_ips[0]
187         src_ip6 = min_value
188         self._set_ip6_fields(src=src_ip6)
189         if len(src_ips) == 2:
190             min_value, max_value = \
191                 self._get_start_end_ipv6(min_value, max_value)
192             stl_vm_flow_var = STLVmFlowVar(name="ip6_src",
193                                            min_value=min_value,
194                                            max_value=max_value,
195                                            size=8,
196                                            op='random',
197                                            step=1)
198             self.vm_flow_vars.append(stl_vm_flow_var)
199             stl_vm_wr_flow_var = STLVmWrFlowVar(fv_name='ip6_src',
200                                                 pkt_offset='IPv6.src',
201                                                 offset_fixup=8)
202             self.vm_flow_vars.append(stl_vm_wr_flow_var)
203
204     def set_dst_ip6(self, dst_ip6):
205         """ set destination ipv6 address fields """
206         dst_ips = dst_ip6.split('-')
207         min_value = dst_ips[0]
208         max_value = dst_ips[1] if len(dst_ips) == 2 else dst_ips[0]
209         dst_ip6 = min_value
210         self._set_ip6_fields(dst=dst_ip6)
211         if len(dst_ips) == 2:
212             min_value, max_value = \
213                 self._get_start_end_ipv6(min_value, max_value)
214             stl_vm_flow_var = STLVmFlowVar(name="dst_ip6",
215                                            min_value=min_value,
216                                            max_value=max_value,
217                                            size=8,
218                                            op='random',
219                                            step=1)
220             self.vm_flow_vars.append(stl_vm_flow_var)
221             stl_vm_wr_flow_var = STLVmWrFlowVar(fv_name='dst_ip6',
222                                                 pkt_offset='IPv6.dst',
223                                                 offset_fixup=8)
224             self.vm_flow_vars.append(stl_vm_wr_flow_var)
225
226     def set_dscp(self, dscp):
227         """ set dscp for trex """
228         dscps = str(dscp).split('-')
229         min_value = int(dscps[0])
230         max_value = int(dscps[1]) if len(dscps) == 2 else int(dscps[0])
231         if len(dscps) == 1:
232             dscp = min_value
233             self._set_ip_fields(tos=dscp)
234         else:
235             stl_vm_flow_var = STLVmFlowVar(name="dscp",
236                                            min_value=min_value,
237                                            max_value=max_value,
238                                            size=2,
239                                            op='inc',
240                                            step=8)
241             self.vm_flow_vars.append(stl_vm_flow_var)
242             stl_vm_wr_flow_var = STLVmWrFlowVar(fv_name='dscp',
243                                                 pkt_offset='IP.tos')
244             self.vm_flow_vars.append(stl_vm_wr_flow_var)
245
246     def set_src_port(self, src_port, count=1):
247         """ set packet source port """
248         src_ports = str(src_port).split('-')
249         min_value = int(src_ports[0])
250         if len(src_ports) == 1:
251             max_value = int(src_ports[0])
252             src_port = min_value
253             self._set_udp_fields(sport=src_port)
254         else:
255             max_value = int(src_ports[1])
256             stl_vm_flow_var = \
257                 STLVmFlowVarRepeatableRandom(name="port_src",
258                                              min_value=min_value,
259                                              max_value=max_value,
260                                              size=2,
261                                              limit=int(count),
262                                              seed=0x1235)
263             self.vm_flow_vars.append(stl_vm_flow_var)
264             stl_vm_wr_flow_var = STLVmWrFlowVar(fv_name='port_src',
265                                                 pkt_offset=self.udp_sport)
266             self.vm_flow_vars.append(stl_vm_wr_flow_var)
267
268     def set_dst_port(self, dst_port, count=1):
269         """ set packet destnation port """
270         dst_ports = str(dst_port).split('-')
271         min_value = int(dst_ports[0])
272         if len(dst_ports) == 1:
273             max_value = int(dst_ports[0])
274             dst_port = min_value
275             self._set_udp_fields(dport=dst_port)
276         else:
277             max_value = int(dst_ports[1])
278             stl_vm_flow_var = \
279                 STLVmFlowVarRepeatableRandom(name="port_dst",
280                                              min_value=min_value,
281                                              max_value=max_value,
282                                              size=2,
283                                              limit=int(count),
284                                              seed=0x1235)
285             self.vm_flow_vars.append(stl_vm_flow_var)
286             stl_vm_wr_flow_var = STLVmWrFlowVar(fv_name='port_dst',
287                                                 pkt_offset=self.udp_dport)
288             self.vm_flow_vars.append(stl_vm_wr_flow_var)
289
290     def set_svlan_cvlan(self, svlan, cvlan):
291         """ set svlan & cvlan """
292         self.qinq = True
293         ether_params = {'type': 0x8100}
294         self._set_ether_fields(**ether_params)
295         svlans = str(svlan['id']).split('-')
296         svlan_min = int(svlans[0])
297         svlan_max = int(svlans[1]) if len(svlans) == 2 else int(svlans[0])
298         if len(svlans) == 2:
299             svlan = self._get_random_value(svlan_min, svlan_max)
300         else:
301             svlan = svlan_min
302         cvlans = str(cvlan['id']).split('-')
303         cvlan_min = int(cvlans[0])
304         cvlan_max = int(cvlans[1]) if len(cvlans) == 2 else int(cvlans[0])
305         if len(cvlans) == 2:
306             cvlan = self._get_random_value(cvlan_min, cvlan_max)
307         else:
308             cvlan = cvlan_min
309         self.qinq_packet = Pkt.Dot1Q(vlan=svlan) / Pkt.Dot1Q(vlan=cvlan)
310
311     def set_qinq(self, qinq):
312         """ set qinq in packet """
313         self.set_svlan_cvlan(qinq['S-VLAN'], qinq['C-VLAN'])
314
315     def set_outer_l2_fields(self, outer_l2):
316         """ setup outer l2 fields from traffic profile """
317         ether_params = {'type': 0x800}
318         self._set_ether_fields(**ether_params)
319         if 'srcmac' in outer_l2:
320             self.set_src_mac(outer_l2['srcmac'])
321         if 'dstmac' in outer_l2:
322             self.set_dst_mac(outer_l2['dstmac'])
323         if 'QinQ' in outer_l2:
324             self.set_qinq(outer_l2['QinQ'])
325
326     def set_outer_l3v4_fields(self, outer_l3v4):
327         """ setup outer l3v4 fields from traffic profile """
328         ip_params = {}
329         if 'proto' in outer_l3v4:
330             ip_params['proto'] = outer_l3v4['proto']
331             if outer_l3v4['proto'] == 'tcp':
332                 self.udp_packet = Pkt.TCP()
333                 self.udp_dport = 'TCP.dport'
334                 self.udp_sport = 'TCP.sport'
335                 tcp_params = {'flags': '', 'window': 0}
336                 self._set_udp_fields(**tcp_params)
337         if 'ttl' in outer_l3v4:
338             ip_params['ttl'] = outer_l3v4['ttl']
339         self._set_ip_fields(**ip_params)
340         if 'dscp' in outer_l3v4:
341             self.set_dscp(outer_l3v4['dscp'])
342         if 'srcip4' in outer_l3v4:
343             self.set_src_ip4(outer_l3v4['srcip4'], outer_l3v4['count'])
344         if 'dstip4' in outer_l3v4:
345             self.set_dst_ip4(outer_l3v4['dstip4'], outer_l3v4['count'])
346
347     def set_outer_l3v6_fields(self, outer_l3v6):
348         """ setup outer l3v6 fields from traffic profile """
349         ether_params = {'type': 0x86dd}
350         self._set_ether_fields(**ether_params)
351         ip6_params = {}
352         if 'proto' in outer_l3v6:
353             ip6_params['proto'] = outer_l3v6['proto']
354             if outer_l3v6['proto'] == 'tcp':
355                 self.udp_packet = Pkt.TCP()
356                 self.udp_dport = 'TCP.dport'
357                 self.udp_sport = 'TCP.sport'
358                 tcp_params = {'flags': '', 'window': 0}
359                 self._set_udp_fields(**tcp_params)
360         if 'ttl' in outer_l3v6:
361             ip6_params['ttl'] = outer_l3v6['ttl']
362         if 'tc' in outer_l3v6:
363             ip6_params['tc'] = outer_l3v6['tc']
364         if 'hlim' in outer_l3v6:
365             ip6_params['hlim'] = outer_l3v6['hlim']
366         self._set_ip6_fields(**ip6_params)
367         if 'srcip6' in outer_l3v6:
368             self.set_src_ip6(outer_l3v6['srcip6'])
369         if 'dstip6' in outer_l3v6:
370             self.set_dst_ip6(outer_l3v6['dstip6'])
371
372     def set_outer_l4_fields(self, outer_l4):
373         """ setup outer l4 fields from traffic profile """
374         if 'srcport' in outer_l4:
375             self.set_src_port(outer_l4['srcport'], outer_l4['count'])
376         if 'dstport' in outer_l4:
377             self.set_dst_port(outer_l4['dstport'], outer_l4['count'])
378
379     def generate_imix_data(self, packet_definition):
380         """ generate packet size for a given traffic profile """
381         imix_count = {}
382         imix_data = {}
383         if not packet_definition:
384             return imix_count
385         imix = packet_definition.get('framesize')
386         if imix:
387             for size in imix:
388                 data = imix[size]
389                 imix_data[int(size[:-1])] = int(data)
390             imix_sum = sum(imix_data.values())
391             if imix_sum > 100:
392                 raise SystemExit("Error in IMIX data")
393             elif imix_sum < 100:
394                 imix_data[64] = imix_data.get(64, 0) + (100 - imix_sum)
395
396             avg_size = 0.0
397             for size in imix_data:
398                 count = int(imix_data[size])
399                 if count:
400                     avg_size += round(size * count / 100, 2)
401                     pps = round(self.pps * count / 100, 0)
402                     imix_count[size] = pps
403             self.rate = round(1342177280 / avg_size, 0) * 2
404             logging.debug("Imax: %s rate: %s", imix_count, self.rate)
405         return imix_count
406
407     def get_streams(self, profile_data):
408         """ generate trex stream
409         :param profile_data:
410         :type profile_data:
411         """
412         self.streams = []
413         self.pps = self.params['traffic_profile'].get('frame_rate', 100)
414         for packet_name in profile_data:
415             outer_l2 = profile_data[packet_name].get('outer_l2')
416             imix_data = self.generate_imix_data(outer_l2)
417             if not imix_data:
418                 imix_data = {64: self.pps}
419             self.generate_vm(profile_data[packet_name])
420             for size in imix_data:
421                 self._generate_streams(size, imix_data[size])
422         self._generate_profile()
423         return self.profile
424
425     def generate_vm(self, packet_definition):
426         """ generate  trex vm with flows setup """
427         self.ether_packet = Pkt.Ether()
428         self.ip_packet = Pkt.IP()
429         self.ip6_packet = None
430         self.udp_packet = Pkt.UDP()
431         self.udp_dport = 'UDP.dport'
432         self.udp_sport = 'UDP.sport'
433         self.qinq = False
434         self.vm_flow_vars = []
435         outer_l2 = packet_definition.get('outer_l2', None)
436         outer_l3v4 = packet_definition.get('outer_l3v4', None)
437         outer_l3v6 = packet_definition.get('outer_l3v6', None)
438         outer_l4 = packet_definition.get('outer_l4', None)
439         if outer_l2:
440             self.set_outer_l2_fields(outer_l2)
441         if outer_l3v4:
442             self.set_outer_l3v4_fields(outer_l3v4)
443         if outer_l3v6:
444             self.set_outer_l3v6_fields(outer_l3v6)
445         if outer_l4:
446             self.set_outer_l4_fields(outer_l4)
447         self.trex_vm = STLScVmRaw(self.vm_flow_vars)
448
449     def generate_packets(self):
450         """ generate packets from trex TG """
451         base_pkt = self.base_pkt
452         size = self.fsize - 4
453         pad = max(0, size - len(base_pkt)) * 'x'
454         self.packets = [STLPktBuilder(pkt=base_pkt / pad,
455                                       vm=vm) for vm in self.vms]
456
457     def _create_single_packet(self, size=64):
458         size = size - 4
459         ether_packet = self.ether_packet
460         ip_packet = self.ip6_packet if self.ip6_packet else self.ip_packet
461         udp_packet = self.udp_packet
462         if self.qinq:
463             qinq_packet = self.qinq_packet
464             base_pkt = ether_packet / qinq_packet / ip_packet / udp_packet
465         else:
466             base_pkt = ether_packet / ip_packet / udp_packet
467         pad = max(0, size - len(base_pkt)) * 'x'
468         packet = STLPktBuilder(pkt=base_pkt / pad, vm=self.trex_vm)
469         return packet
470
471     def _create_single_stream(self, packet_size, pps, isg=0):
472         packet = self._create_single_packet(packet_size)
473         if self.pg_id:
474             self.pg_id += 1
475             stl_flow = STLFlowLatencyStats(pg_id=self.pg_id)
476             stream = STLStream(isg=isg, packet=packet, mode=STLTXCont(pps=pps),
477                                flow_stats=stl_flow)
478         else:
479             stream = STLStream(isg=isg, packet=packet, mode=STLTXCont(pps=pps))
480         return stream
481
482     def _generate_streams(self, packet_size, pps):
483         self.streams.append(self._create_single_stream(packet_size, pps))
484
485     def _generate_profile(self):
486         self.profile = STLProfile(self.streams)
487
488     @classmethod
489     def _get_start_end_ipv6(cls, start_ip, end_ip):
490         try:
491             ip1 = socket.inet_pton(socket.AF_INET6, start_ip)
492             ip2 = socket.inet_pton(socket.AF_INET6, end_ip)
493             hi1, lo1 = struct.unpack('!QQ', ip1)
494             hi2, lo2 = struct.unpack('!QQ', ip2)
495             if ((hi1 << 64) | lo1) > ((hi2 << 64) | lo2):
496                 raise SystemExit("IPv6: start_ip is greater then end_ip")
497             max_p1 = abs(int(lo1) - int(lo2))
498             base_p1 = lo1
499         except Exception as ex_error:
500             raise SystemExit(ex_error)
501         else:
502             return base_p1, max_p1 + base_p1
503
504     @classmethod
505     def _get_random_value(cls, min_port, max_port):
506         cryptogen = SystemRandom()
507         return cryptogen.randrange(min_port, max_port)