blob: c8bf707bd14247730c04d8e3646d909bda10f6dc [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.Timer;
29import java.util.TimerTask;
Jordan Haltermandf4b08a2019-03-05 18:44:03 -080030import java.util.concurrent.atomic.AtomicBoolean;
Thomas Vachuska75aaa672015-04-29 12:24:43 -070031
Jordan Haltermandf4b08a2019-03-05 18:44:03 -080032import static java.lang.Math.max;
33import static java.lang.Math.min;
Thomas Vachuska75aaa672015-04-29 12:24:43 -070034import static org.slf4j.LoggerFactory.getLogger;
35
36/**
37 * Collects flow statistics for the specified switch.
38 */
Thomas Vachuskaa394b952016-06-14 15:02:09 -070039class FlowStatsCollector implements SwitchDataCollector {
alshabibeec3a062014-09-17 18:01:26 -070040
41 private final Logger log = getLogger(getClass());
42
Jordan Haltermandf4b08a2019-03-05 18:44:03 -080043 private static final int SECONDS = 1000;
44
45 // Number of ticks which defines the pause window.
46 private static final int PAUSE_WINDOW = 2;
47 // Number of ticks which defines the high load window
48 private static final int HIGH_WINDOW = 60;
49 // Number of ticks which defines the low load window
50 private static final int LOW_WINDOW = 15;
51 // Multiplier factor of the load
52 private static final int LOAD_FACTOR = 2;
53 // Event/s defining the min load rate
54 private static final int MIN_LOAD_RATE = 50;
55 // Event/s defining the max load rate
56 private static final int MAX_LOAD_RATE = 500;
Thomas Vachuska75aaa672015-04-29 12:24:43 -070057
alshabibeec3a062014-09-17 18:01:26 -070058 private final OpenFlowSwitch sw;
Thomas Vachuska75aaa672015-04-29 12:24:43 -070059 private Timer timer;
Jordan Haltermandf4b08a2019-03-05 18:44:03 -080060 private TimerTask pauseTask;
61 private TimerTask pollTask;
62
63 private SlidingWindowCounter loadCounter;
64 // Defines whether the collector is in pause or not for high load
65 private final AtomicBoolean paused = new AtomicBoolean();
66 // Defines whether the collector is in waiting or not for a previous stats reply
67 private final AtomicBoolean waiting = new AtomicBoolean();
alshabibeec3a062014-09-17 18:01:26 -070068
Thomas Vachuska75aaa672015-04-29 12:24:43 -070069 private int pollInterval;
alshabibeec3a062014-09-17 18:01:26 -070070
Thomas Vachuska75aaa672015-04-29 12:24:43 -070071 /**
72 * Creates a new collector for the given switch and poll frequency.
73 *
74 * @param timer timer to use for scheduling
75 * @param sw switch to pull
76 * @param pollInterval poll frequency in seconds
77 */
78 FlowStatsCollector(Timer timer, OpenFlowSwitch sw, int pollInterval) {
79 this.timer = timer;
Madan Jampani84382b92016-06-22 08:26:49 -070080 this.sw = checkNotNull(sw, "Null switch");
Thomas Vachuska75aaa672015-04-29 12:24:43 -070081 this.pollInterval = pollInterval;
alshabibeec3a062014-09-17 18:01:26 -070082 }
83
Thomas Vachuska75aaa672015-04-29 12:24:43 -070084 /**
85 * Adjusts poll frequency.
86 *
87 * @param pollInterval poll frequency in seconds
88 */
89 synchronized void adjustPollInterval(int pollInterval) {
90 this.pollInterval = pollInterval;
Jordan Haltermandf4b08a2019-03-05 18:44:03 -080091 if (pollTask != null) {
92 pollTask.cancel();
93 }
94 // If we went through start - let's schedule it
95 if (loadCounter != null) {
96 pollTask = new PollTimerTask();
97 timer.scheduleAtFixedRate(pollTask, pollInterval * SECONDS, pollInterval * SECONDS);
98 }
Thomas Vachuska75aaa672015-04-29 12:24:43 -070099 }
alshabibeec3a062014-09-17 18:01:26 -0700100
Jordan Haltermandf4b08a2019-03-05 18:44:03 -0800101 /**
102 * Resets the collector's event count.
103 */
104 @Override
105 public synchronized void resetEvents() {
106 loadCounter.clear();
107 if (paused.compareAndSet(true, false)) {
108 resume();
109 }
110 // Let's reset also waiting, the reply can be discarded/lost
111 // during a change of mastership
112 waiting.set(false);
113 }
114
115 /**
116 * Records a number of flow events that have occurred.
117 *
118 * @param events the number of events that occurred
119 */
120 @Override
121 public void recordEvents(int events) {
122 SlidingWindowCounter loadCounter = this.loadCounter;
123 if (loadCounter != null) {
124 loadCounter.incrementCount(events);
125 }
126 }
127
128 /**
129 * Returns a boolean indicating whether the switch is under high load.
130 * <p>
131 * The switch is considered under high load if the average rate over the last two seconds is
132 * greater than twice the overall rate or 50 flows/sec.
133 *
134 * @return indicates whether the switch is under high load
135 */
136 private boolean isHighLoad() {
137 return loadCounter.getWindowRate(PAUSE_WINDOW)
138 > max(min(loadCounter.getWindowRate(HIGH_WINDOW) * LOAD_FACTOR, MAX_LOAD_RATE), MIN_LOAD_RATE);
139 }
140
141 /**
142 * Returns a boolean indicating whether the switch is under low load.
143 * <p>
144 * The switch is considered under low load if the average rate over the last 15 seconds is
145 * less than the overall rate.
146 *
147 * @return indicates whether the switch is under low load
148 */
149 private boolean isLowLoad() {
150 return loadCounter.getWindowRate(LOW_WINDOW) < loadCounter.getWindowRate(HIGH_WINDOW);
151 }
152
153 private class PauseTimerTask extends TimerTask {
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700154 @Override
155 public void run() {
Jordan Haltermandf4b08a2019-03-05 18:44:03 -0800156 if (isHighLoad()) {
157 if (paused.compareAndSet(false, true)) {
158 pause();
159 }
160 } else if (isLowLoad()) {
161 if (paused.compareAndSet(true, false)) {
162 resume();
163 }
164 }
165 }
166 }
167
168 private class PollTimerTask extends TimerTask {
169 @Override
170 public void run() {
171 // Check whether we are still waiting a previous reply
172 if (sw.getRole() == RoleState.MASTER && !waiting.get()) {
173 // Check whether the switch is under high load from this master. This is done here in case a large
174 // batch was pushed immediately prior to this task running.
175 if (isHighLoad()) {
176 log.debug("Skipping stats collection for {} due to high load; rate: {}; overall: {}",
177 sw.getStringId(),
178 loadCounter.getWindowRate(PAUSE_WINDOW),
179 loadCounter.getWindowRate(HIGH_WINDOW));
180 return;
181 } else {
182 log.debug(
183 "Permitting stats collection for {}; rate: {}; overall: {}",
184 sw.getStringId(),
185 loadCounter.getWindowRate(PAUSE_WINDOW),
186 loadCounter.getWindowRate(HIGH_WINDOW));
187 }
188
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700189 log.trace("Collecting stats for {}", sw.getStringId());
190 OFFlowStatsRequest request = sw.factory().buildFlowStatsRequest()
191 .setMatch(sw.factory().matchWildcardAll())
192 .setTableId(TableId.ALL)
193 .setOutPort(OFPort.NO_MASK)
194 .build();
195 sw.sendMsg(request);
Jordan Haltermandf4b08a2019-03-05 18:44:03 -0800196 // Other flow stats will not be asked
197 // if we don't see first the reply of this request
198 waiting.set(true);
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700199 }
alshabibeec3a062014-09-17 18:01:26 -0700200 }
alshabibeec3a062014-09-17 18:01:26 -0700201 }
202
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700203 public synchronized void start() {
204 // Initially start polling quickly. Then drop down to configured value
205 log.debug("Starting Stats collection thread for {}", sw.getStringId());
Jordan Haltermandf4b08a2019-03-05 18:44:03 -0800206 loadCounter = new SlidingWindowCounter(HIGH_WINDOW);
207 pauseTask = new PauseTimerTask();
208 timer.scheduleAtFixedRate(pauseTask, 1 * SECONDS, 1 * SECONDS);
209 pollTask = new PollTimerTask();
210 timer.scheduleAtFixedRate(pollTask, 1 * SECONDS, pollInterval * SECONDS);
211 }
212
213 private synchronized void pause() {
214 if (pollTask != null) {
215 log.debug("Pausing stats collection for {}; rate: {}; overall: {}",
216 sw.getStringId(),
217 loadCounter.getWindowRate(PAUSE_WINDOW),
218 loadCounter.getWindowRate(HIGH_WINDOW));
219 pollTask.cancel();
220 pollTask = null;
221 }
222 }
223
224 private synchronized void resume() {
225 log.debug("Resuming stats collection for {}; rate: {}; overall: {}",
226 sw.getStringId(),
227 loadCounter.getWindowRate(PAUSE_WINDOW),
228 loadCounter.getWindowRate(HIGH_WINDOW));
229 pollTask = new PollTimerTask();
230 timer.scheduleAtFixedRate(pollTask, pollInterval * SECONDS, pollInterval * SECONDS);
alshabibeec3a062014-09-17 18:01:26 -0700231 }
232
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700233 public synchronized void stop() {
Jordan Haltermandf4b08a2019-03-05 18:44:03 -0800234 if (pauseTask != null) {
235 pauseTask.cancel();
236 pauseTask = null;
Pier Luigi15e281d2018-03-04 14:30:43 +0100237 }
Jordan Haltermandf4b08a2019-03-05 18:44:03 -0800238 if (pollTask != null) {
239 log.debug("Stopping Stats collection thread for {}", sw.getStringId());
240 pollTask.cancel();
241 pollTask = null;
242 }
243 if (loadCounter != null) {
244 loadCounter.destroy();
245 loadCounter = null;
246 }
247 }
248
249 public void received() {
250 waiting.set(false);
alshabibeec3a062014-09-17 18:01:26 -0700251 }
252
253}