1 # Copyright (c) 2016 Cisco and/or its affiliates.
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at:
6 # http://www.apache.org/licenses/LICENSE-2.0
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
14 """Drop rate search algorithms"""
16 from abc import ABCMeta, abstractmethod
17 from enum import Enum, unique
20 class SearchDirection(Enum):
21 """Direction of linear search"""
27 class SearchResults(Enum):
28 """Result of the drop rate search"""
36 """Type of rate units"""
39 PACKETS_PER_SECOND = 2
43 class LossAcceptanceType(Enum):
44 """Type of the loss acceptance criteria"""
49 class DropRateSearch(object):
50 """Abstract class with search algorithm implementation"""
52 __metaclass__ = ABCMeta
55 #duration of traffic run (binary, linear)
57 #initial start rate (binary, linear)
58 self._rate_start = 100
59 #step of the linear search, unit: RateType (self._rate_type)
60 self._rate_linear_step = 10
61 #last rate of the binary search, unit: RateType (self._rate_type)
62 self._last_binary_rate = 0
63 #linear search direction, permitted values: SearchDirection
64 self._search_linear_direction = SearchDirection.TOP_DOWN
65 #upper limit of search, unit: RateType (self._rate_type)
67 #lower limit of search, unit: RateType (self._rate_type)
69 #permitted values: RateType
70 self._rate_type = RateType.PERCENTAGE
71 #accepted loss during search, units: LossAcceptanceType
72 self._loss_acceptance = 0
73 #permitted values: LossAcceptanceType
74 self._loss_acceptance_type = LossAcceptanceType.FRAMES
75 #size of frames to send
76 self._frame_size = "64"
77 #binary convergence criterium type is self._rate_type
78 self._binary_convergence_threshold = 100000
79 #numbers of traffic runs during one rate step
80 self._max_attempts = 1
83 self._search_result = None
84 self._search_result_rate = None
87 def measure_loss(self, rate, frame_size, loss_acceptance,
88 loss_acceptance_type, traffic_type):
89 """Send traffic from TG and measure count of dropped frames
91 :param rate: offered traffic load
92 :param frame_size: size of frame
93 :param loss_acceptance: permitted drop ratio or frames count
94 :param loss_acceptance_type: type of permitted loss
95 :param traffic_type: traffic profile ([2,3]-node-L[2,3], ...)
98 :type loss_acceptance: float
99 :type loss_acceptance_type: LossAcceptanceType
100 :type traffic_type: str
101 :return: drop threshold exceeded? (True/False)
106 def set_search_rate_boundaries(self, max_rate, min_rate):
107 """Set search boundaries: min,max
109 :param max_rate: upper value of search boundaries
110 :param min_rate: lower value of search boundaries
111 :type max_rate: float
112 :type min_rate: float
115 if float(min_rate) <= 0:
116 raise ValueError("min_rate must be higher than 0")
117 elif float(min_rate) > float(max_rate):
118 raise ValueError("min_rate must be lower than max_rate")
120 self._rate_max = float(max_rate)
121 self._rate_min = float(min_rate)
123 def set_search_linear_step(self, step_rate):
124 """Set step size for linear search
126 :param step_rate: linear search step size
127 :type step_rate: float
130 self._rate_linear_step = float(step_rate)
132 def set_search_rate_type_percentage(self):
133 """Set rate type to percentage of linerate
137 self._set_search_rate_type(RateType.PERCENTAGE)
139 def set_search_rate_type_bps(self):
140 """Set rate type to bits per second
144 self._set_search_rate_type(RateType.BITS_PER_SECOND)
146 def set_search_rate_type_pps(self):
147 """Set rate type to packets per second
151 self._set_search_rate_type(RateType.PACKETS_PER_SECOND)
153 def _set_search_rate_type(self, rate_type):
154 """Set rate type to one of RateType-s
156 :param rate_type: type of rate to set
157 :type rate_type: RateType
160 if rate_type not in RateType:
161 raise Exception("rate_type unknown: {}".format(rate_type))
163 self._rate_type = rate_type
165 def set_search_frame_size(self, frame_size):
166 """Set size of frames to send
168 :param frame_size: size of frames
169 :type frame_size: str
172 self._frame_size = frame_size
174 def set_duration(self, duration):
175 """Set the duration of single traffic run
177 :param duration: number of seconds for traffic to run
181 self._duration = int(duration)
183 def get_duration(self):
184 """Return configured duration of single traffic run
186 :return: number of seconds for traffic to run
189 return self._duration
191 def set_binary_convergence_threshold(self, convergence):
192 """Set convergence for binary search
194 :param convergence: treshold value number
195 :type convergence: float
198 self._binary_convergence_threshold = float(convergence)
200 def get_binary_convergence_threshold(self):
201 """Get convergence for binary search
203 :return: treshold value number
206 return self._binary_convergence_threshold
208 def get_rate_type_str(self):
209 """Return rate type representation
211 :return: string representation of rate type
214 if self._rate_type == RateType.PERCENTAGE:
216 elif self._rate_type == RateType.BITS_PER_SECOND:
218 elif self._rate_type == RateType.PACKETS_PER_SECOND:
221 raise ValueError("RateType unknown")
223 def linear_search(self, start_rate, traffic_type):
224 """Linear search of rate with loss below acceptance criteria
226 :param start_rate: initial rate
227 :param traffic_type: traffic profile
228 :type start_rate: float
229 :param traffic_type: str
233 if not self._rate_min <= float(start_rate) <= self._rate_max:
234 raise ValueError("Start rate is not in min,max range")
236 rate = float(start_rate)
237 #the last but one step
242 res = self.measure_loss(rate, self._frame_size,
243 self._loss_acceptance,
244 self._loss_acceptance_type,
246 if self._search_linear_direction == SearchDirection.BOTTOM_UP:
247 #loss occured and it was above acceptance criteria
249 #if this is first run then we didn't find drop rate
250 if prev_rate == None:
251 self._search_result = SearchResults.FAILURE
252 self._search_result_rate = None
254 # else we found the rate, which is value from previous run
256 self._search_result = SearchResults.SUCCESS
257 self._search_result_rate = prev_rate
259 #there was no loss / loss below acceptance criteria
262 rate += self._rate_linear_step
263 if rate > self._rate_max:
264 if prev_rate != self._rate_max:
265 #one last step with rate set to _rate_max
266 rate = self._rate_max
269 self._search_result = SearchResults.SUCCESS
270 self._search_result_rate = prev_rate
275 raise RuntimeError("Unknown search result")
277 elif self._search_linear_direction == SearchDirection.TOP_DOWN:
278 #loss occured, decrease rate
281 rate -= self._rate_linear_step
282 if rate < self._rate_min:
283 if prev_rate != self._rate_min:
284 #one last step with rate set to _rate_min
285 rate = self._rate_min
288 self._search_result = SearchResults.FAILURE
289 self._search_result_rate = None
293 #no loss => non/partial drop rate found
295 self._search_result = SearchResults.SUCCESS
296 self._search_result_rate = rate
299 raise RuntimeError("Unknown search result")
301 raise Exception("Unknown search direction")
303 raise Exception("Wrong codepath")
305 def verify_search_result(self):
306 """Fail if search was not successful
311 if self._search_result == SearchResults.FAILURE:
312 raise Exception('Search FAILED')
313 elif self._search_result in [SearchResults.SUCCESS, SearchResults.SUSPICIOUS]:
314 return self._search_result_rate
316 def binary_search(self, b_min, b_max, traffic_type):
317 """Binary search of rate with loss below acceptance criteria
319 :param b_min: min range rate
320 :param b_max: max range rate
321 :param traffic_type: traffic profile
324 :type traffic_type: str
328 if not self._rate_min <= float(b_min) <= self._rate_max:
329 raise ValueError("Min rate is not in min,max range")
330 if not self._rate_min <= float(b_max) <= self._rate_max:
331 raise ValueError("Max rate is not in min,max range")
332 if float(b_max) < float(b_min):
333 raise ValueError("Min rate is greater then max rate")
336 #rate is half of interval + start of interval
337 rate = ((float(b_max) - float(b_min)) / 2) + float(b_min)
338 #rate diff with previous run
339 rate_diff = abs(self._last_binary_rate - rate)
341 #convergence criterium
342 if float(rate_diff) < float(self._binary_convergence_threshold):
343 if not self._search_result_rate:
344 self._search_result = SearchResults.FAILURE
346 self._search_result = SearchResults.SUCCESS
349 self._last_binary_rate = rate
351 res = self.measure_loss(rate, self._frame_size,
352 self._loss_acceptance,
353 self._loss_acceptance_type,
355 #loss occured and it was above acceptance criteria
357 self.binary_search(b_min, rate, traffic_type)
358 #there was no loss / loss below acceptance criteria
360 self._search_result_rate = rate
361 self.binary_search(rate, b_max, traffic_type)
363 raise RuntimeError("Unknown search result")
365 def combined_search(self):
366 raise NotImplementedError