blob: d32ad1d9146cb14f078a2a8c2d0dc484c8e5fe87 [file] [log] [blame]
Thomas Vachuska329af532015-03-10 02:08:33 -07001/*
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 */
16package org.onosproject.ui.impl;
17
18import com.fasterxml.jackson.databind.JsonNode;
Thomas Vachuska329af532015-03-10 02:08:33 -070019import com.fasterxml.jackson.databind.node.ArrayNode;
20import com.fasterxml.jackson.databind.node.ObjectNode;
Thomas Vachuskac0fe09a2015-05-21 12:56:22 -070021import com.google.common.collect.ImmutableList;
Thomas Vachuska329af532015-03-10 02:08:33 -070022import org.onlab.osgi.ServiceDirectory;
23import org.onlab.packet.IpAddress;
24import org.onosproject.cluster.ClusterEvent;
25import org.onosproject.cluster.ClusterService;
26import org.onosproject.cluster.ControllerNode;
27import org.onosproject.cluster.NodeId;
28import org.onosproject.core.CoreService;
Thomas Vachuskaf0397b52015-05-29 13:50:17 -070029import org.onosproject.incubator.net.PortStatisticsService;
cheng fan35dc0f22015-06-10 06:02:47 +080030import org.onosproject.incubator.net.tunnel.OpticalTunnelEndPoint;
31import org.onosproject.incubator.net.tunnel.Tunnel;
32import org.onosproject.incubator.net.tunnel.TunnelService;
Thomas Vachuska329af532015-03-10 02:08:33 -070033import org.onosproject.mastership.MastershipService;
34import org.onosproject.net.Annotated;
35import org.onosproject.net.AnnotationKeys;
36import org.onosproject.net.Annotations;
37import org.onosproject.net.ConnectPoint;
38import org.onosproject.net.DefaultEdgeLink;
39import org.onosproject.net.Device;
40import org.onosproject.net.DeviceId;
41import org.onosproject.net.EdgeLink;
42import org.onosproject.net.Host;
43import org.onosproject.net.HostId;
44import org.onosproject.net.HostLocation;
45import org.onosproject.net.Link;
46import org.onosproject.net.LinkKey;
Thomas Vachuskac0fe09a2015-05-21 12:56:22 -070047import org.onosproject.net.NetworkResource;
Thomas Vachuska329af532015-03-10 02:08:33 -070048import org.onosproject.net.PortNumber;
49import org.onosproject.net.device.DeviceEvent;
50import org.onosproject.net.device.DeviceService;
51import org.onosproject.net.flow.FlowEntry;
52import org.onosproject.net.flow.FlowRuleService;
53import org.onosproject.net.flow.TrafficTreatment;
54import org.onosproject.net.flow.instructions.Instruction;
55import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
56import org.onosproject.net.host.HostEvent;
57import org.onosproject.net.host.HostService;
Thomas Vachuskac0fe09a2015-05-21 12:56:22 -070058import org.onosproject.net.intent.FlowRuleIntent;
Thomas Vachuska329af532015-03-10 02:08:33 -070059import org.onosproject.net.intent.Intent;
60import org.onosproject.net.intent.IntentService;
61import org.onosproject.net.intent.LinkCollectionIntent;
62import org.onosproject.net.intent.OpticalConnectivityIntent;
63import org.onosproject.net.intent.OpticalPathIntent;
64import org.onosproject.net.intent.PathIntent;
65import org.onosproject.net.link.LinkEvent;
66import org.onosproject.net.link.LinkService;
67import org.onosproject.net.provider.ProviderId;
68import org.onosproject.net.statistic.Load;
69import org.onosproject.net.statistic.StatisticService;
70import org.onosproject.net.topology.Topology;
71import org.onosproject.net.topology.TopologyService;
Simon Huntd2747a02015-04-30 22:41:16 -070072import org.onosproject.ui.JsonUtils;
Thomas Vachuska329af532015-03-10 02:08:33 -070073import org.onosproject.ui.UiConnection;
Simon Hunta0ddb022015-05-01 09:53:01 -070074import org.onosproject.ui.UiMessageHandler;
Simon Hunt0af1ec32015-07-24 12:17:55 -070075import org.onosproject.ui.topo.PropertyPanel;
Thomas Vachuska329af532015-03-10 02:08:33 -070076import org.slf4j.Logger;
77import org.slf4j.LoggerFactory;
78
79import java.text.DecimalFormat;
80import java.util.ArrayList;
81import java.util.Collection;
82import java.util.Collections;
83import java.util.HashMap;
84import java.util.HashSet;
85import java.util.Iterator;
86import java.util.List;
87import java.util.Map;
88import java.util.Set;
89import java.util.concurrent.ConcurrentHashMap;
90
91import static com.google.common.base.Preconditions.checkNotNull;
92import static com.google.common.base.Strings.isNullOrEmpty;
93import static org.onosproject.cluster.ClusterEvent.Type.INSTANCE_ADDED;
94import static org.onosproject.cluster.ClusterEvent.Type.INSTANCE_REMOVED;
95import static org.onosproject.cluster.ControllerNode.State.ACTIVE;
Thomas Vachuska204cb6c2015-06-04 00:03:06 -070096import static org.onosproject.net.DefaultEdgeLink.createEdgeLink;
Thomas Vachuska329af532015-03-10 02:08:33 -070097import static org.onosproject.net.DeviceId.deviceId;
98import static org.onosproject.net.HostId.hostId;
99import static org.onosproject.net.LinkKey.linkKey;
100import static org.onosproject.net.PortNumber.P0;
101import static org.onosproject.net.PortNumber.portNumber;
102import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_ADDED;
103import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_REMOVED;
104import static org.onosproject.net.host.HostEvent.Type.HOST_ADDED;
105import static org.onosproject.net.host.HostEvent.Type.HOST_REMOVED;
106import static org.onosproject.net.link.LinkEvent.Type.LINK_ADDED;
107import static org.onosproject.net.link.LinkEvent.Type.LINK_REMOVED;
Thomas Vachuska204cb6c2015-06-04 00:03:06 -0700108import static org.onosproject.ui.impl.TopologyViewMessageHandlerBase.StatsType.FLOW;
109import static org.onosproject.ui.impl.TopologyViewMessageHandlerBase.StatsType.PORT;
Thomas Vachuska329af532015-03-10 02:08:33 -0700110
111/**
112 * Facility for creating messages bound for the topology viewer.
113 */
Simon Hunta0ddb022015-05-01 09:53:01 -0700114public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
Thomas Vachuska329af532015-03-10 02:08:33 -0700115
Simon Huntd2747a02015-04-30 22:41:16 -0700116 protected static final Logger log =
117 LoggerFactory.getLogger(TopologyViewMessageHandlerBase.class);
Thomas Vachuska329af532015-03-10 02:08:33 -0700118
Simon Huntd2747a02015-04-30 22:41:16 -0700119 private static final ProviderId PID =
120 new ProviderId("core", "org.onosproject.core", true);
Thomas Vachuska329af532015-03-10 02:08:33 -0700121 private static final String COMPACT = "%s/%s-%s/%s";
122
Srikanth Vavilapalli8c1ccca2015-06-08 19:23:24 -0700123 private static final double KILO = 1024;
124 private static final double MEGA = 1024 * KILO;
125 private static final double GIGA = 1024 * MEGA;
Thomas Vachuska329af532015-03-10 02:08:33 -0700126
Srikanth Vavilapalli8c1ccca2015-06-08 19:23:24 -0700127 private static final String GBITS_UNIT = "Gb";
128 private static final String MBITS_UNIT = "Mb";
129 private static final String KBITS_UNIT = "Kb";
130 private static final String BITS_UNIT = "b";
131 private static final String GBYTES_UNIT = "GB";
132 private static final String MBYTES_UNIT = "MB";
133 private static final String KBYTES_UNIT = "KB";
134 private static final String BYTES_UNIT = "B";
135 //4 Kilo Bytes as threshold
136 private static final double BPS_THRESHOLD = 4 * KILO;
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700137
Thomas Vachuska329af532015-03-10 02:08:33 -0700138 protected ServiceDirectory directory;
139 protected ClusterService clusterService;
140 protected DeviceService deviceService;
141 protected LinkService linkService;
142 protected HostService hostService;
143 protected MastershipService mastershipService;
144 protected IntentService intentService;
145 protected FlowRuleService flowService;
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700146 protected StatisticService flowStatsService;
147 protected PortStatisticsService portStatsService;
Thomas Vachuska329af532015-03-10 02:08:33 -0700148 protected TopologyService topologyService;
cheng fan35dc0f22015-06-10 06:02:47 +0800149 protected TunnelService tunnelService;
Thomas Vachuska329af532015-03-10 02:08:33 -0700150
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700151 protected enum StatsType {
152 FLOW, PORT
153 }
154
Thomas Vachuska329af532015-03-10 02:08:33 -0700155 private String version;
156
157 // TODO: extract into an external & durable state; good enough for now and demo
158 private static Map<String, ObjectNode> metaUi = new ConcurrentHashMap<>();
159
160 /**
Thomas Vachuska329af532015-03-10 02:08:33 -0700161 * Returns read-only view of the meta-ui information.
162 *
163 * @return map of id to meta-ui mementos
164 */
165 static Map<String, ObjectNode> getMetaUi() {
166 return Collections.unmodifiableMap(metaUi);
167 }
168
169 @Override
170 public void init(UiConnection connection, ServiceDirectory directory) {
171 super.init(connection, directory);
172 this.directory = checkNotNull(directory, "Directory cannot be null");
173 clusterService = directory.get(ClusterService.class);
174 deviceService = directory.get(DeviceService.class);
175 linkService = directory.get(LinkService.class);
176 hostService = directory.get(HostService.class);
177 mastershipService = directory.get(MastershipService.class);
178 intentService = directory.get(IntentService.class);
179 flowService = directory.get(FlowRuleService.class);
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700180 flowStatsService = directory.get(StatisticService.class);
181 portStatsService = directory.get(PortStatisticsService.class);
Thomas Vachuska329af532015-03-10 02:08:33 -0700182 topologyService = directory.get(TopologyService.class);
cheng fan35dc0f22015-06-10 06:02:47 +0800183 tunnelService = directory.get(TunnelService.class);
Thomas Vachuska329af532015-03-10 02:08:33 -0700184
185 String ver = directory.get(CoreService.class).version().toString();
186 version = ver.replace(".SNAPSHOT", "*").replaceFirst("~.*$", "");
187 }
188
Thomas Vachuska329af532015-03-10 02:08:33 -0700189 // Returns the specified set of IP addresses as a string.
190 private String ip(Set<IpAddress> ipAddresses) {
191 Iterator<IpAddress> it = ipAddresses.iterator();
192 return it.hasNext() ? it.next().toString() : "unknown";
193 }
194
195 // Produces JSON structure from annotations.
196 private JsonNode props(Annotations annotations) {
Simon Huntda580882015-05-12 20:58:18 -0700197 ObjectNode props = objectNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700198 if (annotations != null) {
199 for (String key : annotations.keys()) {
200 props.put(key, annotations.value(key));
201 }
202 }
203 return props;
204 }
205
206 // Produces an informational log message event bound to the client.
207 protected ObjectNode info(long id, String message) {
208 return message("info", id, message);
209 }
210
211 // Produces a warning log message event bound to the client.
212 protected ObjectNode warning(long id, String message) {
213 return message("warning", id, message);
214 }
215
216 // Produces an error log message event bound to the client.
217 protected ObjectNode error(long id, String message) {
218 return message("error", id, message);
219 }
220
221 // Produces a log message event bound to the client.
222 private ObjectNode message(String severity, long id, String message) {
Simon Huntda580882015-05-12 20:58:18 -0700223 ObjectNode payload = objectNode()
Simon Huntd2747a02015-04-30 22:41:16 -0700224 .put("severity", severity)
225 .put("message", message);
Thomas Vachuska329af532015-03-10 02:08:33 -0700226
Simon Huntd2747a02015-04-30 22:41:16 -0700227 return JsonUtils.envelope("message", id, payload);
Thomas Vachuska329af532015-03-10 02:08:33 -0700228 }
229
230 // Produces a set of all hosts listed in the specified JSON array.
231 protected Set<Host> getHosts(ArrayNode array) {
232 Set<Host> hosts = new HashSet<>();
233 if (array != null) {
234 for (JsonNode node : array) {
235 try {
236 addHost(hosts, hostId(node.asText()));
237 } catch (IllegalArgumentException e) {
238 log.debug("Skipping ID {}", node.asText());
239 }
240 }
241 }
242 return hosts;
243 }
244
245 // Adds the specified host to the set of hosts.
246 private void addHost(Set<Host> hosts, HostId hostId) {
247 Host host = hostService.getHost(hostId);
248 if (host != null) {
249 hosts.add(host);
250 }
251 }
252
253
254 // Produces a set of all devices listed in the specified JSON array.
255 protected Set<Device> getDevices(ArrayNode array) {
256 Set<Device> devices = new HashSet<>();
257 if (array != null) {
258 for (JsonNode node : array) {
259 try {
260 addDevice(devices, deviceId(node.asText()));
261 } catch (IllegalArgumentException e) {
262 log.debug("Skipping ID {}", node.asText());
263 }
264 }
265 }
266 return devices;
267 }
268
269 private void addDevice(Set<Device> devices, DeviceId deviceId) {
270 Device device = deviceService.getDevice(deviceId);
271 if (device != null) {
272 devices.add(device);
273 }
274 }
275
276 protected void addHover(Set<Host> hosts, Set<Device> devices, String hover) {
277 try {
278 addHost(hosts, hostId(hover));
279 } catch (IllegalArgumentException e) {
280 try {
281 addDevice(devices, deviceId(hover));
282 } catch (IllegalArgumentException ne) {
283 log.debug("Skipping ID {}", hover);
284 }
285 }
286 }
287
288 // Produces a cluster instance message to the client.
289 protected ObjectNode instanceMessage(ClusterEvent event, String messageType) {
290 ControllerNode node = event.subject();
291 int switchCount = mastershipService.getDevicesOf(node.id()).size();
Simon Huntda580882015-05-12 20:58:18 -0700292 ObjectNode payload = objectNode()
Thomas Vachuska329af532015-03-10 02:08:33 -0700293 .put("id", node.id().toString())
294 .put("ip", node.ip().toString())
295 .put("online", clusterService.getState(node.id()) == ACTIVE)
Thomas Vachuskab6acc7b2015-03-11 09:11:21 -0700296 .put("uiAttached", node.equals(clusterService.getLocalNode()))
Thomas Vachuska329af532015-03-10 02:08:33 -0700297 .put("switches", switchCount);
298
Simon Huntda580882015-05-12 20:58:18 -0700299 ArrayNode labels = arrayNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700300 labels.add(node.id().toString());
301 labels.add(node.ip().toString());
302
303 // Add labels, props and stuff the payload into envelope.
304 payload.set("labels", labels);
305 addMetaUi(node.id().toString(), payload);
306
307 String type = messageType != null ? messageType :
308 ((event.type() == INSTANCE_ADDED) ? "addInstance" :
309 ((event.type() == INSTANCE_REMOVED ? "removeInstance" :
310 "addInstance")));
Simon Huntd2747a02015-04-30 22:41:16 -0700311 return JsonUtils.envelope(type, 0, payload);
Thomas Vachuska329af532015-03-10 02:08:33 -0700312 }
313
314 // Produces a device event message to the client.
315 protected ObjectNode deviceMessage(DeviceEvent event) {
316 Device device = event.subject();
Simon Huntda580882015-05-12 20:58:18 -0700317 ObjectNode payload = objectNode()
Thomas Vachuska329af532015-03-10 02:08:33 -0700318 .put("id", device.id().toString())
319 .put("type", device.type().toString().toLowerCase())
320 .put("online", deviceService.isAvailable(device.id()))
321 .put("master", master(device.id()));
322
323 // Generate labels: id, chassis id, no-label, optional-name
324 String name = device.annotations().value(AnnotationKeys.NAME);
Simon Huntda580882015-05-12 20:58:18 -0700325 ArrayNode labels = arrayNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700326 labels.add("");
327 labels.add(isNullOrEmpty(name) ? device.id().toString() : name);
328 labels.add(device.id().toString());
329
330 // Add labels, props and stuff the payload into envelope.
331 payload.set("labels", labels);
332 payload.set("props", props(device.annotations()));
333 addGeoLocation(device, payload);
334 addMetaUi(device.id().toString(), payload);
335
336 String type = (event.type() == DEVICE_ADDED) ? "addDevice" :
337 ((event.type() == DEVICE_REMOVED) ? "removeDevice" : "updateDevice");
Simon Huntd2747a02015-04-30 22:41:16 -0700338 return JsonUtils.envelope(type, 0, payload);
Thomas Vachuska329af532015-03-10 02:08:33 -0700339 }
340
341 // Produces a link event message to the client.
342 protected ObjectNode linkMessage(LinkEvent event) {
343 Link link = event.subject();
Simon Huntda580882015-05-12 20:58:18 -0700344 ObjectNode payload = objectNode()
Thomas Vachuska329af532015-03-10 02:08:33 -0700345 .put("id", compactLinkString(link))
346 .put("type", link.type().toString().toLowerCase())
347 .put("online", link.state() == Link.State.ACTIVE)
348 .put("linkWidth", 1.2)
349 .put("src", link.src().deviceId().toString())
350 .put("srcPort", link.src().port().toString())
351 .put("dst", link.dst().deviceId().toString())
352 .put("dstPort", link.dst().port().toString());
353 String type = (event.type() == LINK_ADDED) ? "addLink" :
354 ((event.type() == LINK_REMOVED) ? "removeLink" : "updateLink");
Simon Huntd2747a02015-04-30 22:41:16 -0700355 return JsonUtils.envelope(type, 0, payload);
Thomas Vachuska329af532015-03-10 02:08:33 -0700356 }
357
358 // Produces a host event message to the client.
359 protected ObjectNode hostMessage(HostEvent event) {
360 Host host = event.subject();
361 String hostType = host.annotations().value(AnnotationKeys.TYPE);
Simon Huntda580882015-05-12 20:58:18 -0700362 ObjectNode payload = objectNode()
Thomas Vachuska329af532015-03-10 02:08:33 -0700363 .put("id", host.id().toString())
364 .put("type", isNullOrEmpty(hostType) ? "endstation" : hostType)
365 .put("ingress", compactLinkString(edgeLink(host, true)))
366 .put("egress", compactLinkString(edgeLink(host, false)));
Simon Huntda580882015-05-12 20:58:18 -0700367 payload.set("cp", hostConnect(host.location()));
368 payload.set("labels", labels(ip(host.ipAddresses()),
Thomas Vachuska329af532015-03-10 02:08:33 -0700369 host.mac().toString()));
370 payload.set("props", props(host.annotations()));
371 addGeoLocation(host, payload);
372 addMetaUi(host.id().toString(), payload);
373
374 String type = (event.type() == HOST_ADDED) ? "addHost" :
375 ((event.type() == HOST_REMOVED) ? "removeHost" : "updateHost");
Simon Huntd2747a02015-04-30 22:41:16 -0700376 return JsonUtils.envelope(type, 0, payload);
Thomas Vachuska329af532015-03-10 02:08:33 -0700377 }
378
379 // Encodes the specified host location into a JSON object.
Simon Huntda580882015-05-12 20:58:18 -0700380 private ObjectNode hostConnect(HostLocation location) {
381 return objectNode()
Thomas Vachuska329af532015-03-10 02:08:33 -0700382 .put("device", location.deviceId().toString())
383 .put("port", location.port().toLong());
384 }
385
386 // Encodes the specified list of labels a JSON array.
Simon Huntda580882015-05-12 20:58:18 -0700387 private ArrayNode labels(String... labels) {
388 ArrayNode json = arrayNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700389 for (String label : labels) {
390 json.add(label);
391 }
392 return json;
393 }
394
395 // Returns the name of the master node for the specified device id.
396 private String master(DeviceId deviceId) {
397 NodeId master = mastershipService.getMasterFor(deviceId);
398 return master != null ? master.toString() : "";
399 }
400
401 // Generates an edge link from the specified host location.
402 private EdgeLink edgeLink(Host host, boolean ingress) {
403 return new DefaultEdgeLink(PID, new ConnectPoint(host.id(), portNumber(0)),
404 host.location(), ingress);
405 }
406
407 // Adds meta UI information for the specified object.
408 private void addMetaUi(String id, ObjectNode payload) {
409 ObjectNode meta = metaUi.get(id);
410 if (meta != null) {
411 payload.set("metaUi", meta);
412 }
413 }
414
415 // Adds a geo location JSON to the specified payload object.
416 private void addGeoLocation(Annotated annotated, ObjectNode payload) {
417 Annotations annotations = annotated.annotations();
418 if (annotations == null) {
419 return;
420 }
421
422 String slat = annotations.value(AnnotationKeys.LATITUDE);
423 String slng = annotations.value(AnnotationKeys.LONGITUDE);
424 try {
425 if (slat != null && slng != null && !slat.isEmpty() && !slng.isEmpty()) {
426 double lat = Double.parseDouble(slat);
427 double lng = Double.parseDouble(slng);
Simon Huntda580882015-05-12 20:58:18 -0700428 ObjectNode loc = objectNode()
Thomas Vachuska329af532015-03-10 02:08:33 -0700429 .put("type", "latlng").put("lat", lat).put("lng", lng);
430 payload.set("location", loc);
431 }
432 } catch (NumberFormatException e) {
433 log.warn("Invalid geo data latitude={}; longiture={}", slat, slng);
434 }
435 }
436
437 // Updates meta UI information for the specified object.
Simon Huntd2747a02015-04-30 22:41:16 -0700438 protected void updateMetaUi(ObjectNode payload) {
439 metaUi.put(JsonUtils.string(payload, "id"),
440 JsonUtils.node(payload, "memento"));
Thomas Vachuska329af532015-03-10 02:08:33 -0700441 }
442
Simon Huntb745ca62015-07-28 15:37:11 -0700443 // -----------------------------------------------------------------------
444 // Create models of the data to return, that overlays can adjust / augment
445
446 // Returns property panel model for summary response.
Simon Hunt0af1ec32015-07-24 12:17:55 -0700447 protected PropertyPanel summmaryMessage(long sid) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700448 Topology topology = topologyService.currentTopology();
Simon Hunt0af1ec32015-07-24 12:17:55 -0700449 PropertyPanel pp = new PropertyPanel("ONOS Summary", "node")
Simon Huntb745ca62015-07-28 15:37:11 -0700450 .add(new PropertyPanel.Prop("Devices", topology.deviceCount()))
451 .add(new PropertyPanel.Prop("Links", topology.linkCount()))
452 .add(new PropertyPanel.Prop("Hosts", hostService.getHostCount()))
453 .add(new PropertyPanel.Prop("Topology SCCs", topology.clusterCount()))
Simon Hunt0af1ec32015-07-24 12:17:55 -0700454 .add(new PropertyPanel.Separator())
Simon Huntb745ca62015-07-28 15:37:11 -0700455 .add(new PropertyPanel.Prop("Intents", intentService.getIntentCount()))
456 .add(new PropertyPanel.Prop("Tunnels", tunnelService.tunnelCount()))
457 .add(new PropertyPanel.Prop("Flows", flowService.getFlowRuleCount()))
Simon Hunt0af1ec32015-07-24 12:17:55 -0700458 .add(new PropertyPanel.Prop("Version", version));
459
460 return pp;
Thomas Vachuska329af532015-03-10 02:08:33 -0700461 }
462
Simon Huntb745ca62015-07-28 15:37:11 -0700463 // Returns property panel model for device details response.
464 protected PropertyPanel deviceDetails(DeviceId deviceId, long sid) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700465 Device device = deviceService.getDevice(deviceId);
466 Annotations annot = device.annotations();
467 String name = annot.value(AnnotationKeys.NAME);
468 int portCount = deviceService.getPorts(deviceId).size();
469 int flowCount = getFlowCount(deviceId);
cheng fan35dc0f22015-06-10 06:02:47 +0800470 int tunnelCount = getTunnelCount(deviceId);
Simon Huntb745ca62015-07-28 15:37:11 -0700471
472 String title = isNullOrEmpty(name) ? deviceId.toString() : name;
473 String typeId = device.type().toString().toLowerCase();
474
475 PropertyPanel pp = new PropertyPanel(title, typeId)
476 .id(deviceId.toString())
477 .add(new PropertyPanel.Prop("URI", deviceId.toString()))
478 .add(new PropertyPanel.Prop("Vendor", device.manufacturer()))
479 .add(new PropertyPanel.Prop("H/W Version", device.hwVersion()))
480 .add(new PropertyPanel.Prop("S/W Version", device.swVersion()))
481 .add(new PropertyPanel.Prop("Serial Number", device.serialNumber()))
482 .add(new PropertyPanel.Prop("Protocol", annot.value(AnnotationKeys.PROTOCOL)))
483 .add(new PropertyPanel.Separator())
484 .add(new PropertyPanel.Prop("Latitude", annot.value(AnnotationKeys.LATITUDE)))
485 .add(new PropertyPanel.Prop("Longitude", annot.value(AnnotationKeys.LONGITUDE)))
486 .add(new PropertyPanel.Separator())
487 .add(new PropertyPanel.Prop("Ports", portCount))
488 .add(new PropertyPanel.Prop("Flows", flowCount))
489 .add(new PropertyPanel.Prop("Tunnels", tunnelCount));
490
491 return pp;
Thomas Vachuska329af532015-03-10 02:08:33 -0700492 }
493
494 protected int getFlowCount(DeviceId deviceId) {
495 int count = 0;
Simon Huntb745ca62015-07-28 15:37:11 -0700496 for (FlowEntry flowEntry : flowService.getFlowEntries(deviceId)) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700497 count++;
Thomas Vachuska329af532015-03-10 02:08:33 -0700498 }
499 return count;
500 }
501
cheng fan35dc0f22015-06-10 06:02:47 +0800502 protected int getTunnelCount(DeviceId deviceId) {
503 int count = 0;
504 Collection<Tunnel> tunnels = tunnelService.queryAllTunnels();
505 for (Tunnel tunnel : tunnels) {
506 OpticalTunnelEndPoint src = (OpticalTunnelEndPoint) tunnel.src();
507 OpticalTunnelEndPoint dst = (OpticalTunnelEndPoint) tunnel.dst();
508 DeviceId srcDevice = (DeviceId) src.elementId().get();
509 DeviceId dstDevice = (DeviceId) dst.elementId().get();
Simon Huntb745ca62015-07-28 15:37:11 -0700510 if (srcDevice.toString().equals(deviceId.toString()) ||
511 dstDevice.toString().equals(deviceId.toString())) {
cheng fan35dc0f22015-06-10 06:02:47 +0800512 count++;
513 }
514 }
515 return count;
516 }
517
Thomas Vachuska329af532015-03-10 02:08:33 -0700518 // Counts all entries that egress on the given device links.
519 protected Map<Link, Integer> getFlowCounts(DeviceId deviceId) {
520 List<FlowEntry> entries = new ArrayList<>();
521 Set<Link> links = new HashSet<>(linkService.getDeviceEgressLinks(deviceId));
522 Set<Host> hosts = hostService.getConnectedHosts(deviceId);
Simon Huntb745ca62015-07-28 15:37:11 -0700523 for (FlowEntry flowEntry : flowService.getFlowEntries(deviceId)) {
524 entries.add(flowEntry);
Thomas Vachuska329af532015-03-10 02:08:33 -0700525 }
526
527 // Add all edge links to the set
528 if (hosts != null) {
529 for (Host host : hosts) {
530 links.add(new DefaultEdgeLink(host.providerId(),
531 new ConnectPoint(host.id(), P0),
532 host.location(), false));
533 }
534 }
535
536 Map<Link, Integer> counts = new HashMap<>();
537 for (Link link : links) {
538 counts.put(link, getEgressFlows(link, entries));
539 }
540 return counts;
541 }
542
543 // Counts all entries that egress on the link source port.
544 private Integer getEgressFlows(Link link, List<FlowEntry> entries) {
545 int count = 0;
546 PortNumber out = link.src().port();
547 for (FlowEntry entry : entries) {
548 TrafficTreatment treatment = entry.treatment();
Ray Milkey42507352015-03-20 15:16:10 -0700549 for (Instruction instruction : treatment.allInstructions()) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700550 if (instruction.type() == Instruction.Type.OUTPUT &&
551 ((OutputInstruction) instruction).port().equals(out)) {
552 count++;
553 }
554 }
555 }
556 return count;
557 }
558
559
560 // Returns host details response.
Simon Huntb745ca62015-07-28 15:37:11 -0700561 protected PropertyPanel hostDetails(HostId hostId, long sid) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700562 Host host = hostService.getHost(hostId);
563 Annotations annot = host.annotations();
564 String type = annot.value(AnnotationKeys.TYPE);
565 String name = annot.value(AnnotationKeys.NAME);
566 String vlan = host.vlan().toString();
Simon Huntb745ca62015-07-28 15:37:11 -0700567
568 String title = isNullOrEmpty(name) ? hostId.toString() : name;
569 String typeId = isNullOrEmpty(type) ? "endstation" : type;
570
571 PropertyPanel pp = new PropertyPanel(title, typeId)
572 .id(hostId.toString())
573 .add(new PropertyPanel.Prop("MAC", host.mac().toString()))
574 .add(new PropertyPanel.Prop("IP", host.ipAddresses().toString().replaceAll("[\\[\\]]", "")))
575 .add(new PropertyPanel.Prop("VLAN", vlan.equals("-1") ? "none" : vlan))
576 .add(new PropertyPanel.Separator())
577 .add(new PropertyPanel.Prop("Latitude", annot.value(AnnotationKeys.LATITUDE)))
578 .add(new PropertyPanel.Prop("Longitude", annot.value(AnnotationKeys.LONGITUDE)));
579
580 return pp;
Thomas Vachuska329af532015-03-10 02:08:33 -0700581 }
582
583
Simon Huntb745ca62015-07-28 15:37:11 -0700584 // TODO: migrate to Traffic overlay
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700585 // Produces JSON message to trigger flow traffic overview visualization
586 protected ObjectNode trafficSummaryMessage(StatsType type) {
Simon Huntda580882015-05-12 20:58:18 -0700587 ObjectNode payload = objectNode();
588 ArrayNode paths = arrayNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700589 payload.set("paths", paths);
590
Simon Huntda580882015-05-12 20:58:18 -0700591 ObjectNode pathNodeN = objectNode();
592 ArrayNode linksNodeN = arrayNode();
593 ArrayNode labelsN = arrayNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700594
595 pathNodeN.put("class", "plain").put("traffic", false);
596 pathNodeN.set("links", linksNodeN);
597 pathNodeN.set("labels", labelsN);
598 paths.add(pathNodeN);
599
Simon Huntda580882015-05-12 20:58:18 -0700600 ObjectNode pathNodeT = objectNode();
601 ArrayNode linksNodeT = arrayNode();
602 ArrayNode labelsT = arrayNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700603
604 pathNodeT.put("class", "secondary").put("traffic", true);
605 pathNodeT.set("links", linksNodeT);
606 pathNodeT.set("labels", labelsT);
607 paths.add(pathNodeT);
608
Thomas Vachuska204cb6c2015-06-04 00:03:06 -0700609 Map<LinkKey, BiLink> biLinks = consolidateLinks(linkService.getLinks());
610 addEdgeLinks(biLinks);
611 for (BiLink link : biLinks.values()) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700612 boolean bi = link.two != null;
Thomas Vachuska204cb6c2015-06-04 00:03:06 -0700613 if (type == FLOW) {
614 link.addLoad(getLinkLoad(link.one));
615 link.addLoad(bi ? getLinkLoad(link.two) : null);
616 } else if (type == PORT) {
Srikanth Vavilapalli8c1ccca2015-06-08 19:23:24 -0700617 //For a bi-directional traffic links, use
618 //the max link rate of either direction
619 link.addLoad(portStatsService.load(link.one.src()),
620 BPS_THRESHOLD,
621 portStatsService.load(link.one.dst()),
622 BPS_THRESHOLD);
Thomas Vachuska204cb6c2015-06-04 00:03:06 -0700623 }
624 if (link.hasTraffic) {
625 linksNodeT.add(compactLinkString(link.one));
626 labelsT.add(type == PORT ?
Srikanth Vavilapalli8c1ccca2015-06-08 19:23:24 -0700627 formatBitRate(link.rate) + "ps" :
Thomas Vachuska204cb6c2015-06-04 00:03:06 -0700628 formatBytes(link.bytes));
629 } else {
630 linksNodeN.add(compactLinkString(link.one));
631 labelsN.add("");
Thomas Vachuska329af532015-03-10 02:08:33 -0700632 }
633 }
Simon Huntd2747a02015-04-30 22:41:16 -0700634 return JsonUtils.envelope("showTraffic", 0, payload);
Thomas Vachuska329af532015-03-10 02:08:33 -0700635 }
636
Thomas Vachuska204cb6c2015-06-04 00:03:06 -0700637 private Load getLinkLoad(Link link) {
638 if (link.src().elementId() instanceof DeviceId) {
639 return flowStatsService.load(link);
640 }
641 return null;
642 }
643
644 private void addEdgeLinks(Map<LinkKey, BiLink> biLinks) {
645 hostService.getHosts().forEach(host -> {
Thomas Vachuska25455e72015-06-04 11:31:26 -0700646 addLink(biLinks, createEdgeLink(host, true));
647 addLink(biLinks, createEdgeLink(host, false));
Thomas Vachuska204cb6c2015-06-04 00:03:06 -0700648 });
649 }
650
651 private Map<LinkKey, BiLink> consolidateLinks(Iterable<Link> links) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700652 Map<LinkKey, BiLink> biLinks = new HashMap<>();
653 for (Link link : links) {
654 addLink(biLinks, link);
655 }
Thomas Vachuska204cb6c2015-06-04 00:03:06 -0700656 return biLinks;
Thomas Vachuska329af532015-03-10 02:08:33 -0700657 }
658
659 // Produces JSON message to trigger flow overview visualization
Simon Huntd2747a02015-04-30 22:41:16 -0700660 protected ObjectNode flowSummaryMessage(Set<Device> devices) {
Simon Huntda580882015-05-12 20:58:18 -0700661 ObjectNode payload = objectNode();
662 ArrayNode paths = arrayNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700663 payload.set("paths", paths);
664
665 for (Device device : devices) {
666 Map<Link, Integer> counts = getFlowCounts(device.id());
667 for (Link link : counts.keySet()) {
668 addLinkFlows(link, paths, counts.get(link));
669 }
670 }
Simon Huntd2747a02015-04-30 22:41:16 -0700671 return JsonUtils.envelope("showTraffic", 0, payload);
Thomas Vachuska329af532015-03-10 02:08:33 -0700672 }
673
674 private void addLinkFlows(Link link, ArrayNode paths, Integer count) {
Simon Huntda580882015-05-12 20:58:18 -0700675 ObjectNode pathNode = objectNode();
676 ArrayNode linksNode = arrayNode();
677 ArrayNode labels = arrayNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700678 boolean noFlows = count == null || count == 0;
679 pathNode.put("class", noFlows ? "secondary" : "primary");
680 pathNode.put("traffic", false);
681 pathNode.set("links", linksNode.add(compactLinkString(link)));
682 pathNode.set("labels", labels.add(noFlows ? "" : (count.toString() +
683 (count == 1 ? " flow" : " flows"))));
684 paths.add(pathNode);
685 }
686
687
688 // Produces JSON message to trigger traffic visualization
Simon Huntd2747a02015-04-30 22:41:16 -0700689 protected ObjectNode trafficMessage(TrafficClass... trafficClasses) {
Simon Huntda580882015-05-12 20:58:18 -0700690 ObjectNode payload = objectNode();
691 ArrayNode paths = arrayNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700692 payload.set("paths", paths);
693
694 // Classify links based on their traffic traffic first...
695 Map<LinkKey, BiLink> biLinks = classifyLinkTraffic(trafficClasses);
696
697 // Then separate the links into their respective classes and send them out.
698 Map<String, ObjectNode> pathNodes = new HashMap<>();
699 for (BiLink biLink : biLinks.values()) {
700 boolean hasTraffic = biLink.hasTraffic;
Thomas Vachuska5d5858b2015-03-23 16:31:10 -0700701 String tc = (biLink.classes() + (hasTraffic ? " animated" : "")).trim();
Thomas Vachuska329af532015-03-10 02:08:33 -0700702 ObjectNode pathNode = pathNodes.get(tc);
703 if (pathNode == null) {
Simon Huntda580882015-05-12 20:58:18 -0700704 pathNode = objectNode()
Thomas Vachuska329af532015-03-10 02:08:33 -0700705 .put("class", tc).put("traffic", hasTraffic);
Simon Huntda580882015-05-12 20:58:18 -0700706 pathNode.set("links", arrayNode());
707 pathNode.set("labels", arrayNode());
Thomas Vachuska329af532015-03-10 02:08:33 -0700708 pathNodes.put(tc, pathNode);
709 paths.add(pathNode);
710 }
711 ((ArrayNode) pathNode.path("links")).add(compactLinkString(biLink.one));
712 ((ArrayNode) pathNode.path("labels")).add(hasTraffic ? formatBytes(biLink.bytes) : "");
713 }
714
Simon Huntd2747a02015-04-30 22:41:16 -0700715 return JsonUtils.envelope("showTraffic", 0, payload);
Thomas Vachuska329af532015-03-10 02:08:33 -0700716 }
717
718 // Classifies the link traffic according to the specified classes.
719 private Map<LinkKey, BiLink> classifyLinkTraffic(TrafficClass... trafficClasses) {
720 Map<LinkKey, BiLink> biLinks = new HashMap<>();
721 for (TrafficClass trafficClass : trafficClasses) {
722 for (Intent intent : trafficClass.intents) {
723 boolean isOptical = intent instanceof OpticalConnectivityIntent;
724 List<Intent> installables = intentService.getInstallableIntents(intent.key());
725 if (installables != null) {
726 for (Intent installable : installables) {
727 String type = isOptical ? trafficClass.type + " optical" : trafficClass.type;
728 if (installable instanceof PathIntent) {
729 classifyLinks(type, biLinks, trafficClass.showTraffic,
730 ((PathIntent) installable).path().links());
Thomas Vachuskac0fe09a2015-05-21 12:56:22 -0700731 } else if (installable instanceof FlowRuleIntent) {
732 classifyLinks(type, biLinks, trafficClass.showTraffic,
733 linkResources(installable));
Thomas Vachuska329af532015-03-10 02:08:33 -0700734 } else if (installable instanceof LinkCollectionIntent) {
735 classifyLinks(type, biLinks, trafficClass.showTraffic,
736 ((LinkCollectionIntent) installable).links());
737 } else if (installable instanceof OpticalPathIntent) {
738 classifyLinks(type, biLinks, trafficClass.showTraffic,
Thomas Vachuska204cb6c2015-06-04 00:03:06 -0700739 ((OpticalPathIntent) installable).path().links());
Thomas Vachuska329af532015-03-10 02:08:33 -0700740 }
741 }
742 }
743 }
744 }
745 return biLinks;
746 }
747
Thomas Vachuskac0fe09a2015-05-21 12:56:22 -0700748 // Extracts links from the specified flow rule intent resources
749 private Collection<Link> linkResources(Intent installable) {
750 ImmutableList.Builder<Link> builder = ImmutableList.builder();
751 for (NetworkResource r : installable.resources()) {
752 if (r instanceof Link) {
753 builder.add((Link) r);
754 }
755 }
756 return builder.build();
757 }
758
Thomas Vachuska329af532015-03-10 02:08:33 -0700759
760 // Adds the link segments (path or tree) associated with the specified
761 // connectivity intent
762 private void classifyLinks(String type, Map<LinkKey, BiLink> biLinks,
763 boolean showTraffic, Iterable<Link> links) {
764 if (links != null) {
765 for (Link link : links) {
766 BiLink biLink = addLink(biLinks, link);
Thomas Vachuska204cb6c2015-06-04 00:03:06 -0700767 if (showTraffic) {
Thomas Vachuska25455e72015-06-04 11:31:26 -0700768 biLink.addLoad(getLinkLoad(link));
Thomas Vachuska329af532015-03-10 02:08:33 -0700769 }
Thomas Vachuska204cb6c2015-06-04 00:03:06 -0700770 biLink.addClass(type);
Thomas Vachuska329af532015-03-10 02:08:33 -0700771 }
772 }
773 }
774
775
Thomas Vachuska583bc632015-04-14 10:10:57 -0700776 static BiLink addLink(Map<LinkKey, BiLink> biLinks, Link link) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700777 LinkKey key = canonicalLinkKey(link);
778 BiLink biLink = biLinks.get(key);
779 if (biLink != null) {
780 biLink.setOther(link);
781 } else {
782 biLink = new BiLink(key, link);
783 biLinks.put(key, biLink);
784 }
785 return biLink;
786 }
787
Thomas Vachuska329af532015-03-10 02:08:33 -0700788 // Poor-mans formatting to get the labels with byte counts looking nice.
789 private String formatBytes(long bytes) {
790 String unit;
791 double value;
Srikanth Vavilapalli8c1ccca2015-06-08 19:23:24 -0700792 if (bytes > GIGA) {
793 value = bytes / GIGA;
794 unit = GBYTES_UNIT;
795 } else if (bytes > MEGA) {
796 value = bytes / MEGA;
797 unit = MBYTES_UNIT;
798 } else if (bytes > KILO) {
799 value = bytes / KILO;
800 unit = KBYTES_UNIT;
Thomas Vachuska329af532015-03-10 02:08:33 -0700801 } else {
802 value = bytes;
Srikanth Vavilapalli8c1ccca2015-06-08 19:23:24 -0700803 unit = BYTES_UNIT;
804 }
805 DecimalFormat format = new DecimalFormat("#,###.##");
806 return format.format(value) + " " + unit;
807 }
808
Simon Hunt80170a72015-06-10 19:49:57 -0700809 // Poor-mans formatting to get the labels with bit rate looking nice.
Srikanth Vavilapalli8c1ccca2015-06-08 19:23:24 -0700810 private String formatBitRate(long bytes) {
811 String unit;
812 double value;
813 //Convert to bits
814 long bits = bytes * 8;
815 if (bits > GIGA) {
816 value = bits / GIGA;
817 unit = GBITS_UNIT;
Simon Hunt80170a72015-06-10 19:49:57 -0700818
819 // NOTE: temporary hack to clip rate at 10.0 Gbps
820 // Added for the CORD Fabric demo at ONS 2015
821 if (value > 10.0) {
822 value = 10.0;
823 }
824
Srikanth Vavilapalli8c1ccca2015-06-08 19:23:24 -0700825 } else if (bits > MEGA) {
826 value = bits / MEGA;
827 unit = MBITS_UNIT;
828 } else if (bits > KILO) {
829 value = bits / KILO;
830 unit = KBITS_UNIT;
831 } else {
832 value = bits;
833 unit = BITS_UNIT;
Thomas Vachuska329af532015-03-10 02:08:33 -0700834 }
835 DecimalFormat format = new DecimalFormat("#,###.##");
836 return format.format(value) + " " + unit;
837 }
838
Thomas Vachuska329af532015-03-10 02:08:33 -0700839 // Produces compact string representation of a link.
840 private static String compactLinkString(Link link) {
841 return String.format(COMPACT, link.src().elementId(), link.src().port(),
842 link.dst().elementId(), link.dst().port());
843 }
844
Simon Huntb745ca62015-07-28 15:37:11 -0700845 // translates the property panel into JSON, for returning to the client
Simon Hunt0af1ec32015-07-24 12:17:55 -0700846 protected ObjectNode json(PropertyPanel pp) {
847 ObjectNode result = objectNode()
Simon Huntb745ca62015-07-28 15:37:11 -0700848 .put("title", pp.title())
849 .put("type", pp.typeId())
850 .put("id", pp.id());
851
Simon Hunt0af1ec32015-07-24 12:17:55 -0700852 ObjectNode pnode = objectNode();
853 ArrayNode porder = arrayNode();
854 for (PropertyPanel.Prop p : pp.properties()) {
855 porder.add(p.key());
856 pnode.put(p.key(), p.value());
857 }
858 result.set("propOrder", porder);
859 result.set("props", pnode);
860 return result;
861 }
862
Thomas Vachuska329af532015-03-10 02:08:33 -0700863 // Produces JSON property details.
864 private ObjectNode json(String id, String type, Prop... props) {
Simon Huntda580882015-05-12 20:58:18 -0700865 ObjectNode result = objectNode()
Thomas Vachuska329af532015-03-10 02:08:33 -0700866 .put("id", id).put("type", type);
Simon Huntda580882015-05-12 20:58:18 -0700867 ObjectNode pnode = objectNode();
868 ArrayNode porder = arrayNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700869 for (Prop p : props) {
870 porder.add(p.key);
871 pnode.put(p.key, p.value);
872 }
873 result.set("propOrder", porder);
874 result.set("props", pnode);
875 return result;
876 }
877
878 // Produces canonical link key, i.e. one that will match link and its inverse.
Thomas Vachuska583bc632015-04-14 10:10:57 -0700879 static LinkKey canonicalLinkKey(Link link) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700880 String sn = link.src().elementId().toString();
881 String dn = link.dst().elementId().toString();
882 return sn.compareTo(dn) < 0 ?
883 linkKey(link.src(), link.dst()) : linkKey(link.dst(), link.src());
884 }
885
886 // Representation of link and its inverse and any traffic data.
Thomas Vachuska583bc632015-04-14 10:10:57 -0700887 static class BiLink {
Thomas Vachuska329af532015-03-10 02:08:33 -0700888 public final LinkKey key;
889 public final Link one;
890 public Link two;
891 public boolean hasTraffic = false;
892 public long bytes = 0;
Thomas Vachuska5d5858b2015-03-23 16:31:10 -0700893
894 private Set<String> classes = new HashSet<>();
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700895 private long rate;
Thomas Vachuska329af532015-03-10 02:08:33 -0700896
897 BiLink(LinkKey key, Link link) {
898 this.key = key;
899 this.one = link;
900 }
901
902 void setOther(Link link) {
903 this.two = link;
904 }
905
906 void addLoad(Load load) {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700907 addLoad(load, 0);
908 }
909
Thomas Vachuska204cb6c2015-06-04 00:03:06 -0700910 void addLoad(Load load, double threshold) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700911 if (load != null) {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700912 this.hasTraffic = hasTraffic || load.rate() > threshold;
Thomas Vachuska329af532015-03-10 02:08:33 -0700913 this.bytes += load.latest();
Thomas Vachuskafdbc4c22015-05-29 15:53:01 -0700914 this.rate += load.rate();
Thomas Vachuska329af532015-03-10 02:08:33 -0700915 }
916 }
917
Srikanth Vavilapalli8c1ccca2015-06-08 19:23:24 -0700918 void addLoad(Load srcLinkLoad,
919 double srcLinkThreshold,
920 Load dstLinkLoad,
921 double dstLinkThreshold) {
922 //use the max of link load at source or destination
923 if (srcLinkLoad != null) {
924 this.hasTraffic = hasTraffic || srcLinkLoad.rate() > srcLinkThreshold;
925 this.bytes = srcLinkLoad.latest();
926 this.rate = srcLinkLoad.rate();
927 }
928
929 if (dstLinkLoad != null) {
930 if (dstLinkLoad.rate() > this.rate) {
931 this.bytes = dstLinkLoad.latest();
932 this.rate = dstLinkLoad.rate();
933 this.hasTraffic = hasTraffic || dstLinkLoad.rate() > dstLinkThreshold;
934 }
935 }
936 }
937
Thomas Vachuska329af532015-03-10 02:08:33 -0700938 void addClass(String trafficClass) {
Thomas Vachuska5d5858b2015-03-23 16:31:10 -0700939 classes.add(trafficClass);
940 }
941
942 String classes() {
943 StringBuilder sb = new StringBuilder();
944 classes.forEach(c -> sb.append(c).append(" "));
945 return sb.toString().trim();
Thomas Vachuska329af532015-03-10 02:08:33 -0700946 }
947 }
948
949 // Auxiliary key/value carrier.
Simon Hunt93735af2015-07-28 12:10:30 -0700950 @Deprecated
Thomas Vachuska583bc632015-04-14 10:10:57 -0700951 static class Prop {
Thomas Vachuska329af532015-03-10 02:08:33 -0700952 public final String key;
953 public final String value;
954
955 protected Prop(String key, String value) {
956 this.key = key;
957 this.value = value;
958 }
959 }
960
961 // Auxiliary properties separator
Simon Hunt93735af2015-07-28 12:10:30 -0700962 @Deprecated
Thomas Vachuska583bc632015-04-14 10:10:57 -0700963 static class Separator extends Prop {
Thomas Vachuska329af532015-03-10 02:08:33 -0700964 protected Separator() {
965 super("-", "");
966 }
967 }
968
Simon Hunt93735af2015-07-28 12:10:30 -0700969 // TODO: move this to traffic overlay component
Thomas Vachuska329af532015-03-10 02:08:33 -0700970 // Auxiliary carrier of data for requesting traffic message.
Thomas Vachuska583bc632015-04-14 10:10:57 -0700971 static class TrafficClass {
Thomas Vachuska329af532015-03-10 02:08:33 -0700972 public final boolean showTraffic;
973 public final String type;
974 public final Iterable<Intent> intents;
975
976 TrafficClass(String type, Iterable<Intent> intents) {
977 this(type, intents, false);
978 }
979
980 TrafficClass(String type, Iterable<Intent> intents, boolean showTraffic) {
981 this.type = type;
982 this.intents = intents;
983 this.showTraffic = showTraffic;
984 }
985 }
986
987}