Simon Hunt
Committed by Gerrit Code Review

ONOS-2186 - GUI Topo Overlay - (WIP)

- added devicesWithHover(), hostsWithHover(), hovered() to NodeSelection.
- wrote unit tests for NodeSelection.

Change-Id: I6dca0f4f0a4ce2412438c8411102034969ef4343
...@@ -21,6 +21,7 @@ import com.fasterxml.jackson.databind.JsonNode; ...@@ -21,6 +21,7 @@ import com.fasterxml.jackson.databind.JsonNode;
21 import com.fasterxml.jackson.databind.node.ArrayNode; 21 import com.fasterxml.jackson.databind.node.ArrayNode;
22 import com.fasterxml.jackson.databind.node.ObjectNode; 22 import com.fasterxml.jackson.databind.node.ObjectNode;
23 import org.onosproject.net.Device; 23 import org.onosproject.net.Device;
24 +import org.onosproject.net.Element;
24 import org.onosproject.net.Host; 25 import org.onosproject.net.Host;
25 import org.onosproject.net.device.DeviceService; 26 import org.onosproject.net.device.DeviceService;
26 import org.onosproject.net.host.HostService; 27 import org.onosproject.net.host.HostService;
...@@ -55,10 +56,12 @@ public class NodeSelection { ...@@ -55,10 +56,12 @@ public class NodeSelection {
55 56
56 private final Set<Device> devices = new HashSet<>(); 57 private final Set<Device> devices = new HashSet<>();
57 private final Set<Host> hosts = new HashSet<>(); 58 private final Set<Host> hosts = new HashSet<>();
59 + private Element hovered;
58 60
59 /** 61 /**
60 * Creates a node selection entity, from the given payload, using the 62 * Creates a node selection entity, from the given payload, using the
61 - * supplied device and host services. 63 + * supplied device and host services. Note that if a device or host was
64 + * hovered over by the mouse, it is available via {@link #hovered()}.
62 * 65 *
63 * @param payload message payload 66 * @param payload message payload
64 * @param deviceService device service 67 * @param deviceService device service
...@@ -73,25 +76,24 @@ public class NodeSelection { ...@@ -73,25 +76,24 @@ public class NodeSelection {
73 ids = extractIds(payload); 76 ids = extractIds(payload);
74 hover = extractHover(payload); 77 hover = extractHover(payload);
75 78
79 + // start by extracting the hovered element if any
80 + if (isNullOrEmpty(hover)) {
81 + hovered = null;
82 + } else {
83 + setHoveredElement();
84 + }
85 +
86 + // now go find the devices and hosts that are in the selection list
76 Set<String> unmatched = findDevices(ids); 87 Set<String> unmatched = findDevices(ids);
77 unmatched = findHosts(unmatched); 88 unmatched = findHosts(unmatched);
78 if (unmatched.size() > 0) { 89 if (unmatched.size() > 0) {
79 log.debug("Skipping unmatched IDs {}", unmatched); 90 log.debug("Skipping unmatched IDs {}", unmatched);
80 } 91 }
81 92
82 - if (!isNullOrEmpty(hover)) {
83 - unmatched = new HashSet<>();
84 - unmatched.add(hover);
85 - unmatched = findDevices(unmatched);
86 - unmatched = findHosts(unmatched);
87 - if (unmatched.size() > 0) {
88 - log.debug("Skipping unmatched HOVER {}", unmatched);
89 - }
90 - }
91 } 93 }
92 94
93 /** 95 /**
94 - * Returns a view of the selected devices. 96 + * Returns a view of the selected devices (hover not included).
95 * 97 *
96 * @return selected devices 98 * @return selected devices
97 */ 99 */
...@@ -100,7 +102,24 @@ public class NodeSelection { ...@@ -100,7 +102,24 @@ public class NodeSelection {
100 } 102 }
101 103
102 /** 104 /**
103 - * Returns a view of the selected hosts. 105 + * Returns a view of the selected devices, including the hovered device
106 + * if there was one.
107 + *
108 + * @return selected (plus hovered) devices
109 + */
110 + public Set<Device> devicesWithHover() {
111 + Set<Device> withHover;
112 + if (hovered != null && hovered instanceof Device) {
113 + withHover = new HashSet<>(devices);
114 + withHover.add((Device) hovered);
115 + } else {
116 + withHover = devices;
117 + }
118 + return Collections.unmodifiableSet(withHover);
119 + }
120 +
121 + /**
122 + * Returns a view of the selected hosts (hover not included).
104 * 123 *
105 * @return selected hosts 124 * @return selected hosts
106 */ 125 */
...@@ -109,6 +128,33 @@ public class NodeSelection { ...@@ -109,6 +128,33 @@ public class NodeSelection {
109 } 128 }
110 129
111 /** 130 /**
131 + * Returns a view of the selected hosts, including the hovered host
132 + * if thee was one.
133 + *
134 + * @return selected (plus hovered) hosts
135 + */
136 + public Set<Host> hostsWithHover() {
137 + Set<Host> withHover;
138 + if (hovered != null && hovered instanceof Host) {
139 + withHover = new HashSet<>(hosts);
140 + withHover.add((Host) hovered);
141 + } else {
142 + withHover = hosts;
143 + }
144 + return Collections.unmodifiableSet(withHover);
145 + }
146 +
147 + /**
148 + * Returns the element (host or device) over which the mouse was hovering,
149 + * or null.
150 + *
151 + * @return element hovered over
152 + */
153 + public Element hovered() {
154 + return hovered;
155 + }
156 +
157 + /**
112 * Returns true if nothing is selected. 158 * Returns true if nothing is selected.
113 * 159 *
114 * @return true if nothing selected 160 * @return true if nothing selected
...@@ -146,6 +192,26 @@ public class NodeSelection { ...@@ -146,6 +192,26 @@ public class NodeSelection {
146 return JsonUtils.string(payload, HOVER); 192 return JsonUtils.string(payload, HOVER);
147 } 193 }
148 194
195 + private void setHoveredElement() {
196 + Set<String> unmatched;
197 + unmatched = new HashSet<>();
198 + unmatched.add(hover);
199 + unmatched = findDevices(unmatched);
200 + if (devices.size() == 1) {
201 + hovered = devices.iterator().next();
202 + devices.clear();
203 + } else {
204 + unmatched = findHosts(unmatched);
205 + if (hosts.size() == 1) {
206 + hovered = hosts.iterator().next();
207 + hosts.clear();
208 + } else {
209 + hovered = null;
210 + log.debug("Skipping unmatched HOVER {}", unmatched);
211 + }
212 + }
213 + }
214 +
149 private Set<String> findDevices(Set<String> ids) { 215 private Set<String> findDevices(Set<String> ids) {
150 Set<String> unmatched = new HashSet<>(); 216 Set<String> unmatched = new HashSet<>();
151 Device device; 217 Device device;
...@@ -156,9 +222,9 @@ public class NodeSelection { ...@@ -156,9 +222,9 @@ public class NodeSelection {
156 if (device != null) { 222 if (device != null) {
157 devices.add(device); 223 devices.add(device);
158 } else { 224 } else {
159 - log.debug("Device with ID {} not found", id); 225 + unmatched.add(id);
160 } 226 }
161 - } catch (IllegalArgumentException e) { 227 + } catch (Exception e) {
162 unmatched.add(id); 228 unmatched.add(id);
163 } 229 }
164 } 230 }
...@@ -175,9 +241,9 @@ public class NodeSelection { ...@@ -175,9 +241,9 @@ public class NodeSelection {
175 if (host != null) { 241 if (host != null) {
176 hosts.add(host); 242 hosts.add(host);
177 } else { 243 } else {
178 - log.debug("Host with ID {} not found", id); 244 + unmatched.add(id);
179 } 245 }
180 - } catch (IllegalArgumentException e) { 246 + } catch (Exception e) {
181 unmatched.add(id); 247 unmatched.add(id);
182 } 248 }
183 } 249 }
......
1 +/*
2 + * Copyright 2015 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 + */
17 +
18 +package org.onosproject.ui.topo;
19 +
20 +import com.fasterxml.jackson.databind.ObjectMapper;
21 +import com.fasterxml.jackson.databind.node.ArrayNode;
22 +import com.fasterxml.jackson.databind.node.ObjectNode;
23 +import org.junit.Test;
24 +import org.onlab.packet.ChassisId;
25 +import org.onlab.packet.IpAddress;
26 +import org.onlab.packet.MacAddress;
27 +import org.onlab.packet.VlanId;
28 +import org.onosproject.net.Annotations;
29 +import org.onosproject.net.Device;
30 +import org.onosproject.net.DeviceId;
31 +import org.onosproject.net.Host;
32 +import org.onosproject.net.HostId;
33 +import org.onosproject.net.HostLocation;
34 +import org.onosproject.net.device.DeviceService;
35 +import org.onosproject.net.device.DeviceServiceAdapter;
36 +import org.onosproject.net.host.HostService;
37 +import org.onosproject.net.host.HostServiceAdapter;
38 +import org.onosproject.net.provider.ProviderId;
39 +
40 +import java.util.Set;
41 +
42 +import static org.junit.Assert.*;
43 +
44 +/**
45 + * Unit tests for {@link NodeSelection}.
46 + */
47 +public class NodeSelectionTest {
48 +
49 + private static class FakeDevice implements Device {
50 +
51 + private final DeviceId id;
52 +
53 + FakeDevice(DeviceId id) {
54 + this.id = id;
55 + }
56 +
57 + @Override
58 + public DeviceId id() {
59 + return id;
60 + }
61 +
62 + @Override
63 + public Type type() {
64 + return null;
65 + }
66 +
67 + @Override
68 + public String manufacturer() {
69 + return null;
70 + }
71 +
72 + @Override
73 + public String hwVersion() {
74 + return null;
75 + }
76 +
77 + @Override
78 + public String swVersion() {
79 + return null;
80 + }
81 +
82 + @Override
83 + public String serialNumber() {
84 + return null;
85 + }
86 +
87 + @Override
88 + public ChassisId chassisId() {
89 + return null;
90 + }
91 +
92 + @Override
93 + public Annotations annotations() {
94 + return null;
95 + }
96 +
97 + @Override
98 + public ProviderId providerId() {
99 + return null;
100 + }
101 + }
102 +
103 + private static class FakeHost implements Host {
104 +
105 + private final HostId id;
106 +
107 + FakeHost(HostId id) {
108 + this.id = id;
109 + }
110 +
111 + @Override
112 + public HostId id() {
113 + return id;
114 + }
115 +
116 + @Override
117 + public MacAddress mac() {
118 + return null;
119 + }
120 +
121 + @Override
122 + public VlanId vlan() {
123 + return null;
124 + }
125 +
126 + @Override
127 + public Set<IpAddress> ipAddresses() {
128 + return null;
129 + }
130 +
131 + @Override
132 + public HostLocation location() {
133 + return null;
134 + }
135 +
136 + @Override
137 + public Annotations annotations() {
138 + return null;
139 + }
140 +
141 + @Override
142 + public ProviderId providerId() {
143 + return null;
144 + }
145 + }
146 +
147 +
148 +
149 + private final ObjectMapper mapper = new ObjectMapper();
150 +
151 + private static final String IDS = "ids";
152 + private static final String HOVER = "hover";
153 +
154 + private static final DeviceId DEVICE_1_ID = DeviceId.deviceId("Device-1");
155 + private static final DeviceId DEVICE_2_ID = DeviceId.deviceId("Device-2");
156 + private static final HostId HOST_A_ID = HostId.hostId("aa:aa:aa:aa:aa:aa/1");
157 + private static final HostId HOST_B_ID = HostId.hostId("bb:bb:bb:bb:bb:bb/2");
158 +
159 + private static final Device DEVICE_1 = new FakeDevice(DEVICE_1_ID);
160 + private static final Device DEVICE_2 = new FakeDevice(DEVICE_2_ID);
161 + private static final Host HOST_A = new FakeHost(HOST_A_ID);
162 + private static final Host HOST_B = new FakeHost(HOST_B_ID);
163 +
164 + // ==================
165 + // == FAKE SERVICES
166 + private static class FakeDevices extends DeviceServiceAdapter {
167 + @Override
168 + public Device getDevice(DeviceId deviceId) {
169 + if (DEVICE_1_ID.equals(deviceId)) {
170 + return DEVICE_1;
171 + }
172 + if (DEVICE_2_ID.equals(deviceId)) {
173 + return DEVICE_2;
174 + }
175 + return null;
176 + }
177 + }
178 +
179 + private static class FakeHosts extends HostServiceAdapter {
180 + @Override
181 + public Host getHost(HostId hostId) {
182 + if (HOST_A_ID.equals(hostId)) {
183 + return HOST_A;
184 + }
185 + if (HOST_B_ID.equals(hostId)) {
186 + return HOST_B;
187 + }
188 + return null;
189 + }
190 + }
191 +
192 + private DeviceService deviceService = new FakeDevices();
193 + private HostService hostService = new FakeHosts();
194 +
195 + private NodeSelection ns;
196 +
197 + private ObjectNode objectNode() {
198 + return mapper.createObjectNode();
199 + }
200 +
201 + private ArrayNode arrayNode() {
202 + return mapper.createArrayNode();
203 + }
204 +
205 + private NodeSelection createNodeSelection(ObjectNode payload) {
206 + return new NodeSelection(payload, deviceService, hostService);
207 + }
208 +
209 + // selection JSON payload creation methods
210 + private ObjectNode emptySelection() {
211 + ObjectNode payload = objectNode();
212 + ArrayNode ids = arrayNode();
213 + payload.set(IDS, ids);
214 + return payload;
215 + }
216 +
217 + private ObjectNode oneDeviceSelected() {
218 + ObjectNode payload = objectNode();
219 + ArrayNode ids = arrayNode();
220 + payload.set(IDS, ids);
221 + ids.add(DEVICE_1_ID.toString());
222 + return payload;
223 + }
224 +
225 + private ObjectNode oneHostSelected() {
226 + ObjectNode payload = objectNode();
227 + ArrayNode ids = arrayNode();
228 + payload.set(IDS, ids);
229 + ids.add(HOST_A_ID.toString());
230 + return payload;
231 + }
232 +
233 + private ObjectNode twoHostsOneDeviceSelected() {
234 + ObjectNode payload = objectNode();
235 + ArrayNode ids = arrayNode();
236 + payload.set(IDS, ids);
237 + ids.add(HOST_A_ID.toString());
238 + ids.add(DEVICE_1_ID.toString());
239 + ids.add(HOST_B_ID.toString());
240 + return payload;
241 + }
242 +
243 + private ObjectNode oneHostAndHoveringDeviceSelected() {
244 + ObjectNode payload = objectNode();
245 + ArrayNode ids = arrayNode();
246 + payload.set(IDS, ids);
247 + ids.add(HOST_A_ID.toString());
248 + payload.put(HOVER, DEVICE_2_ID.toString());
249 + return payload;
250 + }
251 +
252 + private ObjectNode twoDevicesOneHostAndHoveringHostSelected() {
253 + ObjectNode payload = objectNode();
254 + ArrayNode ids = arrayNode();
255 + payload.set(IDS, ids);
256 + ids.add(HOST_A_ID.toString());
257 + ids.add(DEVICE_1_ID.toString());
258 + ids.add(DEVICE_2_ID.toString());
259 + payload.put(HOVER, HOST_B_ID.toString());
260 + return payload;
261 + }
262 +
263 +
264 + @Test
265 + public void basic() {
266 + ns = createNodeSelection(emptySelection());
267 + assertEquals("unexpected devices", 0, ns.devices().size());
268 + assertEquals("unexpected devices w/hover", 0, ns.devicesWithHover().size());
269 + assertEquals("unexpected hosts", 0, ns.hosts().size());
270 + assertEquals("unexpected hosts w/hover", 0, ns.hostsWithHover().size());
271 + assertTrue("unexpected selection", ns.none());
272 + assertNull("hover?", ns.hovered());
273 + }
274 +
275 + @Test
276 + public void oneDevice() {
277 + ns = createNodeSelection(oneDeviceSelected());
278 + assertEquals("missing device", 1, ns.devices().size());
279 + assertTrue("missing device 1", ns.devices().contains(DEVICE_1));
280 + assertEquals("missing device w/hover", 1, ns.devicesWithHover().size());
281 + assertTrue("missing device 1 w/hover", ns.devicesWithHover().contains(DEVICE_1));
282 + assertEquals("unexpected hosts", 0, ns.hosts().size());
283 + assertEquals("unexpected hosts w/hover", 0, ns.hostsWithHover().size());
284 + assertFalse("unexpected selection", ns.none());
285 + assertNull("hover?", ns.hovered());
286 + }
287 +
288 + @Test
289 + public void oneHost() {
290 + ns = createNodeSelection(oneHostSelected());
291 + assertEquals("unexpected devices", 0, ns.devices().size());
292 + assertEquals("unexpected devices w/hover", 0, ns.devicesWithHover().size());
293 + assertEquals("missing host", 1, ns.hosts().size());
294 + assertTrue("missing host A", ns.hosts().contains(HOST_A));
295 + assertEquals("missing host w/hover", 1, ns.hostsWithHover().size());
296 + assertTrue("missing host A w/hover", ns.hostsWithHover().contains(HOST_A));
297 + assertFalse("unexpected selection", ns.none());
298 + assertNull("hover?", ns.hovered());
299 + }
300 +
301 + @Test
302 + public void twoHostsOneDevice() {
303 + ns = createNodeSelection(twoHostsOneDeviceSelected());
304 + assertEquals("missing device", 1, ns.devices().size());
305 + assertTrue("missing device 1", ns.devices().contains(DEVICE_1));
306 + assertEquals("missing device w/hover", 1, ns.devicesWithHover().size());
307 + assertTrue("missing device 1 w/hover", ns.devicesWithHover().contains(DEVICE_1));
308 + assertEquals("unexpected hosts", 2, ns.hosts().size());
309 + assertTrue("missing host A", ns.hosts().contains(HOST_A));
310 + assertTrue("missing host B", ns.hosts().contains(HOST_B));
311 + assertEquals("unexpected hosts w/hover", 2, ns.hostsWithHover().size());
312 + assertTrue("missing host A w/hover", ns.hostsWithHover().contains(HOST_A));
313 + assertTrue("missing host B w/hover", ns.hostsWithHover().contains(HOST_B));
314 + assertFalse("unexpected selection", ns.none());
315 + assertNull("hover?", ns.hovered());
316 + }
317 +
318 + @Test
319 + public void oneHostAndHoveringDevice() {
320 + ns = createNodeSelection(oneHostAndHoveringDeviceSelected());
321 + assertEquals("unexpected devices", 0, ns.devices().size());
322 + assertEquals("unexpected devices w/hover", 1, ns.devicesWithHover().size());
323 + assertTrue("missing device 2 w/hover", ns.devicesWithHover().contains(DEVICE_2));
324 + assertEquals("missing host", 1, ns.hosts().size());
325 + assertTrue("missing host A", ns.hosts().contains(HOST_A));
326 + assertEquals("missing host w/hover", 1, ns.hostsWithHover().size());
327 + assertTrue("missing host A w/hover", ns.hostsWithHover().contains(HOST_A));
328 + assertFalse("unexpected selection", ns.none());
329 + assertEquals("missing hover device 2", DEVICE_2, ns.hovered());
330 + }
331 +
332 + @Test
333 + public void twoDevicesOneHostAndHoveringHost() {
334 + ns = createNodeSelection(twoDevicesOneHostAndHoveringHostSelected());
335 + assertEquals("missing devices", 2, ns.devices().size());
336 + assertTrue("missing device 1", ns.devices().contains(DEVICE_1));
337 + assertTrue("missing device 2", ns.devices().contains(DEVICE_2));
338 + assertEquals("missing devices w/hover", 2, ns.devicesWithHover().size());
339 + assertTrue("missing device 1 w/hover", ns.devicesWithHover().contains(DEVICE_1));
340 + assertTrue("missing device 2 w/hover", ns.devicesWithHover().contains(DEVICE_2));
341 + assertEquals("missing host", 1, ns.hosts().size());
342 + assertTrue("missing host A", ns.hosts().contains(HOST_A));
343 + assertEquals("missing host w/hover", 2, ns.hostsWithHover().size());
344 + assertTrue("missing host A w/hover", ns.hostsWithHover().contains(HOST_A));
345 + assertTrue("missing host B w/hover", ns.hostsWithHover().contains(HOST_B));
346 + assertFalse("unexpected selection", ns.none());
347 + assertEquals("missing hover host B", HOST_B, ns.hovered());
348 + }
349 +}
...@@ -186,7 +186,7 @@ public class TrafficMonitor { ...@@ -186,7 +186,7 @@ public class TrafficMonitor {
186 switch (mode) { 186 switch (mode) {
187 case DEV_LINK_FLOWS: 187 case DEV_LINK_FLOWS:
188 // only care about devices (not hosts) 188 // only care about devices (not hosts)
189 - if (selectedNodes.devices().isEmpty()) { 189 + if (selectedNodes.devicesWithHover().isEmpty()) {
190 sendClearAll(); 190 sendClearAll();
191 } else { 191 } else {
192 scheduleTask(); 192 scheduleTask();
...@@ -371,11 +371,11 @@ public class TrafficMonitor { ...@@ -371,11 +371,11 @@ public class TrafficMonitor {
371 private Highlights deviceLinkFlows() { 371 private Highlights deviceLinkFlows() {
372 Highlights highlights = new Highlights(); 372 Highlights highlights = new Highlights();
373 373
374 - if (selectedNodes != null && !selectedNodes.devices().isEmpty()) { 374 + if (selectedNodes != null && !selectedNodes.devicesWithHover().isEmpty()) {
375 // capture flow counts on bilinks 375 // capture flow counts on bilinks
376 TrafficLinkMap linkMap = new TrafficLinkMap(); 376 TrafficLinkMap linkMap = new TrafficLinkMap();
377 377
378 - for (Device device : selectedNodes.devices()) { 378 + for (Device device : selectedNodes.devicesWithHover()) {
379 Map<Link, Integer> counts = getLinkFlowCounts(device.id()); 379 Map<Link, Integer> counts = getLinkFlowCounts(device.id());
380 for (Link link : counts.keySet()) { 380 for (Link link : counts.keySet()) {
381 TrafficLink tlink = linkMap.add(link); 381 TrafficLink tlink = linkMap.add(link);
......
...@@ -50,7 +50,7 @@ public class IntentSelection { ...@@ -50,7 +50,7 @@ public class IntentSelection {
50 */ 50 */
51 public IntentSelection(NodeSelection nodes, TopoIntentFilter filter) { 51 public IntentSelection(NodeSelection nodes, TopoIntentFilter filter) {
52 this.nodes = nodes; 52 this.nodes = nodes;
53 - intents = filter.findPathIntents(nodes.hosts(), nodes.devices()); 53 + intents = filter.findPathIntents(nodes.hostsWithHover(), nodes.devicesWithHover());
54 if (intents.size() == 1) { 54 if (intents.size() == 1) {
55 index = 0; // pre-select a single intent 55 index = 0; // pre-select a single intent
56 } 56 }
......