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