blob: 43c3fdb9f51c7ee18387827fe5c5792d82addeea [file] [log] [blame]
Simon Hunt1911fe42017-05-02 18:25:58 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2017-present Open Networking Foundation
Simon Hunt1911fe42017-05-02 18:25:58 -07003 *
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 */
17
18package org.onosproject.ui.impl;
19
Thomas Vachuska52f2cd12018-11-08 21:20:04 -080020import org.onosproject.net.statistic.PortStatisticsService.MetricType;
Pier59266bc2018-03-15 12:10:24 -070021import org.onosproject.net.DefaultEdgeLink;
Simon Hunta7aea842017-05-03 19:42:50 -070022import org.onosproject.net.DeviceId;
23import org.onosproject.net.Link;
24import org.onosproject.net.statistic.Load;
Simon Hunt1911fe42017-05-02 18:25:58 -070025import org.onosproject.ui.impl.topo.util.ServicesBundle;
Simon Hunta7aea842017-05-03 19:42:50 -070026import org.onosproject.ui.impl.topo.util.TrafficLink;
27import org.onosproject.ui.impl.topo.util.TrafficLinkMap;
Simon Hunt1911fe42017-05-02 18:25:58 -070028import org.onosproject.ui.topo.AbstractTopoMonitor;
Simon Hunta7aea842017-05-03 19:42:50 -070029import org.onosproject.ui.topo.Highlights;
Simon Hunt1911fe42017-05-02 18:25:58 -070030import org.onosproject.ui.topo.TopoUtils;
31import org.slf4j.Logger;
32import org.slf4j.LoggerFactory;
33
Simon Hunta7aea842017-05-03 19:42:50 -070034import java.util.HashSet;
35import java.util.Set;
Simon Hunt1911fe42017-05-02 18:25:58 -070036import java.util.Timer;
37import java.util.TimerTask;
38
Thomas Vachuska52f2cd12018-11-08 21:20:04 -080039import static org.onosproject.net.statistic.PortStatisticsService.MetricType.BYTES;
40import static org.onosproject.net.statistic.PortStatisticsService.MetricType.PACKETS;
Pier59266bc2018-03-15 12:10:24 -070041import static org.onosproject.net.DefaultEdgeLink.createEdgeLinks;
Simon Hunt1911fe42017-05-02 18:25:58 -070042import static org.onosproject.ui.impl.TrafficMonitorBase.Mode.IDLE;
43
44/**
45 * Base superclass for traffic monitor (both 'classic' and 'topo2' versions).
46 */
47public abstract class TrafficMonitorBase extends AbstractTopoMonitor {
48
49 private final Logger log = LoggerFactory.getLogger(getClass());
50
51 // 4 Kilo Bytes as threshold
Simon Hunta7aea842017-05-03 19:42:50 -070052 protected static final double BPS_THRESHOLD = 4 * TopoUtils.N_KILO;
Simon Hunt1911fe42017-05-02 18:25:58 -070053
54
55 /**
56 * Designates the different modes of operation.
57 */
58 public enum Mode {
59 IDLE,
60 ALL_FLOW_TRAFFIC_BYTES,
61 ALL_PORT_TRAFFIC_BIT_PS,
62 ALL_PORT_TRAFFIC_PKT_PS,
63 DEV_LINK_FLOWS,
64 RELATED_INTENTS,
65 SELECTED_INTENT
66 }
67
68 /**
69 * Number of milliseconds between invocations of sending traffic data.
70 */
71 protected final long trafficPeriod;
72
73 /**
74 * Holds references to services.
75 */
76 protected final ServicesBundle services;
77
78 /**
79 * Current operating mode.
80 */
81 protected Mode mode = Mode.IDLE;
82
83 private final Timer timer;
84 private TimerTask trafficTask = null;
85
86 /**
87 * Constructs the monitor, initializing the task period and
88 * services bundle reference.
89 *
90 * @param trafficPeriod traffic task period in ms
91 * @param servicesBundle bundle of services
92 */
93 protected TrafficMonitorBase(long trafficPeriod,
94 ServicesBundle servicesBundle) {
95 this.trafficPeriod = trafficPeriod;
96 this.services = servicesBundle;
97 timer = new Timer("uiTopo-" + getClass().getSimpleName());
98 }
99
100 /**
101 * Initiates monitoring of traffic for a given mode.
102 * This causes a background traffic task to be
103 * scheduled to repeatedly compute and transmit the appropriate traffic
104 * data to the client.
105 * <p>
106 * The monitoring mode is expected to be one of:
107 * <ul>
108 * <li>ALL_FLOW_TRAFFIC_BYTES</li>
109 * <li>ALL_PORT_TRAFFIC_BIT_PS</li>
110 * <li>ALL_PORT_TRAFFIC_PKT_PS</li>
111 * <li>SELECTED_INTENT</li>
112 * </ul>
113 *
114 * @param mode the monitoring mode
115 */
116 public synchronized void monitor(Mode mode) {
117 this.mode = mode;
118
119 switch (mode) {
120
121 case ALL_FLOW_TRAFFIC_BYTES:
122 clearSelection();
123 scheduleTask();
124 sendAllFlowTraffic();
125 break;
126
127 case ALL_PORT_TRAFFIC_BIT_PS:
128 clearSelection();
129 scheduleTask();
130 sendAllPortTrafficBits();
131 break;
132
133 case ALL_PORT_TRAFFIC_PKT_PS:
134 clearSelection();
135 scheduleTask();
136 sendAllPortTrafficPackets();
137 break;
138
139 case SELECTED_INTENT:
140 sendSelectedIntentTraffic();
141 scheduleTask();
142 break;
143
144 default:
145 log.warn("Unexpected call to monitor({})", mode);
146 clearAll();
147 break;
148 }
149 }
150
151 /**
152 * Subclass should compile and send appropriate highlights data showing
153 * flow traffic (bytes on links).
154 */
155 protected abstract void sendAllFlowTraffic();
156
157 /**
158 * Subclass should compile and send appropriate highlights data showing
159 * bits per second, as computed using port stats.
160 */
161 protected abstract void sendAllPortTrafficBits();
162
163 /**
164 * Subclass should compile and send appropriate highlights data showing
165 * packets per second, as computed using port stats.
166 */
167 protected abstract void sendAllPortTrafficPackets();
168
169 /**
170 * Subclass should compile and send appropriate highlights data showing
171 * number of flows traversing links for the "selected" device(s).
172 */
173 protected abstract void sendDeviceLinkFlows();
174
175 /**
176 * Subclass should compile and send appropriate highlights data showing
177 * traffic traversing links for the "selected" intent.
178 */
179 protected abstract void sendSelectedIntentTraffic();
180
181 /**
182 * Subclass should send a "clear highlights" event.
183 */
184 protected abstract void sendClearHighlights();
185
186 /**
187 * Subclasses should clear any selection state.
188 */
189 protected abstract void clearSelection();
190
191 /**
192 * Sets the mode to IDLE, clears the selection, cancels the background
193 * task, and sends a clear highlights event to the client.
194 */
195 protected void clearAll() {
196 this.mode = Mode.IDLE;
197 clearSelection();
198 cancelTask();
199 sendClearHighlights();
200 }
201
202 /**
203 * Schedules the background monitor task to run.
204 */
205 protected synchronized void scheduleTask() {
206 if (trafficTask == null) {
207 log.debug("Starting up background traffic task...");
208 trafficTask = new TrafficUpdateTask();
209 timer.schedule(trafficTask, trafficPeriod, trafficPeriod);
210 } else {
211 log.debug("(traffic task already running)");
212 }
213 }
214
215 /**
216 * Cancels the background monitor task.
217 */
218 protected synchronized void cancelTask() {
219 if (trafficTask != null) {
220 trafficTask.cancel();
221 trafficTask = null;
222 }
223 }
224
225 /**
226 * Stops monitoring. (Invokes {@link #clearAll}, if not idle).
227 */
228 public synchronized void stopMonitoring() {
229 log.debug("STOP monitoring");
230 if (mode != IDLE) {
231 clearAll();
232 }
233 }
234
235
236 // =======================================================================
Simon Hunta7aea842017-05-03 19:42:50 -0700237 // === Methods for computing traffic on links
238
239 /**
240 * Generates a {@link Highlights} object summarizing the traffic on the
241 * network, ready to be transmitted back to the client for display on
242 * the topology view.
243 *
244 * @param type the type of statistics to be displayed
245 * @return highlights, representing links to be labeled/colored
246 */
247 protected Highlights trafficSummary(TrafficLink.StatsType type) {
248 Highlights highlights = new Highlights();
249
250 // TODO: consider whether a map would be better...
251 Set<TrafficLink> linksWithTraffic = computeLinksWithTraffic(type);
252
253 Set<TrafficLink> aggregatedLinks = doAggregation(linksWithTraffic);
254
255 for (TrafficLink tlink : aggregatedLinks) {
256 highlights.add(tlink.highlight(type));
257 }
258 return highlights;
259 }
260
261 /**
262 * Generates a set of "traffic links" encapsulating information about the
263 * traffic on each link (that is deemed to have traffic).
264 *
265 * @param type the type of statistics to be displayed
266 * @return the set of links with traffic
267 */
268 protected Set<TrafficLink> computeLinksWithTraffic(TrafficLink.StatsType type) {
269 TrafficLinkMap linkMap = new TrafficLinkMap();
270 compileLinks(linkMap);
271 addEdgeLinks(linkMap);
272
273 Set<TrafficLink> linksWithTraffic = new HashSet<>();
Simon Hunta7aea842017-05-03 19:42:50 -0700274
275 for (TrafficLink tlink : linkMap.biLinks()) {
276 if (type == TrafficLink.StatsType.FLOW_STATS) {
277 attachFlowLoad(tlink);
278 } else if (type == TrafficLink.StatsType.PORT_STATS) {
279 attachPortLoad(tlink, BYTES);
280 } else if (type == TrafficLink.StatsType.PORT_PACKET_STATS) {
281 attachPortLoad(tlink, PACKETS);
282 }
283
284 // we only want to report on links deemed to have traffic
285 if (tlink.hasTraffic()) {
286 linksWithTraffic.add(tlink);
287 }
288 }
289 return linksWithTraffic;
290 }
291
292 /**
293 * Iterates across the set of links in the topology and generates the
294 * appropriate set of traffic links.
295 *
296 * @param linkMap link map to augment with traffic links
297 */
298 protected void compileLinks(TrafficLinkMap linkMap) {
299 services.link().getLinks().forEach(linkMap::add);
300 }
301
302 /**
303 * Iterates across the set of hosts in the topology and generates the
304 * appropriate set of traffic links for the edge links.
305 *
306 * @param linkMap link map to augment with traffic links
307 */
308 protected void addEdgeLinks(TrafficLinkMap linkMap) {
309 services.host().getHosts().forEach(host -> {
Pier59266bc2018-03-15 12:10:24 -0700310 // Ingress edge links
311 Set<DefaultEdgeLink> edgeLinks = createEdgeLinks(host, true);
312 edgeLinks.forEach(linkMap::add);
313 // Egress edge links
314 edgeLinks = createEdgeLinks(host, false);
315 edgeLinks.forEach(linkMap::add);
Simon Hunta7aea842017-05-03 19:42:50 -0700316 });
317 }
318
319 /**
320 * Processes the given traffic link to attach the "flow load" attributed
321 * to the underlying topology links.
322 *
323 * @param link the traffic link to process
324 */
325 protected void attachFlowLoad(TrafficLink link) {
326 link.addLoad(getLinkFlowLoad(link.one()));
327 link.addLoad(getLinkFlowLoad(link.two()));
328 }
329
330 /**
331 * Returns the load for the given link, as determined by the statistics
332 * service. May return null.
333 *
334 * @param link the link on which to look up the stats
335 * @return the corresponding load (or null)
336 */
337 protected Load getLinkFlowLoad(Link link) {
338 if (link != null && link.src().elementId() instanceof DeviceId) {
339 return services.flowStats().load(link);
340 }
341 return null;
342 }
343
344 /**
345 * Processes the given traffic link to attach the "port load" attributed
346 * to the underlying topology links, for the specified metric type (either
347 * bytes/sec or packets/sec).
348 *
349 * @param link the traffic link to process
350 * @param metricType the metric type (bytes or packets)
351 */
352 protected void attachPortLoad(TrafficLink link, MetricType metricType) {
353 // For bi-directional traffic links, use
354 // the max link rate of either direction
355 // (we choose 'one' since we know that is never null)
356 Link one = link.one();
357 Load egressSrc = services.portStats().load(one.src(), metricType);
358 Load egressDst = services.portStats().load(one.dst(), metricType);
359 link.addLoad(maxLoad(egressSrc, egressDst), metricType == BYTES ? BPS_THRESHOLD : 0);
360 }
361
362 /**
363 * Returns the load with the greatest rate.
364 *
365 * @param a load a
366 * @param b load b
367 * @return the larger of the two
368 */
369 protected Load maxLoad(Load a, Load b) {
370 if (a == null) {
371 return b;
372 }
373 if (b == null) {
374 return a;
375 }
376 return a.rate() > b.rate() ? a : b;
377 }
378
379
380 /**
381 * Subclasses (well, Traffic2Monitor really) can override this method and
382 * process the traffic links before generating the highlights object.
383 * In particular, links that roll up into "synthetic links" between
384 * regions should show aggregated data from the constituent links.
385 * <p>
386 * This default implementation does nothing.
387 *
388 * @param linksWithTraffic link data for all links
389 * @return transformed link data appropriate to the region display
390 */
391 protected Set<TrafficLink> doAggregation(Set<TrafficLink> linksWithTraffic) {
392 return linksWithTraffic;
393 }
394
395
396 // =======================================================================
Simon Hunt1911fe42017-05-02 18:25:58 -0700397 // === Background Task
398
399 // Provides periodic update of traffic information to the client
400 private class TrafficUpdateTask extends TimerTask {
401 @Override
402 public void run() {
403 try {
404 switch (mode) {
405 case ALL_FLOW_TRAFFIC_BYTES:
406 sendAllFlowTraffic();
407 break;
408 case ALL_PORT_TRAFFIC_BIT_PS:
409 sendAllPortTrafficBits();
410 break;
411 case ALL_PORT_TRAFFIC_PKT_PS:
412 sendAllPortTrafficPackets();
413 break;
414 case DEV_LINK_FLOWS:
415 sendDeviceLinkFlows();
416 break;
417 case SELECTED_INTENT:
418 sendSelectedIntentTraffic();
419 break;
420
421 default:
422 // RELATED_INTENTS and IDLE modes should never invoke
423 // the background task, but if they do, they have
424 // nothing to do
425 break;
426 }
427
428 } catch (Exception e) {
429 log.warn("Unable to process traffic task due to {}", e.getMessage());
430 log.warn("Boom!", e);
431 }
432 }
433 }
Simon Hunt1911fe42017-05-02 18:25:58 -0700434}