blob: 8d885a13582b44520cb538323979896e36958ec2 [file] [log] [blame]
Brian O'Connor8c685362015-12-05 15:27:27 -08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
Brian O'Connor8c685362015-12-05 15:27:27 -08003 *
4 * 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
7 *
8 * 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.
15 */
16
17package org.onosproject.provider.of.flow.impl;
18
Sangsik Yoonb1b823f2016-05-16 18:55:39 +090019import com.google.common.collect.Iterables;
20import org.onlab.osgi.DefaultServiceDirectory;
21import org.onosproject.net.DeviceId;
Charles Chan14967c22015-12-07 11:11:50 -080022import org.onosproject.net.driver.DriverService;
Brian O'Connor8c685362015-12-05 15:27:27 -080023import org.onosproject.net.flow.FlowEntry;
Sangsik Yoonb1b823f2016-05-16 18:55:39 +090024import org.onosproject.net.flow.FlowRuleService;
Brian O'Connor8c685362015-12-05 15:27:27 -080025import org.onosproject.net.flow.StoredFlowEntry;
Brian O'Connor8c685362015-12-05 15:27:27 -080026import org.onosproject.net.flow.instructions.Instruction;
27import org.onosproject.net.flow.instructions.Instructions;
Sangsik Yoonb1b823f2016-05-16 18:55:39 +090028import org.onosproject.net.statistic.DefaultLoad;
29import org.onosproject.net.statistic.PollInterval;
30import org.onosproject.openflow.controller.Dpid;
Brian O'Connor8c685362015-12-05 15:27:27 -080031import org.onosproject.openflow.controller.OpenFlowSwitch;
32import org.onosproject.openflow.controller.RoleState;
33import org.projectfloodlight.openflow.protocol.OFFlowStatsRequest;
34import org.projectfloodlight.openflow.protocol.match.Match;
35import org.projectfloodlight.openflow.types.OFPort;
36import org.projectfloodlight.openflow.types.TableId;
37import org.slf4j.Logger;
38
Brian O'Connor8c685362015-12-05 15:27:27 -080039import java.util.Optional;
Brian O'Connor8c685362015-12-05 15:27:27 -080040import java.util.concurrent.Executors;
41import java.util.concurrent.ScheduledExecutorService;
42import java.util.concurrent.ScheduledFuture;
43import java.util.concurrent.TimeUnit;
44
45import static com.google.common.base.Preconditions.checkNotNull;
Sangsik Yoonb1b823f2016-05-16 18:55:39 +090046import static java.lang.Thread.sleep;
Brian O'Connor8c685362015-12-05 15:27:27 -080047import static org.onlab.util.Tools.groupedThreads;
Brian O'Connor8c685362015-12-05 15:27:27 -080048import static org.slf4j.LoggerFactory.getLogger;
49
50/**
51 * Efficiently and adaptively collects flow statistics for the specified switch.
52 */
Thomas Vachuskaa394b952016-06-14 15:02:09 -070053public class NewAdaptiveFlowStatsCollector implements SwitchDataCollector {
Brian O'Connor8c685362015-12-05 15:27:27 -080054 private final Logger log = getLogger(getClass());
55
Jonathan Hart84f4f312016-03-03 08:17:11 -080056 private static final String CHECK_AND_MOVE_LOG =
57 "checkAndMoveLiveFlowInternal: flowId={}, state={}, afterLiveType={}"
58 + ", liveTime={}, life={}, bytes={}, packets={}, fromLastSeen={}"
59 + ", priority={}, selector={}, treatment={} dpid={}";
60
61 private static final String CHECK_AND_MOVE_COUNT_LOG =
Sangsik Yoonb1b823f2016-05-16 18:55:39 +090062 "checkAndMoveLiveFlowAll: Total Flow_Count={}, "
Jonathan Hart84f4f312016-03-03 08:17:11 -080063 + ", IMMEDIATE_FLOW_Count={}, SHORT_FLOW_Count={}"
Sangsik Yoonb1b823f2016-05-16 18:55:39 +090064 + ", MID_FLOW_Count={}, LONG_FLOW_Count={}, UNKNOWN_FLOW_Count={}";
Jonathan Hart84f4f312016-03-03 08:17:11 -080065
Sangsik Yoonb1b823f2016-05-16 18:55:39 +090066 private static final int SLEEP_LOOP_COUNT = 10;
67 private static final int SLEEP_MS = 100;
Jonathan Hart84f4f312016-03-03 08:17:11 -080068
Charles Chan14967c22015-12-07 11:11:50 -080069 private final DriverService driverService;
Brian O'Connor8c685362015-12-05 15:27:27 -080070 private final OpenFlowSwitch sw;
Sangsik Yoonb1b823f2016-05-16 18:55:39 +090071 private final DeviceId did;
Brian O'Connor8c685362015-12-05 15:27:27 -080072
73 private ScheduledExecutorService adaptiveFlowStatsScheduler =
HIGUCHI Yutad9e01052016-04-14 09:31:42 -070074 Executors.newScheduledThreadPool(4, groupedThreads("onos/flow", "device-stats-collector-%d", log));
Brian O'Connor8c685362015-12-05 15:27:27 -080075 private ScheduledFuture<?> calAndShortFlowsThread;
76 private ScheduledFuture<?> midFlowsThread;
77 private ScheduledFuture<?> longFlowsThread;
78
79 // Task that calculates all flowEntries' FlowLiveType and collects stats IMMEDIATE flows every calAndPollInterval
80 private CalAndShortFlowsTask calAndShortFlowsTask;
81 // Task that collects stats MID flows every 2*calAndPollInterval
82 private MidFlowsTask midFlowsTask;
83 // Task that collects stats LONG flows every 3*calAndPollInterval
84 private LongFlowsTask longFlowsTask;
85
86 private static final int CAL_AND_POLL_TIMES = 1; // must be always 0
87 private static final int MID_POLL_TIMES = 2; // variable greater or equal than 1
88 private static final int LONG_POLL_TIMES = 3; // variable greater or equal than MID_POLL_TIMES
89 //TODO: make ENTIRE_POLL_TIMES configurable with enable or disable
90 // must be variable greater or equal than common multiple of MID_POLL_TIMES and LONG_POLL_TIMES
91 private static final int ENTIRE_POLL_TIMES = 6;
92
93 private static final int DEFAULT_CAL_AND_POLL_FREQUENCY = 5;
94 private static final int MIN_CAL_AND_POLL_FREQUENCY = 2;
95 private static final int MAX_CAL_AND_POLL_FREQUENCY = 60;
96
97 private int calAndPollInterval; // CAL_AND_POLL_TIMES * DEFAULT_CAL_AND_POLL_FREQUENCY;
98 private int midPollInterval; // MID_POLL_TIMES * DEFAULT_CAL_AND_POLL_FREQUENCY;
99 private int longPollInterval; // LONG_POLL_TIMES * DEFAULT_CAL_AND_POLL_FREQUENCY;
100 // only used for checking condition at each task if it collects entire flows from a given switch or not
101 private int entirePollInterval; // ENTIRE_POLL_TIMES * DEFAULT_CAL_AND_POLL_FREQUENCY;
102
103 // Number of call count of each Task,
104 // for undoing collection except only entire flows collecting task in CalAndShortFlowsTask
105 private int callCountCalAndShortFlowsTask = 0; // increased CAL_AND_POLL_TIMES whenever Task is called
106 private int callCountMidFlowsTask = 0; // increased MID_POLL_TIMES whenever Task is called
107 private int callCountLongFlowsTask = 0; // increased LONG_POLL_TIMES whenever Task is called
108
Brian O'Connor8c685362015-12-05 15:27:27 -0800109 private boolean isFirstTimeStart = true;
110
111 public static final long NO_FLOW_MISSING_XID = (-1);
112 private long flowMissingXid = NO_FLOW_MISSING_XID;
113
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900114 private FlowRuleService flowRuleService;
115
Brian O'Connor8c685362015-12-05 15:27:27 -0800116 /**
117 * Creates a new adaptive collector for the given switch and default cal_and_poll frequency.
118 *
Thomas Vachuska708d3032016-02-18 11:11:46 -0800119 * @param driverService driver service reference
120 * @param sw switch to pull
121 * @param pollInterval cal and immediate poll frequency in seconds
Brian O'Connor8c685362015-12-05 15:27:27 -0800122 */
Thomas Vachuska708d3032016-02-18 11:11:46 -0800123 NewAdaptiveFlowStatsCollector(DriverService driverService, OpenFlowSwitch sw, int pollInterval) {
Charles Chan14967c22015-12-07 11:11:50 -0800124 this.driverService = driverService;
Brian O'Connor8c685362015-12-05 15:27:27 -0800125 this.sw = sw;
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900126 this.did = DeviceId.deviceId(Dpid.uri(sw.getId()));
127
128 flowRuleService = get(FlowRuleService.class);
129
Brian O'Connor8c685362015-12-05 15:27:27 -0800130 initMemberVars(pollInterval);
131 }
132
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900133 /**
134 * Returns the reference to the implementation of the specified service.
135 *
136 * @param serviceClass service class
137 * @param <T> type of service
138 * @return service implementation
139 * @throws org.onlab.osgi.ServiceNotFoundException if service is unavailable
140 */
141 private static <T> T get(Class<T> serviceClass) {
142 return DefaultServiceDirectory.getService(serviceClass);
143 }
144
Brian O'Connor8c685362015-12-05 15:27:27 -0800145 // check calAndPollInterval validity and set all pollInterval values and finally initialize each task call count
146 private void initMemberVars(int pollInterval) {
147 if (pollInterval < MIN_CAL_AND_POLL_FREQUENCY) {
148 this.calAndPollInterval = MIN_CAL_AND_POLL_FREQUENCY;
149 } else if (pollInterval >= MAX_CAL_AND_POLL_FREQUENCY) {
150 this.calAndPollInterval = MAX_CAL_AND_POLL_FREQUENCY;
151 } else {
152 this.calAndPollInterval = pollInterval;
153 }
154
155 calAndPollInterval = CAL_AND_POLL_TIMES * calAndPollInterval;
156 midPollInterval = MID_POLL_TIMES * calAndPollInterval;
157 longPollInterval = LONG_POLL_TIMES * calAndPollInterval;
158 entirePollInterval = ENTIRE_POLL_TIMES * calAndPollInterval;
159
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900160 // Set the PollInterval values for statistic manager and others usage
161 DefaultLoad.setPollInterval(calAndPollInterval);
162
163 PollInterval pollInterval1Instance = PollInterval.getInstance();
164
165 pollInterval1Instance.setPollInterval(calAndPollInterval);
166 pollInterval1Instance.setMidPollInterval(midPollInterval);
167 pollInterval1Instance.setLongPollInterval(longPollInterval);
168 pollInterval1Instance.setEntirePollInterval(entirePollInterval);
169
Brian O'Connor8c685362015-12-05 15:27:27 -0800170 callCountCalAndShortFlowsTask = 0;
171 callCountMidFlowsTask = 0;
172 callCountLongFlowsTask = 0;
173
174 flowMissingXid = NO_FLOW_MISSING_XID;
175 }
176
177 /**
178 * Adjusts adaptive poll frequency.
179 *
180 * @param pollInterval poll frequency in seconds
181 */
182 synchronized void adjustCalAndPollInterval(int pollInterval) {
183 initMemberVars(pollInterval);
184
185 if (calAndShortFlowsThread != null) {
186 calAndShortFlowsThread.cancel(false);
187 }
188 if (midFlowsThread != null) {
189 midFlowsThread.cancel(false);
190 }
191 if (longFlowsThread != null) {
192 longFlowsThread.cancel(false);
193 }
194
195 calAndShortFlowsTask = new CalAndShortFlowsTask();
196 calAndShortFlowsThread = adaptiveFlowStatsScheduler.scheduleWithFixedDelay(
197 calAndShortFlowsTask,
198 0,
199 calAndPollInterval,
200 TimeUnit.SECONDS);
201
202 midFlowsTask = new MidFlowsTask();
203 midFlowsThread = adaptiveFlowStatsScheduler.scheduleWithFixedDelay(
204 midFlowsTask,
205 0,
206 midPollInterval,
207 TimeUnit.SECONDS);
208
209 longFlowsTask = new LongFlowsTask();
210 longFlowsThread = adaptiveFlowStatsScheduler.scheduleWithFixedDelay(
211 longFlowsTask,
212 0,
213 longPollInterval,
214 TimeUnit.SECONDS);
215
Jonathan Hart84f4f312016-03-03 08:17:11 -0800216 log.debug("calAndPollInterval={} is adjusted", calAndPollInterval);
Brian O'Connor8c685362015-12-05 15:27:27 -0800217 }
218
219 private class CalAndShortFlowsTask implements Runnable {
220 @Override
221 public void run() {
222 if (sw.getRole() == RoleState.MASTER) {
223 log.trace("CalAndShortFlowsTask Collecting AdaptiveStats for {}", sw.getStringId());
224
225 if (isFirstTimeStart) {
226 // isFirstTimeStart, get entire flow stats from a given switch sw
227 log.trace("CalAndShortFlowsTask Collecting Entire AdaptiveStats at first time start for {}",
228 sw.getStringId());
229 ofFlowStatsRequestAllSend();
230
231 callCountCalAndShortFlowsTask += CAL_AND_POLL_TIMES;
232 isFirstTimeStart = false;
233 } else if (callCountCalAndShortFlowsTask == ENTIRE_POLL_TIMES) {
234 // entire_poll_times, get entire flow stats from a given switch sw
235 log.trace("CalAndShortFlowsTask Collecting Entire AdaptiveStats for {}", sw.getStringId());
236 ofFlowStatsRequestAllSend();
237
238 callCountCalAndShortFlowsTask = CAL_AND_POLL_TIMES;
239 //TODO: check flows deleted in switch, but exist in controller flow table, then remove them
240 //
241 } else {
242 calAndShortFlowsTaskInternal();
243 callCountCalAndShortFlowsTask += CAL_AND_POLL_TIMES;
244 }
245 }
246 }
247 }
248
249 // send openflow flow stats request message with getting all flow entries to a given switch sw
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900250 private synchronized void ofFlowStatsRequestAllSend() {
Brian O'Connor8c685362015-12-05 15:27:27 -0800251 OFFlowStatsRequest request = sw.factory().buildFlowStatsRequest()
252 .setMatch(sw.factory().matchWildcardAll())
253 .setTableId(TableId.ALL)
254 .setOutPort(OFPort.NO_MASK)
255 .build();
256
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900257 // set the request xid to check the reply in OpenFlowRuleProvider
258 // After processing the reply of this request message,
259 // this must be set to NO_FLOW_MISSING_XID(-1) by provider
260 setFlowMissingXid(request.getXid());
261 log.debug("ofFlowStatsRequestAllSend: request={}, dpid={}",
Jonathan Hart84f4f312016-03-03 08:17:11 -0800262 request.toString(), sw.getStringId());
Brian O'Connor8c685362015-12-05 15:27:27 -0800263
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900264 sw.sendMsg(request);
Brian O'Connor8c685362015-12-05 15:27:27 -0800265 }
266
267 // send openflow flow stats request message with getting the specific flow entry(fe) to a given switch sw
268 private void ofFlowStatsRequestFlowSend(FlowEntry fe) {
269 // set find match
270 Match match = FlowModBuilder.builder(fe, sw.factory(), Optional.empty(),
Charles Chan14967c22015-12-07 11:11:50 -0800271 Optional.of(driverService)).buildMatch();
Brian O'Connor8c685362015-12-05 15:27:27 -0800272 // set find tableId
273 TableId tableId = TableId.of(fe.tableId());
274 // set output port
275 Instruction ins = fe.treatment().allInstructions().stream()
276 .filter(i -> (i.type() == Instruction.Type.OUTPUT))
277 .findFirst()
278 .orElse(null);
279 OFPort ofPort = OFPort.NO_MASK;
280 if (ins != null) {
281 Instructions.OutputInstruction out = (Instructions.OutputInstruction) ins;
282 ofPort = OFPort.of((int) ((out.port().toLong())));
283 }
284
285 OFFlowStatsRequest request = sw.factory().buildFlowStatsRequest()
286 .setMatch(match)
287 .setTableId(tableId)
288 .setOutPort(ofPort)
289 .build();
290
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900291 // Wait for 1 second until the FlowRuleProvider finishes to process FlowStatReply message
292 int loop = 0;
293 boolean interrupted = false;
294 while (!interrupted && getFlowMissingXid() != NO_FLOW_MISSING_XID) {
295 if (loop++ < SLEEP_LOOP_COUNT) {
296 log.debug("ofFlowStatsRequestFlowSend: previous FlowStatsRequestAll (xid={})" +
297 " does not be processed yet, do sleep for {} ms, for {}",
298 getFlowMissingXid(),
299 SLEEP_MS,
300 sw.getStringId());
301 try {
302 sleep(SLEEP_MS);
303 } catch (InterruptedException ie) {
304 log.debug("ofFlowStatsRequestFlowSend: Interrupted Exception = {}, for {}",
305 ie.toString(),
306 sw.getStringId());
307 // for exiting while loop gracefully
308 interrupted = true;
309 }
310 } else {
311 log.debug("ofFlowStatsRequestFlowSend: previous FlowStatsRequestAll (xid={})" +
312 " does not be processed yet, for {} ms," +
313 " just set xid with NO_FLOW_MISSING_XID, for {}",
314 getFlowMissingXid(),
315 loop * SLEEP_MS,
316 sw.getStringId());
Brian O'Connor8c685362015-12-05 15:27:27 -0800317
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900318 setFlowMissingXid(NO_FLOW_MISSING_XID);
319 break;
320 }
Brian O'Connor8c685362015-12-05 15:27:27 -0800321 }
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900322
323 sw.sendMsg(request);
324
Brian O'Connor8c685362015-12-05 15:27:27 -0800325 }
326
327 private void calAndShortFlowsTaskInternal() {
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900328 checkAndMoveLiveFlowAll();
Brian O'Connor8c685362015-12-05 15:27:27 -0800329
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900330 ofFlowStatsRequestInternal(FlowEntry.FlowLiveType.SHORT);
331 }
332
333 private void ofFlowStatsRequestInternal(FlowEntry.FlowLiveType liveType) {
334
335 Iterable<FlowEntry> flowEntries =
336 flowRuleService.getFlowEntriesByLiveType(did, liveType);
337
338 flowEntries.forEach(fe -> {
Brian O'Connor8c685362015-12-05 15:27:27 -0800339 ofFlowStatsRequestFlowSend(fe);
340 });
341 }
342
343 private class MidFlowsTask implements Runnable {
344 @Override
345 public void run() {
346 if (sw.getRole() == RoleState.MASTER) {
347 log.trace("MidFlowsTask Collecting AdaptiveStats for {}", sw.getStringId());
348
349 // skip collecting because CalAndShortFlowsTask collects entire flow stats from a given switch sw
350 if (callCountMidFlowsTask == ENTIRE_POLL_TIMES) {
351 callCountMidFlowsTask = MID_POLL_TIMES;
352 } else {
353 midFlowsTaskInternal();
354 callCountMidFlowsTask += MID_POLL_TIMES;
355 }
356 }
357 }
358 }
359
360 private void midFlowsTaskInternal() {
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900361 ofFlowStatsRequestInternal(FlowEntry.FlowLiveType.MID);
Brian O'Connor8c685362015-12-05 15:27:27 -0800362 }
363
364 private class LongFlowsTask implements Runnable {
365 @Override
366 public void run() {
367 if (sw.getRole() == RoleState.MASTER) {
368 log.trace("LongFlowsTask Collecting AdaptiveStats for {}", sw.getStringId());
369
370 // skip collecting because CalAndShortFlowsTask collects entire flow stats from a given switch sw
371 if (callCountLongFlowsTask == ENTIRE_POLL_TIMES) {
372 callCountLongFlowsTask = LONG_POLL_TIMES;
373 } else {
374 longFlowsTaskInternal();
375 callCountLongFlowsTask += LONG_POLL_TIMES;
376 }
377 }
378 }
379 }
380
381 private void longFlowsTaskInternal() {
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900382 ofFlowStatsRequestInternal(FlowEntry.FlowLiveType.LONG);
Brian O'Connor8c685362015-12-05 15:27:27 -0800383 }
384
385 /**
Jonathan Hart84f4f312016-03-03 08:17:11 -0800386 * Starts adaptive flow statistic collection.
Brian O'Connor8c685362015-12-05 15:27:27 -0800387 */
388 public synchronized void start() {
389 log.debug("Starting AdaptiveStats collection thread for {}", sw.getStringId());
390 callCountCalAndShortFlowsTask = 0;
391 callCountMidFlowsTask = 0;
392 callCountLongFlowsTask = 0;
393
394 isFirstTimeStart = true;
395
396 // Initially start polling quickly. Then drop down to configured value
397 calAndShortFlowsTask = new CalAndShortFlowsTask();
398 calAndShortFlowsThread = adaptiveFlowStatsScheduler.scheduleWithFixedDelay(
399 calAndShortFlowsTask,
400 1,
401 calAndPollInterval,
402 TimeUnit.SECONDS);
403
404 midFlowsTask = new MidFlowsTask();
405 midFlowsThread = adaptiveFlowStatsScheduler.scheduleWithFixedDelay(
406 midFlowsTask,
407 1,
408 midPollInterval,
409 TimeUnit.SECONDS);
410
411 longFlowsTask = new LongFlowsTask();
412 longFlowsThread = adaptiveFlowStatsScheduler.scheduleWithFixedDelay(
413 longFlowsTask,
414 1,
415 longPollInterval,
416 TimeUnit.SECONDS);
417
418 log.info("Started");
419 }
420
421 /**
Jonathan Hart84f4f312016-03-03 08:17:11 -0800422 * Stops adaptive flow statistic collection.
Brian O'Connor8c685362015-12-05 15:27:27 -0800423 */
424 public synchronized void stop() {
425 log.debug("Stopping AdaptiveStats collection thread for {}", sw.getStringId());
426 if (calAndShortFlowsThread != null) {
427 calAndShortFlowsThread.cancel(true);
428 }
429 if (midFlowsThread != null) {
430 midFlowsThread.cancel(true);
431 }
432 if (longFlowsThread != null) {
433 longFlowsThread.cancel(true);
434 }
435
436 adaptiveFlowStatsScheduler.shutdownNow();
437
438 isFirstTimeStart = false;
439
440 log.info("Stopped");
441 }
442
443 /**
Jonathan Hart84f4f312016-03-03 08:17:11 -0800444 * Returns flowMissingXid that indicates the execution of flowMissing process or not(NO_FLOW_MISSING_XID(-1)).
Brian O'Connor8c685362015-12-05 15:27:27 -0800445 *
446 * @return xid of missing flow
447 */
448 public long getFlowMissingXid() {
449 return flowMissingXid;
450 }
451
452 /**
Jonathan Hart84f4f312016-03-03 08:17:11 -0800453 * Sets flowMissingXid, namely OFFlowStatsRequest match any ALL message Id.
Brian O'Connor8c685362015-12-05 15:27:27 -0800454 *
455 * @param flowMissingXid the OFFlowStatsRequest message Id
Brian O'Connor8c685362015-12-05 15:27:27 -0800456 */
457 public void setFlowMissingXid(long flowMissingXid) {
458 this.flowMissingXid = flowMissingXid;
459 }
460
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900461 /**
462 * Calculates the flow live type.
463 *
464 * @param life the flow life time in seconds
465 * @return computed flow live type
466 */
467 public FlowEntry.FlowLiveType calFlowLiveType(long life) {
468 if (life < 0) {
469 return FlowEntry.FlowLiveType.UNKNOWN;
470 } else if (life < calAndPollInterval) {
471 return FlowEntry.FlowLiveType.IMMEDIATE;
472 } else if (life < midPollInterval) {
473 return FlowEntry.FlowLiveType.SHORT;
474 } else if (life < longPollInterval) {
475 return FlowEntry.FlowLiveType.MID;
476 } else { // >= longPollInterval
477 return FlowEntry.FlowLiveType.LONG;
478 }
479 }
Brian O'Connor8c685362015-12-05 15:27:27 -0800480
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900481 /**
482 * Calculates and set the flow live type.
483 * It maybe called pushFlowMetrics of FlowRuleService for the ReplyFlowStat message
484 * at the first time and every entire polling time.
485 *
486 * @param fe the flow entry rule
487 * @return computed flow live type
488 */
489 public FlowEntry.FlowLiveType calAndSetFlowLiveType(StoredFlowEntry fe) {
490 checkNotNull(fe);
Brian O'Connor8c685362015-12-05 15:27:27 -0800491
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900492 long life = fe.life();
Brian O'Connor8c685362015-12-05 15:27:27 -0800493
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900494 if (life < 0) {
495 fe.setLiveType(FlowEntry.FlowLiveType.UNKNOWN);
496 } else if (life < calAndPollInterval) {
497 fe.setLiveType(FlowEntry.FlowLiveType.IMMEDIATE);
498 } else if (life < midPollInterval) {
499 fe.setLiveType(FlowEntry.FlowLiveType.SHORT);
500 } else if (life < longPollInterval) {
501 fe.setLiveType(FlowEntry.FlowLiveType.MID);
502 } else { // >= longPollInterval
503 fe.setLiveType(FlowEntry.FlowLiveType.LONG);
Brian O'Connor8c685362015-12-05 15:27:27 -0800504 }
505
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900506 return fe.liveType();
507 }
Brian O'Connor8c685362015-12-05 15:27:27 -0800508
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900509 /**
510 * Check and move live type for all type flow entries in table at every calAndPollInterval time.
511 *
512 */
513 private void checkAndMoveLiveFlowAll() {
Brian O'Connor8c685362015-12-05 15:27:27 -0800514
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900515 Iterable<FlowEntry> flowEntries = flowRuleService.getFlowEntries(did);
Brian O'Connor8c685362015-12-05 15:27:27 -0800516
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900517 flowEntries.forEach(fe -> {
518 checkAndMoveLiveFlowInternal((StoredFlowEntry) fe);
519 });
Brian O'Connor8c685362015-12-05 15:27:27 -0800520
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900521 // print table counts for debug
522 if (log.isDebugEnabled()) {
523 Iterable<FlowEntry> fes;
524 synchronized (this) {
525 long totalFlowCount = flowRuleService.getFlowRuleCount();
526 fes = flowRuleService.getFlowEntriesByLiveType(
527 did, FlowEntry.FlowLiveType.IMMEDIATE);
528 long immediateFlowCount = Iterables.size(fes);
529 fes = flowRuleService.getFlowEntriesByLiveType(
530 did, FlowEntry.FlowLiveType.SHORT);
531 long shortFlowCount = Iterables.size(fes);
532 fes = flowRuleService.getFlowEntriesByLiveType(
533 did, FlowEntry.FlowLiveType.MID);
534 long midFlowCount = Iterables.size(fes);
535 fes = flowRuleService.getFlowEntriesByLiveType(
536 did, FlowEntry.FlowLiveType.LONG);
537 long longFlowCount = Iterables.size(fes);
538 fes = flowRuleService.getFlowEntriesByLiveType(
539 did, FlowEntry.FlowLiveType.UNKNOWN);
540 long unknownFlowCount = Iterables.size(fes);
Brian O'Connor8c685362015-12-05 15:27:27 -0800541
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900542 log.trace(CHECK_AND_MOVE_COUNT_LOG, totalFlowCount,
543 immediateFlowCount, shortFlowCount, midFlowCount, longFlowCount, unknownFlowCount);
Brian O'Connor8c685362015-12-05 15:27:27 -0800544
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900545 if (immediateFlowCount < 0) {
546 log.error("Immediate flow count is negative");
Jonathan Hart84f4f312016-03-03 08:17:11 -0800547 }
Brian O'Connor8c685362015-12-05 15:27:27 -0800548 }
549 }
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900550 log.trace("checkAndMoveLiveFlowAll, AdaptiveStats for {}", sw.getStringId());
551 }
Brian O'Connor8c685362015-12-05 15:27:27 -0800552
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900553 // check and set the flow live type based on current time
554 private boolean checkAndMoveLiveFlowInternal(StoredFlowEntry fe) {
555 long fromLastSeen = ((System.currentTimeMillis() - fe.lastSeen()) / 1000);
556 // fe.life() unit is SECOND!
557 long liveTime = fe.life() + fromLastSeen;
Brian O'Connor8c685362015-12-05 15:27:27 -0800558
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900559 FlowEntry.FlowLiveType oldLiveType = fe.liveType();
Brian O'Connor8c685362015-12-05 15:27:27 -0800560
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900561 switch (fe.liveType()) {
562 case IMMEDIATE:
563 if (liveTime >= calAndPollInterval) {
564 fe.setLiveType(FlowEntry.FlowLiveType.SHORT);
Jonathan Hart84f4f312016-03-03 08:17:11 -0800565 }
566 break;
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900567 case SHORT:
568 if (liveTime >= midPollInterval) {
569 fe.setLiveType(FlowEntry.FlowLiveType.MID);
Jonathan Hart84f4f312016-03-03 08:17:11 -0800570 }
571 break;
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900572 case MID:
Jonathan Hart84f4f312016-03-03 08:17:11 -0800573 if (liveTime >= longPollInterval) {
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900574 fe.setLiveType(FlowEntry.FlowLiveType.LONG);
Jonathan Hart84f4f312016-03-03 08:17:11 -0800575 }
576 break;
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900577 case LONG:
Jonathan Hart84f4f312016-03-03 08:17:11 -0800578 if (fromLastSeen > entirePollInterval) {
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900579 log.trace("checkAndMoveLiveFlowInternal, flow may be already removed at switch.");
Brian O'Connor8c685362015-12-05 15:27:27 -0800580 return false;
Jonathan Hart84f4f312016-03-03 08:17:11 -0800581 }
582 break;
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900583 case UNKNOWN: // Unknown live type is calculated and set with correct flow live type here.
584 calAndSetFlowLiveType(fe);
585 break;
Jonathan Hart84f4f312016-03-03 08:17:11 -0800586 default:
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900587 // Error Live Type
588 log.error("checkAndMoveLiveFlowInternal, Unknown Live Type error!"
589 + " AdaptiveStats collection thread for {}",
590 sw.getStringId());
Jonathan Hart84f4f312016-03-03 08:17:11 -0800591 return false;
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900592 }
Brian O'Connor8c685362015-12-05 15:27:27 -0800593
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900594 if (log.isTraceEnabled()) {
595 log.trace(CHECK_AND_MOVE_LOG, fe.id(), fe.state(), fe.liveType(),
Jonathan Hart84f4f312016-03-03 08:17:11 -0800596 liveTime, fe.life(), fe.bytes(), fe.packets(), fromLastSeen,
597 fe.priority(), fe.selector().criteria(), fe.treatment(),
598 sw.getStringId());
Brian O'Connor8c685362015-12-05 15:27:27 -0800599 }
600
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900601 return true;
Brian O'Connor8c685362015-12-05 15:27:27 -0800602 }
603}