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