1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
|
# 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.
""" RFC2544 Throughput implemenation """
from __future__ import absolute_import
from __future__ import division
import logging
from trex_stl_lib.trex_stl_client import STLStream
from trex_stl_lib.trex_stl_streams import STLFlowLatencyStats
from trex_stl_lib.trex_stl_streams import STLTXCont
from yardstick.network_services.traffic_profile.traffic_profile \
import TrexProfile
LOGGING = logging.getLogger(__name__)
class RFC2544Profile(TrexProfile):
""" This class handles rfc2544 implemenation. """
def __init__(self, traffic_generator):
super(RFC2544Profile, self).__init__(traffic_generator)
self.generator = None
self.max_rate = None
self.min_rate = None
self.ports = None
self.rate = 100
self.drop_percent_at_max_tx = None
self.throughput_max = None
def register_generator(self, generator):
self.generator = generator
def execute(self, traffic_generator=None):
""" Generate the stream and run traffic on the given ports """
if traffic_generator is not None and self.generator is None:
self.generator = traffic_generator
if self.ports is not None:
return
self.ports = []
priv_ports = self.generator.priv_ports
pub_ports = self.generator.pub_ports
# start from 1 for private_1, public_1, etc.
for index, (priv_port, pub_port) in enumerate(zip(priv_ports, pub_ports), 1):
profile_data = self.params.get('private_{}'.format(index), '')
self.ports.append(priv_port)
# pass profile_data directly, don't use self.profile_data
self.generator.client.add_streams(self.get_streams(profile_data), ports=priv_port)
profile_data = self.params.get('public_{}'.format(index), '')
# correlated traffic doesn't use public traffic?
if not profile_data or self.generator.rfc2544_helper.correlated_traffic:
continue
# just get the pub_port
self.ports.append(pub_port)
self.generator.client.add_streams(self.get_streams(profile_data), ports=pub_port)
self.max_rate = self.rate
self.min_rate = 0
self.generator.client.start(ports=self.ports, mult=self.get_multiplier(),
duration=30, force=True)
self.drop_percent_at_max_tx = 0
self.throughput_max = 0
def get_multiplier(self):
""" Get the rate at which next iteration to run """
self.rate = round((self.max_rate + self.min_rate) / 2.0, 2)
multiplier = round(self.rate / self.pps, 2)
return str(multiplier)
def get_drop_percentage(self, generator=None):
""" Calculate the drop percentage and run the traffic """
if generator is None:
generator = self.generator
run_duration = self.generator.RUN_DURATION
samples = self.generator.generate_samples()
in_packets = sum([value['in_packets'] for value in samples.values()])
out_packets = sum([value['out_packets'] for value in samples.values()])
packet_drop = abs(out_packets - in_packets)
drop_percent = 100.0
try:
drop_percent = round((packet_drop / float(out_packets)) * 100, 5)
except ZeroDivisionError:
LOGGING.info('No traffic is flowing')
# TODO(esm): RFC2544 doesn't tolerate packet loss, why do we?
tolerance_low = generator.rfc2544_helper.tolerance_low
tolerance_high = generator.rfc2544_helper.tolerance_high
tx_rate = out_packets / run_duration
rx_rate = in_packets / run_duration
throughput_max = self.throughput_max
drop_percent_at_max_tx = self.drop_percent_at_max_tx
if self.drop_percent_at_max_tx is None:
self.rate = tx_rate
self.first_run = False
if drop_percent > tolerance_high:
# TODO(esm): why don't we discard results that are out of tolerance?
self.max_rate = self.rate
if throughput_max == 0:
throughput_max = rx_rate
drop_percent_at_max_tx = drop_percent
elif drop_percent >= tolerance_low:
# TODO(esm): why do we update the samples dict in this case
# and not update our tracking values?
throughput_max = rx_rate
drop_percent_at_max_tx = drop_percent
elif drop_percent >= self.drop_percent_at_max_tx:
# TODO(esm): why don't we discard results that are out of tolerance?
self.min_rate = self.rate
self.drop_percent_at_max_tx = drop_percent_at_max_tx = drop_percent
self.throughput_max = throughput_max = rx_rate
else:
# TODO(esm): why don't we discard results that are out of tolerance?
self.min_rate = self.rate
generator.clear_client_stats()
generator.start_client(mult=self.get_multiplier(),
duration=run_duration, force=True)
# if correlated traffic update the Throughput
if generator.rfc2544_helper.correlated_traffic:
throughput_max *= 2
samples.update({
'TxThroughput': tx_rate,
'RxThroughput': rx_rate,
'CurrentDropPercentage': drop_percent,
'Throughput': throughput_max,
'DropPercentage': drop_percent_at_max_tx,
})
return samples
def execute_latency(self, generator=None, samples=None):
if generator is None:
generator = self.generator
if samples is None:
samples = generator.generate_samples()
self.pps, multiplier = self.calculate_pps(samples)
self.ports = []
self.pg_id = self.params['traffic_profile'].get('pg_id', 1)
priv_ports = generator.priv_ports
pub_ports = generator.pub_ports
for index, (priv_port, pub_port) in enumerate(zip(priv_ports, pub_ports), 1):
profile_data = self.params.get('private_{}'.format(index), '')
self.ports.append(priv_port)
generator.client.add_streams(self.get_streams(profile_data),
ports=priv_port)
profile_data = self.params.get('public_{}'.format(index), '')
if not profile_data or generator.correlated_traffic:
continue
pub_port = generator.pub_ports[index]
self.ports.append(pub_port)
generator.client.add_streams(self.get_streams(profile_data),
ports=pub_port)
generator.start_client(ports=self.ports, mult=str(multiplier),
duration=120, force=True)
self.first_run = False
def calculate_pps(self, samples):
pps = round(samples['Throughput'] / 2, 2)
multiplier = round(self.rate / self.pps, 2)
return pps, multiplier
def create_single_stream(self, packet_size, pps, isg=0):
packet = self._create_single_packet(packet_size)
if pps:
stl_mode = STLTXCont(pps=pps)
else:
stl_mode = STLTXCont(pps=self.pps)
if self.pg_id:
LOGGING.debug("pg_id: %s", self.pg_id)
stl_flow_stats = STLFlowLatencyStats(pg_id=self.pg_id)
stream = STLStream(isg=isg, packet=packet, mode=stl_mode,
flow_stats=stl_flow_stats)
self.pg_id += 1
else:
stream = STLStream(isg=isg, packet=packet, mode=stl_mode)
return stream
|