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