blob: c585d9216cc48d6202abb44b3da1cda87d118b28 [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
Simon Hunt1911fe42017-05-02 18:25:58 -0700295
296 /**
297 * Holds references to services.
298 */
299 protected final ServicesBundle services;
300
301 /**
302 * Current operating mode.
303 */
304 protected Mode mode = Mode.IDLE;
305
306 private final Timer timer;
307 private TimerTask trafficTask = null;
308
309 /**
310 * Constructs the monitor, initializing the task period and
311 * services bundle reference.
312 *
Simon Hunt1911fe42017-05-02 18:25:58 -0700313 * @param servicesBundle bundle of services
Sean Condonadeb7162019-04-13 20:56:14 +0100314 * @param msgHandler Traffic Message handler
Simon Hunt1911fe42017-05-02 18:25:58 -0700315 */
Thomas Vachuskaa5e986d2021-04-06 11:14:09 -0700316 protected TrafficMonitorBase(ServicesBundle servicesBundle,
Sean Condonadeb7162019-04-13 20:56:14 +0100317 TopoologyTrafficMessageHandlerAbstract msgHandler) {
Simon Hunt1911fe42017-05-02 18:25:58 -0700318 this.services = servicesBundle;
Sean Condonadeb7162019-04-13 20:56:14 +0100319 this.msgHandler = msgHandler;
Simon Hunt1911fe42017-05-02 18:25:58 -0700320 timer = new Timer("uiTopo-" + getClass().getSimpleName());
Sean Condonadeb7162019-04-13 20:56:14 +0100321 intentFilter = new TopoIntentFilter(servicesBundle);
Simon Hunt1911fe42017-05-02 18:25:58 -0700322 }
323
324 /**
325 * Initiates monitoring of traffic for a given mode.
326 * This causes a background traffic task to be
327 * scheduled to repeatedly compute and transmit the appropriate traffic
328 * data to the client.
329 * <p>
330 * The monitoring mode is expected to be one of:
331 * <ul>
332 * <li>ALL_FLOW_TRAFFIC_BYTES</li>
333 * <li>ALL_PORT_TRAFFIC_BIT_PS</li>
334 * <li>ALL_PORT_TRAFFIC_PKT_PS</li>
335 * <li>SELECTED_INTENT</li>
336 * </ul>
337 *
338 * @param mode the monitoring mode
339 */
340 public synchronized void monitor(Mode mode) {
341 this.mode = mode;
342
343 switch (mode) {
344
345 case ALL_FLOW_TRAFFIC_BYTES:
346 clearSelection();
347 scheduleTask();
348 sendAllFlowTraffic();
349 break;
350
351 case ALL_PORT_TRAFFIC_BIT_PS:
352 clearSelection();
353 scheduleTask();
354 sendAllPortTrafficBits();
355 break;
356
357 case ALL_PORT_TRAFFIC_PKT_PS:
358 clearSelection();
359 scheduleTask();
360 sendAllPortTrafficPackets();
361 break;
362
363 case SELECTED_INTENT:
364 sendSelectedIntentTraffic();
365 scheduleTask();
366 break;
367
368 default:
369 log.warn("Unexpected call to monitor({})", mode);
370 clearAll();
371 break;
372 }
373 }
374
Thomas Vachuska2b4de872021-03-30 16:31:34 -0700375
376 public synchronized void monitor(int index) {
377 mode = CUSTOM_TRAFFIC_MONITOR;
378 List<UiTopoHighlighterFactory> factories = services.get(UiExtensionService.class)
379 .getTopoHighlighterFactories();
380 if (factories.isEmpty()) {
381 return;
382 }
383
384 UiTopoHighlighterFactory factory = factories.get(index % factories.size());
385 topoHighlighter = factory.newTopoHighlighter();
386 clearSelection();
387 scheduleTask();
388 sendCustomTraffic();
389 }
390
Simon Hunt1911fe42017-05-02 18:25:58 -0700391 /**
Sean Condonadeb7162019-04-13 20:56:14 +0100392 * Monitor for traffic data to be sent back to the web client, under
393 * the given mode, using the given selection of devices and hosts.
394 * In the case of "device link flows", this causes a background traffic
395 * task to be scheduled to repeatedly compute and transmit the appropriate
396 * traffic data to the client. In the case of "related intents", no
397 * repeating task is scheduled.
398 * <p>
399 * The monitoring mode is expected to be one of:
400 * <ul>
401 * <li>DEV_LINK_FLOWS</li>
402 * <li>RELATED_INTENTS</li>
403 * </ul>
404 *
405 * @param mode monitoring mode
406 * @param nodeSelection how to select a node
407 */
408 public synchronized void monitor(Mode mode, NodeSelection nodeSelection) {
409 log.debug("monitor: {} -- {}", mode, nodeSelection);
410 this.mode = mode;
411 this.selectedNodes = nodeSelection;
412
413 switch (mode) {
414 case DEV_LINK_FLOWS:
415 // only care about devices (not hosts)
416 if (selectedNodes.devicesWithHover().isEmpty()) {
417 clearAll();
418 } else {
419 scheduleTask();
420 sendDeviceLinkFlows();
421 }
422 break;
423
424 case RELATED_INTENTS:
425 if (selectedNodes.none()) {
426 clearAll();
427 } else {
428 selectedIntents = new IntentSelection(selectedNodes, intentFilter);
429 if (selectedIntents.none()) {
430 clearAll();
431 } else {
432 sendSelectedIntents();
433 }
434 }
435 break;
436
437 default:
438 log.debug("Unexpected call to monitor({}, {})", mode, nodeSelection);
439 clearAll();
440 break;
441 }
442 }
443
444 /**
445 * Monitor for traffic data to be sent back to the web client, for the
446 * given intent.
447 *
448 * @param intent the intent to monitor
449 */
450 public synchronized void monitor(Intent intent) {
451 log.debug("monitor intent: {}", intent.id());
452 selectedNodes = null;
453 selectedIntents = new IntentSelection(intent);
454 mode = SELECTED_INTENT;
455 scheduleTask();
456 sendSelectedIntentTraffic();
457 }
458
459 /**
Simon Hunt1911fe42017-05-02 18:25:58 -0700460 * Subclass should compile and send appropriate highlights data showing
461 * flow traffic (bytes on links).
462 */
463 protected abstract void sendAllFlowTraffic();
464
465 /**
466 * Subclass should compile and send appropriate highlights data showing
467 * bits per second, as computed using port stats.
468 */
469 protected abstract void sendAllPortTrafficBits();
470
471 /**
472 * Subclass should compile and send appropriate highlights data showing
473 * packets per second, as computed using port stats.
474 */
475 protected abstract void sendAllPortTrafficPackets();
476
477 /**
478 * Subclass should compile and send appropriate highlights data showing
479 * number of flows traversing links for the "selected" device(s).
480 */
481 protected abstract void sendDeviceLinkFlows();
482
483 /**
484 * Subclass should compile and send appropriate highlights data showing
485 * traffic traversing links for the "selected" intent.
486 */
487 protected abstract void sendSelectedIntentTraffic();
488
489 /**
Thomas Vachuska2b4de872021-03-30 16:31:34 -0700490 * Subclass should compile and send appropriate highlights data showing
491 * custom traffic on links.
492 */
493 protected abstract void sendCustomTraffic();
494
495 /**
Simon Hunt1911fe42017-05-02 18:25:58 -0700496 * Subclass should send a "clear highlights" event.
497 */
498 protected abstract void sendClearHighlights();
499
500 /**
501 * Subclasses should clear any selection state.
502 */
503 protected abstract void clearSelection();
504
505 /**
506 * Sets the mode to IDLE, clears the selection, cancels the background
507 * task, and sends a clear highlights event to the client.
508 */
509 protected void clearAll() {
510 this.mode = Mode.IDLE;
511 clearSelection();
512 cancelTask();
513 sendClearHighlights();
514 }
515
516 /**
517 * Schedules the background monitor task to run.
518 */
519 protected synchronized void scheduleTask() {
520 if (trafficTask == null) {
521 log.debug("Starting up background traffic task...");
522 trafficTask = new TrafficUpdateTask();
523 timer.schedule(trafficTask, trafficPeriod, trafficPeriod);
524 } else {
525 log.debug("(traffic task already running)");
526 }
527 }
528
529 /**
530 * Cancels the background monitor task.
531 */
532 protected synchronized void cancelTask() {
533 if (trafficTask != null) {
534 trafficTask.cancel();
535 trafficTask = null;
536 }
537 }
538
539 /**
540 * Stops monitoring. (Invokes {@link #clearAll}, if not idle).
541 */
542 public synchronized void stopMonitoring() {
543 log.debug("STOP monitoring");
544 if (mode != IDLE) {
545 clearAll();
546 }
547 }
548
549
550 // =======================================================================
Simon Hunta7aea842017-05-03 19:42:50 -0700551 // === Methods for computing traffic on links
552
553 /**
554 * Generates a {@link Highlights} object summarizing the traffic on the
555 * network, ready to be transmitted back to the client for display on
556 * the topology view.
557 *
558 * @param type the type of statistics to be displayed
559 * @return highlights, representing links to be labeled/colored
560 */
561 protected Highlights trafficSummary(TrafficLink.StatsType type) {
562 Highlights highlights = new Highlights();
563
564 // TODO: consider whether a map would be better...
565 Set<TrafficLink> linksWithTraffic = computeLinksWithTraffic(type);
566
567 Set<TrafficLink> aggregatedLinks = doAggregation(linksWithTraffic);
568
569 for (TrafficLink tlink : aggregatedLinks) {
570 highlights.add(tlink.highlight(type));
571 }
572 return highlights;
573 }
574
575 /**
576 * Generates a set of "traffic links" encapsulating information about the
577 * traffic on each link (that is deemed to have traffic).
578 *
579 * @param type the type of statistics to be displayed
580 * @return the set of links with traffic
581 */
582 protected Set<TrafficLink> computeLinksWithTraffic(TrafficLink.StatsType type) {
583 TrafficLinkMap linkMap = new TrafficLinkMap();
584 compileLinks(linkMap);
585 addEdgeLinks(linkMap);
586
587 Set<TrafficLink> linksWithTraffic = new HashSet<>();
Simon Hunta7aea842017-05-03 19:42:50 -0700588
589 for (TrafficLink tlink : linkMap.biLinks()) {
590 if (type == TrafficLink.StatsType.FLOW_STATS) {
591 attachFlowLoad(tlink);
592 } else if (type == TrafficLink.StatsType.PORT_STATS) {
593 attachPortLoad(tlink, BYTES);
594 } else if (type == TrafficLink.StatsType.PORT_PACKET_STATS) {
595 attachPortLoad(tlink, PACKETS);
596 }
597
598 // we only want to report on links deemed to have traffic
599 if (tlink.hasTraffic()) {
600 linksWithTraffic.add(tlink);
601 }
602 }
603 return linksWithTraffic;
604 }
605
606 /**
607 * Iterates across the set of links in the topology and generates the
608 * appropriate set of traffic links.
609 *
610 * @param linkMap link map to augment with traffic links
611 */
612 protected void compileLinks(TrafficLinkMap linkMap) {
613 services.link().getLinks().forEach(linkMap::add);
614 }
615
616 /**
617 * Iterates across the set of hosts in the topology and generates the
618 * appropriate set of traffic links for the edge links.
619 *
620 * @param linkMap link map to augment with traffic links
621 */
622 protected void addEdgeLinks(TrafficLinkMap linkMap) {
623 services.host().getHosts().forEach(host -> {
Pier59266bc2018-03-15 12:10:24 -0700624 // Ingress edge links
625 Set<DefaultEdgeLink> edgeLinks = createEdgeLinks(host, true);
626 edgeLinks.forEach(linkMap::add);
627 // Egress edge links
628 edgeLinks = createEdgeLinks(host, false);
629 edgeLinks.forEach(linkMap::add);
Simon Hunta7aea842017-05-03 19:42:50 -0700630 });
631 }
632
633 /**
634 * Processes the given traffic link to attach the "flow load" attributed
635 * to the underlying topology links.
636 *
637 * @param link the traffic link to process
638 */
639 protected void attachFlowLoad(TrafficLink link) {
640 link.addLoad(getLinkFlowLoad(link.one()));
641 link.addLoad(getLinkFlowLoad(link.two()));
642 }
643
644 /**
645 * Returns the load for the given link, as determined by the statistics
646 * service. May return null.
647 *
648 * @param link the link on which to look up the stats
649 * @return the corresponding load (or null)
650 */
651 protected Load getLinkFlowLoad(Link link) {
652 if (link != null && link.src().elementId() instanceof DeviceId) {
653 return services.flowStats().load(link);
654 }
655 return null;
656 }
657
658 /**
659 * Processes the given traffic link to attach the "port load" attributed
660 * to the underlying topology links, for the specified metric type (either
661 * bytes/sec or packets/sec).
662 *
663 * @param link the traffic link to process
664 * @param metricType the metric type (bytes or packets)
665 */
666 protected void attachPortLoad(TrafficLink link, MetricType metricType) {
667 // For bi-directional traffic links, use
668 // the max link rate of either direction
669 // (we choose 'one' since we know that is never null)
670 Link one = link.one();
671 Load egressSrc = services.portStats().load(one.src(), metricType);
672 Load egressDst = services.portStats().load(one.dst(), metricType);
673 link.addLoad(maxLoad(egressSrc, egressDst), metricType == BYTES ? BPS_THRESHOLD : 0);
674 }
675
676 /**
677 * Returns the load with the greatest rate.
678 *
679 * @param a load a
680 * @param b load b
681 * @return the larger of the two
682 */
683 protected Load maxLoad(Load a, Load b) {
684 if (a == null) {
685 return b;
686 }
687 if (b == null) {
688 return a;
689 }
690 return a.rate() > b.rate() ? a : b;
691 }
692
693
694 /**
695 * Subclasses (well, Traffic2Monitor really) can override this method and
696 * process the traffic links before generating the highlights object.
697 * In particular, links that roll up into "synthetic links" between
698 * regions should show aggregated data from the constituent links.
699 * <p>
700 * This default implementation does nothing.
701 *
702 * @param linksWithTraffic link data for all links
703 * @return transformed link data appropriate to the region display
704 */
705 protected Set<TrafficLink> doAggregation(Set<TrafficLink> linksWithTraffic) {
706 return linksWithTraffic;
707 }
708
709
710 // =======================================================================
Simon Hunt1911fe42017-05-02 18:25:58 -0700711 // === Background Task
712
713 // Provides periodic update of traffic information to the client
714 private class TrafficUpdateTask extends TimerTask {
715 @Override
716 public void run() {
717 try {
718 switch (mode) {
719 case ALL_FLOW_TRAFFIC_BYTES:
720 sendAllFlowTraffic();
721 break;
722 case ALL_PORT_TRAFFIC_BIT_PS:
723 sendAllPortTrafficBits();
724 break;
725 case ALL_PORT_TRAFFIC_PKT_PS:
726 sendAllPortTrafficPackets();
727 break;
728 case DEV_LINK_FLOWS:
729 sendDeviceLinkFlows();
730 break;
731 case SELECTED_INTENT:
732 sendSelectedIntentTraffic();
733 break;
Thomas Vachuska2b4de872021-03-30 16:31:34 -0700734 case CUSTOM_TRAFFIC_MONITOR:
735 sendCustomTraffic();
736 break;
Simon Hunt1911fe42017-05-02 18:25:58 -0700737
738 default:
739 // RELATED_INTENTS and IDLE modes should never invoke
740 // the background task, but if they do, they have
741 // nothing to do
742 break;
743 }
744
745 } catch (Exception e) {
746 log.warn("Unable to process traffic task due to {}", e.getMessage());
747 log.warn("Boom!", e);
748 }
749 }
750 }
Simon Hunt1911fe42017-05-02 18:25:58 -0700751}