blob: 9265e5f5083fdec39acdb30549ab5d17db66d523 [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 Hunt8d22c4b2015-08-06 16:24:43 -070075import org.onosproject.ui.topo.ButtonId;
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
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700126 private static final String SHOW_HIGHLIGHTS = "showHighlights";
127
Srikanth Vavilapalli8c1ccca2015-06-08 19:23:24 -0700128 private static final double KILO = 1024;
129 private static final double MEGA = 1024 * KILO;
130 private static final double GIGA = 1024 * MEGA;
Thomas Vachuska329af532015-03-10 02:08:33 -0700131
Srikanth Vavilapalli8c1ccca2015-06-08 19:23:24 -0700132 private static final String GBITS_UNIT = "Gb";
133 private static final String MBITS_UNIT = "Mb";
134 private static final String KBITS_UNIT = "Kb";
135 private static final String BITS_UNIT = "b";
136 private static final String GBYTES_UNIT = "GB";
137 private static final String MBYTES_UNIT = "MB";
138 private static final String KBYTES_UNIT = "KB";
139 private static final String BYTES_UNIT = "B";
140 //4 Kilo Bytes as threshold
141 private static final double BPS_THRESHOLD = 4 * KILO;
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700142
Thomas Vachuska329af532015-03-10 02:08:33 -0700143 protected ServiceDirectory directory;
144 protected ClusterService clusterService;
145 protected DeviceService deviceService;
146 protected LinkService linkService;
147 protected HostService hostService;
148 protected MastershipService mastershipService;
149 protected IntentService intentService;
150 protected FlowRuleService flowService;
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700151 protected StatisticService flowStatsService;
152 protected PortStatisticsService portStatsService;
Thomas Vachuska329af532015-03-10 02:08:33 -0700153 protected TopologyService topologyService;
cheng fan35dc0f22015-06-10 06:02:47 +0800154 protected TunnelService tunnelService;
Thomas Vachuska329af532015-03-10 02:08:33 -0700155
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700156 protected enum StatsType {
157 FLOW, PORT
158 }
159
Thomas Vachuska329af532015-03-10 02:08:33 -0700160 private String version;
161
162 // TODO: extract into an external & durable state; good enough for now and demo
163 private static Map<String, ObjectNode> metaUi = new ConcurrentHashMap<>();
164
165 /**
Thomas Vachuska329af532015-03-10 02:08:33 -0700166 * Returns read-only view of the meta-ui information.
167 *
168 * @return map of id to meta-ui mementos
169 */
170 static Map<String, ObjectNode> getMetaUi() {
171 return Collections.unmodifiableMap(metaUi);
172 }
173
174 @Override
175 public void init(UiConnection connection, ServiceDirectory directory) {
176 super.init(connection, directory);
177 this.directory = checkNotNull(directory, "Directory cannot be null");
178 clusterService = directory.get(ClusterService.class);
179 deviceService = directory.get(DeviceService.class);
180 linkService = directory.get(LinkService.class);
181 hostService = directory.get(HostService.class);
182 mastershipService = directory.get(MastershipService.class);
183 intentService = directory.get(IntentService.class);
184 flowService = directory.get(FlowRuleService.class);
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700185 flowStatsService = directory.get(StatisticService.class);
186 portStatsService = directory.get(PortStatisticsService.class);
Thomas Vachuska329af532015-03-10 02:08:33 -0700187 topologyService = directory.get(TopologyService.class);
cheng fan35dc0f22015-06-10 06:02:47 +0800188 tunnelService = directory.get(TunnelService.class);
Thomas Vachuska329af532015-03-10 02:08:33 -0700189
190 String ver = directory.get(CoreService.class).version().toString();
191 version = ver.replace(".SNAPSHOT", "*").replaceFirst("~.*$", "");
192 }
193
Thomas Vachuska329af532015-03-10 02:08:33 -0700194 // Returns the specified set of IP addresses as a string.
195 private String ip(Set<IpAddress> ipAddresses) {
196 Iterator<IpAddress> it = ipAddresses.iterator();
197 return it.hasNext() ? it.next().toString() : "unknown";
198 }
199
200 // Produces JSON structure from annotations.
201 private JsonNode props(Annotations annotations) {
Simon Huntda580882015-05-12 20:58:18 -0700202 ObjectNode props = objectNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700203 if (annotations != null) {
204 for (String key : annotations.keys()) {
205 props.put(key, annotations.value(key));
206 }
207 }
208 return props;
209 }
210
211 // Produces an informational log message event bound to the client.
212 protected ObjectNode info(long id, String message) {
213 return message("info", id, message);
214 }
215
216 // Produces a warning log message event bound to the client.
217 protected ObjectNode warning(long id, String message) {
218 return message("warning", id, message);
219 }
220
221 // Produces an error log message event bound to the client.
222 protected ObjectNode error(long id, String message) {
223 return message("error", id, message);
224 }
225
226 // Produces a log message event bound to the client.
227 private ObjectNode message(String severity, long id, String message) {
Simon Huntda580882015-05-12 20:58:18 -0700228 ObjectNode payload = objectNode()
Simon Huntd2747a02015-04-30 22:41:16 -0700229 .put("severity", severity)
230 .put("message", message);
Thomas Vachuska329af532015-03-10 02:08:33 -0700231
Simon Huntd2747a02015-04-30 22:41:16 -0700232 return JsonUtils.envelope("message", id, payload);
Thomas Vachuska329af532015-03-10 02:08:33 -0700233 }
234
235 // Produces a set of all hosts listed in the specified JSON array.
236 protected Set<Host> getHosts(ArrayNode array) {
237 Set<Host> hosts = new HashSet<>();
238 if (array != null) {
239 for (JsonNode node : array) {
240 try {
241 addHost(hosts, hostId(node.asText()));
242 } catch (IllegalArgumentException e) {
243 log.debug("Skipping ID {}", node.asText());
244 }
245 }
246 }
247 return hosts;
248 }
249
250 // Adds the specified host to the set of hosts.
251 private void addHost(Set<Host> hosts, HostId hostId) {
252 Host host = hostService.getHost(hostId);
253 if (host != null) {
254 hosts.add(host);
255 }
256 }
257
258
259 // Produces a set of all devices listed in the specified JSON array.
260 protected Set<Device> getDevices(ArrayNode array) {
261 Set<Device> devices = new HashSet<>();
262 if (array != null) {
263 for (JsonNode node : array) {
264 try {
265 addDevice(devices, deviceId(node.asText()));
266 } catch (IllegalArgumentException e) {
267 log.debug("Skipping ID {}", node.asText());
268 }
269 }
270 }
271 return devices;
272 }
273
274 private void addDevice(Set<Device> devices, DeviceId deviceId) {
275 Device device = deviceService.getDevice(deviceId);
276 if (device != null) {
277 devices.add(device);
278 }
279 }
280
281 protected void addHover(Set<Host> hosts, Set<Device> devices, String hover) {
282 try {
283 addHost(hosts, hostId(hover));
284 } catch (IllegalArgumentException e) {
285 try {
286 addDevice(devices, deviceId(hover));
287 } catch (IllegalArgumentException ne) {
288 log.debug("Skipping ID {}", hover);
289 }
290 }
291 }
292
293 // Produces a cluster instance message to the client.
294 protected ObjectNode instanceMessage(ClusterEvent event, String messageType) {
295 ControllerNode node = event.subject();
296 int switchCount = mastershipService.getDevicesOf(node.id()).size();
Simon Huntda580882015-05-12 20:58:18 -0700297 ObjectNode payload = objectNode()
Thomas Vachuska329af532015-03-10 02:08:33 -0700298 .put("id", node.id().toString())
299 .put("ip", node.ip().toString())
300 .put("online", clusterService.getState(node.id()) == ACTIVE)
Thomas Vachuskab6acc7b2015-03-11 09:11:21 -0700301 .put("uiAttached", node.equals(clusterService.getLocalNode()))
Thomas Vachuska329af532015-03-10 02:08:33 -0700302 .put("switches", switchCount);
303
Simon Huntda580882015-05-12 20:58:18 -0700304 ArrayNode labels = arrayNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700305 labels.add(node.id().toString());
306 labels.add(node.ip().toString());
307
308 // Add labels, props and stuff the payload into envelope.
309 payload.set("labels", labels);
310 addMetaUi(node.id().toString(), payload);
311
312 String type = messageType != null ? messageType :
313 ((event.type() == INSTANCE_ADDED) ? "addInstance" :
314 ((event.type() == INSTANCE_REMOVED ? "removeInstance" :
315 "addInstance")));
Simon Huntd2747a02015-04-30 22:41:16 -0700316 return JsonUtils.envelope(type, 0, payload);
Thomas Vachuska329af532015-03-10 02:08:33 -0700317 }
318
319 // Produces a device event message to the client.
320 protected ObjectNode deviceMessage(DeviceEvent event) {
321 Device device = event.subject();
Simon Huntda580882015-05-12 20:58:18 -0700322 ObjectNode payload = objectNode()
Thomas Vachuska329af532015-03-10 02:08:33 -0700323 .put("id", device.id().toString())
324 .put("type", device.type().toString().toLowerCase())
325 .put("online", deviceService.isAvailable(device.id()))
326 .put("master", master(device.id()));
327
328 // Generate labels: id, chassis id, no-label, optional-name
329 String name = device.annotations().value(AnnotationKeys.NAME);
Simon Huntda580882015-05-12 20:58:18 -0700330 ArrayNode labels = arrayNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700331 labels.add("");
332 labels.add(isNullOrEmpty(name) ? device.id().toString() : name);
333 labels.add(device.id().toString());
334
335 // Add labels, props and stuff the payload into envelope.
336 payload.set("labels", labels);
337 payload.set("props", props(device.annotations()));
338 addGeoLocation(device, payload);
339 addMetaUi(device.id().toString(), payload);
340
341 String type = (event.type() == DEVICE_ADDED) ? "addDevice" :
342 ((event.type() == DEVICE_REMOVED) ? "removeDevice" : "updateDevice");
Simon Huntd2747a02015-04-30 22:41:16 -0700343 return JsonUtils.envelope(type, 0, payload);
Thomas Vachuska329af532015-03-10 02:08:33 -0700344 }
345
346 // Produces a link event message to the client.
347 protected ObjectNode linkMessage(LinkEvent event) {
348 Link link = event.subject();
Simon Huntda580882015-05-12 20:58:18 -0700349 ObjectNode payload = objectNode()
Thomas Vachuska329af532015-03-10 02:08:33 -0700350 .put("id", compactLinkString(link))
351 .put("type", link.type().toString().toLowerCase())
352 .put("online", link.state() == Link.State.ACTIVE)
353 .put("linkWidth", 1.2)
354 .put("src", link.src().deviceId().toString())
355 .put("srcPort", link.src().port().toString())
356 .put("dst", link.dst().deviceId().toString())
357 .put("dstPort", link.dst().port().toString());
358 String type = (event.type() == LINK_ADDED) ? "addLink" :
359 ((event.type() == LINK_REMOVED) ? "removeLink" : "updateLink");
Simon Huntd2747a02015-04-30 22:41:16 -0700360 return JsonUtils.envelope(type, 0, payload);
Thomas Vachuska329af532015-03-10 02:08:33 -0700361 }
362
363 // Produces a host event message to the client.
364 protected ObjectNode hostMessage(HostEvent event) {
365 Host host = event.subject();
366 String hostType = host.annotations().value(AnnotationKeys.TYPE);
Simon Huntda580882015-05-12 20:58:18 -0700367 ObjectNode payload = objectNode()
Thomas Vachuska329af532015-03-10 02:08:33 -0700368 .put("id", host.id().toString())
369 .put("type", isNullOrEmpty(hostType) ? "endstation" : hostType)
370 .put("ingress", compactLinkString(edgeLink(host, true)))
371 .put("egress", compactLinkString(edgeLink(host, false)));
Simon Huntda580882015-05-12 20:58:18 -0700372 payload.set("cp", hostConnect(host.location()));
373 payload.set("labels", labels(ip(host.ipAddresses()),
Thomas Vachuska329af532015-03-10 02:08:33 -0700374 host.mac().toString()));
375 payload.set("props", props(host.annotations()));
376 addGeoLocation(host, payload);
377 addMetaUi(host.id().toString(), payload);
378
379 String type = (event.type() == HOST_ADDED) ? "addHost" :
380 ((event.type() == HOST_REMOVED) ? "removeHost" : "updateHost");
Simon Huntd2747a02015-04-30 22:41:16 -0700381 return JsonUtils.envelope(type, 0, payload);
Thomas Vachuska329af532015-03-10 02:08:33 -0700382 }
383
384 // Encodes the specified host location into a JSON object.
Simon Huntda580882015-05-12 20:58:18 -0700385 private ObjectNode hostConnect(HostLocation location) {
386 return objectNode()
Thomas Vachuska329af532015-03-10 02:08:33 -0700387 .put("device", location.deviceId().toString())
388 .put("port", location.port().toLong());
389 }
390
391 // Encodes the specified list of labels a JSON array.
Simon Huntda580882015-05-12 20:58:18 -0700392 private ArrayNode labels(String... labels) {
393 ArrayNode json = arrayNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700394 for (String label : labels) {
395 json.add(label);
396 }
397 return json;
398 }
399
400 // Returns the name of the master node for the specified device id.
401 private String master(DeviceId deviceId) {
402 NodeId master = mastershipService.getMasterFor(deviceId);
403 return master != null ? master.toString() : "";
404 }
405
406 // Generates an edge link from the specified host location.
407 private EdgeLink edgeLink(Host host, boolean ingress) {
408 return new DefaultEdgeLink(PID, new ConnectPoint(host.id(), portNumber(0)),
409 host.location(), ingress);
410 }
411
412 // Adds meta UI information for the specified object.
413 private void addMetaUi(String id, ObjectNode payload) {
414 ObjectNode meta = metaUi.get(id);
415 if (meta != null) {
416 payload.set("metaUi", meta);
417 }
418 }
419
420 // Adds a geo location JSON to the specified payload object.
421 private void addGeoLocation(Annotated annotated, ObjectNode payload) {
422 Annotations annotations = annotated.annotations();
423 if (annotations == null) {
424 return;
425 }
426
427 String slat = annotations.value(AnnotationKeys.LATITUDE);
428 String slng = annotations.value(AnnotationKeys.LONGITUDE);
429 try {
430 if (slat != null && slng != null && !slat.isEmpty() && !slng.isEmpty()) {
431 double lat = Double.parseDouble(slat);
432 double lng = Double.parseDouble(slng);
Simon Huntda580882015-05-12 20:58:18 -0700433 ObjectNode loc = objectNode()
Thomas Vachuska329af532015-03-10 02:08:33 -0700434 .put("type", "latlng").put("lat", lat).put("lng", lng);
435 payload.set("location", loc);
436 }
437 } catch (NumberFormatException e) {
438 log.warn("Invalid geo data latitude={}; longiture={}", slat, slng);
439 }
440 }
441
442 // Updates meta UI information for the specified object.
Simon Huntd2747a02015-04-30 22:41:16 -0700443 protected void updateMetaUi(ObjectNode payload) {
444 metaUi.put(JsonUtils.string(payload, "id"),
445 JsonUtils.node(payload, "memento"));
Thomas Vachuska329af532015-03-10 02:08:33 -0700446 }
447
Simon Huntb745ca62015-07-28 15:37:11 -0700448 // -----------------------------------------------------------------------
449 // Create models of the data to return, that overlays can adjust / augment
450
451 // Returns property panel model for summary response.
Simon Hunt0af1ec32015-07-24 12:17:55 -0700452 protected PropertyPanel summmaryMessage(long sid) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700453 Topology topology = topologyService.currentTopology();
Simon Hunt0af1ec32015-07-24 12:17:55 -0700454
Simon Hunt00a27ff2015-07-28 18:53:40 -0700455 return new PropertyPanel("ONOS Summary", "node")
Simon Huntfb940112015-07-29 18:36:35 -0700456 .addProp(Properties.DEVICES, topology.deviceCount())
457 .addProp(Properties.LINKS, topology.linkCount())
458 .addProp(Properties.HOSTS, hostService.getHostCount())
459 .addProp(Properties.TOPOLOGY_SSCS, topology.clusterCount())
Simon Hunt00a27ff2015-07-28 18:53:40 -0700460 .addSeparator()
Simon Huntfb940112015-07-29 18:36:35 -0700461 .addProp(Properties.INTENTS, intentService.getIntentCount())
462 .addProp(Properties.TUNNELS, tunnelService.tunnelCount())
463 .addProp(Properties.FLOWS, flowService.getFlowRuleCount())
464 .addProp(Properties.VERSION, version);
Thomas Vachuska329af532015-03-10 02:08:33 -0700465 }
466
Simon Huntb745ca62015-07-28 15:37:11 -0700467 // Returns property panel model for device details response.
468 protected PropertyPanel deviceDetails(DeviceId deviceId, long sid) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700469 Device device = deviceService.getDevice(deviceId);
470 Annotations annot = device.annotations();
471 String name = annot.value(AnnotationKeys.NAME);
472 int portCount = deviceService.getPorts(deviceId).size();
473 int flowCount = getFlowCount(deviceId);
cheng fan35dc0f22015-06-10 06:02:47 +0800474 int tunnelCount = getTunnelCount(deviceId);
Simon Huntb745ca62015-07-28 15:37:11 -0700475
476 String title = isNullOrEmpty(name) ? deviceId.toString() : name;
477 String typeId = device.type().toString().toLowerCase();
478
479 PropertyPanel pp = new PropertyPanel(title, typeId)
Simon Huntfb940112015-07-29 18:36:35 -0700480 .id(deviceId.toString())
Simon Hunt3a0598f2015-08-04 19:59:04 -0700481
Simon Huntfb940112015-07-29 18:36:35 -0700482 .addProp(Properties.URI, deviceId.toString())
483 .addProp(Properties.VENDOR, device.manufacturer())
484 .addProp(Properties.HW_VERSION, device.hwVersion())
485 .addProp(Properties.SW_VERSION, device.swVersion())
486 .addProp(Properties.SERIAL_NUMBER, device.serialNumber())
487 .addProp(Properties.PROTOCOL, annot.value(AnnotationKeys.PROTOCOL))
488 .addSeparator()
Simon Hunt3a0598f2015-08-04 19:59:04 -0700489
Simon Huntfb940112015-07-29 18:36:35 -0700490 .addProp(Properties.LATITUDE, annot.value(AnnotationKeys.LATITUDE))
491 .addProp(Properties.LONGITUDE, annot.value(AnnotationKeys.LONGITUDE))
492 .addSeparator()
Simon Hunt3a0598f2015-08-04 19:59:04 -0700493
Simon Huntfb940112015-07-29 18:36:35 -0700494 .addProp(Properties.PORTS, portCount)
495 .addProp(Properties.FLOWS, flowCount)
Simon Hunt3a0598f2015-08-04 19:59:04 -0700496 .addProp(Properties.TUNNELS, tunnelCount)
Simon Hunt00a27ff2015-07-28 18:53:40 -0700497
Simon Hunt3a0598f2015-08-04 19:59:04 -0700498 .addButton(CoreButtons.SHOW_DEVICE_VIEW)
499 .addButton(CoreButtons.SHOW_FLOW_VIEW)
500 .addButton(CoreButtons.SHOW_PORT_VIEW)
501 .addButton(CoreButtons.SHOW_GROUP_VIEW);
Simon Huntb745ca62015-07-28 15:37:11 -0700502
503 return pp;
Thomas Vachuska329af532015-03-10 02:08:33 -0700504 }
505
506 protected int getFlowCount(DeviceId deviceId) {
507 int count = 0;
Simon Huntb745ca62015-07-28 15:37:11 -0700508 for (FlowEntry flowEntry : flowService.getFlowEntries(deviceId)) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700509 count++;
Thomas Vachuska329af532015-03-10 02:08:33 -0700510 }
511 return count;
512 }
513
cheng fan35dc0f22015-06-10 06:02:47 +0800514 protected int getTunnelCount(DeviceId deviceId) {
515 int count = 0;
516 Collection<Tunnel> tunnels = tunnelService.queryAllTunnels();
517 for (Tunnel tunnel : tunnels) {
518 OpticalTunnelEndPoint src = (OpticalTunnelEndPoint) tunnel.src();
519 OpticalTunnelEndPoint dst = (OpticalTunnelEndPoint) tunnel.dst();
520 DeviceId srcDevice = (DeviceId) src.elementId().get();
521 DeviceId dstDevice = (DeviceId) dst.elementId().get();
Simon Huntb745ca62015-07-28 15:37:11 -0700522 if (srcDevice.toString().equals(deviceId.toString()) ||
523 dstDevice.toString().equals(deviceId.toString())) {
cheng fan35dc0f22015-06-10 06:02:47 +0800524 count++;
525 }
526 }
527 return count;
528 }
529
Thomas Vachuska329af532015-03-10 02:08:33 -0700530 // Counts all entries that egress on the given device links.
531 protected Map<Link, Integer> getFlowCounts(DeviceId deviceId) {
532 List<FlowEntry> entries = new ArrayList<>();
533 Set<Link> links = new HashSet<>(linkService.getDeviceEgressLinks(deviceId));
534 Set<Host> hosts = hostService.getConnectedHosts(deviceId);
Simon Huntb745ca62015-07-28 15:37:11 -0700535 for (FlowEntry flowEntry : flowService.getFlowEntries(deviceId)) {
536 entries.add(flowEntry);
Thomas Vachuska329af532015-03-10 02:08:33 -0700537 }
538
539 // Add all edge links to the set
540 if (hosts != null) {
541 for (Host host : hosts) {
542 links.add(new DefaultEdgeLink(host.providerId(),
543 new ConnectPoint(host.id(), P0),
544 host.location(), false));
545 }
546 }
547
548 Map<Link, Integer> counts = new HashMap<>();
549 for (Link link : links) {
550 counts.put(link, getEgressFlows(link, entries));
551 }
552 return counts;
553 }
554
555 // Counts all entries that egress on the link source port.
556 private Integer getEgressFlows(Link link, List<FlowEntry> entries) {
557 int count = 0;
558 PortNumber out = link.src().port();
559 for (FlowEntry entry : entries) {
560 TrafficTreatment treatment = entry.treatment();
Ray Milkey42507352015-03-20 15:16:10 -0700561 for (Instruction instruction : treatment.allInstructions()) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700562 if (instruction.type() == Instruction.Type.OUTPUT &&
563 ((OutputInstruction) instruction).port().equals(out)) {
564 count++;
565 }
566 }
567 }
568 return count;
569 }
570
571
572 // Returns host details response.
Simon Huntb745ca62015-07-28 15:37:11 -0700573 protected PropertyPanel hostDetails(HostId hostId, long sid) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700574 Host host = hostService.getHost(hostId);
575 Annotations annot = host.annotations();
576 String type = annot.value(AnnotationKeys.TYPE);
577 String name = annot.value(AnnotationKeys.NAME);
578 String vlan = host.vlan().toString();
Simon Huntb745ca62015-07-28 15:37:11 -0700579
580 String title = isNullOrEmpty(name) ? hostId.toString() : name;
581 String typeId = isNullOrEmpty(type) ? "endstation" : type;
582
583 PropertyPanel pp = new PropertyPanel(title, typeId)
Simon Huntfb940112015-07-29 18:36:35 -0700584 .id(hostId.toString())
585 .addProp(Properties.MAC, host.mac())
586 .addProp(Properties.IP, host.ipAddresses(), "[\\[\\]]")
587 .addProp(Properties.VLAN, vlan.equals("-1") ? "none" : vlan)
588 .addSeparator()
589 .addProp(Properties.LATITUDE, annot.value(AnnotationKeys.LATITUDE))
590 .addProp(Properties.LONGITUDE, annot.value(AnnotationKeys.LONGITUDE));
Simon Huntb745ca62015-07-28 15:37:11 -0700591
Simon Hunt00a27ff2015-07-28 18:53:40 -0700592 // TODO: add button descriptors
Simon Huntb745ca62015-07-28 15:37:11 -0700593 return pp;
Thomas Vachuska329af532015-03-10 02:08:33 -0700594 }
595
596
Simon Huntb745ca62015-07-28 15:37:11 -0700597 // TODO: migrate to Traffic overlay
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700598 // Produces JSON message to trigger flow traffic overview visualization
599 protected ObjectNode trafficSummaryMessage(StatsType type) {
Simon Huntda580882015-05-12 20:58:18 -0700600 ObjectNode payload = objectNode();
601 ArrayNode paths = arrayNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700602 payload.set("paths", paths);
603
Simon Huntda580882015-05-12 20:58:18 -0700604 ObjectNode pathNodeN = objectNode();
605 ArrayNode linksNodeN = arrayNode();
606 ArrayNode labelsN = arrayNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700607
608 pathNodeN.put("class", "plain").put("traffic", false);
609 pathNodeN.set("links", linksNodeN);
610 pathNodeN.set("labels", labelsN);
611 paths.add(pathNodeN);
612
Simon Huntda580882015-05-12 20:58:18 -0700613 ObjectNode pathNodeT = objectNode();
614 ArrayNode linksNodeT = arrayNode();
615 ArrayNode labelsT = arrayNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700616
617 pathNodeT.put("class", "secondary").put("traffic", true);
618 pathNodeT.set("links", linksNodeT);
619 pathNodeT.set("labels", labelsT);
620 paths.add(pathNodeT);
621
Thomas Vachuska204cb6c2015-06-04 00:03:06 -0700622 Map<LinkKey, BiLink> biLinks = consolidateLinks(linkService.getLinks());
623 addEdgeLinks(biLinks);
624 for (BiLink link : biLinks.values()) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700625 boolean bi = link.two != null;
Thomas Vachuska204cb6c2015-06-04 00:03:06 -0700626 if (type == FLOW) {
627 link.addLoad(getLinkLoad(link.one));
628 link.addLoad(bi ? getLinkLoad(link.two) : null);
629 } else if (type == PORT) {
Srikanth Vavilapalli8c1ccca2015-06-08 19:23:24 -0700630 //For a bi-directional traffic links, use
631 //the max link rate of either direction
632 link.addLoad(portStatsService.load(link.one.src()),
633 BPS_THRESHOLD,
634 portStatsService.load(link.one.dst()),
635 BPS_THRESHOLD);
Thomas Vachuska204cb6c2015-06-04 00:03:06 -0700636 }
637 if (link.hasTraffic) {
638 linksNodeT.add(compactLinkString(link.one));
639 labelsT.add(type == PORT ?
Srikanth Vavilapalli8c1ccca2015-06-08 19:23:24 -0700640 formatBitRate(link.rate) + "ps" :
Thomas Vachuska204cb6c2015-06-04 00:03:06 -0700641 formatBytes(link.bytes));
642 } else {
643 linksNodeN.add(compactLinkString(link.one));
644 labelsN.add("");
Thomas Vachuska329af532015-03-10 02:08:33 -0700645 }
646 }
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700647 return JsonUtils.envelope(SHOW_HIGHLIGHTS, 0, payload);
Thomas Vachuska329af532015-03-10 02:08:33 -0700648 }
649
Thomas Vachuska204cb6c2015-06-04 00:03:06 -0700650 private Load getLinkLoad(Link link) {
651 if (link.src().elementId() instanceof DeviceId) {
652 return flowStatsService.load(link);
653 }
654 return null;
655 }
656
657 private void addEdgeLinks(Map<LinkKey, BiLink> biLinks) {
658 hostService.getHosts().forEach(host -> {
Thomas Vachuska25455e72015-06-04 11:31:26 -0700659 addLink(biLinks, createEdgeLink(host, true));
660 addLink(biLinks, createEdgeLink(host, false));
Thomas Vachuska204cb6c2015-06-04 00:03:06 -0700661 });
662 }
663
664 private Map<LinkKey, BiLink> consolidateLinks(Iterable<Link> links) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700665 Map<LinkKey, BiLink> biLinks = new HashMap<>();
666 for (Link link : links) {
667 addLink(biLinks, link);
668 }
Thomas Vachuska204cb6c2015-06-04 00:03:06 -0700669 return biLinks;
Thomas Vachuska329af532015-03-10 02:08:33 -0700670 }
671
672 // Produces JSON message to trigger flow overview visualization
Simon Huntd2747a02015-04-30 22:41:16 -0700673 protected ObjectNode flowSummaryMessage(Set<Device> devices) {
Simon Huntda580882015-05-12 20:58:18 -0700674 ObjectNode payload = objectNode();
675 ArrayNode paths = arrayNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700676 payload.set("paths", paths);
677
678 for (Device device : devices) {
679 Map<Link, Integer> counts = getFlowCounts(device.id());
680 for (Link link : counts.keySet()) {
681 addLinkFlows(link, paths, counts.get(link));
682 }
683 }
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700684 return JsonUtils.envelope(SHOW_HIGHLIGHTS, 0, payload);
Thomas Vachuska329af532015-03-10 02:08:33 -0700685 }
686
687 private void addLinkFlows(Link link, ArrayNode paths, Integer count) {
Simon Huntda580882015-05-12 20:58:18 -0700688 ObjectNode pathNode = objectNode();
689 ArrayNode linksNode = arrayNode();
690 ArrayNode labels = arrayNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700691 boolean noFlows = count == null || count == 0;
692 pathNode.put("class", noFlows ? "secondary" : "primary");
693 pathNode.put("traffic", false);
694 pathNode.set("links", linksNode.add(compactLinkString(link)));
695 pathNode.set("labels", labels.add(noFlows ? "" : (count.toString() +
696 (count == 1 ? " flow" : " flows"))));
697 paths.add(pathNode);
698 }
699
700
701 // Produces JSON message to trigger traffic visualization
Simon Huntd2747a02015-04-30 22:41:16 -0700702 protected ObjectNode trafficMessage(TrafficClass... trafficClasses) {
Simon Huntda580882015-05-12 20:58:18 -0700703 ObjectNode payload = objectNode();
704 ArrayNode paths = arrayNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700705 payload.set("paths", paths);
706
707 // Classify links based on their traffic traffic first...
708 Map<LinkKey, BiLink> biLinks = classifyLinkTraffic(trafficClasses);
709
710 // Then separate the links into their respective classes and send them out.
711 Map<String, ObjectNode> pathNodes = new HashMap<>();
712 for (BiLink biLink : biLinks.values()) {
713 boolean hasTraffic = biLink.hasTraffic;
Thomas Vachuska5d5858b2015-03-23 16:31:10 -0700714 String tc = (biLink.classes() + (hasTraffic ? " animated" : "")).trim();
Thomas Vachuska329af532015-03-10 02:08:33 -0700715 ObjectNode pathNode = pathNodes.get(tc);
716 if (pathNode == null) {
Simon Huntda580882015-05-12 20:58:18 -0700717 pathNode = objectNode()
Thomas Vachuska329af532015-03-10 02:08:33 -0700718 .put("class", tc).put("traffic", hasTraffic);
Simon Huntda580882015-05-12 20:58:18 -0700719 pathNode.set("links", arrayNode());
720 pathNode.set("labels", arrayNode());
Thomas Vachuska329af532015-03-10 02:08:33 -0700721 pathNodes.put(tc, pathNode);
722 paths.add(pathNode);
723 }
724 ((ArrayNode) pathNode.path("links")).add(compactLinkString(biLink.one));
725 ((ArrayNode) pathNode.path("labels")).add(hasTraffic ? formatBytes(biLink.bytes) : "");
726 }
727
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700728 return JsonUtils.envelope(SHOW_HIGHLIGHTS, 0, payload);
Thomas Vachuska329af532015-03-10 02:08:33 -0700729 }
730
731 // Classifies the link traffic according to the specified classes.
732 private Map<LinkKey, BiLink> classifyLinkTraffic(TrafficClass... trafficClasses) {
733 Map<LinkKey, BiLink> biLinks = new HashMap<>();
734 for (TrafficClass trafficClass : trafficClasses) {
735 for (Intent intent : trafficClass.intents) {
736 boolean isOptical = intent instanceof OpticalConnectivityIntent;
737 List<Intent> installables = intentService.getInstallableIntents(intent.key());
738 if (installables != null) {
739 for (Intent installable : installables) {
740 String type = isOptical ? trafficClass.type + " optical" : trafficClass.type;
741 if (installable instanceof PathIntent) {
742 classifyLinks(type, biLinks, trafficClass.showTraffic,
743 ((PathIntent) installable).path().links());
Thomas Vachuskac0fe09a2015-05-21 12:56:22 -0700744 } else if (installable instanceof FlowRuleIntent) {
745 classifyLinks(type, biLinks, trafficClass.showTraffic,
746 linkResources(installable));
Thomas Vachuska329af532015-03-10 02:08:33 -0700747 } else if (installable instanceof LinkCollectionIntent) {
748 classifyLinks(type, biLinks, trafficClass.showTraffic,
749 ((LinkCollectionIntent) installable).links());
750 } else if (installable instanceof OpticalPathIntent) {
751 classifyLinks(type, biLinks, trafficClass.showTraffic,
Thomas Vachuska204cb6c2015-06-04 00:03:06 -0700752 ((OpticalPathIntent) installable).path().links());
Thomas Vachuska329af532015-03-10 02:08:33 -0700753 }
754 }
755 }
756 }
757 }
758 return biLinks;
759 }
760
Thomas Vachuskac0fe09a2015-05-21 12:56:22 -0700761 // Extracts links from the specified flow rule intent resources
762 private Collection<Link> linkResources(Intent installable) {
763 ImmutableList.Builder<Link> builder = ImmutableList.builder();
764 for (NetworkResource r : installable.resources()) {
765 if (r instanceof Link) {
766 builder.add((Link) r);
767 }
768 }
769 return builder.build();
770 }
771
Thomas Vachuska329af532015-03-10 02:08:33 -0700772
773 // Adds the link segments (path or tree) associated with the specified
774 // connectivity intent
775 private void classifyLinks(String type, Map<LinkKey, BiLink> biLinks,
776 boolean showTraffic, Iterable<Link> links) {
777 if (links != null) {
778 for (Link link : links) {
779 BiLink biLink = addLink(biLinks, link);
Thomas Vachuska204cb6c2015-06-04 00:03:06 -0700780 if (showTraffic) {
Thomas Vachuska25455e72015-06-04 11:31:26 -0700781 biLink.addLoad(getLinkLoad(link));
Thomas Vachuska329af532015-03-10 02:08:33 -0700782 }
Thomas Vachuska204cb6c2015-06-04 00:03:06 -0700783 biLink.addClass(type);
Thomas Vachuska329af532015-03-10 02:08:33 -0700784 }
785 }
786 }
787
788
Thomas Vachuska583bc632015-04-14 10:10:57 -0700789 static BiLink addLink(Map<LinkKey, BiLink> biLinks, Link link) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700790 LinkKey key = canonicalLinkKey(link);
791 BiLink biLink = biLinks.get(key);
792 if (biLink != null) {
793 biLink.setOther(link);
794 } else {
795 biLink = new BiLink(key, link);
796 biLinks.put(key, biLink);
797 }
798 return biLink;
799 }
800
Thomas Vachuska329af532015-03-10 02:08:33 -0700801 // Poor-mans formatting to get the labels with byte counts looking nice.
802 private String formatBytes(long bytes) {
803 String unit;
804 double value;
Srikanth Vavilapalli8c1ccca2015-06-08 19:23:24 -0700805 if (bytes > GIGA) {
806 value = bytes / GIGA;
807 unit = GBYTES_UNIT;
808 } else if (bytes > MEGA) {
809 value = bytes / MEGA;
810 unit = MBYTES_UNIT;
811 } else if (bytes > KILO) {
812 value = bytes / KILO;
813 unit = KBYTES_UNIT;
Thomas Vachuska329af532015-03-10 02:08:33 -0700814 } else {
815 value = bytes;
Srikanth Vavilapalli8c1ccca2015-06-08 19:23:24 -0700816 unit = BYTES_UNIT;
817 }
818 DecimalFormat format = new DecimalFormat("#,###.##");
819 return format.format(value) + " " + unit;
820 }
821
Simon Hunt80170a72015-06-10 19:49:57 -0700822 // Poor-mans formatting to get the labels with bit rate looking nice.
Srikanth Vavilapalli8c1ccca2015-06-08 19:23:24 -0700823 private String formatBitRate(long bytes) {
824 String unit;
825 double value;
826 //Convert to bits
827 long bits = bytes * 8;
828 if (bits > GIGA) {
829 value = bits / GIGA;
830 unit = GBITS_UNIT;
Simon Hunt80170a72015-06-10 19:49:57 -0700831
832 // NOTE: temporary hack to clip rate at 10.0 Gbps
833 // Added for the CORD Fabric demo at ONS 2015
834 if (value > 10.0) {
835 value = 10.0;
836 }
837
Srikanth Vavilapalli8c1ccca2015-06-08 19:23:24 -0700838 } else if (bits > MEGA) {
839 value = bits / MEGA;
840 unit = MBITS_UNIT;
841 } else if (bits > KILO) {
842 value = bits / KILO;
843 unit = KBITS_UNIT;
844 } else {
845 value = bits;
846 unit = BITS_UNIT;
Thomas Vachuska329af532015-03-10 02:08:33 -0700847 }
848 DecimalFormat format = new DecimalFormat("#,###.##");
849 return format.format(value) + " " + unit;
850 }
851
Thomas Vachuska329af532015-03-10 02:08:33 -0700852 // Produces compact string representation of a link.
853 private static String compactLinkString(Link link) {
854 return String.format(COMPACT, link.src().elementId(), link.src().port(),
855 link.dst().elementId(), link.dst().port());
856 }
857
Simon Huntb745ca62015-07-28 15:37:11 -0700858 // translates the property panel into JSON, for returning to the client
Simon Hunt0af1ec32015-07-24 12:17:55 -0700859 protected ObjectNode json(PropertyPanel pp) {
860 ObjectNode result = objectNode()
Simon Huntb745ca62015-07-28 15:37:11 -0700861 .put("title", pp.title())
862 .put("type", pp.typeId())
863 .put("id", pp.id());
864
Simon Hunt0af1ec32015-07-24 12:17:55 -0700865 ObjectNode pnode = objectNode();
866 ArrayNode porder = arrayNode();
867 for (PropertyPanel.Prop p : pp.properties()) {
868 porder.add(p.key());
869 pnode.put(p.key(), p.value());
870 }
871 result.set("propOrder", porder);
872 result.set("props", pnode);
Simon Huntfb940112015-07-29 18:36:35 -0700873
874 ArrayNode buttons = arrayNode();
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700875 for (ButtonId b : pp.buttons()) {
876 buttons.add(b.id());
Simon Huntfb940112015-07-29 18:36:35 -0700877 }
878 result.set("buttons", buttons);
Simon Hunt0af1ec32015-07-24 12:17:55 -0700879 return result;
880 }
881
Simon Hunt3a0598f2015-08-04 19:59:04 -0700882
Thomas Vachuska329af532015-03-10 02:08:33 -0700883 // Produces canonical link key, i.e. one that will match link and its inverse.
Thomas Vachuska583bc632015-04-14 10:10:57 -0700884 static LinkKey canonicalLinkKey(Link link) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700885 String sn = link.src().elementId().toString();
886 String dn = link.dst().elementId().toString();
887 return sn.compareTo(dn) < 0 ?
888 linkKey(link.src(), link.dst()) : linkKey(link.dst(), link.src());
889 }
890
891 // Representation of link and its inverse and any traffic data.
Thomas Vachuska583bc632015-04-14 10:10:57 -0700892 static class BiLink {
Thomas Vachuska329af532015-03-10 02:08:33 -0700893 public final LinkKey key;
894 public final Link one;
895 public Link two;
896 public boolean hasTraffic = false;
897 public long bytes = 0;
Thomas Vachuska5d5858b2015-03-23 16:31:10 -0700898
899 private Set<String> classes = new HashSet<>();
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700900 private long rate;
Thomas Vachuska329af532015-03-10 02:08:33 -0700901
902 BiLink(LinkKey key, Link link) {
903 this.key = key;
904 this.one = link;
905 }
906
907 void setOther(Link link) {
908 this.two = link;
909 }
910
911 void addLoad(Load load) {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700912 addLoad(load, 0);
913 }
914
Thomas Vachuska204cb6c2015-06-04 00:03:06 -0700915 void addLoad(Load load, double threshold) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700916 if (load != null) {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700917 this.hasTraffic = hasTraffic || load.rate() > threshold;
Thomas Vachuska329af532015-03-10 02:08:33 -0700918 this.bytes += load.latest();
Thomas Vachuskafdbc4c22015-05-29 15:53:01 -0700919 this.rate += load.rate();
Thomas Vachuska329af532015-03-10 02:08:33 -0700920 }
921 }
922
Srikanth Vavilapalli8c1ccca2015-06-08 19:23:24 -0700923 void addLoad(Load srcLinkLoad,
924 double srcLinkThreshold,
925 Load dstLinkLoad,
926 double dstLinkThreshold) {
927 //use the max of link load at source or destination
928 if (srcLinkLoad != null) {
929 this.hasTraffic = hasTraffic || srcLinkLoad.rate() > srcLinkThreshold;
930 this.bytes = srcLinkLoad.latest();
931 this.rate = srcLinkLoad.rate();
932 }
933
934 if (dstLinkLoad != null) {
935 if (dstLinkLoad.rate() > this.rate) {
936 this.bytes = dstLinkLoad.latest();
937 this.rate = dstLinkLoad.rate();
938 this.hasTraffic = hasTraffic || dstLinkLoad.rate() > dstLinkThreshold;
939 }
940 }
941 }
942
Thomas Vachuska329af532015-03-10 02:08:33 -0700943 void addClass(String trafficClass) {
Thomas Vachuska5d5858b2015-03-23 16:31:10 -0700944 classes.add(trafficClass);
945 }
946
947 String classes() {
948 StringBuilder sb = new StringBuilder();
949 classes.forEach(c -> sb.append(c).append(" "));
950 return sb.toString().trim();
Thomas Vachuska329af532015-03-10 02:08:33 -0700951 }
952 }
953
Thomas Vachuska329af532015-03-10 02:08:33 -0700954
Simon Hunt93735af2015-07-28 12:10:30 -0700955 // TODO: move this to traffic overlay component
Thomas Vachuska329af532015-03-10 02:08:33 -0700956 // Auxiliary carrier of data for requesting traffic message.
Thomas Vachuska583bc632015-04-14 10:10:57 -0700957 static class TrafficClass {
Thomas Vachuska329af532015-03-10 02:08:33 -0700958 public final boolean showTraffic;
959 public final String type;
960 public final Iterable<Intent> intents;
961
962 TrafficClass(String type, Iterable<Intent> intents) {
963 this(type, intents, false);
964 }
965
966 TrafficClass(String type, Iterable<Intent> intents, boolean showTraffic) {
967 this.type = type;
968 this.intents = intents;
969 this.showTraffic = showTraffic;
970 }
971 }
972
973}