Thomas Vachuska
Committed by Gerrit Code Review

GUI fixes/breaks.

Change-Id: Ic5c8b087cc32506162153b2756a677c7d9e3bdd7
...@@ -66,7 +66,7 @@ import static org.slf4j.LoggerFactory.getLogger; ...@@ -66,7 +66,7 @@ import static org.slf4j.LoggerFactory.getLogger;
66 public class DefaultTopologyProvider extends AbstractProvider 66 public class DefaultTopologyProvider extends AbstractProvider
67 implements TopologyProvider { 67 implements TopologyProvider {
68 68
69 - private static final int MAX_THREADS = 8; 69 + private static final int MAX_THREADS = 32;
70 private static final int DEFAULT_MAX_EVENTS = 1000; 70 private static final int DEFAULT_MAX_EVENTS = 1000;
71 private static final int DEFAULT_MAX_IDLE_MS = 10; 71 private static final int DEFAULT_MAX_IDLE_MS = 10;
72 private static final int DEFAULT_MAX_BATCH_MS = 50; 72 private static final int DEFAULT_MAX_BATCH_MS = 50;
......
1 +
2 +from mininet.topo import Topo
3 +
4 +class MyTopo( Topo ):
5 + "10 'floating' switch topology"
6 +
7 + def __init__( self ):
8 + # Initialize topology
9 + Topo.__init__( self )
10 +
11 + sw_list = []
12 + swC = self.addSwitch('sc', dpid = 'ffffffff00000001')
13 +
14 + for i in range(1, 201):
15 + switch=self.addSwitch('s'+str(i), dpid = str(i).zfill(16))
16 + self.addLink(switch,swC)
17 +
18 + sw_list.append(switch)
19 +
20 +topos = { 'mytopo': ( lambda: MyTopo() ) }
...@@ -33,6 +33,7 @@ import org.onlab.onos.net.intent.PathIntent; ...@@ -33,6 +33,7 @@ import org.onlab.onos.net.intent.PathIntent;
33 import org.onlab.onos.net.intent.PointToPointIntent; 33 import org.onlab.onos.net.intent.PointToPointIntent;
34 import org.onlab.onos.net.link.LinkService; 34 import org.onlab.onos.net.link.LinkService;
35 35
36 +import java.util.ArrayList;
36 import java.util.HashSet; 37 import java.util.HashSet;
37 import java.util.List; 38 import java.util.List;
38 import java.util.Set; 39 import java.util.Set;
...@@ -73,15 +74,17 @@ public class TopologyViewIntentFilter { ...@@ -73,15 +74,17 @@ public class TopologyViewIntentFilter {
73 * 74 *
74 * @param hosts set of hosts to query by 75 * @param hosts set of hosts to query by
75 * @param devices set of devices to query by 76 * @param devices set of devices to query by
77 + * @param sourceIntents collection of intents to search
76 * @return set of intents that 'match' all hosts and devices given 78 * @return set of intents that 'match' all hosts and devices given
77 */ 79 */
78 - Set<Intent> findPathIntents(Set<Host> hosts, Set<Device> devices) { 80 + List<Intent> findPathIntents(Set<Host> hosts, Set<Device> devices,
81 + Iterable<Intent> sourceIntents) {
79 // Derive from this the set of edge connect points. 82 // Derive from this the set of edge connect points.
80 Set<ConnectPoint> edgePoints = getEdgePoints(hosts); 83 Set<ConnectPoint> edgePoints = getEdgePoints(hosts);
81 84
82 // Iterate over all intents and produce a set that contains only those 85 // Iterate over all intents and produce a set that contains only those
83 // intents that target all selected hosts or derived edge connect points. 86 // intents that target all selected hosts or derived edge connect points.
84 - return getIntents(hosts, devices, edgePoints); 87 + return getIntents(hosts, devices, edgePoints, sourceIntents);
85 } 88 }
86 89
87 90
...@@ -94,10 +97,11 @@ public class TopologyViewIntentFilter { ...@@ -94,10 +97,11 @@ public class TopologyViewIntentFilter {
94 return edgePoints; 97 return edgePoints;
95 } 98 }
96 99
97 - // Produces a set of intents that target all selected hosts, devices or connect points. 100 + // Produces a list of intents that target all selected hosts, devices or connect points.
98 - private Set<Intent> getIntents(Set<Host> hosts, Set<Device> devices, 101 + private List<Intent> getIntents(Set<Host> hosts, Set<Device> devices,
99 - Set<ConnectPoint> edgePoints) { 102 + Set<ConnectPoint> edgePoints,
100 - Set<Intent> intents = new HashSet<>(); 103 + Iterable<Intent> sourceIntents) {
104 + List<Intent> intents = new ArrayList<>();
101 if (hosts.isEmpty() && devices.isEmpty()) { 105 if (hosts.isEmpty() && devices.isEmpty()) {
102 return intents; 106 return intents;
103 } 107 }
...@@ -105,7 +109,7 @@ public class TopologyViewIntentFilter { ...@@ -105,7 +109,7 @@ public class TopologyViewIntentFilter {
105 Set<OpticalConnectivityIntent> opticalIntents = new HashSet<>(); 109 Set<OpticalConnectivityIntent> opticalIntents = new HashSet<>();
106 110
107 // Search through all intents and see if they are relevant to our search. 111 // Search through all intents and see if they are relevant to our search.
108 - for (Intent intent : intentService.getIntents()) { 112 + for (Intent intent : sourceIntents) {
109 if (intentService.getIntentState(intent.id()) == INSTALLED) { 113 if (intentService.getIntentState(intent.id()) == INSTALLED) {
110 boolean isRelevant = false; 114 boolean isRelevant = false;
111 if (intent instanceof HostToHostIntent) { 115 if (intent instanceof HostToHostIntent) {
...@@ -140,7 +144,7 @@ public class TopologyViewIntentFilter { ...@@ -140,7 +144,7 @@ public class TopologyViewIntentFilter {
140 } 144 }
141 145
142 // Indicates whether the specified intent involves all of the given hosts. 146 // Indicates whether the specified intent involves all of the given hosts.
143 - private boolean isIntentRelevantToHosts(HostToHostIntent intent, Set<Host> hosts) { 147 + private boolean isIntentRelevantToHosts(HostToHostIntent intent, Iterable<Host> hosts) {
144 for (Host host : hosts) { 148 for (Host host : hosts) {
145 HostId id = host.id(); 149 HostId id = host.id();
146 // Bail if intent does not involve this host. 150 // Bail if intent does not involve this host.
...@@ -152,7 +156,7 @@ public class TopologyViewIntentFilter { ...@@ -152,7 +156,7 @@ public class TopologyViewIntentFilter {
152 } 156 }
153 157
154 // Indicates whether the specified intent involves all of the given devices. 158 // Indicates whether the specified intent involves all of the given devices.
155 - private boolean isIntentRelevantToDevices(Intent intent, Set<Device> devices) { 159 + private boolean isIntentRelevantToDevices(Intent intent, Iterable<Device> devices) {
156 List<Intent> installables = intentService.getInstallableIntents(intent.id()); 160 List<Intent> installables = intentService.getInstallableIntents(intent.id());
157 for (Device device : devices) { 161 for (Device device : devices) {
158 if (!isIntentRelevantToDevice(installables, device)) { 162 if (!isIntentRelevantToDevice(installables, device)) {
...@@ -192,7 +196,8 @@ public class TopologyViewIntentFilter { ...@@ -192,7 +196,8 @@ public class TopologyViewIntentFilter {
192 return false; 196 return false;
193 } 197 }
194 198
195 - private boolean isIntentRelevant(PointToPointIntent intent, Set<ConnectPoint> edgePoints) { 199 + private boolean isIntentRelevant(PointToPointIntent intent,
200 + Iterable<ConnectPoint> edgePoints) {
196 for (ConnectPoint point : edgePoints) { 201 for (ConnectPoint point : edgePoints) {
197 // Bail if intent does not involve this edge point. 202 // Bail if intent does not involve this edge point.
198 if (!point.equals(intent.egressPoint()) && 203 if (!point.equals(intent.egressPoint()) &&
...@@ -205,7 +210,7 @@ public class TopologyViewIntentFilter { ...@@ -205,7 +210,7 @@ public class TopologyViewIntentFilter {
205 210
206 // Indicates whether the specified intent involves all of the given edge points. 211 // Indicates whether the specified intent involves all of the given edge points.
207 private boolean isIntentRelevant(MultiPointToSinglePointIntent intent, 212 private boolean isIntentRelevant(MultiPointToSinglePointIntent intent,
208 - Set<ConnectPoint> edgePoints) { 213 + Iterable<ConnectPoint> edgePoints) {
209 for (ConnectPoint point : edgePoints) { 214 for (ConnectPoint point : edgePoints) {
210 // Bail if intent does not involve this edge point. 215 // Bail if intent does not involve this edge point.
211 if (!point.equals(intent.egressPoint()) && 216 if (!point.equals(intent.egressPoint()) &&
...@@ -218,7 +223,7 @@ public class TopologyViewIntentFilter { ...@@ -218,7 +223,7 @@ public class TopologyViewIntentFilter {
218 223
219 // Indicates whether the specified intent involves all of the given edge points. 224 // Indicates whether the specified intent involves all of the given edge points.
220 private boolean isIntentRelevant(OpticalConnectivityIntent opticalIntent, 225 private boolean isIntentRelevant(OpticalConnectivityIntent opticalIntent,
221 - Set<Intent> intents) { 226 + Iterable<Intent> intents) {
222 Link ccSrc = getFirstLink(opticalIntent.getSrc(), false); 227 Link ccSrc = getFirstLink(opticalIntent.getSrc(), false);
223 Link ccDst = getFirstLink(opticalIntent.getDst(), true); 228 Link ccDst = getFirstLink(opticalIntent.getDst(), true);
224 229
......
...@@ -676,13 +676,16 @@ public abstract class TopologyViewMessages { ...@@ -676,13 +676,16 @@ public abstract class TopologyViewMessages {
676 List<Intent> installables = intentService.getInstallableIntents(intent.id()); 676 List<Intent> installables = intentService.getInstallableIntents(intent.id());
677 if (installables != null) { 677 if (installables != null) {
678 for (Intent installable : installables) { 678 for (Intent installable : installables) {
679 - String cls = isOptical ? trafficClass.type + " optical" : trafficClass.type; 679 + String type = isOptical ? trafficClass.type + " optical" : trafficClass.type;
680 if (installable instanceof PathIntent) { 680 if (installable instanceof PathIntent) {
681 - classifyLinks(cls, biLinks, ((PathIntent) installable).path().links()); 681 + classifyLinks(type, biLinks, trafficClass.showTraffic,
682 + ((PathIntent) installable).path().links());
682 } else if (installable instanceof LinkCollectionIntent) { 683 } else if (installable instanceof LinkCollectionIntent) {
683 - classifyLinks(cls, biLinks, ((LinkCollectionIntent) installable).links()); 684 + classifyLinks(type, biLinks, trafficClass.showTraffic,
685 + ((LinkCollectionIntent) installable).links());
684 } else if (installable instanceof OpticalPathIntent) { 686 } else if (installable instanceof OpticalPathIntent) {
685 - classifyLinks(cls, biLinks, ((OpticalPathIntent) installable).path().links()); 687 + classifyLinks(type, biLinks, trafficClass.showTraffic,
688 + ((OpticalPathIntent) installable).path().links());
686 } 689 }
687 } 690 }
688 } 691 }
...@@ -695,12 +698,14 @@ public abstract class TopologyViewMessages { ...@@ -695,12 +698,14 @@ public abstract class TopologyViewMessages {
695 // Adds the link segments (path or tree) associated with the specified 698 // Adds the link segments (path or tree) associated with the specified
696 // connectivity intent 699 // connectivity intent
697 private void classifyLinks(String type, Map<LinkKey, BiLink> biLinks, 700 private void classifyLinks(String type, Map<LinkKey, BiLink> biLinks,
698 - Iterable<Link> links) { 701 + boolean showTraffic, Iterable<Link> links) {
699 if (links != null) { 702 if (links != null) {
700 for (Link link : links) { 703 for (Link link : links) {
701 BiLink biLink = addLink(biLinks, link); 704 BiLink biLink = addLink(biLinks, link);
702 if (isInfrastructureEgress(link)) { 705 if (isInfrastructureEgress(link)) {
706 + if (showTraffic) {
703 biLink.addLoad(statService.load(link)); 707 biLink.addLoad(statService.load(link));
708 + }
704 biLink.addClass(type); 709 biLink.addClass(type);
705 } 710 }
706 } 711 }
...@@ -862,12 +867,18 @@ public abstract class TopologyViewMessages { ...@@ -862,12 +867,18 @@ public abstract class TopologyViewMessages {
862 867
863 // Auxiliary carrier of data for requesting traffic message. 868 // Auxiliary carrier of data for requesting traffic message.
864 protected class TrafficClass { 869 protected class TrafficClass {
870 + public final boolean showTraffic;
865 public final String type; 871 public final String type;
866 - public final Set<Intent> intents; 872 + public final Iterable<Intent> intents;
873 +
874 + TrafficClass(String type, Iterable<Intent> intents) {
875 + this(type, intents, false);
876 + }
867 877
868 - TrafficClass(String type, Set<Intent> intents) { 878 + TrafficClass(String type, Iterable<Intent> intents, boolean showTraffic) {
869 this.type = type; 879 this.type = type;
870 this.intents = intents; 880 this.intents = intents;
881 + this.showTraffic = showTraffic;
871 } 882 }
872 } 883 }
873 884
......
...@@ -90,7 +90,8 @@ public class TopologyViewWebSocket ...@@ -90,7 +90,8 @@ public class TopologyViewWebSocket
90 90
91 private static final String APP_ID = "org.onlab.onos.gui"; 91 private static final String APP_ID = "org.onlab.onos.gui";
92 92
93 - private static final long TRAFFIC_FREQUENCY_SEC = 2000; 93 + private static final long TRAFFIC_FREQUENCY = 2000;
94 + private static final long SUMMARY_FREQUENCY = 30000;
94 95
95 private static final Comparator<? super ControllerNode> NODE_COMPARATOR = 96 private static final Comparator<? super ControllerNode> NODE_COMPARATOR =
96 new Comparator<ControllerNode>() { 97 new Comparator<ControllerNode>() {
...@@ -103,9 +104,9 @@ public class TopologyViewWebSocket ...@@ -103,9 +104,9 @@ public class TopologyViewWebSocket
103 104
104 private final Timer timer = new Timer("topology-view"); 105 private final Timer timer = new Timer("topology-view");
105 106
106 - private static final int MAX_EVENTS = 500; 107 + private static final int MAX_EVENTS = 1000;
107 - private static final int MAX_BATCH_MS = 1000; 108 + private static final int MAX_BATCH_MS = 5000;
108 - private static final int MAX_IDLE_MS = 500; 109 + private static final int MAX_IDLE_MS = 1000;
109 110
110 private final ApplicationId appId; 111 private final ApplicationId appId;
111 112
...@@ -122,15 +123,23 @@ public class TopologyViewWebSocket ...@@ -122,15 +123,23 @@ public class TopologyViewWebSocket
122 123
123 private final EventAccumulator eventAccummulator = new InternalEventAccummulator(); 124 private final EventAccumulator eventAccummulator = new InternalEventAccummulator();
124 125
125 - private boolean summaryEnabled = true;
126 private TimerTask trafficTask; 126 private TimerTask trafficTask;
127 private ObjectNode trafficEvent; 127 private ObjectNode trafficEvent;
128 128
129 + private TimerTask summaryTask;
130 + private ObjectNode summaryEvent;
131 +
129 private long lastActive = System.currentTimeMillis(); 132 private long lastActive = System.currentTimeMillis();
130 private boolean listenersRemoved = false; 133 private boolean listenersRemoved = false;
131 134
132 private TopologyViewIntentFilter intentFilter; 135 private TopologyViewIntentFilter intentFilter;
133 136
137 + // Current selection context
138 + private Set<Host> selectedHosts;
139 + private Set<Device> selectedDevices;
140 + private List<Intent> selectedIntents;
141 + private int currentIntentIndex = -1;
142 +
134 /** 143 /**
135 * Creates a new web-socket for serving data to GUI topology view. 144 * Creates a new web-socket for serving data to GUI topology view.
136 * 145 *
...@@ -204,7 +213,6 @@ public class TopologyViewWebSocket ...@@ -204,7 +213,6 @@ public class TopologyViewWebSocket
204 processMessage((ObjectNode) mapper.reader().readTree(data)); 213 processMessage((ObjectNode) mapper.reader().readTree(data));
205 } catch (Exception e) { 214 } catch (Exception e) {
206 log.warn("Unable to parse GUI request {} due to {}", data, e); 215 log.warn("Unable to parse GUI request {} due to {}", data, e);
207 - log.warn("Boom!!!!", e);
208 } 216 }
209 } 217 }
210 218
...@@ -221,19 +229,29 @@ public class TopologyViewWebSocket ...@@ -221,19 +229,29 @@ public class TopologyViewWebSocket
221 } else if (type.equals("addMultiSourceIntent")) { 229 } else if (type.equals("addMultiSourceIntent")) {
222 createMultiSourceIntent(event); 230 createMultiSourceIntent(event);
223 231
224 - } else if (type.equals("requestTraffic")) { 232 + } else if (type.equals("requestRelatedIntents")) {
225 - requestTraffic(event); 233 + requestRelatedIntents(event);
234 + } else if (type.equals("requestNextRelatedIntent")) {
235 + requestNextRelatedIntent(event);
236 + } else if (type.equals("requestSelectedIntentTraffic")) {
237 + requestSelectedIntentTraffic(event);
238 +
226 } else if (type.equals("requestAllTraffic")) { 239 } else if (type.equals("requestAllTraffic")) {
227 requestAllTraffic(event); 240 requestAllTraffic(event);
241 + startTrafficMonitoring(event);
242 +
228 } else if (type.equals("requestDeviceLinkFlows")) { 243 } else if (type.equals("requestDeviceLinkFlows")) {
229 requestDeviceLinkFlows(event); 244 requestDeviceLinkFlows(event);
245 + startTrafficMonitoring(event);
246 +
230 } else if (type.equals("cancelTraffic")) { 247 } else if (type.equals("cancelTraffic")) {
231 cancelTraffic(event); 248 cancelTraffic(event);
232 249
233 } else if (type.equals("requestSummary")) { 250 } else if (type.equals("requestSummary")) {
234 requestSummary(event); 251 requestSummary(event);
252 + startSummaryMonitoring(event);
235 } else if (type.equals("cancelSummary")) { 253 } else if (type.equals("cancelSummary")) {
236 - cancelSummary(event); 254 + stopSummaryMonitoring();
237 255
238 } else if (type.equals("equalizeMasters")) { 256 } else if (type.equals("equalizeMasters")) {
239 equalizeMasters(event); 257 equalizeMasters(event);
...@@ -324,8 +342,9 @@ public class TopologyViewWebSocket ...@@ -324,8 +342,9 @@ public class TopologyViewWebSocket
324 new HostToHostIntent(appId, one, two, 342 new HostToHostIntent(appId, one, two,
325 DefaultTrafficSelector.builder().build(), 343 DefaultTrafficSelector.builder().build(),
326 DefaultTrafficTreatment.builder().build()); 344 DefaultTrafficTreatment.builder().build());
327 - startMonitoring(event); 345 +
328 intentService.submit(intent); 346 intentService.submit(intent);
347 + startMonitoringIntent(event, intent);
329 } 348 }
330 349
331 // Creates multi-source-to-single-dest intent. 350 // Creates multi-source-to-single-dest intent.
...@@ -348,10 +367,24 @@ public class TopologyViewWebSocket ...@@ -348,10 +367,24 @@ public class TopologyViewWebSocket
348 MultiPointToSinglePointIntent intent = 367 MultiPointToSinglePointIntent intent =
349 new MultiPointToSinglePointIntent(appId, selector, treatment, 368 new MultiPointToSinglePointIntent(appId, selector, treatment,
350 ingressPoints, dstHost.location()); 369 ingressPoints, dstHost.location());
351 - startMonitoring(event); 370 +
352 intentService.submit(intent); 371 intentService.submit(intent);
372 + startMonitoringIntent(event, intent);
353 } 373 }
354 374
375 +
376 + private synchronized void startMonitoringIntent(ObjectNode event, Intent intent) {
377 + selectedHosts = new HashSet<>();
378 + selectedDevices = new HashSet<>();
379 + selectedIntents = new ArrayList<>();
380 + selectedIntents.add(intent);
381 + currentIntentIndex = -1;
382 + requestNextRelatedIntent(event);
383 + requestSelectedIntentTraffic(event);
384 + }
385 +
386 +
387 +
355 private Set<ConnectPoint> getHostLocations(Set<HostId> hostIds) { 388 private Set<ConnectPoint> getHostLocations(Set<HostId> hostIds) {
356 Set<ConnectPoint> points = new HashSet<>(); 389 Set<ConnectPoint> points = new HashSet<>();
357 for (HostId hostId : hostIds) { 390 for (HostId hostId : hostIds) {
...@@ -374,17 +407,15 @@ public class TopologyViewWebSocket ...@@ -374,17 +407,15 @@ public class TopologyViewWebSocket
374 } 407 }
375 408
376 409
377 - private synchronized long startMonitoring(ObjectNode event) { 410 + private synchronized long startTrafficMonitoring(ObjectNode event) {
378 - if (trafficTask != null) { 411 + stopTrafficMonitoring();
379 - stopMonitoring();
380 - }
381 trafficEvent = event; 412 trafficEvent = event;
382 trafficTask = new TrafficMonitor(); 413 trafficTask = new TrafficMonitor();
383 - timer.schedule(trafficTask, TRAFFIC_FREQUENCY_SEC, TRAFFIC_FREQUENCY_SEC); 414 + timer.schedule(trafficTask, TRAFFIC_FREQUENCY, TRAFFIC_FREQUENCY);
384 return number(event, "sid"); 415 return number(event, "sid");
385 } 416 }
386 417
387 - private synchronized void stopMonitoring() { 418 + private synchronized void stopTrafficMonitoring() {
388 if (trafficTask != null) { 419 if (trafficTask != null) {
389 trafficTask.cancel(); 420 trafficTask.cancel();
390 trafficTask = null; 421 trafficTask = null;
...@@ -394,13 +425,13 @@ public class TopologyViewWebSocket ...@@ -394,13 +425,13 @@ public class TopologyViewWebSocket
394 425
395 // Subscribes for host traffic messages. 426 // Subscribes for host traffic messages.
396 private synchronized void requestAllTraffic(ObjectNode event) { 427 private synchronized void requestAllTraffic(ObjectNode event) {
397 - long sid = startMonitoring(event); 428 + long sid = startTrafficMonitoring(event);
398 sendMessage(trafficSummaryMessage(sid)); 429 sendMessage(trafficSummaryMessage(sid));
399 } 430 }
400 431
401 private void requestDeviceLinkFlows(ObjectNode event) { 432 private void requestDeviceLinkFlows(ObjectNode event) {
402 ObjectNode payload = payload(event); 433 ObjectNode payload = payload(event);
403 - long sid = startMonitoring(event); 434 + long sid = startTrafficMonitoring(event);
404 435
405 // Get the set of selected hosts and their intents. 436 // Get the set of selected hosts and their intents.
406 ArrayNode ids = (ArrayNode) payload.path("ids"); 437 ArrayNode ids = (ArrayNode) payload.path("ids");
...@@ -416,58 +447,122 @@ public class TopologyViewWebSocket ...@@ -416,58 +447,122 @@ public class TopologyViewWebSocket
416 } 447 }
417 448
418 449
419 - // Subscribes for host traffic messages. 450 + // Requests related intents message.
420 - private synchronized void requestTraffic(ObjectNode event) { 451 + private synchronized void requestRelatedIntents(ObjectNode event) {
421 ObjectNode payload = payload(event); 452 ObjectNode payload = payload(event);
422 if (!payload.has("ids")) { 453 if (!payload.has("ids")) {
423 return; 454 return;
424 } 455 }
425 456
426 - long sid = startMonitoring(event); 457 + long sid = number(event, "sid");
458 +
459 + // Cancel any other traffic monitoring mode.
460 + stopTrafficMonitoring();
427 461
462 + String hover = string(payload, "hover");
463 + if (haveSelectedIntents()) {
428 // Get the set of selected hosts and their intents. 464 // Get the set of selected hosts and their intents.
429 ArrayNode ids = (ArrayNode) payload.path("ids"); 465 ArrayNode ids = (ArrayNode) payload.path("ids");
430 - Set<Host> hosts = getHosts(ids); 466 + selectedHosts = getHosts(ids);
431 - Set<Device> devices = getDevices(ids); 467 + selectedDevices = getDevices(ids);
432 - Set<Intent> intents = intentFilter.findPathIntents(hosts, devices); 468 + selectedIntents = intentFilter.findPathIntents(selectedHosts, selectedDevices,
469 + intentService.getIntents());
470 + currentIntentIndex = -1;
471 +
472 + // Send a message to highlight all links of all monitored intents.
473 + sendMessage(trafficMessage(sid, new TrafficClass("primary", selectedIntents)));
474 + }
433 475
434 - // If there is a hover node, include it in the hosts and find intents.
435 - String hover = string(payload, "hover");
436 - Set<Intent> hoverIntents;
437 if (!isNullOrEmpty(hover)) { 476 if (!isNullOrEmpty(hover)) {
438 - addHover(hosts, devices, hover); 477 + // If there is a hover node, include it in the selection and find intents.
439 - hoverIntents = intentFilter.findPathIntents(hosts, devices); 478 + processExtendedSelection(sid, hover);
440 - intents.removeAll(hoverIntents); 479 + }
480 + }
481 +
482 + private boolean haveSelectedIntents() {
483 + return selectedIntents != null && !selectedIntents.isEmpty();
484 + }
485 +
486 + private void processExtendedSelection(long sid, String hover) {
487 + Set<Host> hoverSelHosts = new HashSet<>(selectedHosts);
488 + Set<Device> hoverSelDevices = new HashSet<>(selectedDevices);
489 + addHover(hoverSelHosts, hoverSelDevices, hover);
441 490
442 - // Send an initial message to highlight all links of all monitored intents. 491 + List<Intent> primary =
443 - sendMessage(trafficMessage(sid, 492 + intentFilter.findPathIntents(hoverSelHosts, hoverSelDevices,
444 - new TrafficClass("primary", hoverIntents), 493 + selectedIntents);
445 - new TrafficClass("secondary", intents))); 494 + Set<Intent> secondary = new HashSet<>(selectedIntents);
495 + secondary.removeAll(primary);
446 496
447 - } else { 497 + // Send a message to highlight all links of all monitored intents.
448 - // Send an initial message to highlight all links of all monitored intents. 498 + sendMessage(trafficMessage(sid, new TrafficClass("primary", primary),
449 - sendMessage(trafficMessage(sid, new TrafficClass("primary", intents))); 499 + new TrafficClass("secondary", secondary)));
500 + }
501 +
502 + // Requests next of the related intents.
503 + private void requestNextRelatedIntent(ObjectNode event) {
504 + if (haveSelectedIntents()) {
505 + currentIntentIndex = (currentIntentIndex + 1) % selectedIntents.size();
506 + Intent selectedIntent = selectedIntents.get(currentIntentIndex);
507 + log.info("Requested next intent {}", selectedIntent.id());
508 +
509 + Set<Intent> primary = new HashSet<>();
510 + primary.add(selectedIntent);
511 +
512 + Set<Intent> secondary = new HashSet<>(selectedIntents);
513 + secondary.remove(selectedIntent);
514 +
515 + // Send a message to highlight all links of the selected intent.
516 + sendMessage(trafficMessage(number(event, "sid"),
517 + new TrafficClass("primary", primary),
518 + new TrafficClass("secondary", secondary)));
519 + }
520 + }
521 +
522 + // Requests monitoring of traffic for the selected intent.
523 + private void requestSelectedIntentTraffic(ObjectNode event) {
524 + if (haveSelectedIntents()) {
525 + Intent selectedIntent = selectedIntents.get(currentIntentIndex);
526 + log.info("Requested traffic for selected {}", selectedIntent.id());
527 +
528 + Set<Intent> primary = new HashSet<>();
529 + primary.add(selectedIntent);
530 +
531 + // Send a message to highlight all links of the selected intent.
532 + sendMessage(trafficMessage(number(event, "sid"),
533 + new TrafficClass("primary", primary, true)));
450 } 534 }
451 } 535 }
452 536
453 // Cancels sending traffic messages. 537 // Cancels sending traffic messages.
454 private void cancelTraffic(ObjectNode event) { 538 private void cancelTraffic(ObjectNode event) {
539 + selectedIntents = null;
455 sendMessage(trafficMessage(number(event, "sid"))); 540 sendMessage(trafficMessage(number(event, "sid")));
456 - stopMonitoring(); 541 + stopTrafficMonitoring();
542 + }
543 +
544 +
545 + private synchronized long startSummaryMonitoring(ObjectNode event) {
546 + stopSummaryMonitoring();
547 + summaryEvent = event;
548 + summaryTask = new SummaryMonitor();
549 + timer.schedule(summaryTask, SUMMARY_FREQUENCY, SUMMARY_FREQUENCY);
550 + return number(event, "sid");
457 } 551 }
458 552
553 + private synchronized void stopSummaryMonitoring() {
554 + if (summaryEvent != null) {
555 + summaryTask.cancel();
556 + summaryTask = null;
557 + summaryEvent = null;
558 + }
559 + }
459 560
460 // Subscribes for summary messages. 561 // Subscribes for summary messages.
461 private synchronized void requestSummary(ObjectNode event) { 562 private synchronized void requestSummary(ObjectNode event) {
462 - summaryEnabled = true;
463 sendMessage(summmaryMessage(number(event, "sid"))); 563 sendMessage(summmaryMessage(number(event, "sid")));
464 } 564 }
465 565
466 - // Cancels sending summary messages.
467 - private synchronized void cancelSummary(ObjectNode event) {
468 - summaryEnabled = false;
469 - }
470 -
471 566
472 // Forces mastership role rebalancing. 567 // Forces mastership role rebalancing.
473 private void equalizeMasters(ObjectNode event) { 568 private void equalizeMasters(ObjectNode event) {
...@@ -550,7 +645,7 @@ public class TopologyViewWebSocket ...@@ -550,7 +645,7 @@ public class TopologyViewWebSocket
550 @Override 645 @Override
551 public void event(IntentEvent event) { 646 public void event(IntentEvent event) {
552 if (trafficEvent != null) { 647 if (trafficEvent != null) {
553 - requestTraffic(trafficEvent); 648 + requestSelectedIntentTraffic(trafficEvent);
554 } 649 }
555 eventAccummulator.add(event); 650 eventAccummulator.add(event);
556 } 651 }
...@@ -564,6 +659,7 @@ public class TopologyViewWebSocket ...@@ -564,6 +659,7 @@ public class TopologyViewWebSocket
564 } 659 }
565 } 660 }
566 661
662 + // Periodic update of the traffic information
567 private class TrafficMonitor extends TimerTask { 663 private class TrafficMonitor extends TimerTask {
568 @Override 664 @Override
569 public void run() { 665 public void run() {
...@@ -574,8 +670,8 @@ public class TopologyViewWebSocket ...@@ -574,8 +670,8 @@ public class TopologyViewWebSocket
574 requestAllTraffic(trafficEvent); 670 requestAllTraffic(trafficEvent);
575 } else if (type.equals("requestDeviceLinkFlows")) { 671 } else if (type.equals("requestDeviceLinkFlows")) {
576 requestDeviceLinkFlows(trafficEvent); 672 requestDeviceLinkFlows(trafficEvent);
577 - } else { 673 + } else if (type.equals("requestSelectedIntentTraffic")) {
578 - requestTraffic(trafficEvent); 674 + requestSelectedIntentTraffic(trafficEvent);
579 } 675 }
580 } 676 }
581 } catch (Exception e) { 677 } catch (Exception e) {
...@@ -585,6 +681,20 @@ public class TopologyViewWebSocket ...@@ -585,6 +681,20 @@ public class TopologyViewWebSocket
585 } 681 }
586 } 682 }
587 683
684 + // Periodic update of the summary information
685 + private class SummaryMonitor extends TimerTask {
686 + @Override
687 + public void run() {
688 + try {
689 + if (summaryEvent != null) {
690 + requestSummary(summaryEvent);
691 + }
692 + } catch (Exception e) {
693 + log.warn("Unable to handle summary request due to {}", e.getMessage());
694 + }
695 + }
696 + }
697 +
588 // Accumulates events to drive methodic update of the summary pane. 698 // Accumulates events to drive methodic update of the summary pane.
589 private class InternalEventAccummulator extends AbstractEventAccumulator { 699 private class InternalEventAccummulator extends AbstractEventAccumulator {
590 protected InternalEventAccummulator() { 700 protected InternalEventAccummulator() {
...@@ -594,7 +704,7 @@ public class TopologyViewWebSocket ...@@ -594,7 +704,7 @@ public class TopologyViewWebSocket
594 @Override 704 @Override
595 public void processEvents(List<Event> events) { 705 public void processEvents(List<Event> events) {
596 try { 706 try {
597 - if (summaryEnabled) { 707 + if (summaryEvent != null) {
598 sendMessage(summmaryMessage(0)); 708 sendMessage(summmaryMessage(0));
599 } 709 }
600 } catch (Exception e) { 710 } catch (Exception e) {
......
...@@ -145,8 +145,10 @@ ...@@ -145,8 +145,10 @@
145 P: togglePorts, 145 P: togglePorts,
146 U: [unpin, 'Unpin node (hover mouse over)'], 146 U: [unpin, 'Unpin node (hover mouse over)'],
147 R: [resetPanZoom, 'Reset pan / zoom'], 147 R: [resetPanZoom, 'Reset pan / zoom'],
148 - V: [showTrafficAction, 'Show related traffic'], 148 + V: [showRelatedIntentsAction, 'Show all related intents'],
149 - A: [showAllTrafficAction, 'Show all traffic'], 149 + N: [showNextIntentAction, 'Show next related intent'],
150 + W: [showSelectedIntentTrafficAction, 'Monitor traffic of selected intent'],
151 + A: [showAllTrafficAction, 'Monitor all traffic'],
150 F: [showDeviceLinkFlowsAction, 'Show device link flows'], 152 F: [showDeviceLinkFlowsAction, 'Show device link flows'],
151 X: [toggleNodeLock, 'Lock / unlock node positions'], 153 X: [toggleNodeLock, 'Lock / unlock node positions'],
152 Z: [toggleOblique, 'Toggle oblique view (Experimental)'], 154 Z: [toggleOblique, 'Toggle oblique view (Experimental)'],
...@@ -209,10 +211,11 @@ ...@@ -209,10 +211,11 @@
209 oblique = false; 211 oblique = false;
210 212
211 // constants 213 // constants
212 - var hoverModeAll = 1, 214 + var hoverModeNone = 0,
215 + hoverModeAll = 1,
213 hoverModeFlows = 2, 216 hoverModeFlows = 2,
214 hoverModeIntents = 3, 217 hoverModeIntents = 3,
215 - hoverMode = hoverModeFlows; 218 + hoverMode = hoverModeNone;
216 219
217 // D3 selections 220 // D3 selections
218 var svg, 221 var svg,
...@@ -394,7 +397,7 @@ ...@@ -394,7 +397,7 @@
394 cancelSummary(); 397 cancelSummary();
395 stopAntTimer(); 398 stopAntTimer();
396 } else { 399 } else {
397 - hoverMode = hoverModeFlows; 400 + hoverMode = hoverModeNone;
398 } 401 }
399 } 402 }
400 403
...@@ -1219,22 +1222,20 @@ ...@@ -1219,22 +1222,20 @@
1219 } 1222 }
1220 1223
1221 function requestTrafficForMode() { 1224 function requestTrafficForMode() {
1222 - if (hoverMode === hoverModeAll) { 1225 + if (hoverMode === hoverModeFlows) {
1223 - requestAllTraffic();
1224 - } else if (hoverMode === hoverModeFlows) {
1225 requestDeviceLinkFlows(); 1226 requestDeviceLinkFlows();
1226 } else if (hoverMode === hoverModeIntents) { 1227 } else if (hoverMode === hoverModeIntents) {
1227 - requestSelectTraffic(); 1228 + requestRelatedIntents();
1228 } 1229 }
1229 } 1230 }
1230 1231
1231 - function showTrafficAction() { 1232 + function showRelatedIntentsAction() {
1232 hoverMode = hoverModeIntents; 1233 hoverMode = hoverModeIntents;
1233 - requestSelectTraffic(); 1234 + requestRelatedIntents();
1234 - flash('Related Traffic'); 1235 + flash('Related intents');
1235 } 1236 }
1236 1237
1237 - function requestSelectTraffic() { 1238 + function requestRelatedIntents() {
1238 function hoverValid() { 1239 function hoverValid() {
1239 return hoverMode === hoverModeIntents && 1240 return hoverMode === hoverModeIntents &&
1240 hovered && 1241 hovered &&
...@@ -1242,13 +1243,24 @@ ...@@ -1242,13 +1243,24 @@
1242 } 1243 }
1243 1244
1244 if (validateSelectionContext()) { 1245 if (validateSelectionContext()) {
1245 - sendMessage('requestTraffic', { 1246 + sendMessage('requestRelatedIntents', {
1246 ids: selectOrder, 1247 ids: selectOrder,
1247 hover: hoverValid() ? hovered.id : '' 1248 hover: hoverValid() ? hovered.id : ''
1248 }); 1249 });
1249 } 1250 }
1250 } 1251 }
1251 1252
1253 + function showNextIntentAction() {
1254 + hoverMode = hoverModeNone;
1255 + sendMessage('requestNextRelatedIntent', {});
1256 + flash('Next related intent');
1257 + }
1258 +
1259 + function showSelectedIntentTrafficAction() {
1260 + hoverMode = hoverModeNone;
1261 + sendMessage('requestSelectedIntentTraffic', {});
1262 + flash('Monitoring selected intent');
1263 + }
1252 1264
1253 function showDeviceLinkFlowsAction() { 1265 function showDeviceLinkFlowsAction() {
1254 hoverMode = hoverModeFlows; 1266 hoverMode = hoverModeFlows;
...@@ -2010,14 +2022,18 @@ ...@@ -2010,14 +2022,18 @@
2010 } 2022 }
2011 2023
2012 function nodeMouseOver(d) { 2024 function nodeMouseOver(d) {
2025 + if (hovered != d) {
2013 hovered = d; 2026 hovered = d;
2014 requestTrafficForMode(); 2027 requestTrafficForMode();
2015 } 2028 }
2029 + }
2016 2030
2017 function nodeMouseOut(d) { 2031 function nodeMouseOut(d) {
2032 + if (hovered != null) {
2018 hovered = null; 2033 hovered = null;
2019 requestTrafficForMode(); 2034 requestTrafficForMode();
2020 } 2035 }
2036 + }
2021 2037
2022 function addHostIcon(node, radius, iid) { 2038 function addHostIcon(node, radius, iid) {
2023 var dim = radius * 1.5, 2039 var dim = radius * 1.5,
...@@ -2498,7 +2514,7 @@ ...@@ -2498,7 +2514,7 @@
2498 wsTrace('rx', msg); 2514 wsTrace('rx', msg);
2499 } 2515 }
2500 function wsTrace(rxtx, msg) { 2516 function wsTrace(rxtx, msg) {
2501 - console.log('[' + rxtx + '] ' + msg); 2517 + // console.log('[' + rxtx + '] ' + msg);
2502 } 2518 }
2503 2519
2504 // NOTE: Temporary hardcoded example for showing detail pane 2520 // NOTE: Temporary hardcoded example for showing detail pane
...@@ -2620,7 +2636,6 @@ ...@@ -2620,7 +2636,6 @@
2620 emptySelect(); 2636 emptySelect();
2621 } else if (nSel === 1) { 2637 } else if (nSel === 1) {
2622 singleSelect(); 2638 singleSelect();
2623 - requestTrafficForMode();
2624 } else { 2639 } else {
2625 multiSelect(); 2640 multiSelect();
2626 } 2641 }
...@@ -2635,12 +2650,14 @@ ...@@ -2635,12 +2650,14 @@
2635 function singleSelect() { 2650 function singleSelect() {
2636 // NOTE: detail is shown from showDetails event callback 2651 // NOTE: detail is shown from showDetails event callback
2637 requestDetails(); 2652 requestDetails();
2653 + cancelTraffic();
2638 requestTrafficForMode(); 2654 requestTrafficForMode();
2639 } 2655 }
2640 2656
2641 function multiSelect() { 2657 function multiSelect() {
2642 haveDetails = true; 2658 haveDetails = true;
2643 populateMultiSelect(); 2659 populateMultiSelect();
2660 + cancelTraffic();
2644 requestTrafficForMode(); 2661 requestTrafficForMode();
2645 } 2662 }
2646 2663
...@@ -2738,7 +2755,7 @@ ...@@ -2738,7 +2755,7 @@
2738 function addSingleSelectActions(data) { 2755 function addSingleSelectActions(data) {
2739 detailPane.append('hr'); 2756 detailPane.append('hr');
2740 // always want to allow 'show traffic' 2757 // always want to allow 'show traffic'
2741 - addAction(detailPane, 'Show Related Traffic', showTrafficAction); 2758 + addAction(detailPane, 'Show Related Traffic', showRelatedIntentsAction);
2742 2759
2743 if (data.type === 'switch') { 2760 if (data.type === 'switch') {
2744 addAction(detailPane, 'Show Device Flows', showDeviceLinkFlowsAction); 2761 addAction(detailPane, 'Show Device Flows', showDeviceLinkFlowsAction);
...@@ -2748,7 +2765,7 @@ ...@@ -2748,7 +2765,7 @@
2748 function addMultiSelectActions() { 2765 function addMultiSelectActions() {
2749 detailPane.append('hr'); 2766 detailPane.append('hr');
2750 // always want to allow 'show traffic' 2767 // always want to allow 'show traffic'
2751 - addAction(detailPane, 'Show Related Traffic', showTrafficAction); 2768 + addAction(detailPane, 'Show Related Traffic', showRelatedIntentsAction);
2752 // if exactly two hosts are selected, also want 'add host intent' 2769 // if exactly two hosts are selected, also want 'add host intent'
2753 if (nSel() === 2 && allSelectionsClass('host')) { 2770 if (nSel() === 2 && allSelectionsClass('host')) {
2754 addAction(detailPane, 'Create Host-to-Host Flow', addHostIntentAction); 2771 addAction(detailPane, 'Create Host-to-Host Flow', addHostIntentAction);
......