Thomas Vachuska

Adding GUI server-side code.

Change-Id: Iaa8452a315762f9a57c5bdaec2de4ec60877d928
...@@ -20,30 +20,40 @@ import com.fasterxml.jackson.databind.ObjectMapper; ...@@ -20,30 +20,40 @@ import com.fasterxml.jackson.databind.ObjectMapper;
20 import com.fasterxml.jackson.databind.node.ArrayNode; 20 import com.fasterxml.jackson.databind.node.ArrayNode;
21 import com.fasterxml.jackson.databind.node.ObjectNode; 21 import com.fasterxml.jackson.databind.node.ObjectNode;
22 import org.eclipse.jetty.websocket.WebSocket; 22 import org.eclipse.jetty.websocket.WebSocket;
23 -import org.onlab.onos.event.Event; 23 +import org.onlab.onos.mastership.MastershipEvent;
24 +import org.onlab.onos.mastership.MastershipListener;
25 +import org.onlab.onos.mastership.MastershipService;
24 import org.onlab.onos.net.Annotations; 26 import org.onlab.onos.net.Annotations;
25 import org.onlab.onos.net.Device; 27 import org.onlab.onos.net.Device;
26 import org.onlab.onos.net.DeviceId; 28 import org.onlab.onos.net.DeviceId;
29 +import org.onlab.onos.net.Host;
30 +import org.onlab.onos.net.HostId;
31 +import org.onlab.onos.net.HostLocation;
27 import org.onlab.onos.net.Link; 32 import org.onlab.onos.net.Link;
28 import org.onlab.onos.net.Path; 33 import org.onlab.onos.net.Path;
29 import org.onlab.onos.net.device.DeviceEvent; 34 import org.onlab.onos.net.device.DeviceEvent;
35 +import org.onlab.onos.net.device.DeviceListener;
30 import org.onlab.onos.net.device.DeviceService; 36 import org.onlab.onos.net.device.DeviceService;
37 +import org.onlab.onos.net.host.HostEvent;
38 +import org.onlab.onos.net.host.HostListener;
39 +import org.onlab.onos.net.host.HostService;
40 +import org.onlab.onos.net.intent.IntentId;
31 import org.onlab.onos.net.link.LinkEvent; 41 import org.onlab.onos.net.link.LinkEvent;
32 -import org.onlab.onos.net.topology.Topology; 42 +import org.onlab.onos.net.link.LinkListener;
33 -import org.onlab.onos.net.topology.TopologyEdge; 43 +import org.onlab.onos.net.link.LinkService;
34 -import org.onlab.onos.net.topology.TopologyEvent; 44 +import org.onlab.onos.net.topology.PathService;
35 -import org.onlab.onos.net.topology.TopologyGraph;
36 -import org.onlab.onos.net.topology.TopologyListener;
37 -import org.onlab.onos.net.topology.TopologyService;
38 -import org.onlab.onos.net.topology.TopologyVertex;
39 import org.onlab.osgi.ServiceDirectory; 45 import org.onlab.osgi.ServiceDirectory;
46 +import org.onlab.packet.IpAddress;
40 47
41 import java.io.IOException; 48 import java.io.IOException;
42 import java.util.HashMap; 49 import java.util.HashMap;
50 +import java.util.Iterator;
43 import java.util.Map; 51 import java.util.Map;
44 import java.util.Set; 52 import java.util.Set;
45 53
54 +import static com.google.common.base.Preconditions.checkNotNull;
46 import static org.onlab.onos.net.DeviceId.deviceId; 55 import static org.onlab.onos.net.DeviceId.deviceId;
56 +import static org.onlab.onos.net.HostId.hostId;
47 import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_ADDED; 57 import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_ADDED;
48 import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_REMOVED; 58 import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_REMOVED;
49 import static org.onlab.onos.net.link.LinkEvent.Type.LINK_ADDED; 59 import static org.onlab.onos.net.link.LinkEvent.Type.LINK_ADDED;
...@@ -52,16 +62,24 @@ import static org.onlab.onos.net.link.LinkEvent.Type.LINK_REMOVED; ...@@ -52,16 +62,24 @@ import static org.onlab.onos.net.link.LinkEvent.Type.LINK_REMOVED;
52 /** 62 /**
53 * Web socket capable of interacting with the GUI topology view. 63 * Web socket capable of interacting with the GUI topology view.
54 */ 64 */
55 -public class TopologyWebSocket implements WebSocket.OnTextMessage, TopologyListener { 65 +public class TopologyWebSocket implements WebSocket.OnTextMessage {
56 66
57 private final ServiceDirectory directory; 67 private final ServiceDirectory directory;
58 - private final TopologyService topologyService;
59 - private final DeviceService deviceService;
60 68
61 private final ObjectMapper mapper = new ObjectMapper(); 69 private final ObjectMapper mapper = new ObjectMapper();
62 70
63 private Connection connection; 71 private Connection connection;
64 72
73 + private final DeviceService deviceService;
74 + private final LinkService linkService;
75 + private final HostService hostService;
76 + private final MastershipService mastershipService;
77 +
78 + private final DeviceListener deviceListener = new InternalDeviceListener();
79 + private final LinkListener linkListener = new InternalLinkListener();
80 + private final HostListener hostListener = new InternalHostListener();
81 + private final MastershipListener mastershipListener = new InternalMastershipListener();
82 +
65 // TODO: extract into an external & durable state; good enough for now and demo 83 // TODO: extract into an external & durable state; good enough for now and demo
66 private static Map<String, ObjectNode> metaUi = new HashMap<>(); 84 private static Map<String, ObjectNode> metaUi = new HashMap<>();
67 85
...@@ -74,81 +92,219 @@ public class TopologyWebSocket implements WebSocket.OnTextMessage, TopologyListe ...@@ -74,81 +92,219 @@ public class TopologyWebSocket implements WebSocket.OnTextMessage, TopologyListe
74 * @param directory service directory 92 * @param directory service directory
75 */ 93 */
76 public TopologyWebSocket(ServiceDirectory directory) { 94 public TopologyWebSocket(ServiceDirectory directory) {
77 - this.directory = directory; 95 + this.directory = checkNotNull(directory, "Directory cannot be null");
78 - topologyService = directory.get(TopologyService.class);
79 deviceService = directory.get(DeviceService.class); 96 deviceService = directory.get(DeviceService.class);
97 + linkService = directory.get(LinkService.class);
98 + hostService = directory.get(HostService.class);
99 + mastershipService = directory.get(MastershipService.class);
80 } 100 }
81 101
82 @Override 102 @Override
83 public void onOpen(Connection connection) { 103 public void onOpen(Connection connection) {
84 this.connection = connection; 104 this.connection = connection;
105 + deviceService.addListener(deviceListener);
106 + linkService.addListener(linkListener);
107 + hostService.addListener(hostListener);
108 + mastershipService.addListener(mastershipListener);
85 109
86 - // Register for topology events... 110 + sendAllDevices();
87 - if (topologyService != null && deviceService != null) { 111 + sendAllLinks();
88 - topologyService.addListener(this);
89 -
90 - Topology topology = topologyService.currentTopology();
91 - TopologyGraph graph = topologyService.getGraph(topology);
92 - for (TopologyVertex vertex : graph.getVertexes()) {
93 - sendMessage(message(new DeviceEvent(DEVICE_ADDED,
94 - deviceService.getDevice(vertex.deviceId()))));
95 } 112 }
96 113
97 - for (TopologyEdge edge : graph.getEdges()) { 114 + private void sendAllDevices() {
98 - sendMessage(message(new LinkEvent(LINK_ADDED, edge.link()))); 115 + for (Device device : deviceService.getDevices()) {
116 + sendMessage(deviceMessage(new DeviceEvent(DEVICE_ADDED, device)));
117 + }
99 } 118 }
100 119
101 - } else { 120 + private void sendAllLinks() {
102 - sendMessage(message("error", "No topology service!!!")); 121 + for (Link link : linkService.getLinks()) {
122 + sendMessage(linkMessage(new LinkEvent(LINK_ADDED, link)));
103 } 123 }
104 } 124 }
105 125
106 @Override 126 @Override
107 public void onClose(int closeCode, String message) { 127 public void onClose(int closeCode, String message) {
108 - TopologyService topologyService = directory.get(TopologyService.class); 128 + deviceService.removeListener(deviceListener);
109 - if (topologyService != null) { 129 + linkService.removeListener(linkListener);
110 - topologyService.removeListener(this); 130 + hostService.removeListener(hostListener);
111 - } 131 + mastershipService.removeListener(mastershipListener);
112 } 132 }
113 133
114 @Override 134 @Override
115 public void onMessage(String data) { 135 public void onMessage(String data) {
116 try { 136 try {
117 ObjectNode event = (ObjectNode) mapper.reader().readTree(data); 137 ObjectNode event = (ObjectNode) mapper.reader().readTree(data);
118 - String type = event.path("event").asText("unknown"); 138 + String type = string(event, "event", "unknown");
119 - ObjectNode payload = (ObjectNode) event.path("payload"); 139 + if (type.equals("showDetails")) {
120 - 140 + showDetails(event);
121 - switch (type) { 141 + } else if (type.equals("updateMeta")) {
122 - case "updateMeta": 142 + updateMetaInformation(event);
123 - metaUi.put(payload.path("id").asText(), payload); 143 + } else if (type.equals("requestPath")) {
124 - break; 144 + sendPath(event);
125 - case "requestPath": 145 + } else if (type.equals("requestTraffic")) {
126 - findPath(deviceId(payload.path("one").asText()), 146 + sendTraffic(event);
127 - deviceId(payload.path("two").asText())); 147 + } else if (type.equals("cancelTraffic")) {
128 - default: 148 + cancelTraffic(event);
129 - break;
130 } 149 }
131 } catch (IOException e) { 150 } catch (IOException e) {
132 System.out.println("Received: " + data); 151 System.out.println("Received: " + data);
133 } 152 }
134 } 153 }
135 154
136 - private void findPath(DeviceId one, DeviceId two) { 155 + // Sends the specified data to the client.
137 - Set<Path> paths = topologyService.getPaths(topologyService.currentTopology(), 156 + private void sendMessage(ObjectNode data) {
138 - one, two); 157 + try {
139 - if (!paths.isEmpty()) { 158 + connection.sendMessage(data.toString());
159 + } catch (IOException e) {
160 + e.printStackTrace();
161 + }
162 + }
163 +
164 + // Retrieves the payload from the specified event.
165 + private ObjectNode payload(ObjectNode event) {
166 + return (ObjectNode) event.path("payload");
167 + }
168 +
169 + // Returns the specified node property as a number
170 + private long number(ObjectNode node, String name) {
171 + return node.path(name).asLong();
172 + }
173 +
174 + // Returns the specified node property as a string.
175 + private String string(ObjectNode node, String name) {
176 + return node.path(name).asText();
177 + }
178 +
179 + // Returns the specified node property as a string.
180 + private String string(ObjectNode node, String name, String defaultValue) {
181 + return node.path(name).asText(defaultValue);
182 + }
183 +
184 + // Returns the specified set of IP addresses as a string.
185 + private String ip(Set<IpAddress> ipAddresses) {
186 + Iterator<IpAddress> it = ipAddresses.iterator();
187 + return it.hasNext() ? it.next().toString() : "unknown";
188 + }
189 +
190 + // Encodes the specified host location into a JSON object.
191 + private ObjectNode location(ObjectMapper mapper, HostLocation location) {
192 + return mapper.createObjectNode()
193 + .put("device", location.deviceId().toString())
194 + .put("port", location.port().toLong());
195 + }
196 +
197 + // Encodes the specified list of labels a JSON array.
198 + private ArrayNode labels(ObjectMapper mapper, String... labels) {
199 + ArrayNode json = mapper.createArrayNode();
200 + for (String label : labels) {
201 + json.add(label);
202 + }
203 + return json;
204 + }
205 +
206 + // Produces JSON structure from annotations.
207 + private JsonNode props(Annotations annotations) {
208 + ObjectNode props = mapper.createObjectNode();
209 + for (String key : annotations.keys()) {
210 + props.put(key, annotations.value(key));
211 + }
212 + return props;
213 + }
214 +
215 + // Produces a log message event bound to the client.
216 + private ObjectNode message(String severity, long id, String message) {
217 + return envelope("message", id,
218 + mapper.createObjectNode()
219 + .put("severity", severity)
220 + .put("message", message));
221 + }
222 +
223 + // Puts the payload into an envelope and returns it.
224 + private ObjectNode envelope(String type, long sid, ObjectNode payload) {
225 + ObjectNode event = mapper.createObjectNode();
226 + event.put("event", type);
227 + if (sid > 0) {
228 + event.put("sid", sid);
229 + }
230 + event.set("payload", payload);
231 + return event;
232 + }
233 +
234 + // Sends back device or host details.
235 + private void showDetails(ObjectNode event) {
236 + ObjectNode payload = payload(event);
237 + String type = string(payload, "type", "unknown");
238 + if (type.equals("device")) {
239 + sendMessage(deviceDetails(deviceId(string(payload, "id")),
240 + number(event, "sid")));
241 + } else if (type.equals("host")) {
242 + sendMessage(hostDetails(hostId(string(payload, "id")),
243 + number(event, "sid")));
244 + }
245 + }
246 +
247 + // Updates device/host meta information.
248 + private void updateMetaInformation(ObjectNode event) {
249 + ObjectNode payload = payload(event);
250 + metaUi.put(string(payload, "id"), payload);
251 + }
252 +
253 + // Sends path message.
254 + private void sendPath(ObjectNode event) {
255 + ObjectNode payload = payload(event);
256 + long id = number(event, "sid");
257 + DeviceId one = deviceId(string(payload, "one"));
258 + DeviceId two = deviceId(string(payload, "two"));
259 +
260 + ObjectNode response = findPath(one, two);
261 + if (response != null) {
262 + sendMessage(envelope("showPath", id, response));
263 + } else {
264 + sendMessage(message("warn", id, "No path found"));
265 + }
266 + }
267 +
268 + // Sends traffic message.
269 + private void sendTraffic(ObjectNode event) {
270 + ObjectNode payload = payload(event);
271 + long id = number(event, "sid");
272 + IntentId intentId = IntentId.valueOf(payload.path("intentId").asLong());
273 +
274 + if (payload != null) {
275 + payload.put("traffic", true);
276 + sendMessage(envelope("showPath", id, payload));
277 + } else {
278 + sendMessage(message("warn", id, "No path found"));
279 + }
280 + }
281 +
282 + // Cancels sending traffic messages.
283 + private void cancelTraffic(ObjectNode event) {
284 + // TODO: implement this
285 + }
286 +
287 + // Finds the path between the specified devices.
288 + private ObjectNode findPath(DeviceId one, DeviceId two) {
289 + PathService pathService = directory.get(PathService.class);
290 + Set<Path> paths = pathService.getPaths(one, two);
291 + if (paths.isEmpty()) {
292 + return null;
293 + } else {
294 + return pathMessage(paths.iterator().next());
295 + }
296 + }
297 +
298 + // Produces a path message to the client.
299 + private ObjectNode pathMessage(Path path) {
140 ObjectNode payload = mapper.createObjectNode(); 300 ObjectNode payload = mapper.createObjectNode();
141 ArrayNode links = mapper.createArrayNode(); 301 ArrayNode links = mapper.createArrayNode();
142 -
143 - Path path = paths.iterator().next();
144 for (Link link : path.links()) { 302 for (Link link : path.links()) {
145 links.add(compactLinkString(link)); 303 links.add(compactLinkString(link));
146 } 304 }
147 305
148 payload.set("links", links); 306 payload.set("links", links);
149 - sendMessage(envelope("showPath", payload)); 307 + return payload;
150 - }
151 - // TODO: when no path, send a message to the client
152 } 308 }
153 309
154 /** 310 /**
...@@ -163,16 +319,8 @@ public class TopologyWebSocket implements WebSocket.OnTextMessage, TopologyListe ...@@ -163,16 +319,8 @@ public class TopologyWebSocket implements WebSocket.OnTextMessage, TopologyListe
163 } 319 }
164 320
165 321
166 - private void sendMessage(String data) {
167 - try {
168 - connection.sendMessage(data);
169 - } catch (IOException e) {
170 - e.printStackTrace();
171 - }
172 - }
173 -
174 // Produces a link event message to the client. 322 // Produces a link event message to the client.
175 - private String message(DeviceEvent event) { 323 + private ObjectNode deviceMessage(DeviceEvent event) {
176 Device device = event.subject(); 324 Device device = event.subject();
177 ObjectNode payload = mapper.createObjectNode() 325 ObjectNode payload = mapper.createObjectNode()
178 .put("id", device.id().toString()) 326 .put("id", device.id().toString())
...@@ -183,7 +331,7 @@ public class TopologyWebSocket implements WebSocket.OnTextMessage, TopologyListe ...@@ -183,7 +331,7 @@ public class TopologyWebSocket implements WebSocket.OnTextMessage, TopologyListe
183 ArrayNode labels = mapper.createArrayNode(); 331 ArrayNode labels = mapper.createArrayNode();
184 labels.add(device.id().toString()); 332 labels.add(device.id().toString());
185 labels.add(device.chassisId().toString()); 333 labels.add(device.chassisId().toString());
186 - labels.add(" "); // compact no-label view 334 + labels.add(""); // compact no-label view
187 labels.add(device.annotations().value("name")); 335 labels.add(device.annotations().value("name"));
188 336
189 // Add labels, props and stuff the payload into envelope. 337 // Add labels, props and stuff the payload into envelope.
...@@ -197,11 +345,11 @@ public class TopologyWebSocket implements WebSocket.OnTextMessage, TopologyListe ...@@ -197,11 +345,11 @@ public class TopologyWebSocket implements WebSocket.OnTextMessage, TopologyListe
197 345
198 String type = (event.type() == DEVICE_ADDED) ? "addDevice" : 346 String type = (event.type() == DEVICE_ADDED) ? "addDevice" :
199 ((event.type() == DEVICE_REMOVED) ? "removeDevice" : "updateDevice"); 347 ((event.type() == DEVICE_REMOVED) ? "removeDevice" : "updateDevice");
200 - return envelope(type, payload); 348 + return envelope(type, 0, payload);
201 } 349 }
202 350
203 // Produces a link event message to the client. 351 // Produces a link event message to the client.
204 - private String message(LinkEvent event) { 352 + private ObjectNode linkMessage(LinkEvent event) {
205 Link link = event.subject(); 353 Link link = event.subject();
206 ObjectNode payload = mapper.createObjectNode() 354 ObjectNode payload = mapper.createObjectNode()
207 .put("type", link.type().toString().toLowerCase()) 355 .put("type", link.type().toString().toLowerCase())
...@@ -211,43 +359,112 @@ public class TopologyWebSocket implements WebSocket.OnTextMessage, TopologyListe ...@@ -211,43 +359,112 @@ public class TopologyWebSocket implements WebSocket.OnTextMessage, TopologyListe
211 .put("dst", link.dst().deviceId().toString()) 359 .put("dst", link.dst().deviceId().toString())
212 .put("dstPort", link.dst().port().toString()); 360 .put("dstPort", link.dst().port().toString());
213 String type = (event.type() == LINK_ADDED) ? "addLink" : 361 String type = (event.type() == LINK_ADDED) ? "addLink" :
214 - ((event.type() == LINK_REMOVED) ? "removeLink" : "removeLink"); 362 + ((event.type() == LINK_REMOVED) ? "removeLink" : "updateLink");
215 - return envelope(type, payload); 363 + return envelope(type, 0, payload);
216 } 364 }
217 365
218 - // Produces JSON structure from annotations. 366 + // Produces a host event message to the client.
219 - private JsonNode props(Annotations annotations) { 367 + private ObjectNode hostMessage(HostEvent event) {
220 - ObjectNode props = mapper.createObjectNode(); 368 + Host host = event.subject();
221 - for (String key : annotations.keys()) { 369 + ObjectNode payload = mapper.createObjectNode()
222 - props.put(key, annotations.value(key)); 370 + .put("id", host.id().toString());
371 + payload.set("cp", location(mapper, host.location()));
372 + payload.set("labels", labels(mapper, ip(host.ipAddresses()),
373 + host.mac().toString()));
374 + return payload;
223 } 375 }
224 - return props; 376 +
377 +
378 + // Returns device details response.
379 + private ObjectNode deviceDetails(DeviceId deviceId, long sid) {
380 + Device device = deviceService.getDevice(deviceId);
381 + Annotations annot = device.annotations();
382 + int portCount = deviceService.getPorts(deviceId).size();
383 + return envelope("showDetails", sid,
384 + json(deviceId.toString(),
385 + device.type().toString().toLowerCase(),
386 + new Prop("Name", annot.value("name")),
387 + new Prop("Vendor", device.manufacturer()),
388 + new Prop("H/W Version", device.hwVersion()),
389 + new Prop("S/W Version", device.swVersion()),
390 + new Prop("Serial Number", device.serialNumber()),
391 + new Separator(),
392 + new Prop("Latitude", annot.value("latitude")),
393 + new Prop("Longitude", annot.value("longitude")),
394 + new Prop("Ports", Integer.toString(portCount))));
225 } 395 }
226 396
227 - // Produces a log message event bound to the client. 397 + // Returns host details response.
228 - private String message(String severity, String message) { 398 + private ObjectNode hostDetails(HostId hostId, long sid) {
229 - return envelope("message", 399 + Host host = hostService.getHost(hostId);
230 - mapper.createObjectNode() 400 + Annotations annot = host.annotations();
231 - .put("severity", severity) 401 + return envelope("showDetails", sid,
232 - .put("message", message)); 402 + json(hostId.toString(), "host",
403 + new Prop("MAC", host.mac().toString()),
404 + new Prop("IP", host.ipAddresses().toString()),
405 + new Separator(),
406 + new Prop("Latitude", annot.value("latitude")),
407 + new Prop("Longitude", annot.value("longitude"))));
233 } 408 }
234 409
235 - // Puts the payload into an envelope and returns it. 410 + // Produces JSON property details.
236 - private String envelope(String type, ObjectNode payload) { 411 + private ObjectNode json(String id, String type, Prop... props) {
237 - ObjectNode event = mapper.createObjectNode(); 412 + ObjectMapper mapper = new ObjectMapper();
238 - event.put("event", type); 413 + ObjectNode result = mapper.createObjectNode()
239 - event.set("payload", payload); 414 + .put("id", id).put("type", type);
240 - return event.toString(); 415 + ObjectNode pnode = mapper.createObjectNode();
416 + ArrayNode porder = mapper.createArrayNode();
417 + for (Prop p : props) {
418 + porder.add(p.key);
419 + pnode.put(p.key, p.value);
420 + }
421 + result.set("propOrder", porder);
422 + result.set("props", pnode);
423 + return result;
241 } 424 }
242 425
426 + // Auxiliary key/value carrier.
427 + private class Prop {
428 + private final String key;
429 + private final String value;
430 +
431 + protected Prop(String key, String value) {
432 + this.key = key;
433 + this.value = value;
434 + }
435 + }
436 +
437 + private class Separator extends Prop {
438 + protected Separator() {
439 + super("-", "");
440 + }
441 + }
442 +
443 + private class InternalDeviceListener implements DeviceListener {
444 + @Override
445 + public void event(DeviceEvent event) {
446 + sendMessage(deviceMessage(event));
447 + }
448 + }
449 +
450 + private class InternalLinkListener implements LinkListener {
451 + @Override
452 + public void event(LinkEvent event) {
453 + sendMessage(linkMessage(event));
454 + }
455 + }
456 +
457 + private class InternalHostListener implements HostListener {
243 @Override 458 @Override
244 - public void event(TopologyEvent event) { 459 + public void event(HostEvent event) {
245 - for (Event reason : event.reasons()) { 460 + sendMessage(hostMessage(event));
246 - if (reason instanceof DeviceEvent) { 461 + }
247 - sendMessage(message((DeviceEvent) reason));
248 - } else if (reason instanceof LinkEvent) {
249 - sendMessage(message((LinkEvent) reason));
250 } 462 }
463 +
464 + private class InternalMastershipListener implements MastershipListener {
465 + @Override
466 + public void event(MastershipEvent event) {
467 +
251 } 468 }
252 } 469 }
253 } 470 }
......