blob: 4aa6b21512653d7885f779767d80bb79589cf79f [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2014-present Open Networking Foundation
Thomas Vachuska781d18b2014-10-27 10:31:25 -07003 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07004 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
Thomas Vachuska781d18b2014-10-27 10:31:25 -07007 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07008 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
Thomas Vachuska781d18b2014-10-27 10:31:25 -070015 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.provider.of.flow.impl;
alshabibeec3a062014-09-17 18:01:26 -070017
Madan Jampani84382b92016-06-22 08:26:49 -070018import static com.google.common.base.Preconditions.checkNotNull;
Jordan Haltermandf4b08a2019-03-05 18:44:03 -080019
20import org.onlab.util.SlidingWindowCounter;
Brian O'Connorabafb502014-12-02 22:26:20 -080021import org.onosproject.openflow.controller.OpenFlowSwitch;
22import org.onosproject.openflow.controller.RoleState;
alshabibeec3a062014-09-17 18:01:26 -070023import org.projectfloodlight.openflow.protocol.OFFlowStatsRequest;
24import org.projectfloodlight.openflow.types.OFPort;
25import org.projectfloodlight.openflow.types.TableId;
26import org.slf4j.Logger;
27
Thomas Vachuska75aaa672015-04-29 12:24:43 -070028import java.util.TimerTask;
pier14e93322020-01-09 13:10:04 +010029import java.util.concurrent.ScheduledExecutorService;
30import java.util.concurrent.ScheduledFuture;
31import java.util.concurrent.TimeUnit;
Jordan Haltermandf4b08a2019-03-05 18:44:03 -080032import java.util.concurrent.atomic.AtomicBoolean;
pier14e93322020-01-09 13:10:04 +010033import java.util.concurrent.atomic.AtomicInteger;
Thomas Vachuska75aaa672015-04-29 12:24:43 -070034
Jordan Haltermandf4b08a2019-03-05 18:44:03 -080035import static java.lang.Math.max;
36import static java.lang.Math.min;
Thomas Vachuska75aaa672015-04-29 12:24:43 -070037import static org.slf4j.LoggerFactory.getLogger;
38
39/**
40 * Collects flow statistics for the specified switch.
41 */
Thomas Vachuskaa394b952016-06-14 15:02:09 -070042class FlowStatsCollector implements SwitchDataCollector {
alshabibeec3a062014-09-17 18:01:26 -070043
44 private final Logger log = getLogger(getClass());
45
pier14e93322020-01-09 13:10:04 +010046 private static final int MS = 1000;
Jordan Haltermandf4b08a2019-03-05 18:44:03 -080047
48 // Number of ticks which defines the pause window.
49 private static final int PAUSE_WINDOW = 2;
50 // Number of ticks which defines the high load window
51 private static final int HIGH_WINDOW = 60;
52 // Number of ticks which defines the low load window
53 private static final int LOW_WINDOW = 15;
54 // Multiplier factor of the load
55 private static final int LOAD_FACTOR = 2;
56 // Event/s defining the min load rate
57 private static final int MIN_LOAD_RATE = 50;
58 // Event/s defining the max load rate
59 private static final int MAX_LOAD_RATE = 500;
Thomas Vachuska75aaa672015-04-29 12:24:43 -070060
alshabibeec3a062014-09-17 18:01:26 -070061 private final OpenFlowSwitch sw;
pier14e93322020-01-09 13:10:04 +010062 private ScheduledExecutorService executorService;
Jordan Haltermandf4b08a2019-03-05 18:44:03 -080063 private TimerTask pauseTask;
pier14e93322020-01-09 13:10:04 +010064 private ScheduledFuture<?> scheduledPauseTask;
Jordan Haltermandf4b08a2019-03-05 18:44:03 -080065 private TimerTask pollTask;
pier14e93322020-01-09 13:10:04 +010066 private ScheduledFuture<?> scheduledPollTask;
Jordan Haltermandf4b08a2019-03-05 18:44:03 -080067
68 private SlidingWindowCounter loadCounter;
69 // Defines whether the collector is in pause or not for high load
70 private final AtomicBoolean paused = new AtomicBoolean();
71 // Defines whether the collector is in waiting or not for a previous stats reply
pier14e93322020-01-09 13:10:04 +010072 private static final int WAITING_ATTEMPTS = 5;
73 private final AtomicInteger waiting = new AtomicInteger(0);
alshabibeec3a062014-09-17 18:01:26 -070074
Thomas Vachuska75aaa672015-04-29 12:24:43 -070075 private int pollInterval;
alshabibeec3a062014-09-17 18:01:26 -070076
Thomas Vachuska75aaa672015-04-29 12:24:43 -070077 /**
78 * Creates a new collector for the given switch and poll frequency.
79 *
pier14e93322020-01-09 13:10:04 +010080 * @param executorService executor used for scheduling
81 * @param sw switch to pull
Thomas Vachuska75aaa672015-04-29 12:24:43 -070082 * @param pollInterval poll frequency in seconds
83 */
pier14e93322020-01-09 13:10:04 +010084 FlowStatsCollector(ScheduledExecutorService executorService, OpenFlowSwitch sw, int pollInterval) {
85 this.executorService = executorService;
Madan Jampani84382b92016-06-22 08:26:49 -070086 this.sw = checkNotNull(sw, "Null switch");
Thomas Vachuska75aaa672015-04-29 12:24:43 -070087 this.pollInterval = pollInterval;
alshabibeec3a062014-09-17 18:01:26 -070088 }
89
Thomas Vachuska75aaa672015-04-29 12:24:43 -070090 /**
91 * Adjusts poll frequency.
92 *
93 * @param pollInterval poll frequency in seconds
94 */
95 synchronized void adjustPollInterval(int pollInterval) {
96 this.pollInterval = pollInterval;
Jordan Haltermandf4b08a2019-03-05 18:44:03 -080097 if (pollTask != null) {
98 pollTask.cancel();
99 }
pier14e93322020-01-09 13:10:04 +0100100 if (scheduledPollTask != null) {
101 scheduledPollTask.cancel(false);
102 }
Jordan Haltermandf4b08a2019-03-05 18:44:03 -0800103 // If we went through start - let's schedule it
104 if (loadCounter != null) {
105 pollTask = new PollTimerTask();
pier14e93322020-01-09 13:10:04 +0100106 scheduledPollTask = executorService.scheduleAtFixedRate(pollTask, pollInterval * MS,
107 pollInterval * MS, TimeUnit.MILLISECONDS);
Jordan Haltermandf4b08a2019-03-05 18:44:03 -0800108 }
pier14e93322020-01-09 13:10:04 +0100109 waiting.set(0);
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700110 }
alshabibeec3a062014-09-17 18:01:26 -0700111
Jordan Haltermandf4b08a2019-03-05 18:44:03 -0800112 /**
113 * Resets the collector's event count.
114 */
115 @Override
116 public synchronized void resetEvents() {
117 loadCounter.clear();
118 if (paused.compareAndSet(true, false)) {
119 resume();
120 }
121 // Let's reset also waiting, the reply can be discarded/lost
122 // during a change of mastership
pier14e93322020-01-09 13:10:04 +0100123 waiting.set(0);
Jordan Haltermandf4b08a2019-03-05 18:44:03 -0800124 }
125
126 /**
127 * Records a number of flow events that have occurred.
128 *
129 * @param events the number of events that occurred
130 */
131 @Override
132 public void recordEvents(int events) {
133 SlidingWindowCounter loadCounter = this.loadCounter;
134 if (loadCounter != null) {
135 loadCounter.incrementCount(events);
136 }
137 }
138
139 /**
140 * Returns a boolean indicating whether the switch is under high load.
141 * <p>
142 * The switch is considered under high load if the average rate over the last two seconds is
143 * greater than twice the overall rate or 50 flows/sec.
144 *
145 * @return indicates whether the switch is under high load
146 */
147 private boolean isHighLoad() {
148 return loadCounter.getWindowRate(PAUSE_WINDOW)
149 > max(min(loadCounter.getWindowRate(HIGH_WINDOW) * LOAD_FACTOR, MAX_LOAD_RATE), MIN_LOAD_RATE);
150 }
151
152 /**
153 * Returns a boolean indicating whether the switch is under low load.
154 * <p>
155 * The switch is considered under low load if the average rate over the last 15 seconds is
156 * less than the overall rate.
157 *
158 * @return indicates whether the switch is under low load
159 */
160 private boolean isLowLoad() {
161 return loadCounter.getWindowRate(LOW_WINDOW) < loadCounter.getWindowRate(HIGH_WINDOW);
162 }
163
164 private class PauseTimerTask extends TimerTask {
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700165 @Override
166 public void run() {
Jordan Haltermandf4b08a2019-03-05 18:44:03 -0800167 if (isHighLoad()) {
168 if (paused.compareAndSet(false, true)) {
169 pause();
170 }
171 } else if (isLowLoad()) {
172 if (paused.compareAndSet(true, false)) {
173 resume();
174 }
175 }
176 }
177 }
178
179 private class PollTimerTask extends TimerTask {
180 @Override
181 public void run() {
182 // Check whether we are still waiting a previous reply
pier14e93322020-01-09 13:10:04 +0100183 if (waiting.getAndDecrement() > 0) {
184 log.debug("Skipping stats collection for {} waiting for previous reply", sw.getStringId());
185 return;
186 }
187 // Check whether we are the master of the switch
188 if (sw.getRole() == RoleState.MASTER) {
Jordan Haltermandf4b08a2019-03-05 18:44:03 -0800189 // Check whether the switch is under high load from this master. This is done here in case a large
190 // batch was pushed immediately prior to this task running.
191 if (isHighLoad()) {
192 log.debug("Skipping stats collection for {} due to high load; rate: {}; overall: {}",
193 sw.getStringId(),
194 loadCounter.getWindowRate(PAUSE_WINDOW),
195 loadCounter.getWindowRate(HIGH_WINDOW));
196 return;
197 } else {
198 log.debug(
199 "Permitting stats collection for {}; rate: {}; overall: {}",
200 sw.getStringId(),
201 loadCounter.getWindowRate(PAUSE_WINDOW),
202 loadCounter.getWindowRate(HIGH_WINDOW));
203 }
204
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700205 log.trace("Collecting stats for {}", sw.getStringId());
206 OFFlowStatsRequest request = sw.factory().buildFlowStatsRequest()
207 .setMatch(sw.factory().matchWildcardAll())
208 .setTableId(TableId.ALL)
209 .setOutPort(OFPort.NO_MASK)
210 .build();
211 sw.sendMsg(request);
Jordan Haltermandf4b08a2019-03-05 18:44:03 -0800212 // Other flow stats will not be asked
213 // if we don't see first the reply of this request
pier14e93322020-01-09 13:10:04 +0100214 waiting.set(WAITING_ATTEMPTS);
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700215 }
alshabibeec3a062014-09-17 18:01:26 -0700216 }
alshabibeec3a062014-09-17 18:01:26 -0700217 }
218
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700219 public synchronized void start() {
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700220 log.debug("Starting Stats collection thread for {}", sw.getStringId());
Jordan Haltermandf4b08a2019-03-05 18:44:03 -0800221 loadCounter = new SlidingWindowCounter(HIGH_WINDOW);
Shibu Vijayakumar7f73c722019-07-04 07:13:37 -0400222 if (pollInterval > 0) {
223 pauseTask = new PauseTimerTask();
224 scheduledPauseTask = executorService.scheduleAtFixedRate(pauseTask, 1 * MS,
225 1 * MS, TimeUnit.MILLISECONDS);
226 pollTask = new PollTimerTask();
227 // Initially start polling quickly. Then drop down to configured value
228 scheduledPollTask = executorService.scheduleAtFixedRate(pollTask, 1 * MS,
229 pollInterval * MS, TimeUnit.MILLISECONDS);
230 } else {
231 // Trigger the poll only once
232 pollTask = new PollTimerTask();
233 executorService.schedule(pollTask, 0, TimeUnit.MILLISECONDS);
234 }
Jordan Haltermandf4b08a2019-03-05 18:44:03 -0800235 }
236
237 private synchronized void pause() {
238 if (pollTask != null) {
239 log.debug("Pausing stats collection for {}; rate: {}; overall: {}",
240 sw.getStringId(),
241 loadCounter.getWindowRate(PAUSE_WINDOW),
242 loadCounter.getWindowRate(HIGH_WINDOW));
243 pollTask.cancel();
244 pollTask = null;
245 }
pier14e93322020-01-09 13:10:04 +0100246 if (scheduledPollTask != null) {
247 scheduledPollTask.cancel(false);
248 scheduledPollTask = null;
249 }
Jordan Haltermandf4b08a2019-03-05 18:44:03 -0800250 }
251
252 private synchronized void resume() {
253 log.debug("Resuming stats collection for {}; rate: {}; overall: {}",
254 sw.getStringId(),
255 loadCounter.getWindowRate(PAUSE_WINDOW),
256 loadCounter.getWindowRate(HIGH_WINDOW));
257 pollTask = new PollTimerTask();
pier14e93322020-01-09 13:10:04 +0100258 scheduledPollTask = executorService.scheduleAtFixedRate(pollTask, pollInterval * MS,
259 pollInterval * MS, TimeUnit.MILLISECONDS);
alshabibeec3a062014-09-17 18:01:26 -0700260 }
261
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700262 public synchronized void stop() {
Jordan Haltermandf4b08a2019-03-05 18:44:03 -0800263 if (pauseTask != null) {
264 pauseTask.cancel();
265 pauseTask = null;
Pier Luigi15e281d2018-03-04 14:30:43 +0100266 }
pier14e93322020-01-09 13:10:04 +0100267 if (scheduledPauseTask != null) {
268 scheduledPauseTask.cancel(false);
269 scheduledPauseTask = null;
270 }
Jordan Haltermandf4b08a2019-03-05 18:44:03 -0800271 if (pollTask != null) {
272 log.debug("Stopping Stats collection thread for {}", sw.getStringId());
273 pollTask.cancel();
274 pollTask = null;
275 }
pier14e93322020-01-09 13:10:04 +0100276 if (scheduledPollTask != null) {
277 scheduledPollTask.cancel(false);
278 scheduledPollTask = null;
279 }
Jordan Haltermandf4b08a2019-03-05 18:44:03 -0800280 if (loadCounter != null) {
281 loadCounter.destroy();
282 loadCounter = null;
283 }
284 }
285
286 public void received() {
pier14e93322020-01-09 13:10:04 +0100287 waiting.set(0);
alshabibeec3a062014-09-17 18:01:26 -0700288 }
289
290}