Thomas Vachuska

ONOS-245 Adding more polish and capability to the GUI.

Change-Id: I20cfd48f10de5f053d0c00dc1460d85d5c0d22de
1 +/*
2 + * Copyright 2014 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onlab.onos.gui;
17 +
18 +import org.onlab.onos.net.ConnectPoint;
19 +import org.onlab.onos.net.Device;
20 +import org.onlab.onos.net.DeviceId;
21 +import org.onlab.onos.net.Host;
22 +import org.onlab.onos.net.HostId;
23 +import org.onlab.onos.net.Link;
24 +import org.onlab.onos.net.device.DeviceService;
25 +import org.onlab.onos.net.host.HostService;
26 +import org.onlab.onos.net.intent.HostToHostIntent;
27 +import org.onlab.onos.net.intent.Intent;
28 +import org.onlab.onos.net.intent.IntentService;
29 +import org.onlab.onos.net.intent.LinkCollectionIntent;
30 +import org.onlab.onos.net.intent.MultiPointToSinglePointIntent;
31 +import org.onlab.onos.net.intent.OpticalConnectivityIntent;
32 +import org.onlab.onos.net.intent.PathIntent;
33 +import org.onlab.onos.net.intent.PointToPointIntent;
34 +import org.onlab.onos.net.link.LinkService;
35 +
36 +import java.util.HashSet;
37 +import java.util.List;
38 +import java.util.Set;
39 +
40 +import static org.onlab.onos.net.intent.IntentState.INSTALLED;
41 +
42 +/**
43 + * Auxiliary facility to query the intent service based on the specified
44 + * set of end-station hosts, edge points or infrastructure devices.
45 + */
46 +public class TopologyViewIntentFilter {
47 +
48 + private final IntentService intentService;
49 + private final DeviceService deviceService;
50 + private final HostService hostService;
51 + private final LinkService linkService;
52 +
53 + /**
54 + * Crreates an intent filter.
55 + *
56 + * @param intentService intent service reference
57 + * @param deviceService device service reference
58 + * @param hostService host service reference
59 + * @param linkService link service reference
60 + */
61 + TopologyViewIntentFilter(IntentService intentService,
62 + DeviceService deviceService,
63 + HostService hostService, LinkService linkService) {
64 + this.intentService = intentService;
65 + this.deviceService = deviceService;
66 + this.hostService = hostService;
67 + this.linkService = linkService;
68 + }
69 +
70 + /**
71 + * Finds all path (host-to-host or point-to-point) intents that pertains
72 + * to the given hosts.
73 + *
74 + * @param hosts set of hosts to query by
75 + * @param devices set of devices to query by
76 + * @return set of intents that 'match' all hosts and devices given
77 + */
78 + Set<Intent> findPathIntents(Set<Host> hosts, Set<Device> devices) {
79 + // Derive from this the set of edge connect points.
80 + Set<ConnectPoint> edgePoints = getEdgePoints(hosts);
81 +
82 + // Iterate over all intents and produce a set that contains only those
83 + // intents that target all selected hosts or derived edge connect points.
84 + return getIntents(hosts, devices, edgePoints);
85 + }
86 +
87 +
88 + // Produces a set of edge points from the specified set of hosts.
89 + private Set<ConnectPoint> getEdgePoints(Set<Host> hosts) {
90 + Set<ConnectPoint> edgePoints = new HashSet<>();
91 + for (Host host : hosts) {
92 + edgePoints.add(host.location());
93 + }
94 + return edgePoints;
95 + }
96 +
97 + // Produces a set of intents that target all selected hosts, devices or connect points.
98 + private Set<Intent> getIntents(Set<Host> hosts, Set<Device> devices,
99 + Set<ConnectPoint> edgePoints) {
100 + Set<Intent> intents = new HashSet<>();
101 + if (hosts.isEmpty() && devices.isEmpty()) {
102 + return intents;
103 + }
104 +
105 + Set<OpticalConnectivityIntent> opticalIntents = new HashSet<>();
106 +
107 + // Search through all intents and see if they are relevant to our search.
108 + for (Intent intent : intentService.getIntents()) {
109 + if (intentService.getIntentState(intent.id()) == INSTALLED) {
110 + boolean isRelevant = false;
111 + if (intent instanceof HostToHostIntent) {
112 + isRelevant = isIntentRelevantToHosts((HostToHostIntent) intent, hosts) &&
113 + isIntentRelevantToDevices(intent, devices);
114 + } else if (intent instanceof PointToPointIntent) {
115 + isRelevant = isIntentRelevant((PointToPointIntent) intent, edgePoints) &&
116 + isIntentRelevantToDevices(intent, devices);
117 + } else if (intent instanceof MultiPointToSinglePointIntent) {
118 + isRelevant = isIntentRelevant((MultiPointToSinglePointIntent) intent, edgePoints) &&
119 + isIntentRelevantToDevices(intent, devices);
120 + } else if (intent instanceof OpticalConnectivityIntent) {
121 + opticalIntents.add((OpticalConnectivityIntent) intent);
122 + }
123 + // TODO: add other intents, e.g. SinglePointToMultiPointIntent
124 +
125 + if (isRelevant) {
126 + intents.add(intent);
127 + }
128 + }
129 + }
130 +
131 + // As a second pass, try to link up any optical intents with the
132 + // packet-level ones.
133 + for (OpticalConnectivityIntent intent : opticalIntents) {
134 + if (isIntentRelevant(intent, intents) &&
135 + isIntentRelevantToDevices(intent, devices)) {
136 + intents.add(intent);
137 + }
138 + }
139 + return intents;
140 + }
141 +
142 + // Indicates whether the specified intent involves all of the given hosts.
143 + private boolean isIntentRelevantToHosts(HostToHostIntent intent, Set<Host> hosts) {
144 + for (Host host : hosts) {
145 + HostId id = host.id();
146 + // Bail if intent does not involve this host.
147 + if (!id.equals(intent.one()) && !id.equals(intent.two())) {
148 + return false;
149 + }
150 + }
151 + return true;
152 + }
153 +
154 + // Indicates whether the specified intent involves all of the given devices.
155 + private boolean isIntentRelevantToDevices(Intent intent, Set<Device> devices) {
156 + List<Intent> installables = intentService.getInstallableIntents(intent.id());
157 + for (Device device : devices) {
158 + if (!isIntentRelevantToDevice(installables, device)) {
159 + return false;
160 + }
161 + }
162 + return true;
163 + }
164 +
165 + // Indicates whether the specified intent involves the given device.
166 + private boolean isIntentRelevantToDevice(List<Intent> installables, Device device) {
167 + for (Intent installable : installables) {
168 + if (installable instanceof PathIntent) {
169 + PathIntent pathIntent = (PathIntent) installable;
170 + if (pathContainsDevice(pathIntent.path().links(), device.id())) {
171 + return true;
172 + }
173 + } else if (installable instanceof LinkCollectionIntent) {
174 + LinkCollectionIntent linksIntent = (LinkCollectionIntent) installable;
175 + if (pathContainsDevice(linksIntent.links(), device.id())) {
176 + return true;
177 + }
178 + }
179 + }
180 + return false;
181 + }
182 +
183 + // Indicates whether the specified intent involves the given device.
184 + private boolean pathContainsDevice(Iterable<Link> links, DeviceId id) {
185 + for (Link link : links) {
186 + if (link.src().elementId().equals(id) || link.dst().elementId().equals(id)) {
187 + return true;
188 + }
189 + }
190 + return false;
191 + }
192 +
193 + private boolean isIntentRelevant(PointToPointIntent intent, Set<ConnectPoint> edgePoints) {
194 + for (ConnectPoint point : edgePoints) {
195 + // Bail if intent does not involve this edge point.
196 + if (!point.equals(intent.egressPoint()) &&
197 + !point.equals(intent.ingressPoint())) {
198 + return false;
199 + }
200 + }
201 + return true;
202 + }
203 +
204 + // Indicates whether the specified intent involves all of the given edge points.
205 + private boolean isIntentRelevant(MultiPointToSinglePointIntent intent,
206 + Set<ConnectPoint> edgePoints) {
207 + for (ConnectPoint point : edgePoints) {
208 + // Bail if intent does not involve this edge point.
209 + if (!point.equals(intent.egressPoint()) &&
210 + !intent.ingressPoints().contains(point)) {
211 + return false;
212 + }
213 + }
214 + return true;
215 + }
216 +
217 + // Indicates whether the specified intent involves all of the given edge points.
218 + private boolean isIntentRelevant(OpticalConnectivityIntent opticalIntent,
219 + Set<Intent> intents) {
220 + Link ccSrc = getFirstLink(opticalIntent.getSrc(), false);
221 + Link ccDst = getFirstLink(opticalIntent.getDst(), true);
222 +
223 + for (Intent intent : intents) {
224 + List<Intent> installables = intentService.getInstallableIntents(intent.id());
225 + for (Intent installable : installables) {
226 + if (installable instanceof PathIntent) {
227 + List<Link> links = ((PathIntent) installable).path().links();
228 + if (links.size() == 3) {
229 + Link tunnel = links.get(1);
230 + if (tunnel.src().equals(ccSrc.src()) &&
231 + tunnel.dst().equals(ccDst.dst())) {
232 + return true;
233 + }
234 + }
235 + }
236 + }
237 + }
238 + return false;
239 + }
240 +
241 + private Link getFirstLink(ConnectPoint point, boolean ingress) {
242 + for (Link link : linkService.getLinks(point)) {
243 + if (point.equals(ingress ? link.src() : link.dst())) {
244 + return link;
245 + }
246 + }
247 + return null;
248 + }
249 +
250 +}
...@@ -35,7 +35,6 @@ import org.onlab.onos.net.Host; ...@@ -35,7 +35,6 @@ import org.onlab.onos.net.Host;
35 import org.onlab.onos.net.HostId; 35 import org.onlab.onos.net.HostId;
36 import org.onlab.onos.net.HostLocation; 36 import org.onlab.onos.net.HostLocation;
37 import org.onlab.onos.net.Link; 37 import org.onlab.onos.net.Link;
38 -import org.onlab.onos.net.Path;
39 import org.onlab.onos.net.device.DeviceEvent; 38 import org.onlab.onos.net.device.DeviceEvent;
40 import org.onlab.onos.net.device.DeviceService; 39 import org.onlab.onos.net.device.DeviceService;
41 import org.onlab.onos.net.host.HostEvent; 40 import org.onlab.onos.net.host.HostEvent;
...@@ -57,6 +56,7 @@ import org.slf4j.Logger; ...@@ -57,6 +56,7 @@ import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory; 56 import org.slf4j.LoggerFactory;
58 57
59 import java.text.DecimalFormat; 58 import java.text.DecimalFormat;
59 +import java.util.HashSet;
60 import java.util.Iterator; 60 import java.util.Iterator;
61 import java.util.List; 61 import java.util.List;
62 import java.util.Map; 62 import java.util.Map;
...@@ -68,6 +68,8 @@ import static com.google.common.base.Strings.isNullOrEmpty; ...@@ -68,6 +68,8 @@ import static com.google.common.base.Strings.isNullOrEmpty;
68 import static org.onlab.onos.cluster.ClusterEvent.Type.INSTANCE_ADDED; 68 import static org.onlab.onos.cluster.ClusterEvent.Type.INSTANCE_ADDED;
69 import static org.onlab.onos.cluster.ClusterEvent.Type.INSTANCE_REMOVED; 69 import static org.onlab.onos.cluster.ClusterEvent.Type.INSTANCE_REMOVED;
70 import static org.onlab.onos.cluster.ControllerNode.State.ACTIVE; 70 import static org.onlab.onos.cluster.ControllerNode.State.ACTIVE;
71 +import static org.onlab.onos.net.DeviceId.deviceId;
72 +import static org.onlab.onos.net.HostId.hostId;
71 import static org.onlab.onos.net.PortNumber.portNumber; 73 import static org.onlab.onos.net.PortNumber.portNumber;
72 import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_ADDED; 74 import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_ADDED;
73 import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_REMOVED; 75 import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_REMOVED;
...@@ -95,6 +97,8 @@ public abstract class TopologyViewMessages { ...@@ -95,6 +97,8 @@ public abstract class TopologyViewMessages {
95 private static final String KB_UNIT = "KB"; 97 private static final String KB_UNIT = "KB";
96 private static final String B_UNIT = "B"; 98 private static final String B_UNIT = "B";
97 99
100 + private static final String ANIMATED = "animated";
101 +
98 protected final ServiceDirectory directory; 102 protected final ServiceDirectory directory;
99 protected final ClusterService clusterService; 103 protected final ClusterService clusterService;
100 protected final DeviceService deviceService; 104 protected final DeviceService deviceService;
...@@ -196,6 +200,64 @@ public abstract class TopologyViewMessages { ...@@ -196,6 +200,64 @@ public abstract class TopologyViewMessages {
196 return event; 200 return event;
197 } 201 }
198 202
203 + // Produces a set of all hosts listed in the specified JSON array.
204 + protected Set<Host> getHosts(ArrayNode array) {
205 + Set<Host> hosts = new HashSet<>();
206 + if (array != null) {
207 + for (JsonNode node : array) {
208 + try {
209 + addHost(hosts, hostId(node.asText()));
210 + } catch (IllegalArgumentException e) {
211 + log.debug("Skipping ID {}", node.asText());
212 + }
213 + }
214 + }
215 + return hosts;
216 + }
217 +
218 + // Adds the specified host to the set of hosts.
219 + private void addHost(Set<Host> hosts, HostId hostId) {
220 + Host host = hostService.getHost(hostId);
221 + if (host != null) {
222 + hosts.add(host);
223 + }
224 + }
225 +
226 +
227 + // Produces a set of all devices listed in the specified JSON array.
228 + protected Set<Device> getDevices(ArrayNode array) {
229 + Set<Device> devices = new HashSet<>();
230 + if (array != null) {
231 + for (JsonNode node : array) {
232 + try {
233 + addDevice(devices, deviceId(node.asText()));
234 + } catch (IllegalArgumentException e) {
235 + log.debug("Skipping ID {}", node.asText());
236 + }
237 + }
238 + }
239 + return devices;
240 + }
241 +
242 + private void addDevice(Set<Device> devices, DeviceId deviceId) {
243 + Device device = deviceService.getDevice(deviceId);
244 + if (device != null) {
245 + devices.add(device);
246 + }
247 + }
248 +
249 + protected void addHover(Set<Host> hosts, Set<Device> devices, String hover) {
250 + try {
251 + addHost(hosts, hostId(hover));
252 + } catch (IllegalArgumentException e) {
253 + try {
254 + addDevice(devices, deviceId(hover));
255 + } catch (IllegalArgumentException ne) {
256 + log.debug("Skipping ID {}", hover);
257 + }
258 + }
259 + }
260 +
199 // Produces a cluster instance message to the client. 261 // Produces a cluster instance message to the client.
200 protected ObjectNode instanceMessage(ClusterEvent event) { 262 protected ObjectNode instanceMessage(ClusterEvent event) {
201 ControllerNode node = event.subject(); 263 ControllerNode node = event.subject();
...@@ -382,16 +444,18 @@ public abstract class TopologyViewMessages { ...@@ -382,16 +444,18 @@ public abstract class TopologyViewMessages {
382 new Prop("Longitude", annot.value("longitude")))); 444 new Prop("Longitude", annot.value("longitude"))));
383 } 445 }
384 446
385 - // Produces a path payload to the client. 447 +
386 - protected ObjectNode pathMessage(Path path, String type) { 448 + // Produces JSON message to trigger traffic overview visualization
449 + protected ObjectNode trafficSummaryMessage(long sid) {
387 ObjectNode payload = mapper.createObjectNode(); 450 ObjectNode payload = mapper.createObjectNode();
388 - ArrayNode links = mapper.createArrayNode(); 451 + ArrayNode paths = mapper.createArrayNode();
389 - for (Link link : path.links()) { 452 + payload.set("paths", paths);
390 - links.add(compactLinkString(link)); 453 + for (Link link : linkService.getLinks()) {
454 + Set<Link> links = new HashSet<>();
455 + links.add(link);
456 + addPathTraffic(paths, "plain", "secondary", links);
391 } 457 }
392 - 458 + return envelope("showTraffic", sid, payload);
393 - payload.put("type", type).set("links", links);
394 - return payload;
395 } 459 }
396 460
397 461
...@@ -409,11 +473,14 @@ public abstract class TopologyViewMessages { ...@@ -409,11 +473,14 @@ public abstract class TopologyViewMessages {
409 for (Intent installable : installables) { 473 for (Intent installable : installables) {
410 String cls = isOptical ? trafficClass.type + " optical" : trafficClass.type; 474 String cls = isOptical ? trafficClass.type + " optical" : trafficClass.type;
411 if (installable instanceof PathIntent) { 475 if (installable instanceof PathIntent) {
412 - addPathTraffic(paths, cls, ((PathIntent) installable).path().links()); 476 + addPathTraffic(paths, cls, ANIMATED,
477 + ((PathIntent) installable).path().links());
413 } else if (installable instanceof LinkCollectionIntent) { 478 } else if (installable instanceof LinkCollectionIntent) {
414 - addPathTraffic(paths, cls, ((LinkCollectionIntent) installable).links()); 479 + addPathTraffic(paths, cls, ANIMATED,
480 + ((LinkCollectionIntent) installable).links());
415 } else if (installable instanceof OpticalPathIntent) { 481 } else if (installable instanceof OpticalPathIntent) {
416 - addPathTraffic(paths, cls, ((OpticalPathIntent) installable).path().links()); 482 + addPathTraffic(paths, cls, ANIMATED,
483 + ((OpticalPathIntent) installable).path().links());
417 } 484 }
418 485
419 } 486 }
...@@ -426,7 +493,8 @@ public abstract class TopologyViewMessages { ...@@ -426,7 +493,8 @@ public abstract class TopologyViewMessages {
426 493
427 // Adds the link segments (path or tree) associated with the specified 494 // Adds the link segments (path or tree) associated with the specified
428 // connectivity intent 495 // connectivity intent
429 - protected void addPathTraffic(ArrayNode paths, String type, Iterable<Link> links) { 496 + protected void addPathTraffic(ArrayNode paths, String type, String trafficType,
497 + Iterable<Link> links) {
430 ObjectNode pathNode = mapper.createObjectNode(); 498 ObjectNode pathNode = mapper.createObjectNode();
431 ArrayNode linksNode = mapper.createArrayNode(); 499 ArrayNode linksNode = mapper.createArrayNode();
432 500
...@@ -445,7 +513,7 @@ public abstract class TopologyViewMessages { ...@@ -445,7 +513,7 @@ public abstract class TopologyViewMessages {
445 labels.add(label); 513 labels.add(label);
446 } 514 }
447 } 515 }
448 - pathNode.put("class", hasTraffic ? type + " animated" : type); 516 + pathNode.put("class", hasTraffic ? type + " " + trafficType : type);
449 pathNode.put("traffic", hasTraffic); 517 pathNode.put("traffic", hasTraffic);
450 pathNode.set("links", linksNode); 518 pathNode.set("links", linksNode);
451 pathNode.set("labels", labels); 519 pathNode.set("labels", labels);
......
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
15 */ 15 */
16 package org.onlab.onos.gui; 16 package org.onlab.onos.gui;
17 17
18 -import com.fasterxml.jackson.databind.JsonNode;
19 import com.fasterxml.jackson.databind.node.ArrayNode; 18 import com.fasterxml.jackson.databind.node.ArrayNode;
20 import com.fasterxml.jackson.databind.node.ObjectNode; 19 import com.fasterxml.jackson.databind.node.ObjectNode;
21 import org.eclipse.jetty.websocket.WebSocket; 20 import org.eclipse.jetty.websocket.WebSocket;
...@@ -24,7 +23,6 @@ import org.onlab.onos.cluster.ClusterEventListener; ...@@ -24,7 +23,6 @@ import org.onlab.onos.cluster.ClusterEventListener;
24 import org.onlab.onos.cluster.ControllerNode; 23 import org.onlab.onos.cluster.ControllerNode;
25 import org.onlab.onos.core.ApplicationId; 24 import org.onlab.onos.core.ApplicationId;
26 import org.onlab.onos.core.CoreService; 25 import org.onlab.onos.core.CoreService;
27 -import org.onlab.onos.net.ConnectPoint;
28 import org.onlab.onos.net.Device; 26 import org.onlab.onos.net.Device;
29 import org.onlab.onos.net.Host; 27 import org.onlab.onos.net.Host;
30 import org.onlab.onos.net.HostId; 28 import org.onlab.onos.net.HostId;
...@@ -39,17 +37,11 @@ import org.onlab.onos.net.intent.HostToHostIntent; ...@@ -39,17 +37,11 @@ import org.onlab.onos.net.intent.HostToHostIntent;
39 import org.onlab.onos.net.intent.Intent; 37 import org.onlab.onos.net.intent.Intent;
40 import org.onlab.onos.net.intent.IntentEvent; 38 import org.onlab.onos.net.intent.IntentEvent;
41 import org.onlab.onos.net.intent.IntentListener; 39 import org.onlab.onos.net.intent.IntentListener;
42 -import org.onlab.onos.net.intent.MultiPointToSinglePointIntent;
43 -import org.onlab.onos.net.intent.OpticalConnectivityIntent;
44 -import org.onlab.onos.net.intent.PathIntent;
45 -import org.onlab.onos.net.intent.PointToPointIntent;
46 import org.onlab.onos.net.link.LinkEvent; 40 import org.onlab.onos.net.link.LinkEvent;
47 import org.onlab.onos.net.link.LinkListener; 41 import org.onlab.onos.net.link.LinkListener;
48 import org.onlab.osgi.ServiceDirectory; 42 import org.onlab.osgi.ServiceDirectory;
49 43
50 import java.io.IOException; 44 import java.io.IOException;
51 -import java.util.HashSet;
52 -import java.util.List;
53 import java.util.Set; 45 import java.util.Set;
54 import java.util.Timer; 46 import java.util.Timer;
55 import java.util.TimerTask; 47 import java.util.TimerTask;
...@@ -60,7 +52,6 @@ import static org.onlab.onos.net.DeviceId.deviceId; ...@@ -60,7 +52,6 @@ import static org.onlab.onos.net.DeviceId.deviceId;
60 import static org.onlab.onos.net.HostId.hostId; 52 import static org.onlab.onos.net.HostId.hostId;
61 import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_ADDED; 53 import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_ADDED;
62 import static org.onlab.onos.net.host.HostEvent.Type.HOST_ADDED; 54 import static org.onlab.onos.net.host.HostEvent.Type.HOST_ADDED;
63 -import static org.onlab.onos.net.intent.IntentState.INSTALLED;
64 import static org.onlab.onos.net.link.LinkEvent.Type.LINK_ADDED; 55 import static org.onlab.onos.net.link.LinkEvent.Type.LINK_ADDED;
65 56
66 /** 57 /**
...@@ -99,6 +90,8 @@ public class TopologyViewWebSocket ...@@ -99,6 +90,8 @@ public class TopologyViewWebSocket
99 private long lastActive = System.currentTimeMillis(); 90 private long lastActive = System.currentTimeMillis();
100 private boolean listenersRemoved = false; 91 private boolean listenersRemoved = false;
101 92
93 + private TopologyViewIntentFilter intentFilter;
94 +
102 /** 95 /**
103 * Creates a new web-socket for serving data to GUI topology view. 96 * Creates a new web-socket for serving data to GUI topology view.
104 * 97 *
...@@ -106,6 +99,9 @@ public class TopologyViewWebSocket ...@@ -106,6 +99,9 @@ public class TopologyViewWebSocket
106 */ 99 */
107 public TopologyViewWebSocket(ServiceDirectory directory) { 100 public TopologyViewWebSocket(ServiceDirectory directory) {
108 super(directory); 101 super(directory);
102 +
103 + intentFilter = new TopologyViewIntentFilter(intentService, deviceService,
104 + hostService, linkService);
109 appId = directory.get(CoreService.class).registerApplication(APP_ID); 105 appId = directory.get(CoreService.class).registerApplication(APP_ID);
110 } 106 }
111 107
...@@ -168,7 +164,14 @@ public class TopologyViewWebSocket ...@@ -168,7 +164,14 @@ public class TopologyViewWebSocket
168 public void onMessage(String data) { 164 public void onMessage(String data) {
169 lastActive = System.currentTimeMillis(); 165 lastActive = System.currentTimeMillis();
170 try { 166 try {
171 - ObjectNode event = (ObjectNode) mapper.reader().readTree(data); 167 + processMessage((ObjectNode) mapper.reader().readTree(data));
168 + } catch (Exception e) {
169 + log.warn("Unable to parse GUI request {} due to {}", data, e);
170 + }
171 + }
172 +
173 + // Processes the specified event.
174 + private void processMessage(ObjectNode event) {
172 String type = string(event, "event", "unknown"); 175 String type = string(event, "event", "unknown");
173 if (type.equals("requestDetails")) { 176 if (type.equals("requestDetails")) {
174 requestDetails(event); 177 requestDetails(event);
...@@ -178,12 +181,11 @@ public class TopologyViewWebSocket ...@@ -178,12 +181,11 @@ public class TopologyViewWebSocket
178 createHostIntent(event); 181 createHostIntent(event);
179 } else if (type.equals("requestTraffic")) { 182 } else if (type.equals("requestTraffic")) {
180 requestTraffic(event); 183 requestTraffic(event);
184 + } else if (type.equals("requestAllTraffic")) {
185 + requestAllTraffic(event);
181 } else if (type.equals("cancelTraffic")) { 186 } else if (type.equals("cancelTraffic")) {
182 cancelTraffic(event); 187 cancelTraffic(event);
183 } 188 }
184 - } catch (Exception e) {
185 - log.warn("Unable to parse GUI request {} due to {}", data, e);
186 - }
187 } 189 }
188 190
189 // Sends the specified data to the client. 191 // Sends the specified data to the client.
...@@ -253,22 +255,36 @@ public class TopologyViewWebSocket ...@@ -253,22 +255,36 @@ public class TopologyViewWebSocket
253 intentService.submit(hostIntent); 255 intentService.submit(hostIntent);
254 } 256 }
255 257
256 - // Sends traffic message. 258 + // Subscribes for host traffic messages.
259 + private synchronized void requestAllTraffic(ObjectNode event) {
260 + ObjectNode payload = payload(event);
261 + long sid = number(event, "sid");
262 + monitorRequest = event;
263 + sendMessage(trafficSummaryMessage(sid));
264 + }
265 +
266 + // Subscribes for host traffic messages.
257 private synchronized void requestTraffic(ObjectNode event) { 267 private synchronized void requestTraffic(ObjectNode event) {
258 ObjectNode payload = payload(event); 268 ObjectNode payload = payload(event);
269 + if (!payload.has("ids")) {
270 + return;
271 + }
272 +
259 long sid = number(event, "sid"); 273 long sid = number(event, "sid");
260 monitorRequest = event; 274 monitorRequest = event;
261 275
262 // Get the set of selected hosts and their intents. 276 // Get the set of selected hosts and their intents.
263 - Set<Host> hosts = getHosts((ArrayNode) payload.path("ids")); 277 + ArrayNode ids = (ArrayNode) payload.path("ids");
264 - Set<Intent> intents = findPathIntents(hosts); 278 + Set<Host> hosts = getHosts(ids);
279 + Set<Device> devices = getDevices(ids);
280 + Set<Intent> intents = intentFilter.findPathIntents(hosts, devices);
265 281
266 // If there is a hover node, include it in the hosts and find intents. 282 // If there is a hover node, include it in the hosts and find intents.
267 String hover = string(payload, "hover"); 283 String hover = string(payload, "hover");
268 Set<Intent> hoverIntents; 284 Set<Intent> hoverIntents;
269 if (!isNullOrEmpty(hover)) { 285 if (!isNullOrEmpty(hover)) {
270 - addHost(hosts, hostId(hover)); 286 + addHover(hosts, devices, hover);
271 - hoverIntents = findPathIntents(hosts); 287 + hoverIntents = intentFilter.findPathIntents(hosts, devices);
272 intents.removeAll(hoverIntents); 288 intents.removeAll(hoverIntents);
273 289
274 // Send an initial message to highlight all links of all monitored intents. 290 // Send an initial message to highlight all links of all monitored intents.
...@@ -288,157 +304,6 @@ public class TopologyViewWebSocket ...@@ -288,157 +304,6 @@ public class TopologyViewWebSocket
288 monitorRequest = null; 304 monitorRequest = null;
289 } 305 }
290 306
291 - // Finds all path (host-to-host or point-to-point) intents that pertains
292 - // to the given hosts.
293 - private Set<Intent> findPathIntents(Set<Host> hosts) {
294 - // Derive from this the set of edge connect points.
295 - Set<ConnectPoint> edgePoints = getEdgePoints(hosts);
296 -
297 - // Iterate over all intents and produce a set that contains only those
298 - // intents that target all selected hosts or derived edge connect points.
299 - return getIntents(hosts, edgePoints);
300 - }
301 -
302 - // Produces a set of intents that target all selected hosts or connect points.
303 - private Set<Intent> getIntents(Set<Host> hosts, Set<ConnectPoint> edgePoints) {
304 - Set<Intent> intents = new HashSet<>();
305 - if (hosts.isEmpty()) {
306 - return intents;
307 - }
308 -
309 - Set<OpticalConnectivityIntent> opticalIntents = new HashSet<>();
310 -
311 - for (Intent intent : intentService.getIntents()) {
312 - if (intentService.getIntentState(intent.id()) == INSTALLED) {
313 - boolean isRelevant = false;
314 - if (intent instanceof HostToHostIntent) {
315 - isRelevant = isIntentRelevant((HostToHostIntent) intent, hosts);
316 - } else if (intent instanceof PointToPointIntent) {
317 - isRelevant = isIntentRelevant((PointToPointIntent) intent, edgePoints);
318 - } else if (intent instanceof MultiPointToSinglePointIntent) {
319 - isRelevant = isIntentRelevant((MultiPointToSinglePointIntent) intent, edgePoints);
320 - } else if (intent instanceof OpticalConnectivityIntent) {
321 - opticalIntents.add((OpticalConnectivityIntent) intent);
322 - }
323 - // TODO: add other intents, e.g. SinglePointToMultiPointIntent
324 -
325 - if (isRelevant) {
326 - intents.add(intent);
327 - }
328 - }
329 - }
330 -
331 - for (OpticalConnectivityIntent intent : opticalIntents) {
332 - if (isIntentRelevant(intent, intents)) {
333 - intents.add(intent);
334 - }
335 - }
336 - return intents;
337 - }
338 -
339 - // Indicates whether the specified intent involves all of the given hosts.
340 - private boolean isIntentRelevant(HostToHostIntent intent, Set<Host> hosts) {
341 - for (Host host : hosts) {
342 - HostId id = host.id();
343 - // Bail if intent does not involve this host.
344 - if (!id.equals(intent.one()) && !id.equals(intent.two())) {
345 - return false;
346 - }
347 - }
348 - return true;
349 - }
350 -
351 - // Indicates whether the specified intent involves all of the given edge points.
352 - private boolean isIntentRelevant(PointToPointIntent intent,
353 - Set<ConnectPoint> edgePoints) {
354 - for (ConnectPoint point : edgePoints) {
355 - // Bail if intent does not involve this edge point.
356 - if (!point.equals(intent.egressPoint()) &&
357 - !point.equals(intent.ingressPoint())) {
358 - return false;
359 - }
360 - }
361 - return true;
362 - }
363 -
364 - // Indicates whether the specified intent involves all of the given edge points.
365 - private boolean isIntentRelevant(MultiPointToSinglePointIntent intent,
366 - Set<ConnectPoint> edgePoints) {
367 - for (ConnectPoint point : edgePoints) {
368 - // Bail if intent does not involve this edge point.
369 - if (!point.equals(intent.egressPoint()) &&
370 - !intent.ingressPoints().contains(point)) {
371 - return false;
372 - }
373 - }
374 - return true;
375 - }
376 -
377 - // Indicates whether the specified intent involves all of the given edge points.
378 - private boolean isIntentRelevant(OpticalConnectivityIntent opticalIntent,
379 - Set<Intent> intents) {
380 - Link ccSrc = getFirstLink(opticalIntent.getSrc(), false);
381 - Link ccDst = getFirstLink(opticalIntent.getDst(), true);
382 -
383 - for (Intent intent : intents) {
384 - List<Intent> installables = intentService.getInstallableIntents(intent.id());
385 - for (Intent installable : installables) {
386 - if (installable instanceof PathIntent) {
387 - List<Link> links = ((PathIntent) installable).path().links();
388 - if (links.size() == 3) {
389 - Link tunnel = links.get(1);
390 - if (tunnel.src().equals(ccSrc.src()) &&
391 - tunnel.dst().equals(ccDst.dst())) {
392 - return true;
393 - }
394 - }
395 - }
396 - }
397 - }
398 - return false;
399 - }
400 -
401 - private Link getFirstLink(ConnectPoint point, boolean ingress) {
402 - for (Link link : linkService.getLinks(point)) {
403 - if (point.equals(ingress ? link.src() : link.dst())) {
404 - return link;
405 - }
406 - }
407 - return null;
408 - }
409 -
410 - // Produces a set of all host ids listed in the specified JSON array.
411 - private Set<Host> getHosts(ArrayNode array) {
412 - Set<Host> hosts = new HashSet<>();
413 - if (array != null) {
414 - for (JsonNode node : array) {
415 - try {
416 - addHost(hosts, hostId(node.asText()));
417 - } catch (IllegalArgumentException e) {
418 - log.debug("Skipping ID {}", node.asText());
419 - }
420 - }
421 - }
422 - return hosts;
423 - }
424 -
425 - private void addHost(Set<Host> hosts, HostId hostId) {
426 - Host host = hostService.getHost(hostId);
427 - if (host != null) {
428 - hosts.add(host);
429 - }
430 - }
431 -
432 - // Produces a set of edge points from the specified set of hosts.
433 - private Set<ConnectPoint> getEdgePoints(Set<Host> hosts) {
434 - Set<ConnectPoint> edgePoints = new HashSet<>();
435 - for (Host host : hosts) {
436 - edgePoints.add(host.location());
437 - }
438 - return edgePoints;
439 - }
440 -
441 -
442 // Adds all internal listeners. 307 // Adds all internal listeners.
443 private void addListeners() { 308 private void addListeners() {
444 clusterService.addListener(clusterListener); 309 clusterService.addListener(clusterListener);
...@@ -506,9 +371,14 @@ public class TopologyViewWebSocket ...@@ -506,9 +371,14 @@ public class TopologyViewWebSocket
506 @Override 371 @Override
507 public void run() { 372 public void run() {
508 if (monitorRequest != null) { 373 if (monitorRequest != null) {
374 + String type = string(monitorRequest, "event", "unknown");
375 + if (type.equals("requestAllTraffic")) {
376 + requestAllTraffic(monitorRequest);
377 + } else {
509 requestTraffic(monitorRequest); 378 requestTraffic(monitorRequest);
510 } 379 }
511 } 380 }
512 } 381 }
382 + }
513 } 383 }
514 384
......
...@@ -135,28 +135,26 @@ svg .node.host circle { ...@@ -135,28 +135,26 @@ svg .node.host circle {
135 stroke-dasharray: 8 4; 135 stroke-dasharray: 8 4;
136 } 136 }
137 137
138 -#topo svg .link.primary {
139 - stroke: #ffA300;
140 - stroke-width: 4px;
141 -}
142 #topo svg .link.secondary { 138 #topo svg .link.secondary {
143 stroke: rgba(0,153,51,0.5); 139 stroke: rgba(0,153,51,0.5);
144 stroke-width: 3px; 140 stroke-width: 3px;
145 } 141 }
142 +#topo svg .link.primary {
143 + stroke: #ffA300;
144 + stroke-width: 4px;
145 +}
146 #topo svg .link.animated { 146 #topo svg .link.animated {
147 stroke: #ffA300; 147 stroke: #ffA300;
148 - Xstroke-width: 6px;
149 - Xstroke-dasharray: 8 8
150 } 148 }
151 149
152 -#topo svg .link.primary.optical {
153 - stroke: #74f;
154 - stroke-width: 6px;
155 -}
156 #topo svg .link.secondary.optical { 150 #topo svg .link.secondary.optical {
157 stroke: rgba(128,64,255,0.5); 151 stroke: rgba(128,64,255,0.5);
158 stroke-width: 4px; 152 stroke-width: 4px;
159 } 153 }
154 +#topo svg .link.primary.optical {
155 + stroke: #74f;
156 + stroke-width: 6px;
157 +}
160 #topo svg .link.animated.optical { 158 #topo svg .link.animated.optical {
161 stroke: #74f; 159 stroke: #74f;
162 stroke-width: 10px; 160 stroke-width: 10px;
...@@ -164,8 +162,6 @@ svg .node.host circle { ...@@ -164,8 +162,6 @@ svg .node.host circle {
164 } 162 }
165 163
166 #topo svg .linkLabel rect { 164 #topo svg .linkLabel rect {
167 - Xstroke: #ccc;
168 - Xstroke-width: 2px;
169 fill: #eee; 165 fill: #eee;
170 stroke: none; 166 stroke: none;
171 } 167 }
......
...@@ -141,6 +141,8 @@ ...@@ -141,6 +141,8 @@
141 U: unpin, 141 U: unpin,
142 R: resetZoomPan, 142 R: resetZoomPan,
143 H: toggleHover, 143 H: toggleHover,
144 + V: showTrafficAction,
145 + A: showAllTrafficAction,
144 esc: handleEscape 146 esc: handleEscape
145 }; 147 };
146 148
...@@ -832,8 +834,9 @@ ...@@ -832,8 +834,9 @@
832 } 834 }
833 835
834 // NOTE: hover is only populated if "show traffic on hover" is 836 // NOTE: hover is only populated if "show traffic on hover" is
835 - // toggled on, and the item hovered is a host... 837 + // toggled on, and the item hovered is a host or a device...
836 - var hoverId = (trafficHover() && hovered && hovered.class === 'host') 838 + var hoverId = (trafficHover() && hovered &&
839 + (hovered.class === 'host' || hovered.class === 'device'))
837 ? hovered.id : ''; 840 ? hovered.id : '';
838 sendMessage('requestTraffic', { 841 sendMessage('requestTraffic', {
839 ids: selectOrder, 842 ids: selectOrder,
...@@ -841,6 +844,10 @@ ...@@ -841,6 +844,10 @@
841 }); 844 });
842 } 845 }
843 846
847 + function showAllTrafficAction() {
848 + sendMessage('requestAllTraffic', {});
849 + }
850 +
844 851
845 // ============================== 852 // ==============================
846 // onos instance panel functions 853 // onos instance panel functions
...@@ -1367,14 +1374,14 @@ ...@@ -1367,14 +1374,14 @@
1367 1374
1368 function nodeMouseOver(d) { 1375 function nodeMouseOver(d) {
1369 hovered = d; 1376 hovered = d;
1370 - if (trafficHover() && d.class === 'host') { 1377 + if (trafficHover() && (d.class === 'host' || d.class === 'device')) {
1371 showTrafficAction(); 1378 showTrafficAction();
1372 } 1379 }
1373 } 1380 }
1374 1381
1375 function nodeMouseOut(d) { 1382 function nodeMouseOut(d) {
1376 hovered = null; 1383 hovered = null;
1377 - if (trafficHover() && d.class === 'host') { 1384 + if (trafficHover() && (d.class === 'host' || d.class === 'device')) {
1378 showTrafficAction(); 1385 showTrafficAction();
1379 } 1386 }
1380 } 1387 }
......