blob: ae3c9098a030b44067b8d4d827fde46af4c79232 [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
Sean Condonadeb7162019-04-13 20:56:14 +010020import com.google.common.collect.ImmutableList;
21import com.google.common.collect.Lists;
22import org.onosproject.net.Device;
23import org.onosproject.net.ElementId;
24import org.onosproject.net.Host;
25import org.onosproject.net.HostId;
26import org.onosproject.net.device.DeviceService;
27import org.onosproject.net.intent.FlowObjectiveIntent;
28import org.onosproject.net.intent.FlowRuleIntent;
29import org.onosproject.net.intent.HostToHostIntent;
30import org.onosproject.net.intent.Intent;
31import org.onosproject.net.intent.LinkCollectionIntent;
32import org.onosproject.net.intent.OpticalConnectivityIntent;
33import org.onosproject.net.intent.OpticalPathIntent;
34import org.onosproject.net.intent.PathIntent;
35import org.onosproject.net.link.LinkService;
Thomas Vachuska52f2cd12018-11-08 21:20:04 -080036import org.onosproject.net.statistic.PortStatisticsService.MetricType;
Pier59266bc2018-03-15 12:10:24 -070037import org.onosproject.net.DefaultEdgeLink;
Simon Hunta7aea842017-05-03 19:42:50 -070038import org.onosproject.net.DeviceId;
39import org.onosproject.net.Link;
40import org.onosproject.net.statistic.Load;
Thomas Vachuska2b4de872021-03-30 16:31:34 -070041import org.onosproject.ui.UiExtensionService;
42import org.onosproject.ui.UiTopoHighlighter;
43import org.onosproject.ui.UiTopoHighlighterFactory;
Sean Condonadeb7162019-04-13 20:56:14 +010044import org.onosproject.ui.impl.topo.TopoologyTrafficMessageHandlerAbstract;
45import org.onosproject.ui.impl.topo.util.IntentSelection;
Simon Hunt1911fe42017-05-02 18:25:58 -070046import org.onosproject.ui.impl.topo.util.ServicesBundle;
Sean Condonadeb7162019-04-13 20:56:14 +010047import org.onosproject.ui.impl.topo.util.TopoIntentFilter;
Simon Hunta7aea842017-05-03 19:42:50 -070048import org.onosproject.ui.impl.topo.util.TrafficLink;
49import org.onosproject.ui.impl.topo.util.TrafficLinkMap;
Simon Hunt1911fe42017-05-02 18:25:58 -070050import org.onosproject.ui.topo.AbstractTopoMonitor;
Sean Condonadeb7162019-04-13 20:56:14 +010051import org.onosproject.ui.topo.DeviceHighlight;
Simon Hunta7aea842017-05-03 19:42:50 -070052import org.onosproject.ui.topo.Highlights;
Sean Condonadeb7162019-04-13 20:56:14 +010053import org.onosproject.ui.topo.HostHighlight;
54import org.onosproject.ui.topo.LinkHighlight;
55import org.onosproject.ui.topo.NodeHighlight;
56import org.onosproject.ui.topo.NodeSelection;
Simon Hunt1911fe42017-05-02 18:25:58 -070057import org.onosproject.ui.topo.TopoUtils;
58import org.slf4j.Logger;
59import org.slf4j.LoggerFactory;
60
Sean Condonadeb7162019-04-13 20:56:14 +010061import java.util.ArrayList;
62import java.util.Collection;
63import java.util.Collections;
Simon Hunta7aea842017-05-03 19:42:50 -070064import java.util.HashSet;
Sean Condonadeb7162019-04-13 20:56:14 +010065import java.util.List;
Simon Hunta7aea842017-05-03 19:42:50 -070066import java.util.Set;
Simon Hunt1911fe42017-05-02 18:25:58 -070067import java.util.Timer;
68import java.util.TimerTask;
Sean Condonadeb7162019-04-13 20:56:14 +010069import java.util.stream.Collectors;
Simon Hunt1911fe42017-05-02 18:25:58 -070070
Sean Condonadeb7162019-04-13 20:56:14 +010071import static org.onosproject.net.DefaultEdgeLink.createEdgeLink;
Thomas Vachuska52f2cd12018-11-08 21:20:04 -080072import static org.onosproject.net.statistic.PortStatisticsService.MetricType.BYTES;
73import static org.onosproject.net.statistic.PortStatisticsService.MetricType.PACKETS;
Pier59266bc2018-03-15 12:10:24 -070074import static org.onosproject.net.DefaultEdgeLink.createEdgeLinks;
Thomas Vachuska2b4de872021-03-30 16:31:34 -070075import static org.onosproject.ui.impl.TrafficMonitorBase.Mode.*;
Simon Hunt1911fe42017-05-02 18:25:58 -070076
77/**
78 * Base superclass for traffic monitor (both 'classic' and 'topo2' versions).
79 */
80public abstract class TrafficMonitorBase extends AbstractTopoMonitor {
81
Sean Condonadeb7162019-04-13 20:56:14 +010082 protected final Logger log = LoggerFactory.getLogger(getClass());
Simon Hunt1911fe42017-05-02 18:25:58 -070083
84 // 4 Kilo Bytes as threshold
Simon Hunta7aea842017-05-03 19:42:50 -070085 protected static final double BPS_THRESHOLD = 4 * TopoUtils.N_KILO;
Sean Condonadeb7162019-04-13 20:56:14 +010086 protected final TopoIntentFilter intentFilter;
87 protected IntentSelection selectedIntents = null;
88 protected final TopoologyTrafficMessageHandlerAbstract msgHandler;
89 protected NodeSelection selectedNodes = null;
Thomas Vachuska2b4de872021-03-30 16:31:34 -070090 protected UiTopoHighlighter topoHighlighter = null;
Sean Condonadeb7162019-04-13 20:56:14 +010091
92 protected void sendSelectedIntents() {
93 log.debug("sendSelectedIntents: {}", selectedIntents);
94 msgHandler.sendHighlights(intentGroup());
95 }
96
97 protected void ensureNodePresent(Highlights highlights, ElementId eid) {
98 String id = eid.toString();
99 NodeHighlight nh = highlights.getNode(id);
100 if (nh == null) {
101 if (eid instanceof DeviceId) {
102 nh = new DeviceHighlight(id);
103 highlights.add((DeviceHighlight) nh);
104 } else if (eid instanceof HostId) {
105 nh = new HostHighlight(id);
106 highlights.add((HostHighlight) nh);
107 }
108 }
109 }
110
111 protected void colorLinks(Highlights highlights, TrafficLinkMap linkMap) {
112 for (TrafficLink tlink : linkMap.biLinks()) {
113 highlights.add(tlink.highlight(TrafficLink.StatsType.TAGGED));
114 }
115 }
116
117 protected void processLinks(TrafficLinkMap linkMap, Iterable<Link> links,
118 LinkHighlight.Flavor flavor, boolean isOptical,
119 boolean showTraffic) {
120 if (links != null) {
121 for (Link link : links) {
122 TrafficLink tlink = linkMap.add(link);
123 tlink.tagFlavor(flavor);
124 tlink.optical(isOptical);
125 if (showTraffic) {
126 tlink.addLoad(getLinkFlowLoad(link));
127 tlink.antMarch(true);
128 }
129 }
130 }
131 }
132
133 protected void updateHighlights(Highlights highlights, Iterable<Link> links) {
134 for (Link link : links) {
135 ensureNodePresent(highlights, link.src().elementId());
136 ensureNodePresent(highlights, link.dst().elementId());
137 }
138 }
139
140 protected Iterable<Link> addEdgeLinksIfNeeded(Intent parentIntent,
141 Collection<Link> links) {
142 if (parentIntent instanceof HostToHostIntent) {
143 links = new HashSet<>(links);
144 HostToHostIntent h2h = (HostToHostIntent) parentIntent;
145 Host h1 = services.host().getHost(h2h.one());
146 Host h2 = services.host().getHost(h2h.two());
147 links.add(createEdgeLink(h1, true));
148 links.add(createEdgeLink(h2, true));
149 }
150 return links;
151 }
152
153 // Extracts links from the specified flow rule intent resources
154 protected Collection<Link> linkResources(Intent installable) {
155 ImmutableList.Builder<Link> builder = ImmutableList.builder();
156 installable.resources().stream().filter(r -> r instanceof Link)
157 .forEach(r -> builder.add((Link) r));
158 return builder.build();
159 }
160
161 protected void createTrafficLinks(Highlights highlights,
162 TrafficLinkMap linkMap, Set<Intent> intents,
163 LinkHighlight.Flavor flavor, boolean showTraffic) {
164 for (Intent intent : intents) {
165 List<Intent> installables = services.intent()
166 .getInstallableIntents(intent.key());
167 Iterable<Link> links = null;
168 if (installables != null) {
169 for (Intent installable : installables) {
170
171 if (installable instanceof PathIntent) {
172 links = ((PathIntent) installable).path().links();
173 } else if (installable instanceof FlowRuleIntent) {
174 Collection<Link> l = new ArrayList<>();
175 l.addAll(linkResources(installable));
176 // Add cross connect links
177 if (intent instanceof OpticalConnectivityIntent) {
178 OpticalConnectivityIntent ocIntent = (OpticalConnectivityIntent) intent;
179 LinkService linkService = services.link();
180 DeviceService deviceService = services.device();
181 l.addAll(linkService.getDeviceIngressLinks(ocIntent.getSrc().deviceId()).stream()
182 .filter(i ->
183 deviceService.getDevice(i.src().deviceId()).type() == Device.Type.SWITCH)
184 .collect(Collectors.toList()));
185 l.addAll(linkService.getDeviceEgressLinks(ocIntent.getDst().deviceId()).stream()
186 .filter(e ->
187 deviceService.getDevice(e.dst().deviceId()).type() == Device.Type.SWITCH)
188 .collect(Collectors.toList()));
189 }
190 links = l;
191 } else if (installable instanceof FlowObjectiveIntent) {
192 links = linkResources(installable);
193 } else if (installable instanceof LinkCollectionIntent) {
194 links = ((LinkCollectionIntent) installable).links();
195 } else if (installable instanceof OpticalPathIntent) {
196 links = ((OpticalPathIntent) installable).path().links();
197 }
198
199 if (links == null) {
200 links = Lists.newArrayList();
201 }
202
203 links = addEdgeLinksIfNeeded(intent, Lists.newArrayList(links));
204
205 boolean isOptical = intent instanceof OpticalConnectivityIntent;
206 processLinks(linkMap, links, flavor, isOptical, showTraffic);
207 updateHighlights(highlights, links);
208 }
209 }
210 }
211 }
212
213 protected void highlightIntentLinks(Highlights highlights,
214 Set<Intent> primary, Set<Intent> secondary) {
215 TrafficLinkMap linkMap = new TrafficLinkMap();
216 // NOTE: highlight secondary first, then primary, so that links shared
217 // by intents are colored correctly ("last man wins")
218 createTrafficLinks(highlights, linkMap, secondary, LinkHighlight.Flavor.SECONDARY_HIGHLIGHT, false);
219 createTrafficLinks(highlights, linkMap, primary, LinkHighlight.Flavor.PRIMARY_HIGHLIGHT, false);
220 colorLinks(highlights, linkMap);
221 }
222
223 protected Highlights intentGroup() {
224 Highlights highlights = new Highlights();
225
226 if (selectedIntents != null && !selectedIntents.none()) {
227 // If 'all' intents are selected, they will all have primary
228 // highlighting; otherwise, the specifically selected intent will
229 // have primary highlighting, and the remainder will have secondary
230 // highlighting.
231 Set<Intent> primary;
232 Set<Intent> secondary;
233 int count = selectedIntents.size();
234
235 Set<Intent> allBut = new HashSet<>(selectedIntents.intents());
236 Intent current;
237
238 if (selectedIntents.all()) {
239 primary = allBut;
240 secondary = Collections.emptySet();
241 log.debug("Highlight all intents ({})", count);
242 } else {
243 current = selectedIntents.current();
244 primary = new HashSet<>();
245 primary.add(current);
246 allBut.remove(current);
247 secondary = allBut;
248 log.debug("Highlight intent: {} ([{}] of {})",
249 current.id(), selectedIntents.index(), count);
250 }
251
252 highlightIntentLinks(highlights, primary, secondary);
253 }
254 return highlights;
255 }
256
257 protected Highlights intentTraffic() {
258 Highlights highlights = new Highlights();
259
260 if (selectedIntents != null && selectedIntents.single()) {
261 Intent current = selectedIntents.current();
262 Set<Intent> primary = new HashSet<>();
263 primary.add(current);
264 log.debug("Highlight traffic for intent: {} ([{}] of {})",
265 current.id(), selectedIntents.index(), selectedIntents.size());
266
267 highlightIntentLinksWithTraffic(highlights, primary);
268 highlights.subdueAllElse(Highlights.Amount.MINIMALLY);
269 }
270 return highlights;
271 }
272
273 private void highlightIntentLinksWithTraffic(Highlights highlights,
274 Set<Intent> primary) {
275 TrafficLinkMap linkMap = new TrafficLinkMap();
276 createTrafficLinks(highlights, linkMap, primary, LinkHighlight.Flavor.PRIMARY_HIGHLIGHT, true);
277 colorLinks(highlights, linkMap);
278 }
Simon Hunt1911fe42017-05-02 18:25:58 -0700279
280
281 /**
282 * Designates the different modes of operation.
283 */
284 public enum Mode {
285 IDLE,
286 ALL_FLOW_TRAFFIC_BYTES,
287 ALL_PORT_TRAFFIC_BIT_PS,
288 ALL_PORT_TRAFFIC_PKT_PS,
289 DEV_LINK_FLOWS,
290 RELATED_INTENTS,
Thomas Vachuska2b4de872021-03-30 16:31:34 -0700291 SELECTED_INTENT,
292 CUSTOM_TRAFFIC_MONITOR
Simon Hunt1911fe42017-05-02 18:25:58 -0700293 }
294
295 /**
296 * Number of milliseconds between invocations of sending traffic data.
297 */
298 protected final long trafficPeriod;
299
300 /**
301 * Holds references to services.
302 */
303 protected final ServicesBundle services;
304
305 /**
306 * Current operating mode.
307 */
308 protected Mode mode = Mode.IDLE;
309
310 private final Timer timer;
311 private TimerTask trafficTask = null;
312
313 /**
314 * Constructs the monitor, initializing the task period and
315 * services bundle reference.
316 *
317 * @param trafficPeriod traffic task period in ms
318 * @param servicesBundle bundle of services
Sean Condonadeb7162019-04-13 20:56:14 +0100319 * @param msgHandler Traffic Message handler
Simon Hunt1911fe42017-05-02 18:25:58 -0700320 */
321 protected TrafficMonitorBase(long trafficPeriod,
Sean Condonadeb7162019-04-13 20:56:14 +0100322 ServicesBundle servicesBundle,
323 TopoologyTrafficMessageHandlerAbstract msgHandler) {
Simon Hunt1911fe42017-05-02 18:25:58 -0700324 this.trafficPeriod = trafficPeriod;
325 this.services = servicesBundle;
Sean Condonadeb7162019-04-13 20:56:14 +0100326 this.msgHandler = msgHandler;
Simon Hunt1911fe42017-05-02 18:25:58 -0700327 timer = new Timer("uiTopo-" + getClass().getSimpleName());
Sean Condonadeb7162019-04-13 20:56:14 +0100328 intentFilter = new TopoIntentFilter(servicesBundle);
Simon Hunt1911fe42017-05-02 18:25:58 -0700329 }
330
331 /**
332 * Initiates monitoring of traffic for a given mode.
333 * This causes a background traffic task to be
334 * scheduled to repeatedly compute and transmit the appropriate traffic
335 * data to the client.
336 * <p>
337 * The monitoring mode is expected to be one of:
338 * <ul>
339 * <li>ALL_FLOW_TRAFFIC_BYTES</li>
340 * <li>ALL_PORT_TRAFFIC_BIT_PS</li>
341 * <li>ALL_PORT_TRAFFIC_PKT_PS</li>
342 * <li>SELECTED_INTENT</li>
343 * </ul>
344 *
345 * @param mode the monitoring mode
346 */
347 public synchronized void monitor(Mode mode) {
348 this.mode = mode;
349
350 switch (mode) {
351
352 case ALL_FLOW_TRAFFIC_BYTES:
353 clearSelection();
354 scheduleTask();
355 sendAllFlowTraffic();
356 break;
357
358 case ALL_PORT_TRAFFIC_BIT_PS:
359 clearSelection();
360 scheduleTask();
361 sendAllPortTrafficBits();
362 break;
363
364 case ALL_PORT_TRAFFIC_PKT_PS:
365 clearSelection();
366 scheduleTask();
367 sendAllPortTrafficPackets();
368 break;
369
370 case SELECTED_INTENT:
371 sendSelectedIntentTraffic();
372 scheduleTask();
373 break;
374
375 default:
376 log.warn("Unexpected call to monitor({})", mode);
377 clearAll();
378 break;
379 }
380 }
381
Thomas Vachuska2b4de872021-03-30 16:31:34 -0700382
383 public synchronized void monitor(int index) {
384 mode = CUSTOM_TRAFFIC_MONITOR;
385 List<UiTopoHighlighterFactory> factories = services.get(UiExtensionService.class)
386 .getTopoHighlighterFactories();
387 if (factories.isEmpty()) {
388 return;
389 }
390
391 UiTopoHighlighterFactory factory = factories.get(index % factories.size());
392 topoHighlighter = factory.newTopoHighlighter();
393 clearSelection();
394 scheduleTask();
395 sendCustomTraffic();
396 }
397
Simon Hunt1911fe42017-05-02 18:25:58 -0700398 /**
Sean Condonadeb7162019-04-13 20:56:14 +0100399 * Monitor for traffic data to be sent back to the web client, under
400 * the given mode, using the given selection of devices and hosts.
401 * In the case of "device link flows", this causes a background traffic
402 * task to be scheduled to repeatedly compute and transmit the appropriate
403 * traffic data to the client. In the case of "related intents", no
404 * repeating task is scheduled.
405 * <p>
406 * The monitoring mode is expected to be one of:
407 * <ul>
408 * <li>DEV_LINK_FLOWS</li>
409 * <li>RELATED_INTENTS</li>
410 * </ul>
411 *
412 * @param mode monitoring mode
413 * @param nodeSelection how to select a node
414 */
415 public synchronized void monitor(Mode mode, NodeSelection nodeSelection) {
416 log.debug("monitor: {} -- {}", mode, nodeSelection);
417 this.mode = mode;
418 this.selectedNodes = nodeSelection;
419
420 switch (mode) {
421 case DEV_LINK_FLOWS:
422 // only care about devices (not hosts)
423 if (selectedNodes.devicesWithHover().isEmpty()) {
424 clearAll();
425 } else {
426 scheduleTask();
427 sendDeviceLinkFlows();
428 }
429 break;
430
431 case RELATED_INTENTS:
432 if (selectedNodes.none()) {
433 clearAll();
434 } else {
435 selectedIntents = new IntentSelection(selectedNodes, intentFilter);
436 if (selectedIntents.none()) {
437 clearAll();
438 } else {
439 sendSelectedIntents();
440 }
441 }
442 break;
443
444 default:
445 log.debug("Unexpected call to monitor({}, {})", mode, nodeSelection);
446 clearAll();
447 break;
448 }
449 }
450
451 /**
452 * Monitor for traffic data to be sent back to the web client, for the
453 * given intent.
454 *
455 * @param intent the intent to monitor
456 */
457 public synchronized void monitor(Intent intent) {
458 log.debug("monitor intent: {}", intent.id());
459 selectedNodes = null;
460 selectedIntents = new IntentSelection(intent);
461 mode = SELECTED_INTENT;
462 scheduleTask();
463 sendSelectedIntentTraffic();
464 }
465
466 /**
Simon Hunt1911fe42017-05-02 18:25:58 -0700467 * Subclass should compile and send appropriate highlights data showing
468 * flow traffic (bytes on links).
469 */
470 protected abstract void sendAllFlowTraffic();
471
472 /**
473 * Subclass should compile and send appropriate highlights data showing
474 * bits per second, as computed using port stats.
475 */
476 protected abstract void sendAllPortTrafficBits();
477
478 /**
479 * Subclass should compile and send appropriate highlights data showing
480 * packets per second, as computed using port stats.
481 */
482 protected abstract void sendAllPortTrafficPackets();
483
484 /**
485 * Subclass should compile and send appropriate highlights data showing
486 * number of flows traversing links for the "selected" device(s).
487 */
488 protected abstract void sendDeviceLinkFlows();
489
490 /**
491 * Subclass should compile and send appropriate highlights data showing
492 * traffic traversing links for the "selected" intent.
493 */
494 protected abstract void sendSelectedIntentTraffic();
495
496 /**
Thomas Vachuska2b4de872021-03-30 16:31:34 -0700497 * Subclass should compile and send appropriate highlights data showing
498 * custom traffic on links.
499 */
500 protected abstract void sendCustomTraffic();
501
502 /**
Simon Hunt1911fe42017-05-02 18:25:58 -0700503 * Subclass should send a "clear highlights" event.
504 */
505 protected abstract void sendClearHighlights();
506
507 /**
508 * Subclasses should clear any selection state.
509 */
510 protected abstract void clearSelection();
511
512 /**
513 * Sets the mode to IDLE, clears the selection, cancels the background
514 * task, and sends a clear highlights event to the client.
515 */
516 protected void clearAll() {
517 this.mode = Mode.IDLE;
518 clearSelection();
519 cancelTask();
520 sendClearHighlights();
521 }
522
523 /**
524 * Schedules the background monitor task to run.
525 */
526 protected synchronized void scheduleTask() {
527 if (trafficTask == null) {
528 log.debug("Starting up background traffic task...");
529 trafficTask = new TrafficUpdateTask();
530 timer.schedule(trafficTask, trafficPeriod, trafficPeriod);
531 } else {
532 log.debug("(traffic task already running)");
533 }
534 }
535
536 /**
537 * Cancels the background monitor task.
538 */
539 protected synchronized void cancelTask() {
540 if (trafficTask != null) {
541 trafficTask.cancel();
542 trafficTask = null;
543 }
544 }
545
546 /**
547 * Stops monitoring. (Invokes {@link #clearAll}, if not idle).
548 */
549 public synchronized void stopMonitoring() {
550 log.debug("STOP monitoring");
551 if (mode != IDLE) {
552 clearAll();
553 }
554 }
555
556
557 // =======================================================================
Simon Hunta7aea842017-05-03 19:42:50 -0700558 // === Methods for computing traffic on links
559
560 /**
561 * Generates a {@link Highlights} object summarizing the traffic on the
562 * network, ready to be transmitted back to the client for display on
563 * the topology view.
564 *
565 * @param type the type of statistics to be displayed
566 * @return highlights, representing links to be labeled/colored
567 */
568 protected Highlights trafficSummary(TrafficLink.StatsType type) {
569 Highlights highlights = new Highlights();
570
571 // TODO: consider whether a map would be better...
572 Set<TrafficLink> linksWithTraffic = computeLinksWithTraffic(type);
573
574 Set<TrafficLink> aggregatedLinks = doAggregation(linksWithTraffic);
575
576 for (TrafficLink tlink : aggregatedLinks) {
577 highlights.add(tlink.highlight(type));
578 }
579 return highlights;
580 }
581
582 /**
583 * Generates a set of "traffic links" encapsulating information about the
584 * traffic on each link (that is deemed to have traffic).
585 *
586 * @param type the type of statistics to be displayed
587 * @return the set of links with traffic
588 */
589 protected Set<TrafficLink> computeLinksWithTraffic(TrafficLink.StatsType type) {
590 TrafficLinkMap linkMap = new TrafficLinkMap();
591 compileLinks(linkMap);
592 addEdgeLinks(linkMap);
593
594 Set<TrafficLink> linksWithTraffic = new HashSet<>();
Simon Hunta7aea842017-05-03 19:42:50 -0700595
596 for (TrafficLink tlink : linkMap.biLinks()) {
597 if (type == TrafficLink.StatsType.FLOW_STATS) {
598 attachFlowLoad(tlink);
599 } else if (type == TrafficLink.StatsType.PORT_STATS) {
600 attachPortLoad(tlink, BYTES);
601 } else if (type == TrafficLink.StatsType.PORT_PACKET_STATS) {
602 attachPortLoad(tlink, PACKETS);
603 }
604
605 // we only want to report on links deemed to have traffic
606 if (tlink.hasTraffic()) {
607 linksWithTraffic.add(tlink);
608 }
609 }
610 return linksWithTraffic;
611 }
612
613 /**
614 * Iterates across the set of links in the topology and generates the
615 * appropriate set of traffic links.
616 *
617 * @param linkMap link map to augment with traffic links
618 */
619 protected void compileLinks(TrafficLinkMap linkMap) {
620 services.link().getLinks().forEach(linkMap::add);
621 }
622
623 /**
624 * Iterates across the set of hosts in the topology and generates the
625 * appropriate set of traffic links for the edge links.
626 *
627 * @param linkMap link map to augment with traffic links
628 */
629 protected void addEdgeLinks(TrafficLinkMap linkMap) {
630 services.host().getHosts().forEach(host -> {
Pier59266bc2018-03-15 12:10:24 -0700631 // Ingress edge links
632 Set<DefaultEdgeLink> edgeLinks = createEdgeLinks(host, true);
633 edgeLinks.forEach(linkMap::add);
634 // Egress edge links
635 edgeLinks = createEdgeLinks(host, false);
636 edgeLinks.forEach(linkMap::add);
Simon Hunta7aea842017-05-03 19:42:50 -0700637 });
638 }
639
640 /**
641 * Processes the given traffic link to attach the "flow load" attributed
642 * to the underlying topology links.
643 *
644 * @param link the traffic link to process
645 */
646 protected void attachFlowLoad(TrafficLink link) {
647 link.addLoad(getLinkFlowLoad(link.one()));
648 link.addLoad(getLinkFlowLoad(link.two()));
649 }
650
651 /**
652 * Returns the load for the given link, as determined by the statistics
653 * service. May return null.
654 *
655 * @param link the link on which to look up the stats
656 * @return the corresponding load (or null)
657 */
658 protected Load getLinkFlowLoad(Link link) {
659 if (link != null && link.src().elementId() instanceof DeviceId) {
660 return services.flowStats().load(link);
661 }
662 return null;
663 }
664
665 /**
666 * Processes the given traffic link to attach the "port load" attributed
667 * to the underlying topology links, for the specified metric type (either
668 * bytes/sec or packets/sec).
669 *
670 * @param link the traffic link to process
671 * @param metricType the metric type (bytes or packets)
672 */
673 protected void attachPortLoad(TrafficLink link, MetricType metricType) {
674 // For bi-directional traffic links, use
675 // the max link rate of either direction
676 // (we choose 'one' since we know that is never null)
677 Link one = link.one();
678 Load egressSrc = services.portStats().load(one.src(), metricType);
679 Load egressDst = services.portStats().load(one.dst(), metricType);
680 link.addLoad(maxLoad(egressSrc, egressDst), metricType == BYTES ? BPS_THRESHOLD : 0);
681 }
682
683 /**
684 * Returns the load with the greatest rate.
685 *
686 * @param a load a
687 * @param b load b
688 * @return the larger of the two
689 */
690 protected Load maxLoad(Load a, Load b) {
691 if (a == null) {
692 return b;
693 }
694 if (b == null) {
695 return a;
696 }
697 return a.rate() > b.rate() ? a : b;
698 }
699
700
701 /**
702 * Subclasses (well, Traffic2Monitor really) can override this method and
703 * process the traffic links before generating the highlights object.
704 * In particular, links that roll up into "synthetic links" between
705 * regions should show aggregated data from the constituent links.
706 * <p>
707 * This default implementation does nothing.
708 *
709 * @param linksWithTraffic link data for all links
710 * @return transformed link data appropriate to the region display
711 */
712 protected Set<TrafficLink> doAggregation(Set<TrafficLink> linksWithTraffic) {
713 return linksWithTraffic;
714 }
715
716
717 // =======================================================================
Simon Hunt1911fe42017-05-02 18:25:58 -0700718 // === Background Task
719
720 // Provides periodic update of traffic information to the client
721 private class TrafficUpdateTask extends TimerTask {
722 @Override
723 public void run() {
724 try {
725 switch (mode) {
726 case ALL_FLOW_TRAFFIC_BYTES:
727 sendAllFlowTraffic();
728 break;
729 case ALL_PORT_TRAFFIC_BIT_PS:
730 sendAllPortTrafficBits();
731 break;
732 case ALL_PORT_TRAFFIC_PKT_PS:
733 sendAllPortTrafficPackets();
734 break;
735 case DEV_LINK_FLOWS:
736 sendDeviceLinkFlows();
737 break;
738 case SELECTED_INTENT:
739 sendSelectedIntentTraffic();
740 break;
Thomas Vachuska2b4de872021-03-30 16:31:34 -0700741 case CUSTOM_TRAFFIC_MONITOR:
742 sendCustomTraffic();
743 break;
Simon Hunt1911fe42017-05-02 18:25:58 -0700744
745 default:
746 // RELATED_INTENTS and IDLE modes should never invoke
747 // the background task, but if they do, they have
748 // nothing to do
749 break;
750 }
751
752 } catch (Exception e) {
753 log.warn("Unable to process traffic task due to {}", e.getMessage());
754 log.warn("Boom!", e);
755 }
756 }
757 }
Simon Hunt1911fe42017-05-02 18:25:58 -0700758}