blob: e99749ec445a1a9d3f294581613577c669ab8705 [file] [log] [blame]
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -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
18import 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;
22import org.onlab.onos.cluster.ClusterEvent;
23import org.onlab.onos.cluster.ClusterService;
24import org.onlab.onos.cluster.ControllerNode;
25import org.onlab.onos.cluster.NodeId;
26import org.onlab.onos.mastership.MastershipService;
Thomas Vachuska0f6baee2014-11-11 15:02:32 -080027import org.onlab.onos.net.Annotated;
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -080028import org.onlab.onos.net.Annotations;
29import org.onlab.onos.net.ConnectPoint;
30import org.onlab.onos.net.DefaultEdgeLink;
31import org.onlab.onos.net.Device;
32import org.onlab.onos.net.DeviceId;
33import org.onlab.onos.net.EdgeLink;
34import org.onlab.onos.net.Host;
35import org.onlab.onos.net.HostId;
36import org.onlab.onos.net.HostLocation;
37import org.onlab.onos.net.Link;
38import org.onlab.onos.net.Path;
39import org.onlab.onos.net.device.DeviceEvent;
40import org.onlab.onos.net.device.DeviceService;
41import org.onlab.onos.net.host.HostEvent;
42import org.onlab.onos.net.host.HostService;
Thomas Vachuskadea45ff2014-11-12 18:35:46 -080043import org.onlab.onos.net.intent.Intent;
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -080044import org.onlab.onos.net.intent.IntentService;
Thomas Vachuskadea45ff2014-11-12 18:35:46 -080045import org.onlab.onos.net.intent.LinkCollectionIntent;
Thomas Vachuska22e34922014-11-14 00:40:55 -080046import org.onlab.onos.net.intent.OpticalConnectivityIntent;
Thomas Vachuskadea4cb32014-11-14 12:14:30 -080047import org.onlab.onos.net.intent.OpticalPathIntent;
Thomas Vachuskadea45ff2014-11-12 18:35:46 -080048import org.onlab.onos.net.intent.PathIntent;
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -080049import org.onlab.onos.net.link.LinkEvent;
50import org.onlab.onos.net.link.LinkService;
51import org.onlab.onos.net.provider.ProviderId;
Thomas Vachuska7c27ad72014-11-14 16:20:10 -080052import org.onlab.onos.net.statistic.Load;
53import org.onlab.onos.net.statistic.StatisticService;
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -080054import org.onlab.osgi.ServiceDirectory;
55import org.onlab.packet.IpAddress;
Thomas Vachuska0f6baee2014-11-11 15:02:32 -080056import org.slf4j.Logger;
57import org.slf4j.LoggerFactory;
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -080058
Thomas Vachuska20322ff2014-11-19 16:22:25 -080059import java.text.DecimalFormat;
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -080060import java.util.Iterator;
Thomas Vachuskadea45ff2014-11-12 18:35:46 -080061import java.util.List;
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -080062import java.util.Map;
63import java.util.Set;
64import java.util.concurrent.ConcurrentHashMap;
65
66import static com.google.common.base.Preconditions.checkNotNull;
Thomas Vachuska89543292014-11-19 11:28:33 -080067import static com.google.common.base.Strings.isNullOrEmpty;
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -080068import static org.onlab.onos.cluster.ClusterEvent.Type.INSTANCE_ADDED;
69import static org.onlab.onos.cluster.ClusterEvent.Type.INSTANCE_REMOVED;
70import static org.onlab.onos.cluster.ControllerNode.State.ACTIVE;
71import static org.onlab.onos.net.PortNumber.portNumber;
72import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_ADDED;
73import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_REMOVED;
74import static org.onlab.onos.net.host.HostEvent.Type.HOST_ADDED;
75import static org.onlab.onos.net.host.HostEvent.Type.HOST_REMOVED;
76import static org.onlab.onos.net.link.LinkEvent.Type.LINK_ADDED;
77import static org.onlab.onos.net.link.LinkEvent.Type.LINK_REMOVED;
78
79/**
80 * Facility for creating messages bound for the topology viewer.
81 */
Thomas Vachuska7c27ad72014-11-14 16:20:10 -080082public abstract class TopologyViewMessages {
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -080083
Thomas Vachuska7c27ad72014-11-14 16:20:10 -080084 protected static final Logger log = LoggerFactory.getLogger(TopologyViewMessages.class);
Thomas Vachuska0f6baee2014-11-11 15:02:32 -080085
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -080086 private static final ProviderId PID = new ProviderId("core", "org.onlab.onos.core", true);
87 private static final String COMPACT = "%s/%s-%s/%s";
88
Thomas Vachuska20322ff2014-11-19 16:22:25 -080089 private static final double KB = 1024;
90 private static final double MB = 1024 * KB;
91 private static final double GB = 1024 * MB;
92
93 private static final String GB_UNIT = "GB";
94 private static final String MB_UNIT = "MB";
95 private static final String KB_UNIT = "KB";
96 private static final String B_UNIT = "B";
97
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -080098 protected final ServiceDirectory directory;
99 protected final ClusterService clusterService;
100 protected final DeviceService deviceService;
101 protected final LinkService linkService;
102 protected final HostService hostService;
103 protected final MastershipService mastershipService;
104 protected final IntentService intentService;
Thomas Vachuska7c27ad72014-11-14 16:20:10 -0800105 protected final StatisticService statService;
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800106
107 protected final ObjectMapper mapper = new ObjectMapper();
108
109 // TODO: extract into an external & durable state; good enough for now and demo
110 private static Map<String, ObjectNode> metaUi = new ConcurrentHashMap<>();
111
112 /**
113 * Creates a messaging facility for creating messages for topology viewer.
114 *
115 * @param directory service directory
116 */
Thomas Vachuska7c27ad72014-11-14 16:20:10 -0800117 protected TopologyViewMessages(ServiceDirectory directory) {
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800118 this.directory = checkNotNull(directory, "Directory cannot be null");
119 clusterService = directory.get(ClusterService.class);
120 deviceService = directory.get(DeviceService.class);
121 linkService = directory.get(LinkService.class);
122 hostService = directory.get(HostService.class);
123 mastershipService = directory.get(MastershipService.class);
124 intentService = directory.get(IntentService.class);
Thomas Vachuska7c27ad72014-11-14 16:20:10 -0800125 statService = directory.get(StatisticService.class);
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800126 }
127
128 // Retrieves the payload from the specified event.
129 protected ObjectNode payload(ObjectNode event) {
130 return (ObjectNode) event.path("payload");
131 }
132
133 // Returns the specified node property as a number
134 protected long number(ObjectNode node, String name) {
135 return node.path(name).asLong();
136 }
137
138 // Returns the specified node property as a string.
139 protected String string(ObjectNode node, String name) {
140 return node.path(name).asText();
141 }
142
143 // Returns the specified node property as a string.
144 protected String string(ObjectNode node, String name, String defaultValue) {
145 return node.path(name).asText(defaultValue);
146 }
147
148 // Returns the specified set of IP addresses as a string.
149 private String ip(Set<IpAddress> ipAddresses) {
150 Iterator<IpAddress> it = ipAddresses.iterator();
151 return it.hasNext() ? it.next().toString() : "unknown";
152 }
153
154 // Produces JSON structure from annotations.
155 private JsonNode props(Annotations annotations) {
156 ObjectNode props = mapper.createObjectNode();
Thomas Vachuska16582df2014-11-15 20:12:17 -0800157 if (annotations != null) {
158 for (String key : annotations.keys()) {
159 props.put(key, annotations.value(key));
160 }
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800161 }
162 return props;
163 }
164
165 // Produces an informational log message event bound to the client.
166 protected ObjectNode info(long id, String message) {
167 return message("info", id, message);
168 }
169
170 // Produces a warning log message event bound to the client.
171 protected ObjectNode warning(long id, String message) {
172 return message("warning", id, message);
173 }
174
175 // Produces an error log message event bound to the client.
176 protected ObjectNode error(long id, String message) {
177 return message("error", id, message);
178 }
179
180 // Produces a log message event bound to the client.
181 private ObjectNode message(String severity, long id, String message) {
182 return envelope("message", id,
183 mapper.createObjectNode()
184 .put("severity", severity)
185 .put("message", message));
186 }
187
188 // Puts the payload into an envelope and returns it.
189 protected ObjectNode envelope(String type, long sid, ObjectNode payload) {
190 ObjectNode event = mapper.createObjectNode();
191 event.put("event", type);
192 if (sid > 0) {
193 event.put("sid", sid);
194 }
195 event.set("payload", payload);
196 return event;
197 }
198
199 // Produces a cluster instance message to the client.
200 protected ObjectNode instanceMessage(ClusterEvent event) {
201 ControllerNode node = event.subject();
202 ObjectNode payload = mapper.createObjectNode()
203 .put("id", node.id().toString())
Thomas Vachuskae4cebaf2014-11-15 18:49:34 -0800204 .put("online", clusterService.getState(node.id()) == ACTIVE)
205 .put("uiAttached", event.subject().equals(clusterService.getLocalNode()));
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800206
207 ArrayNode labels = mapper.createArrayNode();
208 labels.add(node.id().toString());
209 labels.add(node.ip().toString());
210
211 // Add labels, props and stuff the payload into envelope.
212 payload.set("labels", labels);
213 addMetaUi(node.id().toString(), payload);
214
215 String type = (event.type() == INSTANCE_ADDED) ? "addInstance" :
216 ((event.type() == INSTANCE_REMOVED) ? "removeInstance" : "updateInstance");
217 return envelope(type, 0, payload);
218 }
219
220 // Produces a device event message to the client.
221 protected ObjectNode deviceMessage(DeviceEvent event) {
222 Device device = event.subject();
223 ObjectNode payload = mapper.createObjectNode()
224 .put("id", device.id().toString())
225 .put("type", device.type().toString().toLowerCase())
226 .put("online", deviceService.isAvailable(device.id()))
Thomas Vachuska0f6baee2014-11-11 15:02:32 -0800227 .put("master", master(device.id()));
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800228
229 // Generate labels: id, chassis id, no-label, optional-name
230 ArrayNode labels = mapper.createArrayNode();
231 labels.add(device.id().toString());
232 labels.add(device.chassisId().toString());
233 labels.add(""); // compact no-label view
234 labels.add(device.annotations().value("name"));
235
236 // Add labels, props and stuff the payload into envelope.
237 payload.set("labels", labels);
238 payload.set("props", props(device.annotations()));
Thomas Vachuska0f6baee2014-11-11 15:02:32 -0800239 addGeoLocation(device, payload);
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800240 addMetaUi(device.id().toString(), payload);
241
242 String type = (event.type() == DEVICE_ADDED) ? "addDevice" :
243 ((event.type() == DEVICE_REMOVED) ? "removeDevice" : "updateDevice");
244 return envelope(type, 0, payload);
245 }
246
247 // Produces a link event message to the client.
248 protected ObjectNode linkMessage(LinkEvent event) {
249 Link link = event.subject();
250 ObjectNode payload = mapper.createObjectNode()
251 .put("id", compactLinkString(link))
252 .put("type", link.type().toString().toLowerCase())
Thomas Vachuskae4cebaf2014-11-15 18:49:34 -0800253 .put("online", link.state() == Link.State.ACTIVE)
Thomas Vachuskacd2920c2014-11-19 14:49:55 -0800254 .put("linkWidth", 1.2)
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800255 .put("src", link.src().deviceId().toString())
256 .put("srcPort", link.src().port().toString())
257 .put("dst", link.dst().deviceId().toString())
258 .put("dstPort", link.dst().port().toString());
259 String type = (event.type() == LINK_ADDED) ? "addLink" :
260 ((event.type() == LINK_REMOVED) ? "removeLink" : "updateLink");
261 return envelope(type, 0, payload);
262 }
263
264 // Produces a host event message to the client.
265 protected ObjectNode hostMessage(HostEvent event) {
266 Host host = event.subject();
Thomas Vachuska89543292014-11-19 11:28:33 -0800267 String hostType = host.annotations().value("type");
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800268 ObjectNode payload = mapper.createObjectNode()
269 .put("id", host.id().toString())
Thomas Vachuska89543292014-11-19 11:28:33 -0800270 .put("type", isNullOrEmpty(hostType) ? "host" : hostType)
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800271 .put("ingress", compactLinkString(edgeLink(host, true)))
272 .put("egress", compactLinkString(edgeLink(host, false)));
Thomas Vachuska0f6baee2014-11-11 15:02:32 -0800273 payload.set("cp", hostConnect(mapper, host.location()));
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800274 payload.set("labels", labels(mapper, ip(host.ipAddresses()),
275 host.mac().toString()));
276 payload.set("props", props(host.annotations()));
Thomas Vachuska0f6baee2014-11-11 15:02:32 -0800277 addGeoLocation(host, payload);
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800278 addMetaUi(host.id().toString(), payload);
279
280 String type = (event.type() == HOST_ADDED) ? "addHost" :
281 ((event.type() == HOST_REMOVED) ? "removeHost" : "updateHost");
282 return envelope(type, 0, payload);
283 }
284
285 // Encodes the specified host location into a JSON object.
Thomas Vachuska0f6baee2014-11-11 15:02:32 -0800286 private ObjectNode hostConnect(ObjectMapper mapper, HostLocation location) {
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800287 return mapper.createObjectNode()
288 .put("device", location.deviceId().toString())
289 .put("port", location.port().toLong());
290 }
291
292 // Encodes the specified list of labels a JSON array.
293 private ArrayNode labels(ObjectMapper mapper, String... labels) {
294 ArrayNode json = mapper.createArrayNode();
295 for (String label : labels) {
296 json.add(label);
297 }
298 return json;
299 }
300
Thomas Vachuska0f6baee2014-11-11 15:02:32 -0800301 // Returns the name of the master node for the specified device id.
302 private String master(DeviceId deviceId) {
303 NodeId master = mastershipService.getMasterFor(deviceId);
304 return master != null ? master.toString() : "";
305 }
306
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800307 // Generates an edge link from the specified host location.
308 private EdgeLink edgeLink(Host host, boolean ingress) {
309 return new DefaultEdgeLink(PID, new ConnectPoint(host.id(), portNumber(0)),
310 host.location(), ingress);
311 }
312
313 // Adds meta UI information for the specified object.
314 private void addMetaUi(String id, ObjectNode payload) {
315 ObjectNode meta = metaUi.get(id);
316 if (meta != null) {
317 payload.set("metaUi", meta);
318 }
319 }
320
Thomas Vachuska0f6baee2014-11-11 15:02:32 -0800321 // Adds a geo location JSON to the specified payload object.
322 private void addGeoLocation(Annotated annotated, ObjectNode payload) {
323 Annotations annotations = annotated.annotations();
Thomas Vachuska16582df2014-11-15 20:12:17 -0800324 if (annotations == null) {
325 return;
326 }
327
Thomas Vachuska0f6baee2014-11-11 15:02:32 -0800328 String slat = annotations.value("latitude");
329 String slng = annotations.value("longitude");
330 try {
331 if (slat != null && slng != null && !slat.isEmpty() && !slng.isEmpty()) {
332 double lat = Double.parseDouble(slat);
333 double lng = Double.parseDouble(slng);
334 ObjectNode loc = mapper.createObjectNode()
335 .put("type", "latlng").put("lat", lat).put("lng", lng);
336 payload.set("location", loc);
337 }
338 } catch (NumberFormatException e) {
339 log.warn("Invalid geo data latitude={}; longiture={}", slat, slng);
340 }
341 }
342
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800343 // Updates meta UI information for the specified object.
344 protected void updateMetaUi(ObjectNode event) {
345 ObjectNode payload = payload(event);
Simon Hunt3b9cddb2014-11-11 20:50:04 -0800346 metaUi.put(string(payload, "id"), (ObjectNode) payload.path("memento"));
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800347 }
348
349 // Returns device details response.
350 protected ObjectNode deviceDetails(DeviceId deviceId, long sid) {
351 Device device = deviceService.getDevice(deviceId);
352 Annotations annot = device.annotations();
353 int portCount = deviceService.getPorts(deviceId).size();
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800354 return envelope("showDetails", sid,
355 json(deviceId.toString(),
356 device.type().toString().toLowerCase(),
357 new Prop("Name", annot.value("name")),
358 new Prop("Vendor", device.manufacturer()),
359 new Prop("H/W Version", device.hwVersion()),
360 new Prop("S/W Version", device.swVersion()),
361 new Prop("Serial Number", device.serialNumber()),
362 new Separator(),
363 new Prop("Latitude", annot.value("latitude")),
364 new Prop("Longitude", annot.value("longitude")),
365 new Prop("Ports", Integer.toString(portCount)),
366 new Separator(),
Thomas Vachuska0f6baee2014-11-11 15:02:32 -0800367 new Prop("Master", master(deviceId))));
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800368 }
369
370 // Returns host details response.
371 protected ObjectNode hostDetails(HostId hostId, long sid) {
372 Host host = hostService.getHost(hostId);
373 Annotations annot = host.annotations();
Thomas Vachuska89543292014-11-19 11:28:33 -0800374 String type = annot.value("type");
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800375 return envelope("showDetails", sid,
Thomas Vachuska89543292014-11-19 11:28:33 -0800376 json(hostId.toString(), isNullOrEmpty(type) ? "host" : type,
377 new Prop("Name", annot.value("name")),
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800378 new Prop("MAC", host.mac().toString()),
379 new Prop("IP", host.ipAddresses().toString()),
380 new Separator(),
381 new Prop("Latitude", annot.value("latitude")),
382 new Prop("Longitude", annot.value("longitude"))));
383 }
384
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800385 // Produces a path payload to the client.
Thomas Vachuska0f6baee2014-11-11 15:02:32 -0800386 protected ObjectNode pathMessage(Path path, String type) {
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800387 ObjectNode payload = mapper.createObjectNode();
388 ArrayNode links = mapper.createArrayNode();
389 for (Link link : path.links()) {
390 links.add(compactLinkString(link));
391 }
392
Thomas Vachuska0f6baee2014-11-11 15:02:32 -0800393 payload.put("type", type).set("links", links);
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800394 return payload;
395 }
396
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800397
398 // Produces JSON message to trigger traffic visualization
Thomas Vachuskae7591e52014-11-13 21:31:15 -0800399 protected ObjectNode trafficMessage(long sid, TrafficClass... trafficClasses) {
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800400 ObjectNode payload = mapper.createObjectNode();
401 ArrayNode paths = mapper.createArrayNode();
402 payload.set("paths", paths);
403
Thomas Vachuskae7591e52014-11-13 21:31:15 -0800404 for (TrafficClass trafficClass : trafficClasses) {
405 for (Intent intent : trafficClass.intents) {
Thomas Vachuska22e34922014-11-14 00:40:55 -0800406 boolean isOptical = intent instanceof OpticalConnectivityIntent;
Thomas Vachuskae7591e52014-11-13 21:31:15 -0800407 List<Intent> installables = intentService.getInstallableIntents(intent.id());
Thomas Vachuska22e34922014-11-14 00:40:55 -0800408 if (installables != null) {
409 for (Intent installable : installables) {
410 String cls = isOptical ? trafficClass.type + " optical" : trafficClass.type;
Thomas Vachuskadea4cb32014-11-14 12:14:30 -0800411 if (installable instanceof PathIntent) {
412 addPathTraffic(paths, cls, ((PathIntent) installable).path().links());
413 } else if (installable instanceof LinkCollectionIntent) {
414 addPathTraffic(paths, cls, ((LinkCollectionIntent) installable).links());
415 } else if (installable instanceof OpticalPathIntent) {
416 addPathTraffic(paths, cls, ((OpticalPathIntent) installable).path().links());
Thomas Vachuska22e34922014-11-14 00:40:55 -0800417 }
Thomas Vachuskadea4cb32014-11-14 12:14:30 -0800418
Thomas Vachuskae7591e52014-11-13 21:31:15 -0800419 }
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800420 }
421 }
422 }
423
424 return envelope("showTraffic", sid, payload);
425 }
426
427 // Adds the link segments (path or tree) associated with the specified
428 // connectivity intent
Thomas Vachuskadea4cb32014-11-14 12:14:30 -0800429 protected void addPathTraffic(ArrayNode paths, String type, Iterable<Link> links) {
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800430 ObjectNode pathNode = mapper.createObjectNode();
431 ArrayNode linksNode = mapper.createArrayNode();
432
Thomas Vachuska22e34922014-11-14 00:40:55 -0800433 if (links != null) {
434 ArrayNode labels = mapper.createArrayNode();
Thomas Vachuska7c27ad72014-11-14 16:20:10 -0800435 boolean hasTraffic = false;
Thomas Vachuska22e34922014-11-14 00:40:55 -0800436 for (Link link : links) {
Thomas Vachuska20322ff2014-11-19 16:22:25 -0800437 if (isInfrastructureEgress(link)) {
438 linksNode.add(compactLinkString(link));
439 Load load = statService.load(link);
440 String label = "";
441 if (load.rate() > 0) {
442 hasTraffic = true;
443 label = format(load);
444 }
445 labels.add(label);
Thomas Vachuska7c27ad72014-11-14 16:20:10 -0800446 }
Thomas Vachuska22e34922014-11-14 00:40:55 -0800447 }
448 pathNode.put("class", hasTraffic ? type + " animated" : type);
449 pathNode.put("traffic", hasTraffic);
450 pathNode.set("links", linksNode);
451 pathNode.set("labels", labels);
452 paths.add(pathNode);
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800453 }
Thomas Vachuska22e34922014-11-14 00:40:55 -0800454 }
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800455
Thomas Vachuska20322ff2014-11-19 16:22:25 -0800456 // Poor-mans formatting to get the labels with byte counts looking nice.
457 private String format(Load load) {
458 long bytes = load.latest();
459 String unit;
460 double value;
461 if (bytes > GB) {
462 value = bytes / GB;
463 unit = GB_UNIT;
464 } else if (bytes > MB) {
465 value = bytes / MB;
466 unit = MB_UNIT;
467 } else if (bytes > KB) {
468 value = bytes / KB;
469 unit = KB_UNIT;
470 } else {
471 value = bytes;
472 unit = B_UNIT;
473 }
474 DecimalFormat format = new DecimalFormat("#,###.##");
475 return format.format(value) + " " + unit;
476 }
477
478 private boolean isInfrastructureEgress(Link link) {
479 return link.src().elementId() instanceof DeviceId;
480 }
481
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800482 // Produces compact string representation of a link.
483 private static String compactLinkString(Link link) {
484 return String.format(COMPACT, link.src().elementId(), link.src().port(),
485 link.dst().elementId(), link.dst().port());
486 }
487
488 // Produces JSON property details.
489 private ObjectNode json(String id, String type, Prop... props) {
490 ObjectMapper mapper = new ObjectMapper();
491 ObjectNode result = mapper.createObjectNode()
492 .put("id", id).put("type", type);
493 ObjectNode pnode = mapper.createObjectNode();
494 ArrayNode porder = mapper.createArrayNode();
495 for (Prop p : props) {
496 porder.add(p.key);
497 pnode.put(p.key, p.value);
498 }
499 result.set("propOrder", porder);
500 result.set("props", pnode);
501 return result;
502 }
503
504 // Auxiliary key/value carrier.
505 private class Prop {
506 public final String key;
507 public final String value;
508
509 protected Prop(String key, String value) {
510 this.key = key;
511 this.value = value;
512 }
513 }
514
515 // Auxiliary properties separator
516 private class Separator extends Prop {
517 protected Separator() {
518 super("-", "");
519 }
520 }
521
Thomas Vachuskae7591e52014-11-13 21:31:15 -0800522 // Auxiliary carrier of data for requesting traffic message.
523 protected class TrafficClass {
524 public final String type;
525 public final Set<Intent> intents;
526
527 TrafficClass(String type, Set<Intent> intents) {
528 this.type = type;
529 this.intents = intents;
530 }
531 }
532
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800533}