blob: 8dbb111ac79f856466c2618b17e8551a0d53d2ab [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 Hunt3a0598f2015-08-04 19:59:04 -070075import org.onosproject.ui.topo.ButtonDescriptor;
Simon Hunt0af1ec32015-07-24 12:17:55 -070076import org.onosproject.ui.topo.PropertyPanel;
Thomas Vachuska329af532015-03-10 02:08:33 -070077import org.slf4j.Logger;
78import org.slf4j.LoggerFactory;
79
80import java.text.DecimalFormat;
81import java.util.ArrayList;
82import java.util.Collection;
83import java.util.Collections;
84import java.util.HashMap;
85import java.util.HashSet;
86import java.util.Iterator;
87import java.util.List;
88import java.util.Map;
89import java.util.Set;
90import java.util.concurrent.ConcurrentHashMap;
91
92import static com.google.common.base.Preconditions.checkNotNull;
93import static com.google.common.base.Strings.isNullOrEmpty;
94import static org.onosproject.cluster.ClusterEvent.Type.INSTANCE_ADDED;
95import static org.onosproject.cluster.ClusterEvent.Type.INSTANCE_REMOVED;
96import static org.onosproject.cluster.ControllerNode.State.ACTIVE;
Thomas Vachuska204cb6c2015-06-04 00:03:06 -070097import static org.onosproject.net.DefaultEdgeLink.createEdgeLink;
Thomas Vachuska329af532015-03-10 02:08:33 -070098import static org.onosproject.net.DeviceId.deviceId;
99import static org.onosproject.net.HostId.hostId;
100import static org.onosproject.net.LinkKey.linkKey;
101import static org.onosproject.net.PortNumber.P0;
102import static org.onosproject.net.PortNumber.portNumber;
103import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_ADDED;
104import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_REMOVED;
105import static org.onosproject.net.host.HostEvent.Type.HOST_ADDED;
106import static org.onosproject.net.host.HostEvent.Type.HOST_REMOVED;
107import static org.onosproject.net.link.LinkEvent.Type.LINK_ADDED;
108import static org.onosproject.net.link.LinkEvent.Type.LINK_REMOVED;
Thomas Vachuska204cb6c2015-06-04 00:03:06 -0700109import static org.onosproject.ui.impl.TopologyViewMessageHandlerBase.StatsType.FLOW;
110import static org.onosproject.ui.impl.TopologyViewMessageHandlerBase.StatsType.PORT;
Simon Hunt3a0598f2015-08-04 19:59:04 -0700111import static org.onosproject.ui.topo.TopoConstants.CoreButtons;
112import static org.onosproject.ui.topo.TopoConstants.Properties;
Thomas Vachuska329af532015-03-10 02:08:33 -0700113
114/**
115 * Facility for creating messages bound for the topology viewer.
116 */
Simon Hunta0ddb022015-05-01 09:53:01 -0700117public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
Thomas Vachuska329af532015-03-10 02:08:33 -0700118
Simon Huntd2747a02015-04-30 22:41:16 -0700119 protected static final Logger log =
120 LoggerFactory.getLogger(TopologyViewMessageHandlerBase.class);
Thomas Vachuska329af532015-03-10 02:08:33 -0700121
Simon Huntd2747a02015-04-30 22:41:16 -0700122 private static final ProviderId PID =
123 new ProviderId("core", "org.onosproject.core", true);
Thomas Vachuska329af532015-03-10 02:08:33 -0700124 private static final String COMPACT = "%s/%s-%s/%s";
125
Srikanth Vavilapalli8c1ccca2015-06-08 19:23:24 -0700126 private static final double KILO = 1024;
127 private static final double MEGA = 1024 * KILO;
128 private static final double GIGA = 1024 * MEGA;
Thomas Vachuska329af532015-03-10 02:08:33 -0700129
Srikanth Vavilapalli8c1ccca2015-06-08 19:23:24 -0700130 private static final String GBITS_UNIT = "Gb";
131 private static final String MBITS_UNIT = "Mb";
132 private static final String KBITS_UNIT = "Kb";
133 private static final String BITS_UNIT = "b";
134 private static final String GBYTES_UNIT = "GB";
135 private static final String MBYTES_UNIT = "MB";
136 private static final String KBYTES_UNIT = "KB";
137 private static final String BYTES_UNIT = "B";
138 //4 Kilo Bytes as threshold
139 private static final double BPS_THRESHOLD = 4 * KILO;
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700140
Thomas Vachuska329af532015-03-10 02:08:33 -0700141 protected ServiceDirectory directory;
142 protected ClusterService clusterService;
143 protected DeviceService deviceService;
144 protected LinkService linkService;
145 protected HostService hostService;
146 protected MastershipService mastershipService;
147 protected IntentService intentService;
148 protected FlowRuleService flowService;
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700149 protected StatisticService flowStatsService;
150 protected PortStatisticsService portStatsService;
Thomas Vachuska329af532015-03-10 02:08:33 -0700151 protected TopologyService topologyService;
cheng fan35dc0f22015-06-10 06:02:47 +0800152 protected TunnelService tunnelService;
Thomas Vachuska329af532015-03-10 02:08:33 -0700153
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700154 protected enum StatsType {
155 FLOW, PORT
156 }
157
Thomas Vachuska329af532015-03-10 02:08:33 -0700158 private String version;
159
160 // TODO: extract into an external & durable state; good enough for now and demo
161 private static Map<String, ObjectNode> metaUi = new ConcurrentHashMap<>();
162
163 /**
Thomas Vachuska329af532015-03-10 02:08:33 -0700164 * Returns read-only view of the meta-ui information.
165 *
166 * @return map of id to meta-ui mementos
167 */
168 static Map<String, ObjectNode> getMetaUi() {
169 return Collections.unmodifiableMap(metaUi);
170 }
171
172 @Override
173 public void init(UiConnection connection, ServiceDirectory directory) {
174 super.init(connection, directory);
175 this.directory = checkNotNull(directory, "Directory cannot be null");
176 clusterService = directory.get(ClusterService.class);
177 deviceService = directory.get(DeviceService.class);
178 linkService = directory.get(LinkService.class);
179 hostService = directory.get(HostService.class);
180 mastershipService = directory.get(MastershipService.class);
181 intentService = directory.get(IntentService.class);
182 flowService = directory.get(FlowRuleService.class);
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700183 flowStatsService = directory.get(StatisticService.class);
184 portStatsService = directory.get(PortStatisticsService.class);
Thomas Vachuska329af532015-03-10 02:08:33 -0700185 topologyService = directory.get(TopologyService.class);
cheng fan35dc0f22015-06-10 06:02:47 +0800186 tunnelService = directory.get(TunnelService.class);
Thomas Vachuska329af532015-03-10 02:08:33 -0700187
188 String ver = directory.get(CoreService.class).version().toString();
189 version = ver.replace(".SNAPSHOT", "*").replaceFirst("~.*$", "");
190 }
191
Thomas Vachuska329af532015-03-10 02:08:33 -0700192 // Returns the specified set of IP addresses as a string.
193 private String ip(Set<IpAddress> ipAddresses) {
194 Iterator<IpAddress> it = ipAddresses.iterator();
195 return it.hasNext() ? it.next().toString() : "unknown";
196 }
197
198 // Produces JSON structure from annotations.
199 private JsonNode props(Annotations annotations) {
Simon Huntda580882015-05-12 20:58:18 -0700200 ObjectNode props = objectNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700201 if (annotations != null) {
202 for (String key : annotations.keys()) {
203 props.put(key, annotations.value(key));
204 }
205 }
206 return props;
207 }
208
209 // Produces an informational log message event bound to the client.
210 protected ObjectNode info(long id, String message) {
211 return message("info", id, message);
212 }
213
214 // Produces a warning log message event bound to the client.
215 protected ObjectNode warning(long id, String message) {
216 return message("warning", id, message);
217 }
218
219 // Produces an error log message event bound to the client.
220 protected ObjectNode error(long id, String message) {
221 return message("error", id, message);
222 }
223
224 // Produces a log message event bound to the client.
225 private ObjectNode message(String severity, long id, String message) {
Simon Huntda580882015-05-12 20:58:18 -0700226 ObjectNode payload = objectNode()
Simon Huntd2747a02015-04-30 22:41:16 -0700227 .put("severity", severity)
228 .put("message", message);
Thomas Vachuska329af532015-03-10 02:08:33 -0700229
Simon Huntd2747a02015-04-30 22:41:16 -0700230 return JsonUtils.envelope("message", id, payload);
Thomas Vachuska329af532015-03-10 02:08:33 -0700231 }
232
233 // Produces a set of all hosts listed in the specified JSON array.
234 protected Set<Host> getHosts(ArrayNode array) {
235 Set<Host> hosts = new HashSet<>();
236 if (array != null) {
237 for (JsonNode node : array) {
238 try {
239 addHost(hosts, hostId(node.asText()));
240 } catch (IllegalArgumentException e) {
241 log.debug("Skipping ID {}", node.asText());
242 }
243 }
244 }
245 return hosts;
246 }
247
248 // Adds the specified host to the set of hosts.
249 private void addHost(Set<Host> hosts, HostId hostId) {
250 Host host = hostService.getHost(hostId);
251 if (host != null) {
252 hosts.add(host);
253 }
254 }
255
256
257 // Produces a set of all devices listed in the specified JSON array.
258 protected Set<Device> getDevices(ArrayNode array) {
259 Set<Device> devices = new HashSet<>();
260 if (array != null) {
261 for (JsonNode node : array) {
262 try {
263 addDevice(devices, deviceId(node.asText()));
264 } catch (IllegalArgumentException e) {
265 log.debug("Skipping ID {}", node.asText());
266 }
267 }
268 }
269 return devices;
270 }
271
272 private void addDevice(Set<Device> devices, DeviceId deviceId) {
273 Device device = deviceService.getDevice(deviceId);
274 if (device != null) {
275 devices.add(device);
276 }
277 }
278
279 protected void addHover(Set<Host> hosts, Set<Device> devices, String hover) {
280 try {
281 addHost(hosts, hostId(hover));
282 } catch (IllegalArgumentException e) {
283 try {
284 addDevice(devices, deviceId(hover));
285 } catch (IllegalArgumentException ne) {
286 log.debug("Skipping ID {}", hover);
287 }
288 }
289 }
290
291 // Produces a cluster instance message to the client.
292 protected ObjectNode instanceMessage(ClusterEvent event, String messageType) {
293 ControllerNode node = event.subject();
294 int switchCount = mastershipService.getDevicesOf(node.id()).size();
Simon Huntda580882015-05-12 20:58:18 -0700295 ObjectNode payload = objectNode()
Thomas Vachuska329af532015-03-10 02:08:33 -0700296 .put("id", node.id().toString())
297 .put("ip", node.ip().toString())
298 .put("online", clusterService.getState(node.id()) == ACTIVE)
Thomas Vachuskab6acc7b2015-03-11 09:11:21 -0700299 .put("uiAttached", node.equals(clusterService.getLocalNode()))
Thomas Vachuska329af532015-03-10 02:08:33 -0700300 .put("switches", switchCount);
301
Simon Huntda580882015-05-12 20:58:18 -0700302 ArrayNode labels = arrayNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700303 labels.add(node.id().toString());
304 labels.add(node.ip().toString());
305
306 // Add labels, props and stuff the payload into envelope.
307 payload.set("labels", labels);
308 addMetaUi(node.id().toString(), payload);
309
310 String type = messageType != null ? messageType :
311 ((event.type() == INSTANCE_ADDED) ? "addInstance" :
312 ((event.type() == INSTANCE_REMOVED ? "removeInstance" :
313 "addInstance")));
Simon Huntd2747a02015-04-30 22:41:16 -0700314 return JsonUtils.envelope(type, 0, payload);
Thomas Vachuska329af532015-03-10 02:08:33 -0700315 }
316
317 // Produces a device event message to the client.
318 protected ObjectNode deviceMessage(DeviceEvent event) {
319 Device device = event.subject();
Simon Huntda580882015-05-12 20:58:18 -0700320 ObjectNode payload = objectNode()
Thomas Vachuska329af532015-03-10 02:08:33 -0700321 .put("id", device.id().toString())
322 .put("type", device.type().toString().toLowerCase())
323 .put("online", deviceService.isAvailable(device.id()))
324 .put("master", master(device.id()));
325
326 // Generate labels: id, chassis id, no-label, optional-name
327 String name = device.annotations().value(AnnotationKeys.NAME);
Simon Huntda580882015-05-12 20:58:18 -0700328 ArrayNode labels = arrayNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700329 labels.add("");
330 labels.add(isNullOrEmpty(name) ? device.id().toString() : name);
331 labels.add(device.id().toString());
332
333 // Add labels, props and stuff the payload into envelope.
334 payload.set("labels", labels);
335 payload.set("props", props(device.annotations()));
336 addGeoLocation(device, payload);
337 addMetaUi(device.id().toString(), payload);
338
339 String type = (event.type() == DEVICE_ADDED) ? "addDevice" :
340 ((event.type() == DEVICE_REMOVED) ? "removeDevice" : "updateDevice");
Simon Huntd2747a02015-04-30 22:41:16 -0700341 return JsonUtils.envelope(type, 0, payload);
Thomas Vachuska329af532015-03-10 02:08:33 -0700342 }
343
344 // Produces a link event message to the client.
345 protected ObjectNode linkMessage(LinkEvent event) {
346 Link link = event.subject();
Simon Huntda580882015-05-12 20:58:18 -0700347 ObjectNode payload = objectNode()
Thomas Vachuska329af532015-03-10 02:08:33 -0700348 .put("id", compactLinkString(link))
349 .put("type", link.type().toString().toLowerCase())
350 .put("online", link.state() == Link.State.ACTIVE)
351 .put("linkWidth", 1.2)
352 .put("src", link.src().deviceId().toString())
353 .put("srcPort", link.src().port().toString())
354 .put("dst", link.dst().deviceId().toString())
355 .put("dstPort", link.dst().port().toString());
356 String type = (event.type() == LINK_ADDED) ? "addLink" :
357 ((event.type() == LINK_REMOVED) ? "removeLink" : "updateLink");
Simon Huntd2747a02015-04-30 22:41:16 -0700358 return JsonUtils.envelope(type, 0, payload);
Thomas Vachuska329af532015-03-10 02:08:33 -0700359 }
360
361 // Produces a host event message to the client.
362 protected ObjectNode hostMessage(HostEvent event) {
363 Host host = event.subject();
364 String hostType = host.annotations().value(AnnotationKeys.TYPE);
Simon Huntda580882015-05-12 20:58:18 -0700365 ObjectNode payload = objectNode()
Thomas Vachuska329af532015-03-10 02:08:33 -0700366 .put("id", host.id().toString())
367 .put("type", isNullOrEmpty(hostType) ? "endstation" : hostType)
368 .put("ingress", compactLinkString(edgeLink(host, true)))
369 .put("egress", compactLinkString(edgeLink(host, false)));
Simon Huntda580882015-05-12 20:58:18 -0700370 payload.set("cp", hostConnect(host.location()));
371 payload.set("labels", labels(ip(host.ipAddresses()),
Thomas Vachuska329af532015-03-10 02:08:33 -0700372 host.mac().toString()));
373 payload.set("props", props(host.annotations()));
374 addGeoLocation(host, payload);
375 addMetaUi(host.id().toString(), payload);
376
377 String type = (event.type() == HOST_ADDED) ? "addHost" :
378 ((event.type() == HOST_REMOVED) ? "removeHost" : "updateHost");
Simon Huntd2747a02015-04-30 22:41:16 -0700379 return JsonUtils.envelope(type, 0, payload);
Thomas Vachuska329af532015-03-10 02:08:33 -0700380 }
381
382 // Encodes the specified host location into a JSON object.
Simon Huntda580882015-05-12 20:58:18 -0700383 private ObjectNode hostConnect(HostLocation location) {
384 return objectNode()
Thomas Vachuska329af532015-03-10 02:08:33 -0700385 .put("device", location.deviceId().toString())
386 .put("port", location.port().toLong());
387 }
388
389 // Encodes the specified list of labels a JSON array.
Simon Huntda580882015-05-12 20:58:18 -0700390 private ArrayNode labels(String... labels) {
391 ArrayNode json = arrayNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700392 for (String label : labels) {
393 json.add(label);
394 }
395 return json;
396 }
397
398 // Returns the name of the master node for the specified device id.
399 private String master(DeviceId deviceId) {
400 NodeId master = mastershipService.getMasterFor(deviceId);
401 return master != null ? master.toString() : "";
402 }
403
404 // Generates an edge link from the specified host location.
405 private EdgeLink edgeLink(Host host, boolean ingress) {
406 return new DefaultEdgeLink(PID, new ConnectPoint(host.id(), portNumber(0)),
407 host.location(), ingress);
408 }
409
410 // Adds meta UI information for the specified object.
411 private void addMetaUi(String id, ObjectNode payload) {
412 ObjectNode meta = metaUi.get(id);
413 if (meta != null) {
414 payload.set("metaUi", meta);
415 }
416 }
417
418 // Adds a geo location JSON to the specified payload object.
419 private void addGeoLocation(Annotated annotated, ObjectNode payload) {
420 Annotations annotations = annotated.annotations();
421 if (annotations == null) {
422 return;
423 }
424
425 String slat = annotations.value(AnnotationKeys.LATITUDE);
426 String slng = annotations.value(AnnotationKeys.LONGITUDE);
427 try {
428 if (slat != null && slng != null && !slat.isEmpty() && !slng.isEmpty()) {
429 double lat = Double.parseDouble(slat);
430 double lng = Double.parseDouble(slng);
Simon Huntda580882015-05-12 20:58:18 -0700431 ObjectNode loc = objectNode()
Thomas Vachuska329af532015-03-10 02:08:33 -0700432 .put("type", "latlng").put("lat", lat).put("lng", lng);
433 payload.set("location", loc);
434 }
435 } catch (NumberFormatException e) {
436 log.warn("Invalid geo data latitude={}; longiture={}", slat, slng);
437 }
438 }
439
440 // Updates meta UI information for the specified object.
Simon Huntd2747a02015-04-30 22:41:16 -0700441 protected void updateMetaUi(ObjectNode payload) {
442 metaUi.put(JsonUtils.string(payload, "id"),
443 JsonUtils.node(payload, "memento"));
Thomas Vachuska329af532015-03-10 02:08:33 -0700444 }
445
Simon Huntb745ca62015-07-28 15:37:11 -0700446 // -----------------------------------------------------------------------
447 // Create models of the data to return, that overlays can adjust / augment
448
449 // Returns property panel model for summary response.
Simon Hunt0af1ec32015-07-24 12:17:55 -0700450 protected PropertyPanel summmaryMessage(long sid) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700451 Topology topology = topologyService.currentTopology();
Simon Hunt0af1ec32015-07-24 12:17:55 -0700452
Simon Hunt00a27ff2015-07-28 18:53:40 -0700453 return new PropertyPanel("ONOS Summary", "node")
Simon Huntfb940112015-07-29 18:36:35 -0700454 .addProp(Properties.DEVICES, topology.deviceCount())
455 .addProp(Properties.LINKS, topology.linkCount())
456 .addProp(Properties.HOSTS, hostService.getHostCount())
457 .addProp(Properties.TOPOLOGY_SSCS, topology.clusterCount())
Simon Hunt00a27ff2015-07-28 18:53:40 -0700458 .addSeparator()
Simon Huntfb940112015-07-29 18:36:35 -0700459 .addProp(Properties.INTENTS, intentService.getIntentCount())
460 .addProp(Properties.TUNNELS, tunnelService.tunnelCount())
461 .addProp(Properties.FLOWS, flowService.getFlowRuleCount())
462 .addProp(Properties.VERSION, version);
Thomas Vachuska329af532015-03-10 02:08:33 -0700463 }
464
Simon Huntb745ca62015-07-28 15:37:11 -0700465 // Returns property panel model for device details response.
466 protected PropertyPanel deviceDetails(DeviceId deviceId, long sid) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700467 Device device = deviceService.getDevice(deviceId);
468 Annotations annot = device.annotations();
469 String name = annot.value(AnnotationKeys.NAME);
470 int portCount = deviceService.getPorts(deviceId).size();
471 int flowCount = getFlowCount(deviceId);
cheng fan35dc0f22015-06-10 06:02:47 +0800472 int tunnelCount = getTunnelCount(deviceId);
Simon Huntb745ca62015-07-28 15:37:11 -0700473
474 String title = isNullOrEmpty(name) ? deviceId.toString() : name;
475 String typeId = device.type().toString().toLowerCase();
476
477 PropertyPanel pp = new PropertyPanel(title, typeId)
Simon Huntfb940112015-07-29 18:36:35 -0700478 .id(deviceId.toString())
Simon Hunt3a0598f2015-08-04 19:59:04 -0700479
Simon Huntfb940112015-07-29 18:36:35 -0700480 .addProp(Properties.URI, deviceId.toString())
481 .addProp(Properties.VENDOR, device.manufacturer())
482 .addProp(Properties.HW_VERSION, device.hwVersion())
483 .addProp(Properties.SW_VERSION, device.swVersion())
484 .addProp(Properties.SERIAL_NUMBER, device.serialNumber())
485 .addProp(Properties.PROTOCOL, annot.value(AnnotationKeys.PROTOCOL))
486 .addSeparator()
Simon Hunt3a0598f2015-08-04 19:59:04 -0700487
Simon Huntfb940112015-07-29 18:36:35 -0700488 .addProp(Properties.LATITUDE, annot.value(AnnotationKeys.LATITUDE))
489 .addProp(Properties.LONGITUDE, annot.value(AnnotationKeys.LONGITUDE))
490 .addSeparator()
Simon Hunt3a0598f2015-08-04 19:59:04 -0700491
Simon Huntfb940112015-07-29 18:36:35 -0700492 .addProp(Properties.PORTS, portCount)
493 .addProp(Properties.FLOWS, flowCount)
Simon Hunt3a0598f2015-08-04 19:59:04 -0700494 .addProp(Properties.TUNNELS, tunnelCount)
Simon Hunt00a27ff2015-07-28 18:53:40 -0700495
Simon Hunt3a0598f2015-08-04 19:59:04 -0700496 .addButton(CoreButtons.SHOW_DEVICE_VIEW)
497 .addButton(CoreButtons.SHOW_FLOW_VIEW)
498 .addButton(CoreButtons.SHOW_PORT_VIEW)
499 .addButton(CoreButtons.SHOW_GROUP_VIEW);
Simon Huntb745ca62015-07-28 15:37:11 -0700500
501 return pp;
Thomas Vachuska329af532015-03-10 02:08:33 -0700502 }
503
504 protected int getFlowCount(DeviceId deviceId) {
505 int count = 0;
Simon Huntb745ca62015-07-28 15:37:11 -0700506 for (FlowEntry flowEntry : flowService.getFlowEntries(deviceId)) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700507 count++;
Thomas Vachuska329af532015-03-10 02:08:33 -0700508 }
509 return count;
510 }
511
cheng fan35dc0f22015-06-10 06:02:47 +0800512 protected int getTunnelCount(DeviceId deviceId) {
513 int count = 0;
514 Collection<Tunnel> tunnels = tunnelService.queryAllTunnels();
515 for (Tunnel tunnel : tunnels) {
516 OpticalTunnelEndPoint src = (OpticalTunnelEndPoint) tunnel.src();
517 OpticalTunnelEndPoint dst = (OpticalTunnelEndPoint) tunnel.dst();
518 DeviceId srcDevice = (DeviceId) src.elementId().get();
519 DeviceId dstDevice = (DeviceId) dst.elementId().get();
Simon Huntb745ca62015-07-28 15:37:11 -0700520 if (srcDevice.toString().equals(deviceId.toString()) ||
521 dstDevice.toString().equals(deviceId.toString())) {
cheng fan35dc0f22015-06-10 06:02:47 +0800522 count++;
523 }
524 }
525 return count;
526 }
527
Thomas Vachuska329af532015-03-10 02:08:33 -0700528 // Counts all entries that egress on the given device links.
529 protected Map<Link, Integer> getFlowCounts(DeviceId deviceId) {
530 List<FlowEntry> entries = new ArrayList<>();
531 Set<Link> links = new HashSet<>(linkService.getDeviceEgressLinks(deviceId));
532 Set<Host> hosts = hostService.getConnectedHosts(deviceId);
Simon Huntb745ca62015-07-28 15:37:11 -0700533 for (FlowEntry flowEntry : flowService.getFlowEntries(deviceId)) {
534 entries.add(flowEntry);
Thomas Vachuska329af532015-03-10 02:08:33 -0700535 }
536
537 // Add all edge links to the set
538 if (hosts != null) {
539 for (Host host : hosts) {
540 links.add(new DefaultEdgeLink(host.providerId(),
541 new ConnectPoint(host.id(), P0),
542 host.location(), false));
543 }
544 }
545
546 Map<Link, Integer> counts = new HashMap<>();
547 for (Link link : links) {
548 counts.put(link, getEgressFlows(link, entries));
549 }
550 return counts;
551 }
552
553 // Counts all entries that egress on the link source port.
554 private Integer getEgressFlows(Link link, List<FlowEntry> entries) {
555 int count = 0;
556 PortNumber out = link.src().port();
557 for (FlowEntry entry : entries) {
558 TrafficTreatment treatment = entry.treatment();
Ray Milkey42507352015-03-20 15:16:10 -0700559 for (Instruction instruction : treatment.allInstructions()) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700560 if (instruction.type() == Instruction.Type.OUTPUT &&
561 ((OutputInstruction) instruction).port().equals(out)) {
562 count++;
563 }
564 }
565 }
566 return count;
567 }
568
569
570 // Returns host details response.
Simon Huntb745ca62015-07-28 15:37:11 -0700571 protected PropertyPanel hostDetails(HostId hostId, long sid) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700572 Host host = hostService.getHost(hostId);
573 Annotations annot = host.annotations();
574 String type = annot.value(AnnotationKeys.TYPE);
575 String name = annot.value(AnnotationKeys.NAME);
576 String vlan = host.vlan().toString();
Simon Huntb745ca62015-07-28 15:37:11 -0700577
578 String title = isNullOrEmpty(name) ? hostId.toString() : name;
579 String typeId = isNullOrEmpty(type) ? "endstation" : type;
580
581 PropertyPanel pp = new PropertyPanel(title, typeId)
Simon Huntfb940112015-07-29 18:36:35 -0700582 .id(hostId.toString())
583 .addProp(Properties.MAC, host.mac())
584 .addProp(Properties.IP, host.ipAddresses(), "[\\[\\]]")
585 .addProp(Properties.VLAN, vlan.equals("-1") ? "none" : vlan)
586 .addSeparator()
587 .addProp(Properties.LATITUDE, annot.value(AnnotationKeys.LATITUDE))
588 .addProp(Properties.LONGITUDE, annot.value(AnnotationKeys.LONGITUDE));
Simon Huntb745ca62015-07-28 15:37:11 -0700589
Simon Hunt00a27ff2015-07-28 18:53:40 -0700590 // TODO: add button descriptors
Simon Huntb745ca62015-07-28 15:37:11 -0700591 return pp;
Thomas Vachuska329af532015-03-10 02:08:33 -0700592 }
593
594
Simon Huntb745ca62015-07-28 15:37:11 -0700595 // TODO: migrate to Traffic overlay
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700596 // Produces JSON message to trigger flow traffic overview visualization
597 protected ObjectNode trafficSummaryMessage(StatsType type) {
Simon Huntda580882015-05-12 20:58:18 -0700598 ObjectNode payload = objectNode();
599 ArrayNode paths = arrayNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700600 payload.set("paths", paths);
601
Simon Huntda580882015-05-12 20:58:18 -0700602 ObjectNode pathNodeN = objectNode();
603 ArrayNode linksNodeN = arrayNode();
604 ArrayNode labelsN = arrayNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700605
606 pathNodeN.put("class", "plain").put("traffic", false);
607 pathNodeN.set("links", linksNodeN);
608 pathNodeN.set("labels", labelsN);
609 paths.add(pathNodeN);
610
Simon Huntda580882015-05-12 20:58:18 -0700611 ObjectNode pathNodeT = objectNode();
612 ArrayNode linksNodeT = arrayNode();
613 ArrayNode labelsT = arrayNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700614
615 pathNodeT.put("class", "secondary").put("traffic", true);
616 pathNodeT.set("links", linksNodeT);
617 pathNodeT.set("labels", labelsT);
618 paths.add(pathNodeT);
619
Thomas Vachuska204cb6c2015-06-04 00:03:06 -0700620 Map<LinkKey, BiLink> biLinks = consolidateLinks(linkService.getLinks());
621 addEdgeLinks(biLinks);
622 for (BiLink link : biLinks.values()) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700623 boolean bi = link.two != null;
Thomas Vachuska204cb6c2015-06-04 00:03:06 -0700624 if (type == FLOW) {
625 link.addLoad(getLinkLoad(link.one));
626 link.addLoad(bi ? getLinkLoad(link.two) : null);
627 } else if (type == PORT) {
Srikanth Vavilapalli8c1ccca2015-06-08 19:23:24 -0700628 //For a bi-directional traffic links, use
629 //the max link rate of either direction
630 link.addLoad(portStatsService.load(link.one.src()),
631 BPS_THRESHOLD,
632 portStatsService.load(link.one.dst()),
633 BPS_THRESHOLD);
Thomas Vachuska204cb6c2015-06-04 00:03:06 -0700634 }
635 if (link.hasTraffic) {
636 linksNodeT.add(compactLinkString(link.one));
637 labelsT.add(type == PORT ?
Srikanth Vavilapalli8c1ccca2015-06-08 19:23:24 -0700638 formatBitRate(link.rate) + "ps" :
Thomas Vachuska204cb6c2015-06-04 00:03:06 -0700639 formatBytes(link.bytes));
640 } else {
641 linksNodeN.add(compactLinkString(link.one));
642 labelsN.add("");
Thomas Vachuska329af532015-03-10 02:08:33 -0700643 }
644 }
Simon Huntd2747a02015-04-30 22:41:16 -0700645 return JsonUtils.envelope("showTraffic", 0, payload);
Thomas Vachuska329af532015-03-10 02:08:33 -0700646 }
647
Thomas Vachuska204cb6c2015-06-04 00:03:06 -0700648 private Load getLinkLoad(Link link) {
649 if (link.src().elementId() instanceof DeviceId) {
650 return flowStatsService.load(link);
651 }
652 return null;
653 }
654
655 private void addEdgeLinks(Map<LinkKey, BiLink> biLinks) {
656 hostService.getHosts().forEach(host -> {
Thomas Vachuska25455e72015-06-04 11:31:26 -0700657 addLink(biLinks, createEdgeLink(host, true));
658 addLink(biLinks, createEdgeLink(host, false));
Thomas Vachuska204cb6c2015-06-04 00:03:06 -0700659 });
660 }
661
662 private Map<LinkKey, BiLink> consolidateLinks(Iterable<Link> links) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700663 Map<LinkKey, BiLink> biLinks = new HashMap<>();
664 for (Link link : links) {
665 addLink(biLinks, link);
666 }
Thomas Vachuska204cb6c2015-06-04 00:03:06 -0700667 return biLinks;
Thomas Vachuska329af532015-03-10 02:08:33 -0700668 }
669
670 // Produces JSON message to trigger flow overview visualization
Simon Huntd2747a02015-04-30 22:41:16 -0700671 protected ObjectNode flowSummaryMessage(Set<Device> devices) {
Simon Huntda580882015-05-12 20:58:18 -0700672 ObjectNode payload = objectNode();
673 ArrayNode paths = arrayNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700674 payload.set("paths", paths);
675
676 for (Device device : devices) {
677 Map<Link, Integer> counts = getFlowCounts(device.id());
678 for (Link link : counts.keySet()) {
679 addLinkFlows(link, paths, counts.get(link));
680 }
681 }
Simon Huntd2747a02015-04-30 22:41:16 -0700682 return JsonUtils.envelope("showTraffic", 0, payload);
Thomas Vachuska329af532015-03-10 02:08:33 -0700683 }
684
685 private void addLinkFlows(Link link, ArrayNode paths, Integer count) {
Simon Huntda580882015-05-12 20:58:18 -0700686 ObjectNode pathNode = objectNode();
687 ArrayNode linksNode = arrayNode();
688 ArrayNode labels = arrayNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700689 boolean noFlows = count == null || count == 0;
690 pathNode.put("class", noFlows ? "secondary" : "primary");
691 pathNode.put("traffic", false);
692 pathNode.set("links", linksNode.add(compactLinkString(link)));
693 pathNode.set("labels", labels.add(noFlows ? "" : (count.toString() +
694 (count == 1 ? " flow" : " flows"))));
695 paths.add(pathNode);
696 }
697
698
699 // Produces JSON message to trigger traffic visualization
Simon Huntd2747a02015-04-30 22:41:16 -0700700 protected ObjectNode trafficMessage(TrafficClass... trafficClasses) {
Simon Huntda580882015-05-12 20:58:18 -0700701 ObjectNode payload = objectNode();
702 ArrayNode paths = arrayNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700703 payload.set("paths", paths);
704
705 // Classify links based on their traffic traffic first...
706 Map<LinkKey, BiLink> biLinks = classifyLinkTraffic(trafficClasses);
707
708 // Then separate the links into their respective classes and send them out.
709 Map<String, ObjectNode> pathNodes = new HashMap<>();
710 for (BiLink biLink : biLinks.values()) {
711 boolean hasTraffic = biLink.hasTraffic;
Thomas Vachuska5d5858b2015-03-23 16:31:10 -0700712 String tc = (biLink.classes() + (hasTraffic ? " animated" : "")).trim();
Thomas Vachuska329af532015-03-10 02:08:33 -0700713 ObjectNode pathNode = pathNodes.get(tc);
714 if (pathNode == null) {
Simon Huntda580882015-05-12 20:58:18 -0700715 pathNode = objectNode()
Thomas Vachuska329af532015-03-10 02:08:33 -0700716 .put("class", tc).put("traffic", hasTraffic);
Simon Huntda580882015-05-12 20:58:18 -0700717 pathNode.set("links", arrayNode());
718 pathNode.set("labels", arrayNode());
Thomas Vachuska329af532015-03-10 02:08:33 -0700719 pathNodes.put(tc, pathNode);
720 paths.add(pathNode);
721 }
722 ((ArrayNode) pathNode.path("links")).add(compactLinkString(biLink.one));
723 ((ArrayNode) pathNode.path("labels")).add(hasTraffic ? formatBytes(biLink.bytes) : "");
724 }
725
Simon Huntd2747a02015-04-30 22:41:16 -0700726 return JsonUtils.envelope("showTraffic", 0, payload);
Thomas Vachuska329af532015-03-10 02:08:33 -0700727 }
728
729 // Classifies the link traffic according to the specified classes.
730 private Map<LinkKey, BiLink> classifyLinkTraffic(TrafficClass... trafficClasses) {
731 Map<LinkKey, BiLink> biLinks = new HashMap<>();
732 for (TrafficClass trafficClass : trafficClasses) {
733 for (Intent intent : trafficClass.intents) {
734 boolean isOptical = intent instanceof OpticalConnectivityIntent;
735 List<Intent> installables = intentService.getInstallableIntents(intent.key());
736 if (installables != null) {
737 for (Intent installable : installables) {
738 String type = isOptical ? trafficClass.type + " optical" : trafficClass.type;
739 if (installable instanceof PathIntent) {
740 classifyLinks(type, biLinks, trafficClass.showTraffic,
741 ((PathIntent) installable).path().links());
Thomas Vachuskac0fe09a2015-05-21 12:56:22 -0700742 } else if (installable instanceof FlowRuleIntent) {
743 classifyLinks(type, biLinks, trafficClass.showTraffic,
744 linkResources(installable));
Thomas Vachuska329af532015-03-10 02:08:33 -0700745 } else if (installable instanceof LinkCollectionIntent) {
746 classifyLinks(type, biLinks, trafficClass.showTraffic,
747 ((LinkCollectionIntent) installable).links());
748 } else if (installable instanceof OpticalPathIntent) {
749 classifyLinks(type, biLinks, trafficClass.showTraffic,
Thomas Vachuska204cb6c2015-06-04 00:03:06 -0700750 ((OpticalPathIntent) installable).path().links());
Thomas Vachuska329af532015-03-10 02:08:33 -0700751 }
752 }
753 }
754 }
755 }
756 return biLinks;
757 }
758
Thomas Vachuskac0fe09a2015-05-21 12:56:22 -0700759 // Extracts links from the specified flow rule intent resources
760 private Collection<Link> linkResources(Intent installable) {
761 ImmutableList.Builder<Link> builder = ImmutableList.builder();
762 for (NetworkResource r : installable.resources()) {
763 if (r instanceof Link) {
764 builder.add((Link) r);
765 }
766 }
767 return builder.build();
768 }
769
Thomas Vachuska329af532015-03-10 02:08:33 -0700770
771 // Adds the link segments (path or tree) associated with the specified
772 // connectivity intent
773 private void classifyLinks(String type, Map<LinkKey, BiLink> biLinks,
774 boolean showTraffic, Iterable<Link> links) {
775 if (links != null) {
776 for (Link link : links) {
777 BiLink biLink = addLink(biLinks, link);
Thomas Vachuska204cb6c2015-06-04 00:03:06 -0700778 if (showTraffic) {
Thomas Vachuska25455e72015-06-04 11:31:26 -0700779 biLink.addLoad(getLinkLoad(link));
Thomas Vachuska329af532015-03-10 02:08:33 -0700780 }
Thomas Vachuska204cb6c2015-06-04 00:03:06 -0700781 biLink.addClass(type);
Thomas Vachuska329af532015-03-10 02:08:33 -0700782 }
783 }
784 }
785
786
Thomas Vachuska583bc632015-04-14 10:10:57 -0700787 static BiLink addLink(Map<LinkKey, BiLink> biLinks, Link link) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700788 LinkKey key = canonicalLinkKey(link);
789 BiLink biLink = biLinks.get(key);
790 if (biLink != null) {
791 biLink.setOther(link);
792 } else {
793 biLink = new BiLink(key, link);
794 biLinks.put(key, biLink);
795 }
796 return biLink;
797 }
798
Thomas Vachuska329af532015-03-10 02:08:33 -0700799 // Poor-mans formatting to get the labels with byte counts looking nice.
800 private String formatBytes(long bytes) {
801 String unit;
802 double value;
Srikanth Vavilapalli8c1ccca2015-06-08 19:23:24 -0700803 if (bytes > GIGA) {
804 value = bytes / GIGA;
805 unit = GBYTES_UNIT;
806 } else if (bytes > MEGA) {
807 value = bytes / MEGA;
808 unit = MBYTES_UNIT;
809 } else if (bytes > KILO) {
810 value = bytes / KILO;
811 unit = KBYTES_UNIT;
Thomas Vachuska329af532015-03-10 02:08:33 -0700812 } else {
813 value = bytes;
Srikanth Vavilapalli8c1ccca2015-06-08 19:23:24 -0700814 unit = BYTES_UNIT;
815 }
816 DecimalFormat format = new DecimalFormat("#,###.##");
817 return format.format(value) + " " + unit;
818 }
819
Simon Hunt80170a72015-06-10 19:49:57 -0700820 // Poor-mans formatting to get the labels with bit rate looking nice.
Srikanth Vavilapalli8c1ccca2015-06-08 19:23:24 -0700821 private String formatBitRate(long bytes) {
822 String unit;
823 double value;
824 //Convert to bits
825 long bits = bytes * 8;
826 if (bits > GIGA) {
827 value = bits / GIGA;
828 unit = GBITS_UNIT;
Simon Hunt80170a72015-06-10 19:49:57 -0700829
830 // NOTE: temporary hack to clip rate at 10.0 Gbps
831 // Added for the CORD Fabric demo at ONS 2015
832 if (value > 10.0) {
833 value = 10.0;
834 }
835
Srikanth Vavilapalli8c1ccca2015-06-08 19:23:24 -0700836 } else if (bits > MEGA) {
837 value = bits / MEGA;
838 unit = MBITS_UNIT;
839 } else if (bits > KILO) {
840 value = bits / KILO;
841 unit = KBITS_UNIT;
842 } else {
843 value = bits;
844 unit = BITS_UNIT;
Thomas Vachuska329af532015-03-10 02:08:33 -0700845 }
846 DecimalFormat format = new DecimalFormat("#,###.##");
847 return format.format(value) + " " + unit;
848 }
849
Thomas Vachuska329af532015-03-10 02:08:33 -0700850 // Produces compact string representation of a link.
851 private static String compactLinkString(Link link) {
852 return String.format(COMPACT, link.src().elementId(), link.src().port(),
853 link.dst().elementId(), link.dst().port());
854 }
855
Simon Huntb745ca62015-07-28 15:37:11 -0700856 // translates the property panel into JSON, for returning to the client
Simon Hunt0af1ec32015-07-24 12:17:55 -0700857 protected ObjectNode json(PropertyPanel pp) {
858 ObjectNode result = objectNode()
Simon Huntb745ca62015-07-28 15:37:11 -0700859 .put("title", pp.title())
860 .put("type", pp.typeId())
861 .put("id", pp.id());
862
Simon Hunt0af1ec32015-07-24 12:17:55 -0700863 ObjectNode pnode = objectNode();
864 ArrayNode porder = arrayNode();
865 for (PropertyPanel.Prop p : pp.properties()) {
866 porder.add(p.key());
867 pnode.put(p.key(), p.value());
868 }
869 result.set("propOrder", porder);
870 result.set("props", pnode);
Simon Huntfb940112015-07-29 18:36:35 -0700871
872 ArrayNode buttons = arrayNode();
Simon Hunt3a0598f2015-08-04 19:59:04 -0700873 for (ButtonDescriptor b : pp.buttons()) {
874 buttons.add(json(b));
Simon Huntfb940112015-07-29 18:36:35 -0700875 }
876 result.set("buttons", buttons);
Simon Hunt0af1ec32015-07-24 12:17:55 -0700877 return result;
878 }
879
Simon Hunt3a0598f2015-08-04 19:59:04 -0700880 // translates the button descriptor into JSON
881 private ObjectNode json(ButtonDescriptor bdesc) {
882 return objectNode()
883 .put("id", bdesc.id())
884 .put("gid", bdesc.glyphId())
885 .put("tt", bdesc.tooltip());
886 }
887
888
Thomas Vachuska329af532015-03-10 02:08:33 -0700889 // Produces canonical link key, i.e. one that will match link and its inverse.
Thomas Vachuska583bc632015-04-14 10:10:57 -0700890 static LinkKey canonicalLinkKey(Link link) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700891 String sn = link.src().elementId().toString();
892 String dn = link.dst().elementId().toString();
893 return sn.compareTo(dn) < 0 ?
894 linkKey(link.src(), link.dst()) : linkKey(link.dst(), link.src());
895 }
896
897 // Representation of link and its inverse and any traffic data.
Thomas Vachuska583bc632015-04-14 10:10:57 -0700898 static class BiLink {
Thomas Vachuska329af532015-03-10 02:08:33 -0700899 public final LinkKey key;
900 public final Link one;
901 public Link two;
902 public boolean hasTraffic = false;
903 public long bytes = 0;
Thomas Vachuska5d5858b2015-03-23 16:31:10 -0700904
905 private Set<String> classes = new HashSet<>();
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700906 private long rate;
Thomas Vachuska329af532015-03-10 02:08:33 -0700907
908 BiLink(LinkKey key, Link link) {
909 this.key = key;
910 this.one = link;
911 }
912
913 void setOther(Link link) {
914 this.two = link;
915 }
916
917 void addLoad(Load load) {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700918 addLoad(load, 0);
919 }
920
Thomas Vachuska204cb6c2015-06-04 00:03:06 -0700921 void addLoad(Load load, double threshold) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700922 if (load != null) {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700923 this.hasTraffic = hasTraffic || load.rate() > threshold;
Thomas Vachuska329af532015-03-10 02:08:33 -0700924 this.bytes += load.latest();
Thomas Vachuskafdbc4c22015-05-29 15:53:01 -0700925 this.rate += load.rate();
Thomas Vachuska329af532015-03-10 02:08:33 -0700926 }
927 }
928
Srikanth Vavilapalli8c1ccca2015-06-08 19:23:24 -0700929 void addLoad(Load srcLinkLoad,
930 double srcLinkThreshold,
931 Load dstLinkLoad,
932 double dstLinkThreshold) {
933 //use the max of link load at source or destination
934 if (srcLinkLoad != null) {
935 this.hasTraffic = hasTraffic || srcLinkLoad.rate() > srcLinkThreshold;
936 this.bytes = srcLinkLoad.latest();
937 this.rate = srcLinkLoad.rate();
938 }
939
940 if (dstLinkLoad != null) {
941 if (dstLinkLoad.rate() > this.rate) {
942 this.bytes = dstLinkLoad.latest();
943 this.rate = dstLinkLoad.rate();
944 this.hasTraffic = hasTraffic || dstLinkLoad.rate() > dstLinkThreshold;
945 }
946 }
947 }
948
Thomas Vachuska329af532015-03-10 02:08:33 -0700949 void addClass(String trafficClass) {
Thomas Vachuska5d5858b2015-03-23 16:31:10 -0700950 classes.add(trafficClass);
951 }
952
953 String classes() {
954 StringBuilder sb = new StringBuilder();
955 classes.forEach(c -> sb.append(c).append(" "));
956 return sb.toString().trim();
Thomas Vachuska329af532015-03-10 02:08:33 -0700957 }
958 }
959
Thomas Vachuska329af532015-03-10 02:08:33 -0700960
Simon Hunt93735af2015-07-28 12:10:30 -0700961 // TODO: move this to traffic overlay component
Thomas Vachuska329af532015-03-10 02:08:33 -0700962 // Auxiliary carrier of data for requesting traffic message.
Thomas Vachuska583bc632015-04-14 10:10:57 -0700963 static class TrafficClass {
Thomas Vachuska329af532015-03-10 02:08:33 -0700964 public final boolean showTraffic;
965 public final String type;
966 public final Iterable<Intent> intents;
967
968 TrafficClass(String type, Iterable<Intent> intents) {
969 this(type, intents, false);
970 }
971
972 TrafficClass(String type, Iterable<Intent> intents, boolean showTraffic) {
973 this.type = type;
974 this.intents = intents;
975 this.showTraffic = showTraffic;
976 }
977 }
978
979}