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 #linear search direction, permitted values: SearchDirection
62 self._search_linear_direction = SearchDirection.TOP_DOWN
63 #upper limit of search, unit: RateType (self._rate_type)
65 #lower limit of search, unit: RateType (self._rate_type)
67 #permitted values: RateType
68 self._rate_type = RateType.PERCENTAGE
69 #accepted loss during search, units: LossAcceptanceType
70 self._loss_acceptance = 0
71 #permitted values: LossAcceptanceType
72 self._loss_acceptance_type = LossAcceptanceType.FRAMES
73 #size of frames to send
74 self._frame_size = "64"
75 #binary convergence criterium type is self._rate_type
76 self._binary_convergence_threshhold = "0.01"
77 #numbers of traffic runs during one rate step
78 self._max_attempts = 1
81 self._search_result = None
82 self._search_result_rate = None
85 def measure_loss(self, rate, frame_size, loss_acceptance,
86 loss_acceptance_type, traffic_type):
87 """Send traffic from TG and measure count of dropped frames
89 :param rate: offered traffic load
90 :param frame_size: size of frame
91 :param loss_acceptance: permitted drop ratio or frames count
92 :param loss_acceptance_type: type of permitted loss
93 :param traffic_type: traffic profile ([2,3]-node-L[2,3], ...)
96 :type loss_acceptance: float
97 :type loss_acceptance_type: LossAcceptanceType
98 :type traffic_type: str
99 :return: drop threshhold exceeded? (True/False)
104 def set_search_rate_boundaries(self, max_rate, min_rate):
105 """Set search boundaries: min,max
107 :param max_rate: upper value of search boundaries
108 :param min_rate: lower value of search boundaries
109 :type max_rate: float
110 :type min_rate: float
113 if float(min_rate) <= 0:
114 raise ValueError("min_rate must be higher than 0")
115 elif float(min_rate) > float(max_rate):
116 raise ValueError("min_rate must be lower than max_rate")
118 self._rate_max = float(max_rate)
119 self._rate_min = float(min_rate)
121 def set_search_linear_step(self, step_rate):
122 """Set step size for linear search
124 :param step_rate: linear search step size
125 :type step_rate: float
128 self._rate_linear_step = float(step_rate)
130 def set_search_rate_type_percentage(self):
131 """Set rate type to percentage of linerate
135 self._set_search_rate_type(RateType.PERCENTAGE)
137 def set_search_rate_type_bps(self):
138 """Set rate type to bits per second
142 self._set_search_rate_type(RateType.BITS_PER_SECOND)
144 def set_search_rate_type_pps(self):
145 """Set rate type to packets per second
149 self._set_search_rate_type(RateType.PACKETS_PER_SECOND)
151 def _set_search_rate_type(self, rate_type):
152 """Set rate type to one of RateType-s
154 :param rate_type: type of rate to set
155 :type rate_type: RateType
158 if rate_type not in RateType:
159 raise Exception("rate_type unknown: {}".format(rate_type))
161 self._rate_type = rate_type
163 def set_search_frame_size(self, frame_size):
164 """Set size of frames to send
166 :param frame_size: size of frames
167 :type frame_size: str
170 self._frame_size = frame_size
172 def set_duration(self, duration):
173 """Set the duration of single traffic run
175 :param duration: number of seconds for traffic to run
179 self._duration = int(duration)
181 def get_duration(self):
182 """Return configured duration of single traffic run
184 :return: number of seconds for traffic to run
187 return self._duration
189 def get_rate_type_str(self):
190 """Return rate type representation
192 :return: string representation of rate type
195 if self._rate_type == RateType.PERCENTAGE:
197 elif self._rate_type == RateType.BITS_PER_SECOND:
199 elif self._rate_type == RateType.PACKETS_PER_SECOND:
202 raise ValueError("RateType unknown")
204 def linear_search(self, start_rate, traffic_type):
205 """Linear search of rate with loss below acceptance criteria
207 :param start_rate: initial rate
208 :param traffic_type: traffic profile
209 :type start_rate: float
210 :param traffic_type: str
214 if not self._rate_min <= float(start_rate) <= self._rate_max:
215 raise ValueError("Start rate is not in min,max range")
217 rate = float(start_rate)
218 #the last but one step
223 res = self.measure_loss(rate, self._frame_size,
224 self._loss_acceptance,
225 self._loss_acceptance_type,
227 if self._search_linear_direction == SearchDirection.BOTTOM_UP:
228 #loss occured and it was above acceptance criteria
230 #if this is first run then we didn't find drop rate
231 if prev_rate == None:
232 self._search_result = SearchResults.FAILURE
233 self._search_result_rate = None
235 # else we found the rate, which is value from previous run
237 self._search_result = SearchResults.SUCCESS
238 self._search_result_rate = prev_rate
240 #there was no loss / loss below acceptance criteria
243 rate += self._rate_linear_step
244 if rate > self._rate_max:
245 if prev_rate != self._rate_max:
246 #one last step with rate set to _rate_max
247 rate = self._rate_max
250 self._search_result = SearchResults.SUCCESS
251 self._search_result_rate = prev_rate
256 raise RuntimeError("Unknown search result")
258 elif self._search_linear_direction == SearchDirection.TOP_DOWN:
259 #loss occured, decrease rate
262 rate -= self._rate_linear_step
263 if rate < self._rate_min:
264 if prev_rate != self._rate_min:
265 #one last step with rate set to _rate_min
266 rate = self._rate_min
269 self._search_result = SearchResults.FAILURE
270 self._search_result_rate = None
274 #no loss => non/partial drop rate found
276 self._search_result = SearchResults.SUCCESS
277 self._search_result_rate = rate
280 raise RuntimeError("Unknown search result")
282 raise Exception("Unknown search direction")
284 raise Exception("Wrong codepath")
286 def verify_search_result(self):
287 """Fail if search was not successful
292 if self._search_result == SearchResults.FAILURE:
293 raise Exception('Search FAILED')
294 elif self._search_result in [SearchResults.SUCCESS, SearchResults.SUSPICIOUS]:
295 return self._search_result_rate
297 def binary_search(self):
298 raise NotImplementedError
300 def combined_search(self):
301 raise NotImplementedError