blob: 130f88f83abd112cfac814d821e2c509d53b46b7 [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;
21import org.onlab.osgi.ServiceDirectory;
22import org.onlab.packet.IpAddress;
23import org.onosproject.cluster.ClusterEvent;
24import org.onosproject.cluster.ClusterService;
25import org.onosproject.cluster.ControllerNode;
26import org.onosproject.cluster.NodeId;
27import org.onosproject.core.CoreService;
Thomas Vachuskaf0397b52015-05-29 13:50:17 -070028import org.onosproject.incubator.net.PortStatisticsService;
cheng fan35dc0f22015-06-10 06:02:47 +080029import org.onosproject.incubator.net.tunnel.OpticalTunnelEndPoint;
30import org.onosproject.incubator.net.tunnel.Tunnel;
31import org.onosproject.incubator.net.tunnel.TunnelService;
Thomas Vachuska329af532015-03-10 02:08:33 -070032import org.onosproject.mastership.MastershipService;
33import org.onosproject.net.Annotated;
34import org.onosproject.net.AnnotationKeys;
35import org.onosproject.net.Annotations;
36import org.onosproject.net.ConnectPoint;
37import org.onosproject.net.DefaultEdgeLink;
38import org.onosproject.net.Device;
39import org.onosproject.net.DeviceId;
40import org.onosproject.net.EdgeLink;
41import org.onosproject.net.Host;
42import org.onosproject.net.HostId;
43import org.onosproject.net.HostLocation;
44import org.onosproject.net.Link;
Thomas Vachuska329af532015-03-10 02:08:33 -070045import org.onosproject.net.PortNumber;
46import org.onosproject.net.device.DeviceEvent;
47import org.onosproject.net.device.DeviceService;
48import org.onosproject.net.flow.FlowEntry;
49import org.onosproject.net.flow.FlowRuleService;
50import org.onosproject.net.flow.TrafficTreatment;
51import org.onosproject.net.flow.instructions.Instruction;
52import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
53import org.onosproject.net.host.HostEvent;
54import org.onosproject.net.host.HostService;
Thomas Vachuska329af532015-03-10 02:08:33 -070055import org.onosproject.net.intent.IntentService;
Thomas Vachuska329af532015-03-10 02:08:33 -070056import org.onosproject.net.link.LinkEvent;
57import org.onosproject.net.link.LinkService;
58import org.onosproject.net.provider.ProviderId;
Thomas Vachuska329af532015-03-10 02:08:33 -070059import org.onosproject.net.statistic.StatisticService;
60import org.onosproject.net.topology.Topology;
61import org.onosproject.net.topology.TopologyService;
Simon Huntd2747a02015-04-30 22:41:16 -070062import org.onosproject.ui.JsonUtils;
Thomas Vachuska329af532015-03-10 02:08:33 -070063import org.onosproject.ui.UiConnection;
Simon Hunta0ddb022015-05-01 09:53:01 -070064import org.onosproject.ui.UiMessageHandler;
Simon Hunta17fa672015-08-19 18:42:22 -070065import org.onosproject.ui.impl.topo.ServicesBundle;
Simon Hunt8d22c4b2015-08-06 16:24:43 -070066import org.onosproject.ui.topo.ButtonId;
Simon Hunta17fa672015-08-19 18:42:22 -070067import org.onosproject.ui.topo.DeviceHighlight;
68import org.onosproject.ui.topo.Highlights;
69import org.onosproject.ui.topo.HostHighlight;
70import org.onosproject.ui.topo.LinkHighlight;
Simon Hunt0af1ec32015-07-24 12:17:55 -070071import org.onosproject.ui.topo.PropertyPanel;
Thomas Vachuska329af532015-03-10 02:08:33 -070072import org.slf4j.Logger;
73import org.slf4j.LoggerFactory;
74
Thomas Vachuska329af532015-03-10 02:08:33 -070075import java.util.ArrayList;
76import java.util.Collection;
77import java.util.Collections;
78import java.util.HashMap;
79import java.util.HashSet;
80import java.util.Iterator;
81import java.util.List;
82import java.util.Map;
83import java.util.Set;
84import java.util.concurrent.ConcurrentHashMap;
85
86import static com.google.common.base.Preconditions.checkNotNull;
87import static com.google.common.base.Strings.isNullOrEmpty;
88import static org.onosproject.cluster.ClusterEvent.Type.INSTANCE_ADDED;
89import static org.onosproject.cluster.ClusterEvent.Type.INSTANCE_REMOVED;
90import static org.onosproject.cluster.ControllerNode.State.ACTIVE;
Thomas Vachuska204cb6c2015-06-04 00:03:06 -070091import static org.onosproject.net.DefaultEdgeLink.createEdgeLink;
Thomas Vachuska329af532015-03-10 02:08:33 -070092import static org.onosproject.net.PortNumber.portNumber;
93import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_ADDED;
94import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_REMOVED;
95import static org.onosproject.net.host.HostEvent.Type.HOST_ADDED;
96import static org.onosproject.net.host.HostEvent.Type.HOST_REMOVED;
97import static org.onosproject.net.link.LinkEvent.Type.LINK_ADDED;
98import static org.onosproject.net.link.LinkEvent.Type.LINK_REMOVED;
Simon Hunta17fa672015-08-19 18:42:22 -070099import static org.onosproject.ui.impl.topo.TopoUtils.compactLinkString;
Simon Hunt3a0598f2015-08-04 19:59:04 -0700100import static org.onosproject.ui.topo.TopoConstants.CoreButtons;
101import static org.onosproject.ui.topo.TopoConstants.Properties;
Thomas Vachuska329af532015-03-10 02:08:33 -0700102
103/**
104 * Facility for creating messages bound for the topology viewer.
105 */
Simon Hunta0ddb022015-05-01 09:53:01 -0700106public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
Thomas Vachuska329af532015-03-10 02:08:33 -0700107
Simon Huntd2747a02015-04-30 22:41:16 -0700108 protected static final Logger log =
109 LoggerFactory.getLogger(TopologyViewMessageHandlerBase.class);
Thomas Vachuska329af532015-03-10 02:08:33 -0700110
Simon Huntd2747a02015-04-30 22:41:16 -0700111 private static final ProviderId PID =
112 new ProviderId("core", "org.onosproject.core", true);
Thomas Vachuska329af532015-03-10 02:08:33 -0700113
Simon Hunta17fa672015-08-19 18:42:22 -0700114 protected static final String SHOW_HIGHLIGHTS = "showHighlights";
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700115
Thomas Vachuska329af532015-03-10 02:08:33 -0700116 protected ServiceDirectory directory;
117 protected ClusterService clusterService;
118 protected DeviceService deviceService;
119 protected LinkService linkService;
120 protected HostService hostService;
121 protected MastershipService mastershipService;
122 protected IntentService intentService;
123 protected FlowRuleService flowService;
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700124 protected StatisticService flowStatsService;
125 protected PortStatisticsService portStatsService;
Thomas Vachuska329af532015-03-10 02:08:33 -0700126 protected TopologyService topologyService;
cheng fan35dc0f22015-06-10 06:02:47 +0800127 protected TunnelService tunnelService;
Thomas Vachuska329af532015-03-10 02:08:33 -0700128
Simon Hunta17fa672015-08-19 18:42:22 -0700129 protected ServicesBundle servicesBundle;
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700130
Thomas Vachuska329af532015-03-10 02:08:33 -0700131 private String version;
132
133 // TODO: extract into an external & durable state; good enough for now and demo
134 private static Map<String, ObjectNode> metaUi = new ConcurrentHashMap<>();
135
136 /**
Thomas Vachuska329af532015-03-10 02:08:33 -0700137 * Returns read-only view of the meta-ui information.
138 *
139 * @return map of id to meta-ui mementos
140 */
141 static Map<String, ObjectNode> getMetaUi() {
142 return Collections.unmodifiableMap(metaUi);
143 }
144
145 @Override
146 public void init(UiConnection connection, ServiceDirectory directory) {
147 super.init(connection, directory);
148 this.directory = checkNotNull(directory, "Directory cannot be null");
149 clusterService = directory.get(ClusterService.class);
150 deviceService = directory.get(DeviceService.class);
151 linkService = directory.get(LinkService.class);
152 hostService = directory.get(HostService.class);
153 mastershipService = directory.get(MastershipService.class);
154 intentService = directory.get(IntentService.class);
155 flowService = directory.get(FlowRuleService.class);
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700156 flowStatsService = directory.get(StatisticService.class);
157 portStatsService = directory.get(PortStatisticsService.class);
Thomas Vachuska329af532015-03-10 02:08:33 -0700158 topologyService = directory.get(TopologyService.class);
cheng fan35dc0f22015-06-10 06:02:47 +0800159 tunnelService = directory.get(TunnelService.class);
Thomas Vachuska329af532015-03-10 02:08:33 -0700160
Simon Hunta17fa672015-08-19 18:42:22 -0700161 servicesBundle = new ServicesBundle(intentService, deviceService,
162 hostService, linkService,
163 flowService,
164 flowStatsService, portStatsService);
165
Thomas Vachuska329af532015-03-10 02:08:33 -0700166 String ver = directory.get(CoreService.class).version().toString();
167 version = ver.replace(".SNAPSHOT", "*").replaceFirst("~.*$", "");
168 }
169
Thomas Vachuska329af532015-03-10 02:08:33 -0700170 // Returns the specified set of IP addresses as a string.
171 private String ip(Set<IpAddress> ipAddresses) {
172 Iterator<IpAddress> it = ipAddresses.iterator();
173 return it.hasNext() ? it.next().toString() : "unknown";
174 }
175
176 // Produces JSON structure from annotations.
177 private JsonNode props(Annotations annotations) {
Simon Huntda580882015-05-12 20:58:18 -0700178 ObjectNode props = objectNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700179 if (annotations != null) {
180 for (String key : annotations.keys()) {
181 props.put(key, annotations.value(key));
182 }
183 }
184 return props;
185 }
186
187 // Produces an informational log message event bound to the client.
188 protected ObjectNode info(long id, String message) {
189 return message("info", id, message);
190 }
191
192 // Produces a warning log message event bound to the client.
193 protected ObjectNode warning(long id, String message) {
194 return message("warning", id, message);
195 }
196
197 // Produces an error log message event bound to the client.
198 protected ObjectNode error(long id, String message) {
199 return message("error", id, message);
200 }
201
202 // Produces a log message event bound to the client.
203 private ObjectNode message(String severity, long id, String message) {
Simon Huntda580882015-05-12 20:58:18 -0700204 ObjectNode payload = objectNode()
Simon Huntd2747a02015-04-30 22:41:16 -0700205 .put("severity", severity)
206 .put("message", message);
Thomas Vachuska329af532015-03-10 02:08:33 -0700207
Simon Huntd2747a02015-04-30 22:41:16 -0700208 return JsonUtils.envelope("message", id, payload);
Thomas Vachuska329af532015-03-10 02:08:33 -0700209 }
210
Thomas Vachuska329af532015-03-10 02:08:33 -0700211 // Produces a cluster instance message to the client.
212 protected ObjectNode instanceMessage(ClusterEvent event, String messageType) {
213 ControllerNode node = event.subject();
214 int switchCount = mastershipService.getDevicesOf(node.id()).size();
Simon Huntda580882015-05-12 20:58:18 -0700215 ObjectNode payload = objectNode()
Thomas Vachuska329af532015-03-10 02:08:33 -0700216 .put("id", node.id().toString())
217 .put("ip", node.ip().toString())
218 .put("online", clusterService.getState(node.id()) == ACTIVE)
Thomas Vachuskab6acc7b2015-03-11 09:11:21 -0700219 .put("uiAttached", node.equals(clusterService.getLocalNode()))
Thomas Vachuska329af532015-03-10 02:08:33 -0700220 .put("switches", switchCount);
221
Simon Huntda580882015-05-12 20:58:18 -0700222 ArrayNode labels = arrayNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700223 labels.add(node.id().toString());
224 labels.add(node.ip().toString());
225
226 // Add labels, props and stuff the payload into envelope.
227 payload.set("labels", labels);
228 addMetaUi(node.id().toString(), payload);
229
230 String type = messageType != null ? messageType :
231 ((event.type() == INSTANCE_ADDED) ? "addInstance" :
232 ((event.type() == INSTANCE_REMOVED ? "removeInstance" :
233 "addInstance")));
Simon Huntd2747a02015-04-30 22:41:16 -0700234 return JsonUtils.envelope(type, 0, payload);
Thomas Vachuska329af532015-03-10 02:08:33 -0700235 }
236
237 // Produces a device event message to the client.
238 protected ObjectNode deviceMessage(DeviceEvent event) {
239 Device device = event.subject();
Simon Huntda580882015-05-12 20:58:18 -0700240 ObjectNode payload = objectNode()
Thomas Vachuska329af532015-03-10 02:08:33 -0700241 .put("id", device.id().toString())
242 .put("type", device.type().toString().toLowerCase())
243 .put("online", deviceService.isAvailable(device.id()))
244 .put("master", master(device.id()));
245
246 // Generate labels: id, chassis id, no-label, optional-name
247 String name = device.annotations().value(AnnotationKeys.NAME);
Simon Huntda580882015-05-12 20:58:18 -0700248 ArrayNode labels = arrayNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700249 labels.add("");
250 labels.add(isNullOrEmpty(name) ? device.id().toString() : name);
251 labels.add(device.id().toString());
252
253 // Add labels, props and stuff the payload into envelope.
254 payload.set("labels", labels);
255 payload.set("props", props(device.annotations()));
256 addGeoLocation(device, payload);
257 addMetaUi(device.id().toString(), payload);
258
259 String type = (event.type() == DEVICE_ADDED) ? "addDevice" :
260 ((event.type() == DEVICE_REMOVED) ? "removeDevice" : "updateDevice");
Simon Huntd2747a02015-04-30 22:41:16 -0700261 return JsonUtils.envelope(type, 0, payload);
Thomas Vachuska329af532015-03-10 02:08:33 -0700262 }
263
264 // Produces a link event message to the client.
265 protected ObjectNode linkMessage(LinkEvent event) {
266 Link link = event.subject();
Simon Huntda580882015-05-12 20:58:18 -0700267 ObjectNode payload = objectNode()
Thomas Vachuska329af532015-03-10 02:08:33 -0700268 .put("id", compactLinkString(link))
269 .put("type", link.type().toString().toLowerCase())
270 .put("online", link.state() == Link.State.ACTIVE)
271 .put("linkWidth", 1.2)
272 .put("src", link.src().deviceId().toString())
273 .put("srcPort", link.src().port().toString())
274 .put("dst", link.dst().deviceId().toString())
275 .put("dstPort", link.dst().port().toString());
276 String type = (event.type() == LINK_ADDED) ? "addLink" :
277 ((event.type() == LINK_REMOVED) ? "removeLink" : "updateLink");
Simon Huntd2747a02015-04-30 22:41:16 -0700278 return JsonUtils.envelope(type, 0, payload);
Thomas Vachuska329af532015-03-10 02:08:33 -0700279 }
280
281 // Produces a host event message to the client.
282 protected ObjectNode hostMessage(HostEvent event) {
283 Host host = event.subject();
284 String hostType = host.annotations().value(AnnotationKeys.TYPE);
Simon Huntda580882015-05-12 20:58:18 -0700285 ObjectNode payload = objectNode()
Thomas Vachuska329af532015-03-10 02:08:33 -0700286 .put("id", host.id().toString())
287 .put("type", isNullOrEmpty(hostType) ? "endstation" : hostType)
288 .put("ingress", compactLinkString(edgeLink(host, true)))
289 .put("egress", compactLinkString(edgeLink(host, false)));
Simon Huntda580882015-05-12 20:58:18 -0700290 payload.set("cp", hostConnect(host.location()));
291 payload.set("labels", labels(ip(host.ipAddresses()),
Thomas Vachuska329af532015-03-10 02:08:33 -0700292 host.mac().toString()));
293 payload.set("props", props(host.annotations()));
294 addGeoLocation(host, payload);
295 addMetaUi(host.id().toString(), payload);
296
297 String type = (event.type() == HOST_ADDED) ? "addHost" :
298 ((event.type() == HOST_REMOVED) ? "removeHost" : "updateHost");
Simon Huntd2747a02015-04-30 22:41:16 -0700299 return JsonUtils.envelope(type, 0, payload);
Thomas Vachuska329af532015-03-10 02:08:33 -0700300 }
301
302 // Encodes the specified host location into a JSON object.
Simon Huntda580882015-05-12 20:58:18 -0700303 private ObjectNode hostConnect(HostLocation location) {
304 return objectNode()
Thomas Vachuska329af532015-03-10 02:08:33 -0700305 .put("device", location.deviceId().toString())
306 .put("port", location.port().toLong());
307 }
308
309 // Encodes the specified list of labels a JSON array.
Simon Huntda580882015-05-12 20:58:18 -0700310 private ArrayNode labels(String... labels) {
311 ArrayNode json = arrayNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700312 for (String label : labels) {
313 json.add(label);
314 }
315 return json;
316 }
317
318 // Returns the name of the master node for the specified device id.
319 private String master(DeviceId deviceId) {
320 NodeId master = mastershipService.getMasterFor(deviceId);
321 return master != null ? master.toString() : "";
322 }
323
324 // Generates an edge link from the specified host location.
325 private EdgeLink edgeLink(Host host, boolean ingress) {
326 return new DefaultEdgeLink(PID, new ConnectPoint(host.id(), portNumber(0)),
327 host.location(), ingress);
328 }
329
330 // Adds meta UI information for the specified object.
331 private void addMetaUi(String id, ObjectNode payload) {
332 ObjectNode meta = metaUi.get(id);
333 if (meta != null) {
334 payload.set("metaUi", meta);
335 }
336 }
337
338 // Adds a geo location JSON to the specified payload object.
339 private void addGeoLocation(Annotated annotated, ObjectNode payload) {
340 Annotations annotations = annotated.annotations();
341 if (annotations == null) {
342 return;
343 }
344
345 String slat = annotations.value(AnnotationKeys.LATITUDE);
346 String slng = annotations.value(AnnotationKeys.LONGITUDE);
347 try {
348 if (slat != null && slng != null && !slat.isEmpty() && !slng.isEmpty()) {
349 double lat = Double.parseDouble(slat);
350 double lng = Double.parseDouble(slng);
Simon Huntda580882015-05-12 20:58:18 -0700351 ObjectNode loc = objectNode()
Thomas Vachuska329af532015-03-10 02:08:33 -0700352 .put("type", "latlng").put("lat", lat).put("lng", lng);
353 payload.set("location", loc);
354 }
355 } catch (NumberFormatException e) {
356 log.warn("Invalid geo data latitude={}; longiture={}", slat, slng);
357 }
358 }
359
360 // Updates meta UI information for the specified object.
Simon Huntd2747a02015-04-30 22:41:16 -0700361 protected void updateMetaUi(ObjectNode payload) {
362 metaUi.put(JsonUtils.string(payload, "id"),
363 JsonUtils.node(payload, "memento"));
Thomas Vachuska329af532015-03-10 02:08:33 -0700364 }
365
Simon Hunta17fa672015-08-19 18:42:22 -0700366
Simon Huntb745ca62015-07-28 15:37:11 -0700367 // -----------------------------------------------------------------------
368 // Create models of the data to return, that overlays can adjust / augment
369
370 // Returns property panel model for summary response.
Simon Hunt0af1ec32015-07-24 12:17:55 -0700371 protected PropertyPanel summmaryMessage(long sid) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700372 Topology topology = topologyService.currentTopology();
Simon Hunt0af1ec32015-07-24 12:17:55 -0700373
Simon Hunt00a27ff2015-07-28 18:53:40 -0700374 return new PropertyPanel("ONOS Summary", "node")
Simon Huntfb940112015-07-29 18:36:35 -0700375 .addProp(Properties.DEVICES, topology.deviceCount())
376 .addProp(Properties.LINKS, topology.linkCount())
377 .addProp(Properties.HOSTS, hostService.getHostCount())
378 .addProp(Properties.TOPOLOGY_SSCS, topology.clusterCount())
Simon Hunt00a27ff2015-07-28 18:53:40 -0700379 .addSeparator()
Simon Huntfb940112015-07-29 18:36:35 -0700380 .addProp(Properties.INTENTS, intentService.getIntentCount())
381 .addProp(Properties.TUNNELS, tunnelService.tunnelCount())
382 .addProp(Properties.FLOWS, flowService.getFlowRuleCount())
383 .addProp(Properties.VERSION, version);
Thomas Vachuska329af532015-03-10 02:08:33 -0700384 }
385
Simon Huntb745ca62015-07-28 15:37:11 -0700386 // Returns property panel model for device details response.
387 protected PropertyPanel deviceDetails(DeviceId deviceId, long sid) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700388 Device device = deviceService.getDevice(deviceId);
389 Annotations annot = device.annotations();
390 String name = annot.value(AnnotationKeys.NAME);
391 int portCount = deviceService.getPorts(deviceId).size();
392 int flowCount = getFlowCount(deviceId);
cheng fan35dc0f22015-06-10 06:02:47 +0800393 int tunnelCount = getTunnelCount(deviceId);
Simon Huntb745ca62015-07-28 15:37:11 -0700394
395 String title = isNullOrEmpty(name) ? deviceId.toString() : name;
396 String typeId = device.type().toString().toLowerCase();
397
398 PropertyPanel pp = new PropertyPanel(title, typeId)
Simon Huntfb940112015-07-29 18:36:35 -0700399 .id(deviceId.toString())
Simon Hunt3a0598f2015-08-04 19:59:04 -0700400
Simon Huntfb940112015-07-29 18:36:35 -0700401 .addProp(Properties.URI, deviceId.toString())
402 .addProp(Properties.VENDOR, device.manufacturer())
403 .addProp(Properties.HW_VERSION, device.hwVersion())
404 .addProp(Properties.SW_VERSION, device.swVersion())
405 .addProp(Properties.SERIAL_NUMBER, device.serialNumber())
406 .addProp(Properties.PROTOCOL, annot.value(AnnotationKeys.PROTOCOL))
407 .addSeparator()
Simon Hunt3a0598f2015-08-04 19:59:04 -0700408
Simon Huntfb940112015-07-29 18:36:35 -0700409 .addProp(Properties.LATITUDE, annot.value(AnnotationKeys.LATITUDE))
410 .addProp(Properties.LONGITUDE, annot.value(AnnotationKeys.LONGITUDE))
411 .addSeparator()
Simon Hunt3a0598f2015-08-04 19:59:04 -0700412
Simon Huntfb940112015-07-29 18:36:35 -0700413 .addProp(Properties.PORTS, portCount)
414 .addProp(Properties.FLOWS, flowCount)
Simon Hunt3a0598f2015-08-04 19:59:04 -0700415 .addProp(Properties.TUNNELS, tunnelCount)
Simon Hunt00a27ff2015-07-28 18:53:40 -0700416
Simon Hunt3a0598f2015-08-04 19:59:04 -0700417 .addButton(CoreButtons.SHOW_DEVICE_VIEW)
418 .addButton(CoreButtons.SHOW_FLOW_VIEW)
419 .addButton(CoreButtons.SHOW_PORT_VIEW)
420 .addButton(CoreButtons.SHOW_GROUP_VIEW);
Simon Huntb745ca62015-07-28 15:37:11 -0700421
422 return pp;
Thomas Vachuska329af532015-03-10 02:08:33 -0700423 }
424
425 protected int getFlowCount(DeviceId deviceId) {
426 int count = 0;
Simon Huntb745ca62015-07-28 15:37:11 -0700427 for (FlowEntry flowEntry : flowService.getFlowEntries(deviceId)) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700428 count++;
Thomas Vachuska329af532015-03-10 02:08:33 -0700429 }
430 return count;
431 }
432
cheng fan35dc0f22015-06-10 06:02:47 +0800433 protected int getTunnelCount(DeviceId deviceId) {
434 int count = 0;
435 Collection<Tunnel> tunnels = tunnelService.queryAllTunnels();
436 for (Tunnel tunnel : tunnels) {
437 OpticalTunnelEndPoint src = (OpticalTunnelEndPoint) tunnel.src();
438 OpticalTunnelEndPoint dst = (OpticalTunnelEndPoint) tunnel.dst();
439 DeviceId srcDevice = (DeviceId) src.elementId().get();
440 DeviceId dstDevice = (DeviceId) dst.elementId().get();
Simon Huntb745ca62015-07-28 15:37:11 -0700441 if (srcDevice.toString().equals(deviceId.toString()) ||
442 dstDevice.toString().equals(deviceId.toString())) {
cheng fan35dc0f22015-06-10 06:02:47 +0800443 count++;
444 }
445 }
446 return count;
447 }
448
Simon Hunta17fa672015-08-19 18:42:22 -0700449 // Counts all flow entries that egress on the links of the given device.
450 private Map<Link, Integer> getLinkFlowCounts(DeviceId deviceId) {
451 // get the flows for the device
Thomas Vachuska329af532015-03-10 02:08:33 -0700452 List<FlowEntry> entries = new ArrayList<>();
Simon Huntb745ca62015-07-28 15:37:11 -0700453 for (FlowEntry flowEntry : flowService.getFlowEntries(deviceId)) {
454 entries.add(flowEntry);
Thomas Vachuska329af532015-03-10 02:08:33 -0700455 }
456
Simon Hunta17fa672015-08-19 18:42:22 -0700457 // get egress links from device, and include edge links
458 Set<Link> links = new HashSet<>(linkService.getDeviceEgressLinks(deviceId));
459 Set<Host> hosts = hostService.getConnectedHosts(deviceId);
Thomas Vachuska329af532015-03-10 02:08:33 -0700460 if (hosts != null) {
461 for (Host host : hosts) {
Simon Hunta17fa672015-08-19 18:42:22 -0700462 links.add(createEdgeLink(host, false));
Thomas Vachuska329af532015-03-10 02:08:33 -0700463 }
464 }
465
Simon Hunta17fa672015-08-19 18:42:22 -0700466 // compile flow counts per link
Thomas Vachuska329af532015-03-10 02:08:33 -0700467 Map<Link, Integer> counts = new HashMap<>();
468 for (Link link : links) {
469 counts.put(link, getEgressFlows(link, entries));
470 }
471 return counts;
472 }
473
474 // Counts all entries that egress on the link source port.
Simon Hunta17fa672015-08-19 18:42:22 -0700475 private int getEgressFlows(Link link, List<FlowEntry> entries) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700476 int count = 0;
477 PortNumber out = link.src().port();
478 for (FlowEntry entry : entries) {
479 TrafficTreatment treatment = entry.treatment();
Ray Milkey42507352015-03-20 15:16:10 -0700480 for (Instruction instruction : treatment.allInstructions()) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700481 if (instruction.type() == Instruction.Type.OUTPUT &&
482 ((OutputInstruction) instruction).port().equals(out)) {
483 count++;
484 }
485 }
486 }
487 return count;
488 }
489
Thomas Vachuska329af532015-03-10 02:08:33 -0700490 // Returns host details response.
Simon Huntb745ca62015-07-28 15:37:11 -0700491 protected PropertyPanel hostDetails(HostId hostId, long sid) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700492 Host host = hostService.getHost(hostId);
493 Annotations annot = host.annotations();
494 String type = annot.value(AnnotationKeys.TYPE);
495 String name = annot.value(AnnotationKeys.NAME);
496 String vlan = host.vlan().toString();
Simon Huntb745ca62015-07-28 15:37:11 -0700497
498 String title = isNullOrEmpty(name) ? hostId.toString() : name;
499 String typeId = isNullOrEmpty(type) ? "endstation" : type;
500
501 PropertyPanel pp = new PropertyPanel(title, typeId)
Simon Huntfb940112015-07-29 18:36:35 -0700502 .id(hostId.toString())
503 .addProp(Properties.MAC, host.mac())
504 .addProp(Properties.IP, host.ipAddresses(), "[\\[\\]]")
505 .addProp(Properties.VLAN, vlan.equals("-1") ? "none" : vlan)
506 .addSeparator()
507 .addProp(Properties.LATITUDE, annot.value(AnnotationKeys.LATITUDE))
508 .addProp(Properties.LONGITUDE, annot.value(AnnotationKeys.LONGITUDE));
Simon Huntb745ca62015-07-28 15:37:11 -0700509
Simon Hunta17fa672015-08-19 18:42:22 -0700510 // Potentially add button descriptors here
Simon Huntb745ca62015-07-28 15:37:11 -0700511 return pp;
Thomas Vachuska329af532015-03-10 02:08:33 -0700512 }
513
514
Simon Hunta17fa672015-08-19 18:42:22 -0700515 // ----------------------------------------------------------------------
516
517 /**
518 * Transforms the given highlights model into a JSON message payload.
519 *
520 * @param highlights the model to transform
521 * @return JSON payload
522 */
523 protected ObjectNode json(Highlights highlights) {
Simon Huntda580882015-05-12 20:58:18 -0700524 ObjectNode payload = objectNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700525
Simon Hunta17fa672015-08-19 18:42:22 -0700526 ArrayNode devices = arrayNode();
527 ArrayNode hosts = arrayNode();
528 ArrayNode links = arrayNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700529
Simon Hunta17fa672015-08-19 18:42:22 -0700530 payload.set("devices", devices);
531 payload.set("hosts", hosts);
532 payload.set("links", links);
Thomas Vachuska329af532015-03-10 02:08:33 -0700533
Simon Hunta17fa672015-08-19 18:42:22 -0700534 highlights.devices().forEach(dh -> devices.add(json(dh)));
535 highlights.hosts().forEach(hh -> hosts.add(json(hh)));
536 jsonifyLinks(links, highlights.links());
Thomas Vachuska329af532015-03-10 02:08:33 -0700537
Simon Hunta17fa672015-08-19 18:42:22 -0700538 return payload;
Thomas Vachuska329af532015-03-10 02:08:33 -0700539 }
540
Simon Hunta17fa672015-08-19 18:42:22 -0700541 private void jsonifyLinks(ArrayNode links, Set<LinkHighlight> hilites) {
542 // a little more complicated than devices or hosts, since we are
543 // grouping the link highlights by CSS classes
Thomas Vachuska204cb6c2015-06-04 00:03:06 -0700544
Simon Hunta17fa672015-08-19 18:42:22 -0700545 // TODO: refactor this method (including client side) to use new format
546 // as a more compact representation of the data...
547 // * links:
548 // * "primary animated":
549 // * "link01" -> "label"
550 // * "link02" -> "label"
551 // * "secondary":
552 // * "link04" -> "label"
553 // * "link05" -> ""
Thomas Vachuska329af532015-03-10 02:08:33 -0700554
555
Simon Hunta17fa672015-08-19 18:42:22 -0700556 Map<String, List<String>> linkIdMap = new HashMap<>();
557 Map<String, List<String>> linkLabelMap = new HashMap<>();
558 List<String> ids;
559 List<String> labels;
Thomas Vachuska329af532015-03-10 02:08:33 -0700560
Simon Hunta17fa672015-08-19 18:42:22 -0700561 for (LinkHighlight lh : hilites) {
562 String cls = lh.cssClasses();
563 ids = linkIdMap.get(cls);
564 labels = linkLabelMap.get(cls);
Thomas Vachuska329af532015-03-10 02:08:33 -0700565
Simon Hunta17fa672015-08-19 18:42:22 -0700566 if (ids == null) { // labels will be null also
567 ids = new ArrayList<>();
568 linkIdMap.put(cls, ids);
569 labels = new ArrayList<>();
570 linkLabelMap.put(cls, labels);
Simon Hunt80170a72015-06-10 19:49:57 -0700571 }
572
Simon Hunta17fa672015-08-19 18:42:22 -0700573 ids.add(lh.elementId());
574 labels.add(lh.label());
Thomas Vachuska329af532015-03-10 02:08:33 -0700575 }
Simon Hunta17fa672015-08-19 18:42:22 -0700576
577 for (String cls : linkIdMap.keySet()) {
578 ObjectNode group = objectNode();
579 links.add(group);
580
581 group.put("class", cls);
582
583 ArrayNode lnks = arrayNode();
584 ArrayNode labs = arrayNode();
585 group.set("links", lnks);
586 group.set("labels", labs);
587
588 linkIdMap.get(cls).forEach(lnks::add);
589 linkLabelMap.get(cls).forEach(labs::add);
590 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700591 }
592
Simon Hunta17fa672015-08-19 18:42:22 -0700593
594 protected ObjectNode json(DeviceHighlight dh) {
595 // TODO: implement this once we know what a device highlight looks like
596 return objectNode();
597 }
598
599 protected ObjectNode json(HostHighlight hh) {
600 // TODO: implement this once we know what a host highlight looks like
601 return objectNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700602 }
603
Simon Huntb745ca62015-07-28 15:37:11 -0700604 // translates the property panel into JSON, for returning to the client
Simon Hunt0af1ec32015-07-24 12:17:55 -0700605 protected ObjectNode json(PropertyPanel pp) {
606 ObjectNode result = objectNode()
Simon Huntb745ca62015-07-28 15:37:11 -0700607 .put("title", pp.title())
608 .put("type", pp.typeId())
609 .put("id", pp.id());
610
Simon Hunt0af1ec32015-07-24 12:17:55 -0700611 ObjectNode pnode = objectNode();
612 ArrayNode porder = arrayNode();
613 for (PropertyPanel.Prop p : pp.properties()) {
614 porder.add(p.key());
615 pnode.put(p.key(), p.value());
616 }
617 result.set("propOrder", porder);
618 result.set("props", pnode);
Simon Huntfb940112015-07-29 18:36:35 -0700619
620 ArrayNode buttons = arrayNode();
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700621 for (ButtonId b : pp.buttons()) {
622 buttons.add(b.id());
Simon Huntfb940112015-07-29 18:36:35 -0700623 }
624 result.set("buttons", buttons);
Simon Hunt0af1ec32015-07-24 12:17:55 -0700625 return result;
626 }
627
Thomas Vachuska329af532015-03-10 02:08:33 -0700628}