blob: f4336913d352e29ec93d75b8dd2bd09ca5b77938 [file] [log] [blame]
Thomas Vachuska7d638d32014-11-07 10:24:43 -08001/*
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 */
16package org.onlab.onos.gui;
17
Thomas Vachuskad472c6e2014-11-07 19:11:05 -080018import com.fasterxml.jackson.databind.JsonNode;
19import com.fasterxml.jackson.databind.ObjectMapper;
20import com.fasterxml.jackson.databind.node.ArrayNode;
21import com.fasterxml.jackson.databind.node.ObjectNode;
Thomas Vachuska7d638d32014-11-07 10:24:43 -080022import org.eclipse.jetty.websocket.WebSocket;
Thomas Vachuska4830d392014-11-09 17:09:56 -080023import org.onlab.onos.core.ApplicationId;
24import org.onlab.onos.core.CoreService;
Thomas Vachuska690e5f62014-11-09 08:26:47 -080025import org.onlab.onos.mastership.MastershipEvent;
26import org.onlab.onos.mastership.MastershipListener;
27import org.onlab.onos.mastership.MastershipService;
Thomas Vachuskad472c6e2014-11-07 19:11:05 -080028import org.onlab.onos.net.Annotations;
Thomas Vachuska4830d392014-11-09 17:09:56 -080029import org.onlab.onos.net.ConnectPoint;
30import org.onlab.onos.net.DefaultEdgeLink;
Thomas Vachuskad472c6e2014-11-07 19:11:05 -080031import org.onlab.onos.net.Device;
Thomas Vachuskad1be50d2014-11-08 16:10:20 -080032import org.onlab.onos.net.DeviceId;
Thomas Vachuska4830d392014-11-09 17:09:56 -080033import org.onlab.onos.net.ElementId;
Thomas Vachuska690e5f62014-11-09 08:26:47 -080034import org.onlab.onos.net.Host;
35import org.onlab.onos.net.HostId;
36import org.onlab.onos.net.HostLocation;
Thomas Vachuskad472c6e2014-11-07 19:11:05 -080037import org.onlab.onos.net.Link;
Thomas Vachuskad1be50d2014-11-08 16:10:20 -080038import org.onlab.onos.net.Path;
Thomas Vachuskad472c6e2014-11-07 19:11:05 -080039import org.onlab.onos.net.device.DeviceEvent;
Thomas Vachuska690e5f62014-11-09 08:26:47 -080040import org.onlab.onos.net.device.DeviceListener;
Thomas Vachuska7d638d32014-11-07 10:24:43 -080041import org.onlab.onos.net.device.DeviceService;
Thomas Vachuska4830d392014-11-09 17:09:56 -080042import org.onlab.onos.net.flow.DefaultTrafficSelector;
43import org.onlab.onos.net.flow.DefaultTrafficTreatment;
Thomas Vachuska690e5f62014-11-09 08:26:47 -080044import org.onlab.onos.net.host.HostEvent;
45import org.onlab.onos.net.host.HostListener;
46import org.onlab.onos.net.host.HostService;
Thomas Vachuska4830d392014-11-09 17:09:56 -080047import org.onlab.onos.net.intent.HostToHostIntent;
48import org.onlab.onos.net.intent.Intent;
49import org.onlab.onos.net.intent.IntentEvent;
Thomas Vachuska690e5f62014-11-09 08:26:47 -080050import org.onlab.onos.net.intent.IntentId;
Thomas Vachuska4830d392014-11-09 17:09:56 -080051import org.onlab.onos.net.intent.IntentListener;
52import org.onlab.onos.net.intent.IntentService;
53import org.onlab.onos.net.intent.PathIntent;
Thomas Vachuskad472c6e2014-11-07 19:11:05 -080054import org.onlab.onos.net.link.LinkEvent;
Thomas Vachuska690e5f62014-11-09 08:26:47 -080055import org.onlab.onos.net.link.LinkListener;
56import org.onlab.onos.net.link.LinkService;
Thomas Vachuska4830d392014-11-09 17:09:56 -080057import org.onlab.onos.net.provider.ProviderId;
Thomas Vachuska690e5f62014-11-09 08:26:47 -080058import org.onlab.onos.net.topology.PathService;
Thomas Vachuska7d638d32014-11-07 10:24:43 -080059import org.onlab.osgi.ServiceDirectory;
Thomas Vachuska690e5f62014-11-09 08:26:47 -080060import org.onlab.packet.IpAddress;
Thomas Vachuska7d638d32014-11-07 10:24:43 -080061
62import java.io.IOException;
Thomas Vachuska690e5f62014-11-09 08:26:47 -080063import java.util.Iterator;
Thomas Vachuska4830d392014-11-09 17:09:56 -080064import java.util.List;
Thomas Vachuskad1be50d2014-11-08 16:10:20 -080065import java.util.Map;
66import java.util.Set;
Thomas Vachuska4830d392014-11-09 17:09:56 -080067import java.util.concurrent.ConcurrentHashMap;
Thomas Vachuska7d638d32014-11-07 10:24:43 -080068
Thomas Vachuska690e5f62014-11-09 08:26:47 -080069import static com.google.common.base.Preconditions.checkNotNull;
Thomas Vachuskad1be50d2014-11-08 16:10:20 -080070import static org.onlab.onos.net.DeviceId.deviceId;
Thomas Vachuska690e5f62014-11-09 08:26:47 -080071import static org.onlab.onos.net.HostId.hostId;
Thomas Vachuska4830d392014-11-09 17:09:56 -080072import static org.onlab.onos.net.PortNumber.portNumber;
Thomas Vachuskad472c6e2014-11-07 19:11:05 -080073import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_ADDED;
74import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_REMOVED;
Thomas Vachuska4830d392014-11-09 17:09:56 -080075import static org.onlab.onos.net.host.HostEvent.Type.HOST_ADDED;
76import static org.onlab.onos.net.host.HostEvent.Type.HOST_REMOVED;
Thomas Vachuskad472c6e2014-11-07 19:11:05 -080077import static org.onlab.onos.net.link.LinkEvent.Type.LINK_ADDED;
78import static org.onlab.onos.net.link.LinkEvent.Type.LINK_REMOVED;
79
Thomas Vachuska7d638d32014-11-07 10:24:43 -080080/**
81 * Web socket capable of interacting with the GUI topology view.
82 */
Thomas Vachuska690e5f62014-11-09 08:26:47 -080083public class TopologyWebSocket implements WebSocket.OnTextMessage {
Thomas Vachuska7d638d32014-11-07 10:24:43 -080084
Thomas Vachuska4830d392014-11-09 17:09:56 -080085 private static final String APP_ID = "org.onlab.onos.gui";
86 private static final ProviderId PID = new ProviderId("core", "org.onlab.onos.core", true);
87
88 private final ApplicationId appId;
Thomas Vachuska7d638d32014-11-07 10:24:43 -080089 private final ServiceDirectory directory;
Thomas Vachuska7d638d32014-11-07 10:24:43 -080090
Thomas Vachuskad472c6e2014-11-07 19:11:05 -080091 private final ObjectMapper mapper = new ObjectMapper();
92
Thomas Vachuska7d638d32014-11-07 10:24:43 -080093 private Connection connection;
94
Thomas Vachuska690e5f62014-11-09 08:26:47 -080095 private final DeviceService deviceService;
96 private final LinkService linkService;
97 private final HostService hostService;
98 private final MastershipService mastershipService;
Thomas Vachuska4830d392014-11-09 17:09:56 -080099 private final IntentService intentService;
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800100
101 private final DeviceListener deviceListener = new InternalDeviceListener();
102 private final LinkListener linkListener = new InternalLinkListener();
103 private final HostListener hostListener = new InternalHostListener();
104 private final MastershipListener mastershipListener = new InternalMastershipListener();
Thomas Vachuska4830d392014-11-09 17:09:56 -0800105 private final IntentListener intentListener = new InternalIntentListener();
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800106
Thomas Vachuskad1be50d2014-11-08 16:10:20 -0800107 // TODO: extract into an external & durable state; good enough for now and demo
Thomas Vachuska4830d392014-11-09 17:09:56 -0800108 private static Map<String, ObjectNode> metaUi = new ConcurrentHashMap<>();
109
110 // Intents that are being monitored for the GUI
111 private static Map<IntentId, Long> intentsToMonitor = new ConcurrentHashMap<>();
Thomas Vachuskad1be50d2014-11-08 16:10:20 -0800112
113 private static final String COMPACT = "%s/%s-%s/%s";
114
115
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800116 /**
117 * Creates a new web-socket for serving data to GUI topology view.
118 *
119 * @param directory service directory
120 */
121 public TopologyWebSocket(ServiceDirectory directory) {
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800122 this.directory = checkNotNull(directory, "Directory cannot be null");
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800123 deviceService = directory.get(DeviceService.class);
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800124 linkService = directory.get(LinkService.class);
125 hostService = directory.get(HostService.class);
126 mastershipService = directory.get(MastershipService.class);
Thomas Vachuska4830d392014-11-09 17:09:56 -0800127 intentService = directory.get(IntentService.class);
128
129 appId = directory.get(CoreService.class).registerApplication(APP_ID);
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800130 }
131
132 @Override
133 public void onOpen(Connection connection) {
134 this.connection = connection;
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800135 deviceService.addListener(deviceListener);
136 linkService.addListener(linkListener);
137 hostService.addListener(hostListener);
138 mastershipService.addListener(mastershipListener);
Thomas Vachuska4830d392014-11-09 17:09:56 -0800139 intentService.addListener(intentListener);
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800140
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800141 sendAllDevices();
142 sendAllLinks();
Thomas Vachuska4830d392014-11-09 17:09:56 -0800143 sendAllHosts();
144 }
145
146 private void sendAllHosts() {
147 for (Host host : hostService.getHosts()) {
148 sendMessage(hostMessage(new HostEvent(HOST_ADDED, host)));
149 }
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800150 }
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800151
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800152 private void sendAllDevices() {
153 for (Device device : deviceService.getDevices()) {
154 sendMessage(deviceMessage(new DeviceEvent(DEVICE_ADDED, device)));
155 }
156 }
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800157
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800158 private void sendAllLinks() {
159 for (Link link : linkService.getLinks()) {
160 sendMessage(linkMessage(new LinkEvent(LINK_ADDED, link)));
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800161 }
162 }
163
164 @Override
165 public void onClose(int closeCode, String message) {
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800166 deviceService.removeListener(deviceListener);
167 linkService.removeListener(linkListener);
168 hostService.removeListener(hostListener);
169 mastershipService.removeListener(mastershipListener);
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800170 }
171
172 @Override
173 public void onMessage(String data) {
Thomas Vachuskad1be50d2014-11-08 16:10:20 -0800174 try {
175 ObjectNode event = (ObjectNode) mapper.reader().readTree(data);
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800176 String type = string(event, "event", "unknown");
177 if (type.equals("showDetails")) {
178 showDetails(event);
179 } else if (type.equals("updateMeta")) {
180 updateMetaInformation(event);
181 } else if (type.equals("requestPath")) {
Thomas Vachuska4830d392014-11-09 17:09:56 -0800182 createHostIntent(event);
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800183 } else if (type.equals("requestTraffic")) {
184 sendTraffic(event);
185 } else if (type.equals("cancelTraffic")) {
186 cancelTraffic(event);
Thomas Vachuskad1be50d2014-11-08 16:10:20 -0800187 }
Thomas Vachuska4830d392014-11-09 17:09:56 -0800188 } catch (Exception e) {
189 System.out.println("WTF?! " + data);
190 e.printStackTrace();
Thomas Vachuskad1be50d2014-11-08 16:10:20 -0800191 }
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800192 }
193
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800194 // Sends the specified data to the client.
195 private void sendMessage(ObjectNode data) {
196 try {
197 connection.sendMessage(data.toString());
198 } catch (IOException e) {
199 e.printStackTrace();
Thomas Vachuskad1be50d2014-11-08 16:10:20 -0800200 }
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800201 }
202
203 // Retrieves the payload from the specified event.
204 private ObjectNode payload(ObjectNode event) {
205 return (ObjectNode) event.path("payload");
206 }
207
208 // Returns the specified node property as a number
209 private long number(ObjectNode node, String name) {
210 return node.path(name).asLong();
211 }
212
213 // Returns the specified node property as a string.
214 private String string(ObjectNode node, String name) {
215 return node.path(name).asText();
216 }
217
218 // Returns the specified node property as a string.
219 private String string(ObjectNode node, String name, String defaultValue) {
220 return node.path(name).asText(defaultValue);
221 }
222
223 // Returns the specified set of IP addresses as a string.
224 private String ip(Set<IpAddress> ipAddresses) {
225 Iterator<IpAddress> it = ipAddresses.iterator();
226 return it.hasNext() ? it.next().toString() : "unknown";
227 }
228
229 // Encodes the specified host location into a JSON object.
230 private ObjectNode location(ObjectMapper mapper, HostLocation location) {
231 return mapper.createObjectNode()
232 .put("device", location.deviceId().toString())
233 .put("port", location.port().toLong());
234 }
235
236 // Encodes the specified list of labels a JSON array.
237 private ArrayNode labels(ObjectMapper mapper, String... labels) {
238 ArrayNode json = mapper.createArrayNode();
239 for (String label : labels) {
240 json.add(label);
241 }
242 return json;
243 }
244
245 // Produces JSON structure from annotations.
246 private JsonNode props(Annotations annotations) {
247 ObjectNode props = mapper.createObjectNode();
248 for (String key : annotations.keys()) {
249 props.put(key, annotations.value(key));
250 }
251 return props;
252 }
253
254 // Produces a log message event bound to the client.
255 private ObjectNode message(String severity, long id, String message) {
256 return envelope("message", id,
257 mapper.createObjectNode()
258 .put("severity", severity)
259 .put("message", message));
260 }
261
262 // Puts the payload into an envelope and returns it.
263 private ObjectNode envelope(String type, long sid, ObjectNode payload) {
264 ObjectNode event = mapper.createObjectNode();
265 event.put("event", type);
266 if (sid > 0) {
267 event.put("sid", sid);
268 }
269 event.set("payload", payload);
270 return event;
271 }
272
273 // Sends back device or host details.
274 private void showDetails(ObjectNode event) {
275 ObjectNode payload = payload(event);
276 String type = string(payload, "type", "unknown");
277 if (type.equals("device")) {
278 sendMessage(deviceDetails(deviceId(string(payload, "id")),
279 number(event, "sid")));
280 } else if (type.equals("host")) {
281 sendMessage(hostDetails(hostId(string(payload, "id")),
282 number(event, "sid")));
283 }
284 }
285
286 // Updates device/host meta information.
287 private void updateMetaInformation(ObjectNode event) {
288 ObjectNode payload = payload(event);
289 metaUi.put(string(payload, "id"), payload);
290 }
291
Thomas Vachuska4830d392014-11-09 17:09:56 -0800292 // Creates host-to-host intent.
293 private void createHostIntent(ObjectNode event) {
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800294 ObjectNode payload = payload(event);
295 long id = number(event, "sid");
Thomas Vachuska4830d392014-11-09 17:09:56 -0800296 // TODO: add protection against device ids and non-existent hosts.
297 HostId one = hostId(string(payload, "one"));
298 HostId two = hostId(string(payload, "two"));
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800299
Thomas Vachuska4830d392014-11-09 17:09:56 -0800300 HostToHostIntent hostIntent = new HostToHostIntent(appId, one, two,
301 DefaultTrafficSelector.builder().build(),
302 DefaultTrafficTreatment.builder().build());
303 intentsToMonitor.put(hostIntent.id(), number(event, "sid"));
304 intentService.submit(hostIntent);
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800305 }
306
307 // Sends traffic message.
308 private void sendTraffic(ObjectNode event) {
309 ObjectNode payload = payload(event);
310 long id = number(event, "sid");
311 IntentId intentId = IntentId.valueOf(payload.path("intentId").asLong());
312
313 if (payload != null) {
314 payload.put("traffic", true);
315 sendMessage(envelope("showPath", id, payload));
316 } else {
317 sendMessage(message("warn", id, "No path found"));
318 }
319 }
320
321 // Cancels sending traffic messages.
322 private void cancelTraffic(ObjectNode event) {
323 // TODO: implement this
324 }
325
326 // Finds the path between the specified devices.
327 private ObjectNode findPath(DeviceId one, DeviceId two) {
328 PathService pathService = directory.get(PathService.class);
329 Set<Path> paths = pathService.getPaths(one, two);
330 if (paths.isEmpty()) {
331 return null;
332 } else {
333 return pathMessage(paths.iterator().next());
334 }
335 }
336
337 // Produces a path message to the client.
338 private ObjectNode pathMessage(Path path) {
339 ObjectNode payload = mapper.createObjectNode();
340 ArrayNode links = mapper.createArrayNode();
341 for (Link link : path.links()) {
342 links.add(compactLinkString(link));
343 }
344
345 payload.set("links", links);
346 return payload;
Thomas Vachuskad1be50d2014-11-08 16:10:20 -0800347 }
348
349 /**
350 * Returns a compact string representing the given link.
351 *
352 * @param link infrastructure link
353 * @return formatted link string
354 */
355 public static String compactLinkString(Link link) {
Thomas Vachuska4830d392014-11-09 17:09:56 -0800356 return String.format(COMPACT, link.src().elementId(), link.src().port(),
357 link.dst().elementId(), link.dst().port());
Thomas Vachuskad1be50d2014-11-08 16:10:20 -0800358 }
359
360
Thomas Vachuskad472c6e2014-11-07 19:11:05 -0800361 // Produces a link event message to the client.
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800362 private ObjectNode deviceMessage(DeviceEvent event) {
Thomas Vachuskad472c6e2014-11-07 19:11:05 -0800363 Device device = event.subject();
364 ObjectNode payload = mapper.createObjectNode()
365 .put("id", device.id().toString())
366 .put("type", device.type().toString().toLowerCase())
367 .put("online", deviceService.isAvailable(device.id()));
368
369 // Generate labels: id, chassis id, no-label, optional-name
370 ArrayNode labels = mapper.createArrayNode();
371 labels.add(device.id().toString());
372 labels.add(device.chassisId().toString());
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800373 labels.add(""); // compact no-label view
Thomas Vachuskad472c6e2014-11-07 19:11:05 -0800374 labels.add(device.annotations().value("name"));
375
376 // Add labels, props and stuff the payload into envelope.
377 payload.set("labels", labels);
378 payload.set("props", props(device.annotations()));
Thomas Vachuska4830d392014-11-09 17:09:56 -0800379 addMetaUi(device.id(), payload);
Thomas Vachuskad472c6e2014-11-07 19:11:05 -0800380
381 String type = (event.type() == DEVICE_ADDED) ? "addDevice" :
382 ((event.type() == DEVICE_REMOVED) ? "removeDevice" : "updateDevice");
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800383 return envelope(type, 0, payload);
Thomas Vachuskad472c6e2014-11-07 19:11:05 -0800384 }
385
386 // Produces a link event message to the client.
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800387 private ObjectNode linkMessage(LinkEvent event) {
Thomas Vachuskad472c6e2014-11-07 19:11:05 -0800388 Link link = event.subject();
389 ObjectNode payload = mapper.createObjectNode()
Thomas Vachuska70c16ad2014-11-09 11:19:44 -0800390 .put("id", compactLinkString(link))
Thomas Vachuskad472c6e2014-11-07 19:11:05 -0800391 .put("type", link.type().toString().toLowerCase())
392 .put("linkWidth", 2)
393 .put("src", link.src().deviceId().toString())
394 .put("srcPort", link.src().port().toString())
395 .put("dst", link.dst().deviceId().toString())
396 .put("dstPort", link.dst().port().toString());
397 String type = (event.type() == LINK_ADDED) ? "addLink" :
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800398 ((event.type() == LINK_REMOVED) ? "removeLink" : "updateLink");
399 return envelope(type, 0, payload);
Thomas Vachuskad472c6e2014-11-07 19:11:05 -0800400 }
401
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800402 // Produces a host event message to the client.
403 private ObjectNode hostMessage(HostEvent event) {
404 Host host = event.subject();
405 ObjectNode payload = mapper.createObjectNode()
Thomas Vachuska4830d392014-11-09 17:09:56 -0800406 .put("id", host.id().toString())
407 .put("ingress", compactLinkString(edgeLink(host, true)))
408 .put("egress", compactLinkString(edgeLink(host, false)));
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800409 payload.set("cp", location(mapper, host.location()));
410 payload.set("labels", labels(mapper, ip(host.ipAddresses()),
411 host.mac().toString()));
Thomas Vachuska4830d392014-11-09 17:09:56 -0800412 payload.set("props", props(host.annotations()));
413 addMetaUi(host.id(), payload);
414
415 String type = (event.type() == HOST_ADDED) ? "addHost" :
416 ((event.type() == HOST_REMOVED) ? "removeHost" : "updateHost");
417 return envelope(type, 0, payload);
418 }
419
420 private DefaultEdgeLink edgeLink(Host host, boolean ingress) {
421 return new DefaultEdgeLink(PID, new ConnectPoint(host.id(), portNumber(0)),
422 host.location(), ingress);
423 }
424
425 private void addMetaUi(ElementId id, ObjectNode payload) {
426 ObjectNode meta = metaUi.get(id.toString());
427 if (meta != null) {
428 payload.set("metaUi", meta);
429 }
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800430 }
431
432
433 // Returns device details response.
434 private ObjectNode deviceDetails(DeviceId deviceId, long sid) {
435 Device device = deviceService.getDevice(deviceId);
436 Annotations annot = device.annotations();
437 int portCount = deviceService.getPorts(deviceId).size();
438 return envelope("showDetails", sid,
439 json(deviceId.toString(),
440 device.type().toString().toLowerCase(),
441 new Prop("Name", annot.value("name")),
442 new Prop("Vendor", device.manufacturer()),
443 new Prop("H/W Version", device.hwVersion()),
444 new Prop("S/W Version", device.swVersion()),
445 new Prop("Serial Number", device.serialNumber()),
446 new Separator(),
447 new Prop("Latitude", annot.value("latitude")),
448 new Prop("Longitude", annot.value("longitude")),
449 new Prop("Ports", Integer.toString(portCount))));
450 }
451
452 // Returns host details response.
453 private ObjectNode hostDetails(HostId hostId, long sid) {
454 Host host = hostService.getHost(hostId);
455 Annotations annot = host.annotations();
456 return envelope("showDetails", sid,
457 json(hostId.toString(), "host",
458 new Prop("MAC", host.mac().toString()),
459 new Prop("IP", host.ipAddresses().toString()),
460 new Separator(),
461 new Prop("Latitude", annot.value("latitude")),
462 new Prop("Longitude", annot.value("longitude"))));
463 }
464
465 // Produces JSON property details.
466 private ObjectNode json(String id, String type, Prop... props) {
467 ObjectMapper mapper = new ObjectMapper();
468 ObjectNode result = mapper.createObjectNode()
469 .put("id", id).put("type", type);
470 ObjectNode pnode = mapper.createObjectNode();
471 ArrayNode porder = mapper.createArrayNode();
472 for (Prop p : props) {
473 porder.add(p.key);
474 pnode.put(p.key, p.value);
Thomas Vachuskad472c6e2014-11-07 19:11:05 -0800475 }
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800476 result.set("propOrder", porder);
477 result.set("props", pnode);
478 return result;
Thomas Vachuskad472c6e2014-11-07 19:11:05 -0800479 }
480
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800481 // Auxiliary key/value carrier.
482 private class Prop {
483 private final String key;
484 private final String value;
485
486 protected Prop(String key, String value) {
487 this.key = key;
488 this.value = value;
489 }
Thomas Vachuskad472c6e2014-11-07 19:11:05 -0800490 }
491
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800492 private class Separator extends Prop {
493 protected Separator() {
494 super("-", "");
495 }
Thomas Vachuskad472c6e2014-11-07 19:11:05 -0800496 }
497
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800498 private class InternalDeviceListener implements DeviceListener {
499 @Override
500 public void event(DeviceEvent event) {
501 sendMessage(deviceMessage(event));
502 }
503 }
504
505 private class InternalLinkListener implements LinkListener {
506 @Override
507 public void event(LinkEvent event) {
508 sendMessage(linkMessage(event));
509 }
510 }
511
512 private class InternalHostListener implements HostListener {
513 @Override
514 public void event(HostEvent event) {
515 sendMessage(hostMessage(event));
516 }
517 }
518
519 private class InternalMastershipListener implements MastershipListener {
520 @Override
521 public void event(MastershipEvent event) {
522
Thomas Vachuskad472c6e2014-11-07 19:11:05 -0800523 }
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800524 }
Thomas Vachuska4830d392014-11-09 17:09:56 -0800525
526 private class InternalIntentListener implements IntentListener {
527 @Override
528 public void event(IntentEvent event) {
529 Intent intent = event.subject();
530 Long sid = intentsToMonitor.get(intent.id());
531 if (sid != null) {
532 List<Intent> installable = intentService.getInstallableIntents(intent.id());
533 if (installable != null && !installable.isEmpty()) {
534 PathIntent pathIntent = (PathIntent) installable.iterator().next();
535 Path path = pathIntent.path();
536 ObjectNode payload = pathMessage(path).put("intentId", intent.id().toString());
537 sendMessage(envelope("showPath", sid, payload));
538 }
539 }
540 }
541 }
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800542}
543