1 # Copyright (c) 2016-2017 Intel Corporation
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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.
17 from trex_stl_lib import api as Pkt
18 from trex_stl_lib import trex_stl_client
19 from trex_stl_lib import trex_stl_packet_builder_scapy
20 from trex_stl_lib import trex_stl_streams
22 from yardstick.network_services.traffic_profile import trex_traffic_profile
25 LOGGING = logging.getLogger(__name__)
30 class PortPgIDMap(object):
31 """Port and pg_id mapping class
33 "pg_id" is the identification STL library gives to each stream. In the
34 RFC2544Profile class, the traffic has a STLProfile per port, which contains
35 one or several streams, one per packet size defined in the IMIX test case
38 Example of port <-> pg_id map:
39 self._port_pg_id_map = {
47 self._last_port = None
48 self._port_pg_id_map = {}
50 def add_port(self, port):
51 self._last_port = port
52 self._port_pg_id_map[port] = []
54 def get_pg_ids(self, port):
55 return self._port_pg_id_map.get(port, [])
57 def increase_pg_id(self, port=None):
58 port = self._last_port if not port else port
61 pg_id_list = self._port_pg_id_map.get(port)
64 pg_id_list = self._port_pg_id_map[port]
66 pg_id_list.append(self._pg_id)
70 class RFC2544Profile(trex_traffic_profile.TrexProfile):
71 """TRex RFC2544 traffic profile"""
73 TOLERANCE_LIMIT = 0.01
75 def __init__(self, traffic_generator):
76 super(RFC2544Profile, self).__init__(traffic_generator)
78 self.rate = self.config.frame_rate
79 self.max_rate = self.config.frame_rate
81 self.drop_percent_max = 0
83 def register_generator(self, generator):
84 self.generator = generator
86 def stop_traffic(self, traffic_generator=None):
87 """"Stop traffic injection, reset counters and remove streams"""
88 if traffic_generator is not None and self.generator is None:
89 self.generator = traffic_generator
91 self.generator.client.stop()
92 self.generator.client.reset()
93 self.generator.client.remove_all_streams()
95 def execute_traffic(self, traffic_generator=None):
96 """Generate the stream and run traffic on the given ports
98 :param traffic_generator: (TrexTrafficGenRFC) traffic generator
99 :return ports: (list of int) indexes of ports
100 port_pg_id: (dict) port indexes and pg_id [1] map
101 [1] https://trex-tgn.cisco.com/trex/doc/cp_stl_docs/api/
102 profile_code.html#stlstream-modes
104 if traffic_generator is not None and self.generator is None:
105 self.generator = traffic_generator
107 port_pg_id = PortPgIDMap()
109 for vld_id, intfs in sorted(self.generator.networks.items()):
110 profile_data = self.params.get(vld_id)
113 if (vld_id.startswith(self.DOWNLINK) and
114 self.generator.rfc2544_helper.correlated_traffic):
117 port_num = int(self.generator.port_num(intf))
118 ports.append(port_num)
119 port_pg_id.add_port(port_num)
120 profile = self._create_profile(profile_data,
121 self.rate, port_pg_id,
122 self.config.enable_latency)
123 self.generator.client.add_streams(profile, ports=[port_num])
125 self.generator.client.start(ports=ports,
126 duration=self.config.duration,
128 return ports, port_pg_id
130 def _create_profile(self, profile_data, rate, port_pg_id, enable_latency):
131 """Create a STL profile (list of streams) for a port"""
133 for packet_name in profile_data:
134 imix = (profile_data[packet_name].
135 get('outer_l2', {}).get('framesize'))
136 imix_data = self._create_imix_data(imix)
137 self._create_vm(profile_data[packet_name])
138 _streams = self._create_streams(imix_data, rate, port_pg_id,
140 streams.extend(_streams)
141 return trex_stl_streams.STLProfile(streams)
143 def _create_imix_data(self, imix):
144 """Generate the IMIX distribution for a STL profile
146 The input information is the framesize dictionary in a test case
147 traffic profile definition. E.g.:
157 This function normalizes the sum of framesize weights to 100 and
158 returns a dictionary of frame sizes in bytes and weight in percentage.
160 imix_count = {64: 25, 128: 75}
162 :param imix: (dict) IMIX size and weight
168 imix_count = {size.upper().replace('B', ''): int(weight)
169 for size, weight in imix.items()}
170 imix_sum = sum(imix_count.values())
172 imix_count = {64: 100}
175 weight_normalize = float(imix_sum) / 100
176 return {size: float(weight) / weight_normalize
177 for size, weight in imix_count.items()}
179 def _create_vm(self, packet_definition):
180 """Create the STL Raw instructions"""
181 self.ether_packet = Pkt.Ether()
182 self.ip_packet = Pkt.IP()
183 self.ip6_packet = None
184 self.udp_packet = Pkt.UDP()
185 self.udp[DST_PORT] = 'UDP.dport'
186 self.udp[SRC_PORT] = 'UDP.sport'
188 self.vm_flow_vars = []
189 outer_l2 = packet_definition.get('outer_l2')
190 outer_l3v4 = packet_definition.get('outer_l3v4')
191 outer_l3v6 = packet_definition.get('outer_l3v6')
192 outer_l4 = packet_definition.get('outer_l4')
194 self._set_outer_l2_fields(outer_l2)
196 self._set_outer_l3v4_fields(outer_l3v4)
198 self._set_outer_l3v6_fields(outer_l3v6)
200 self._set_outer_l4_fields(outer_l4)
201 self.trex_vm = trex_stl_packet_builder_scapy.STLScVmRaw(
204 def _create_single_packet(self, size=64):
206 ether_packet = self.ether_packet
207 ip_packet = self.ip6_packet if self.ip6_packet else self.ip_packet
208 udp_packet = self.udp_packet
210 qinq_packet = self.qinq_packet
211 base_pkt = ether_packet / qinq_packet / ip_packet / udp_packet
213 base_pkt = ether_packet / ip_packet / udp_packet
214 pad = max(0, size - len(base_pkt)) * 'x'
215 return trex_stl_packet_builder_scapy.STLPktBuilder(
216 pkt=base_pkt / pad, vm=self.trex_vm)
218 def _create_streams(self, imix_data, rate, port_pg_id, enable_latency):
219 """Create a list of streams per packet size
221 The STL TX mode speed of the generated streams will depend on the frame
222 weight and the frame rate. Both the frame weight and the total frame
223 rate are normalized to 100. The STL TX mode speed, defined in
224 percentage, is the combitation of both percentages. E.g.:
227 --> STLTXmode percentage = 10 (%)
231 --> STLTXmode percentage = 40 (%)
233 :param imix_data: (dict) IMIX size and weight
234 :param rate: (float) normalized [0..100] total weight
235 :param pg_id: (PortPgIDMap) port / pg_id (list) map
238 for size, weight in ((int(size), float(weight)) for (size, weight)
239 in imix_data.items() if float(weight) > 0):
240 packet = self._create_single_packet(size)
241 pg_id = port_pg_id.increase_pg_id()
242 stl_flow = (trex_stl_streams.STLFlowLatencyStats(pg_id=pg_id) if
243 enable_latency else None)
244 mode = trex_stl_streams.STLTXCont(percentage=weight * rate / 100)
245 streams.append(trex_stl_client.STLStream(
246 packet=packet, flow_stats=stl_flow, mode=mode))
249 def get_drop_percentage(self, samples, tol_low, tol_high,
251 """Calculate the drop percentage and run the traffic"""
253 out_pkt_end = sum(port['out_packets'] for port in samples[-1].values())
254 in_pkt_end = sum(port['in_packets'] for port in samples[-1].values())
255 out_pkt_ini = sum(port['out_packets'] for port in samples[0].values())
256 in_pkt_ini = sum(port['in_packets'] for port in samples[0].values())
257 time_diff = (list(samples[-1].values())[0]['timestamp'] -
258 list(samples[0].values())[0]['timestamp']).total_seconds()
259 out_packets = out_pkt_end - out_pkt_ini
260 in_packets = in_pkt_end - in_pkt_ini
261 tx_rate_fps = float(out_packets) / time_diff
262 rx_rate_fps = float(in_packets) / time_diff
265 # https://tools.ietf.org/html/rfc2544#section-26.3
267 drop_percent = round(
268 (float(abs(out_packets - in_packets)) / out_packets) * 100, 5)
270 tol_high = max(tol_high, self.TOLERANCE_LIMIT)
271 tol_low = min(tol_low, self.TOLERANCE_LIMIT)
272 if drop_percent > tol_high:
273 self.max_rate = self.rate
274 elif drop_percent < tol_low:
275 self.min_rate = self.rate
279 last_rate = self.rate
280 self.rate = round(float(self.max_rate + self.min_rate) / 2.0, 5)
282 throughput = rx_rate_fps * 2 if correlated_traffic else rx_rate_fps
284 if drop_percent > self.drop_percent_max:
285 self.drop_percent_max = drop_percent
287 latency = {port_num: value['latency']
288 for port_num, value in samples[-1].items()}
291 'TxThroughput': tx_rate_fps,
292 'RxThroughput': rx_rate_fps,
293 'CurrentDropPercentage': drop_percent,
294 'Throughput': throughput,
295 'DropPercentage': self.drop_percent_max,
299 return completed, output