blob: 456d81af76dbc64be53aef74ae8c48455399729e [file] [log] [blame]
Thomas Vachuska329af532015-03-10 02:08:33 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
Thomas Vachuska329af532015-03-10 02:08:33 -07003 *
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;
Simon Hunt95d56fd2015-11-12 11:06:44 -080023import org.onlab.util.DefaultHashMap;
Thomas Vachuska329af532015-03-10 02:08:33 -070024import org.onosproject.cluster.ClusterEvent;
Thomas Vachuska329af532015-03-10 02:08:33 -070025import org.onosproject.cluster.ControllerNode;
26import org.onosproject.cluster.NodeId;
27import org.onosproject.core.CoreService;
cheng fan35dc0f22015-06-10 06:02:47 +080028import org.onosproject.incubator.net.tunnel.OpticalTunnelEndPoint;
29import org.onosproject.incubator.net.tunnel.Tunnel;
Thomas Vachuska329af532015-03-10 02:08:33 -070030import org.onosproject.net.Annotated;
31import org.onosproject.net.AnnotationKeys;
32import org.onosproject.net.Annotations;
33import org.onosproject.net.ConnectPoint;
34import org.onosproject.net.DefaultEdgeLink;
35import org.onosproject.net.Device;
36import org.onosproject.net.DeviceId;
37import org.onosproject.net.EdgeLink;
Simon Huntf4fd2a22016-08-10 15:41:09 -070038import org.onosproject.net.ElementId;
Thomas Vachuska329af532015-03-10 02:08:33 -070039import org.onosproject.net.Host;
40import org.onosproject.net.HostId;
41import org.onosproject.net.HostLocation;
42import org.onosproject.net.Link;
Thomas Vachuska329af532015-03-10 02:08:33 -070043import org.onosproject.net.device.DeviceEvent;
Thomas Vachuska329af532015-03-10 02:08:33 -070044import org.onosproject.net.flow.FlowEntry;
Thomas Vachuska329af532015-03-10 02:08:33 -070045import org.onosproject.net.host.HostEvent;
Thomas Vachuska329af532015-03-10 02:08:33 -070046import org.onosproject.net.link.LinkEvent;
Thomas Vachuska329af532015-03-10 02:08:33 -070047import org.onosproject.net.provider.ProviderId;
Thomas Vachuska329af532015-03-10 02:08:33 -070048import org.onosproject.net.topology.Topology;
Simon Huntd2747a02015-04-30 22:41:16 -070049import org.onosproject.ui.JsonUtils;
Thomas Vachuska329af532015-03-10 02:08:33 -070050import org.onosproject.ui.UiConnection;
Simon Hunta0ddb022015-05-01 09:53:01 -070051import org.onosproject.ui.UiMessageHandler;
Simon Hunted804d52016-03-30 09:51:40 -070052import org.onosproject.ui.impl.topo.util.ServicesBundle;
Simon Hunt0af1ec32015-07-24 12:17:55 -070053import org.onosproject.ui.topo.PropertyPanel;
Thomas Vachuska329af532015-03-10 02:08:33 -070054import org.slf4j.Logger;
55import org.slf4j.LoggerFactory;
56
Thomas Vachuska329af532015-03-10 02:08:33 -070057import java.util.Collection;
58import java.util.Collections;
Thomas Vachuska329af532015-03-10 02:08:33 -070059import java.util.Iterator;
Thomas Vachuska329af532015-03-10 02:08:33 -070060import java.util.Map;
chengfanc553c952016-07-22 15:48:23 +080061import java.util.Optional;
Simon Huntf4fd2a22016-08-10 15:41:09 -070062import java.util.Set;
Thomas Vachuska329af532015-03-10 02:08:33 -070063import java.util.concurrent.ConcurrentHashMap;
64
Thomas Vachuska329af532015-03-10 02:08:33 -070065import static com.google.common.base.Strings.isNullOrEmpty;
Thomas Vachuska329af532015-03-10 02:08:33 -070066import static org.onosproject.net.PortNumber.portNumber;
Simon Hunt3a0598f2015-08-04 19:59:04 -070067import static org.onosproject.ui.topo.TopoConstants.CoreButtons;
68import static org.onosproject.ui.topo.TopoConstants.Properties;
Simon Huntd3ceffa2015-08-25 12:44:35 -070069import static org.onosproject.ui.topo.TopoUtils.compactLinkString;
Thomas Vachuska329af532015-03-10 02:08:33 -070070
71/**
72 * Facility for creating messages bound for the topology viewer.
73 */
Simon Hunta0ddb022015-05-01 09:53:01 -070074public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
Thomas Vachuska329af532015-03-10 02:08:33 -070075
Simon Huntf4fd2a22016-08-10 15:41:09 -070076 private static final String NO_GEO_VALUE = "0.0";
Simon Hunt10618f62017-06-15 19:30:52 -070077 private static final String DASH = "-";
78
79 // nav paths are the view names for hot-link navigation from topo view...
80 private static final String DEVICE_NAV_PATH = "device";
81 private static final String HOST_NAV_PATH = "host";
Simon Huntf4fd2a22016-08-10 15:41:09 -070082
Simon Hunt95d56fd2015-11-12 11:06:44 -080083 // default to an "add" event...
84 private static final DefaultHashMap<ClusterEvent.Type, String> CLUSTER_EVENT =
85 new DefaultHashMap<>("addInstance");
86
87 // default to an "update" event...
88 private static final DefaultHashMap<DeviceEvent.Type, String> DEVICE_EVENT =
89 new DefaultHashMap<>("updateDevice");
90 private static final DefaultHashMap<LinkEvent.Type, String> LINK_EVENT =
91 new DefaultHashMap<>("updateLink");
92 private static final DefaultHashMap<HostEvent.Type, String> HOST_EVENT =
93 new DefaultHashMap<>("updateHost");
94
95 // but call out specific events that we care to differentiate...
96 static {
97 CLUSTER_EVENT.put(ClusterEvent.Type.INSTANCE_REMOVED, "removeInstance");
98
99 DEVICE_EVENT.put(DeviceEvent.Type.DEVICE_ADDED, "addDevice");
100 DEVICE_EVENT.put(DeviceEvent.Type.DEVICE_REMOVED, "removeDevice");
101
102 LINK_EVENT.put(LinkEvent.Type.LINK_ADDED, "addLink");
103 LINK_EVENT.put(LinkEvent.Type.LINK_REMOVED, "removeLink");
104
105 HOST_EVENT.put(HostEvent.Type.HOST_ADDED, "addHost");
106 HOST_EVENT.put(HostEvent.Type.HOST_REMOVED, "removeHost");
107 HOST_EVENT.put(HostEvent.Type.HOST_MOVED, "moveHost");
108 }
109
Simon Huntd2747a02015-04-30 22:41:16 -0700110 protected static final Logger log =
111 LoggerFactory.getLogger(TopologyViewMessageHandlerBase.class);
Thomas Vachuska329af532015-03-10 02:08:33 -0700112
Simon Huntd2747a02015-04-30 22:41:16 -0700113 private static final ProviderId PID =
114 new ProviderId("core", "org.onosproject.core", true);
Thomas Vachuska329af532015-03-10 02:08:33 -0700115
Thomas Vachuska329af532015-03-10 02:08:33 -0700116 // TODO: extract into an external & durable state; good enough for now and demo
117 private static Map<String, ObjectNode> metaUi = new ConcurrentHashMap<>();
118
119 /**
Thomas Vachuska329af532015-03-10 02:08:33 -0700120 * Returns read-only view of the meta-ui information.
121 *
122 * @return map of id to meta-ui mementos
123 */
124 static Map<String, ObjectNode> getMetaUi() {
125 return Collections.unmodifiableMap(metaUi);
126 }
127
Simon Hunt1911fe42017-05-02 18:25:58 -0700128
129 protected ServicesBundle services;
130
131 private String version;
132
133
Thomas Vachuska329af532015-03-10 02:08:33 -0700134 @Override
135 public void init(UiConnection connection, ServiceDirectory directory) {
136 super.init(connection, directory);
Simon Hunt1911fe42017-05-02 18:25:58 -0700137 services = new ServicesBundle(directory);
138 setVersionString(directory);
139 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700140
Simon Hunt1911fe42017-05-02 18:25:58 -0700141 // Creates a palatable version string to display on the summary panel
142 private void setVersionString(ServiceDirectory directory) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700143 String ver = directory.get(CoreService.class).version().toString();
144 version = ver.replace(".SNAPSHOT", "*").replaceFirst("~.*$", "");
145 }
146
Simon Hunt10618f62017-06-15 19:30:52 -0700147 // Returns the first of the given set of IP addresses as a string.
Thomas Vachuska329af532015-03-10 02:08:33 -0700148 private String ip(Set<IpAddress> ipAddresses) {
149 Iterator<IpAddress> it = ipAddresses.iterator();
150 return it.hasNext() ? it.next().toString() : "unknown";
151 }
152
153 // Produces JSON structure from annotations.
154 private JsonNode props(Annotations annotations) {
Simon Huntda580882015-05-12 20:58:18 -0700155 ObjectNode props = objectNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700156 if (annotations != null) {
157 for (String key : annotations.keys()) {
158 props.put(key, annotations.value(key));
159 }
160 }
161 return props;
162 }
163
164 // Produces an informational log message event bound to the client.
Simon Hunt36740d02017-06-07 11:25:51 -0700165 protected ObjectNode info(String message) {
166 return message("info", message);
Thomas Vachuska329af532015-03-10 02:08:33 -0700167 }
168
169 // Produces a warning log message event bound to the client.
Simon Hunt36740d02017-06-07 11:25:51 -0700170 protected ObjectNode warning(String message) {
171 return message("warning", message);
Thomas Vachuska329af532015-03-10 02:08:33 -0700172 }
173
174 // Produces an error log message event bound to the client.
Simon Hunt36740d02017-06-07 11:25:51 -0700175 protected ObjectNode error(String message) {
176 return message("error", message);
Thomas Vachuska329af532015-03-10 02:08:33 -0700177 }
178
179 // Produces a log message event bound to the client.
Simon Hunt36740d02017-06-07 11:25:51 -0700180 private ObjectNode message(String severity, String message) {
Simon Huntda580882015-05-12 20:58:18 -0700181 ObjectNode payload = objectNode()
Simon Huntd2747a02015-04-30 22:41:16 -0700182 .put("severity", severity)
183 .put("message", message);
Thomas Vachuska329af532015-03-10 02:08:33 -0700184
Simon Hunt36740d02017-06-07 11:25:51 -0700185 return JsonUtils.envelope("message", payload);
Thomas Vachuska329af532015-03-10 02:08:33 -0700186 }
187
Thomas Vachuska329af532015-03-10 02:08:33 -0700188 // Produces a cluster instance message to the client.
Simon Hunt95d56fd2015-11-12 11:06:44 -0800189 protected ObjectNode instanceMessage(ClusterEvent event, String msgType) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700190 ControllerNode node = event.subject();
Simon Hunt1911fe42017-05-02 18:25:58 -0700191 int switchCount = services.mastership().getDevicesOf(node.id()).size();
Simon Huntda580882015-05-12 20:58:18 -0700192 ObjectNode payload = objectNode()
Thomas Vachuska329af532015-03-10 02:08:33 -0700193 .put("id", node.id().toString())
194 .put("ip", node.ip().toString())
Simon Hunt1911fe42017-05-02 18:25:58 -0700195 .put("online", services.cluster().getState(node.id()).isActive())
196 .put("ready", services.cluster().getState(node.id()).isReady())
197 .put("uiAttached", node.equals(services.cluster().getLocalNode()))
Thomas Vachuska329af532015-03-10 02:08:33 -0700198 .put("switches", switchCount);
199
Simon Huntda580882015-05-12 20:58:18 -0700200 ArrayNode labels = arrayNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700201 labels.add(node.id().toString());
202 labels.add(node.ip().toString());
203
204 // Add labels, props and stuff the payload into envelope.
205 payload.set("labels", labels);
206 addMetaUi(node.id().toString(), payload);
207
Simon Hunt95d56fd2015-11-12 11:06:44 -0800208 String type = msgType != null ? msgType : CLUSTER_EVENT.get(event.type());
Simon Hunt36740d02017-06-07 11:25:51 -0700209 return JsonUtils.envelope(type, payload);
Thomas Vachuska329af532015-03-10 02:08:33 -0700210 }
211
212 // Produces a device event message to the client.
213 protected ObjectNode deviceMessage(DeviceEvent event) {
214 Device device = event.subject();
Simon Hunt1e20dae2016-10-28 11:26:26 -0700215 String uiType = device.annotations().value(AnnotationKeys.UI_TYPE);
216 String devType = uiType != null ? uiType :
217 device.type().toString().toLowerCase();
Simon Hunt10618f62017-06-15 19:30:52 -0700218 String name = device.annotations().value(AnnotationKeys.NAME);
219 name = isNullOrEmpty(name) ? device.id().toString() : name;
Simon Hunt1e20dae2016-10-28 11:26:26 -0700220
Simon Huntda580882015-05-12 20:58:18 -0700221 ObjectNode payload = objectNode()
Thomas Vachuska329af532015-03-10 02:08:33 -0700222 .put("id", device.id().toString())
Simon Hunt1e20dae2016-10-28 11:26:26 -0700223 .put("type", devType)
Simon Hunt1911fe42017-05-02 18:25:58 -0700224 .put("online", services.device().isAvailable(device.id()))
Thomas Vachuska329af532015-03-10 02:08:33 -0700225 .put("master", master(device.id()));
226
Simon Hunt10618f62017-06-15 19:30:52 -0700227 payload.set("labels", labels("", name, device.id().toString()));
Thomas Vachuska329af532015-03-10 02:08:33 -0700228 payload.set("props", props(device.annotations()));
229 addGeoLocation(device, payload);
230 addMetaUi(device.id().toString(), payload);
231
Simon Hunt95d56fd2015-11-12 11:06:44 -0800232 String type = DEVICE_EVENT.get(event.type());
Simon Hunt36740d02017-06-07 11:25:51 -0700233 return JsonUtils.envelope(type, payload);
Thomas Vachuska329af532015-03-10 02:08:33 -0700234 }
235
236 // Produces a link event message to the client.
237 protected ObjectNode linkMessage(LinkEvent event) {
238 Link link = event.subject();
Simon Huntda580882015-05-12 20:58:18 -0700239 ObjectNode payload = objectNode()
Thomas Vachuska329af532015-03-10 02:08:33 -0700240 .put("id", compactLinkString(link))
241 .put("type", link.type().toString().toLowerCase())
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800242 .put("expected", link.isExpected())
Thomas Vachuska329af532015-03-10 02:08:33 -0700243 .put("online", link.state() == Link.State.ACTIVE)
244 .put("linkWidth", 1.2)
245 .put("src", link.src().deviceId().toString())
246 .put("srcPort", link.src().port().toString())
247 .put("dst", link.dst().deviceId().toString())
248 .put("dstPort", link.dst().port().toString());
Simon Hunt95d56fd2015-11-12 11:06:44 -0800249 String type = LINK_EVENT.get(event.type());
Simon Hunt36740d02017-06-07 11:25:51 -0700250 return JsonUtils.envelope(type, payload);
Thomas Vachuska329af532015-03-10 02:08:33 -0700251 }
252
253 // Produces a host event message to the client.
254 protected ObjectNode hostMessage(HostEvent event) {
255 Host host = event.subject();
Charles Chan33f28a92015-11-13 13:12:38 -0800256 Host prevHost = event.prevSubject();
Simon Hunt1e20dae2016-10-28 11:26:26 -0700257 String hostType = host.annotations().value(AnnotationKeys.UI_TYPE);
Simon Hunt10618f62017-06-15 19:30:52 -0700258 String ip = ip(host.ipAddresses());
Simon Hunt95d56fd2015-11-12 11:06:44 -0800259
Simon Huntda580882015-05-12 20:58:18 -0700260 ObjectNode payload = objectNode()
Thomas Vachuska329af532015-03-10 02:08:33 -0700261 .put("id", host.id().toString())
262 .put("type", isNullOrEmpty(hostType) ? "endstation" : hostType)
263 .put("ingress", compactLinkString(edgeLink(host, true)))
264 .put("egress", compactLinkString(edgeLink(host, false)));
Simon Hunt10618f62017-06-15 19:30:52 -0700265
Simon Huntda580882015-05-12 20:58:18 -0700266 payload.set("cp", hostConnect(host.location()));
Charles Chan33f28a92015-11-13 13:12:38 -0800267 if (prevHost != null && prevHost.location() != null) {
268 payload.set("prevCp", hostConnect(prevHost.location()));
Simon Hunt95d56fd2015-11-12 11:06:44 -0800269 }
Simon Hunt10618f62017-06-15 19:30:52 -0700270 payload.set("labels", labels(nameForHost(host), ip, host.mac().toString()));
Thomas Vachuska329af532015-03-10 02:08:33 -0700271 payload.set("props", props(host.annotations()));
272 addGeoLocation(host, payload);
273 addMetaUi(host.id().toString(), payload);
274
Simon Hunt95d56fd2015-11-12 11:06:44 -0800275 String type = HOST_EVENT.get(event.type());
Simon Hunt36740d02017-06-07 11:25:51 -0700276 return JsonUtils.envelope(type, payload);
Thomas Vachuska329af532015-03-10 02:08:33 -0700277 }
278
279 // Encodes the specified host location into a JSON object.
Simon Huntda580882015-05-12 20:58:18 -0700280 private ObjectNode hostConnect(HostLocation location) {
281 return objectNode()
Thomas Vachuska329af532015-03-10 02:08:33 -0700282 .put("device", location.deviceId().toString())
283 .put("port", location.port().toLong());
284 }
285
286 // Encodes the specified list of labels a JSON array.
Simon Huntda580882015-05-12 20:58:18 -0700287 private ArrayNode labels(String... labels) {
288 ArrayNode json = arrayNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700289 for (String label : labels) {
290 json.add(label);
291 }
292 return json;
293 }
294
295 // Returns the name of the master node for the specified device id.
296 private String master(DeviceId deviceId) {
Simon Hunt1911fe42017-05-02 18:25:58 -0700297 NodeId master = services.mastership().getMasterFor(deviceId);
Thomas Vachuska329af532015-03-10 02:08:33 -0700298 return master != null ? master.toString() : "";
299 }
300
301 // Generates an edge link from the specified host location.
302 private EdgeLink edgeLink(Host host, boolean ingress) {
303 return new DefaultEdgeLink(PID, new ConnectPoint(host.id(), portNumber(0)),
304 host.location(), ingress);
305 }
306
307 // Adds meta UI information for the specified object.
308 private void addMetaUi(String id, ObjectNode payload) {
309 ObjectNode meta = metaUi.get(id);
310 if (meta != null) {
311 payload.set("metaUi", meta);
312 }
313 }
314
315 // Adds a geo location JSON to the specified payload object.
316 private void addGeoLocation(Annotated annotated, ObjectNode payload) {
317 Annotations annotations = annotated.annotations();
318 if (annotations == null) {
319 return;
320 }
321
Simon Huntfd7106c2016-02-09 15:05:26 -0800322 String slat = annotations.value(AnnotationKeys.LATITUDE);
Simon Huntf27a9292017-05-04 17:36:26 -0700323 String slng = annotations.value(AnnotationKeys.LONGITUDE);
Simon Huntf4fd2a22016-08-10 15:41:09 -0700324 boolean validLat = slat != null && !slat.equals(NO_GEO_VALUE);
Simon Huntf27a9292017-05-04 17:36:26 -0700325 boolean validLng = slng != null && !slng.equals(NO_GEO_VALUE);
Simon Huntf4fd2a22016-08-10 15:41:09 -0700326 if (validLat && validLng) {
327 try {
Simon Huntfd7106c2016-02-09 15:05:26 -0800328 double lat = Double.parseDouble(slat);
Simon Huntf27a9292017-05-04 17:36:26 -0700329 double lng = Double.parseDouble(slng);
Simon Huntda580882015-05-12 20:58:18 -0700330 ObjectNode loc = objectNode()
Simon Huntf27a9292017-05-04 17:36:26 -0700331 .put("locType", "geo")
332 .put("latOrY", lat)
333 .put("longOrX", lng);
Thomas Vachuska329af532015-03-10 02:08:33 -0700334 payload.set("location", loc);
Simon Huntf4fd2a22016-08-10 15:41:09 -0700335 } catch (NumberFormatException e) {
Simon Huntf27a9292017-05-04 17:36:26 -0700336 log.warn("Invalid geo data: latitude={}, longitude={}", slat, slng);
Thomas Vachuska329af532015-03-10 02:08:33 -0700337 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700338 }
339 }
340
341 // Updates meta UI information for the specified object.
Simon Huntd2747a02015-04-30 22:41:16 -0700342 protected void updateMetaUi(ObjectNode payload) {
343 metaUi.put(JsonUtils.string(payload, "id"),
344 JsonUtils.node(payload, "memento"));
Thomas Vachuska329af532015-03-10 02:08:33 -0700345 }
346
Simon Hunta17fa672015-08-19 18:42:22 -0700347
Simon Huntb745ca62015-07-28 15:37:11 -0700348 // -----------------------------------------------------------------------
349 // Create models of the data to return, that overlays can adjust / augment
350
351 // Returns property panel model for summary response.
Simon Hunt8a0429a2017-01-06 16:52:47 -0800352 protected PropertyPanel summmaryMessage() {
Simon Hunt1911fe42017-05-02 18:25:58 -0700353 Topology topology = services.topology().currentTopology();
Simon Hunt0af1ec32015-07-24 12:17:55 -0700354
Simon Hunt00a27ff2015-07-28 18:53:40 -0700355 return new PropertyPanel("ONOS Summary", "node")
Simon Hunt1911fe42017-05-02 18:25:58 -0700356 .addProp(Properties.VERSION, version)
357 .addSeparator()
358 .addProp(Properties.DEVICES, services.device().getDeviceCount())
359 .addProp(Properties.LINKS, topology.linkCount())
360 .addProp(Properties.HOSTS, services.host().getHostCount())
361 .addProp(Properties.TOPOLOGY_SSCS, topology.clusterCount())
362 .addSeparator()
363 .addProp(Properties.INTENTS, services.intent().getIntentCount())
364 .addProp(Properties.TUNNELS, services.tunnel().tunnelCount())
365 .addProp(Properties.FLOWS, services.flow().getFlowRuleCount());
Thomas Vachuska329af532015-03-10 02:08:33 -0700366 }
367
Simon Huntb745ca62015-07-28 15:37:11 -0700368 // Returns property panel model for device details response.
Simon Hunt8a0429a2017-01-06 16:52:47 -0800369 protected PropertyPanel deviceDetails(DeviceId deviceId) {
Simon Hunt1911fe42017-05-02 18:25:58 -0700370 Device device = services.device().getDevice(deviceId);
Thomas Vachuska329af532015-03-10 02:08:33 -0700371 Annotations annot = device.annotations();
372 String name = annot.value(AnnotationKeys.NAME);
Simon Hunt1911fe42017-05-02 18:25:58 -0700373 int portCount = services.device().getPorts(deviceId).size();
Thomas Vachuska329af532015-03-10 02:08:33 -0700374 int flowCount = getFlowCount(deviceId);
cheng fan35dc0f22015-06-10 06:02:47 +0800375 int tunnelCount = getTunnelCount(deviceId);
Simon Huntb745ca62015-07-28 15:37:11 -0700376
377 String title = isNullOrEmpty(name) ? deviceId.toString() : name;
378 String typeId = device.type().toString().toLowerCase();
379
Simon Hunt1911fe42017-05-02 18:25:58 -0700380 return new PropertyPanel(title, typeId)
Simon Hunt10618f62017-06-15 19:30:52 -0700381 .navPath(DEVICE_NAV_PATH)
Simon Hunt1911fe42017-05-02 18:25:58 -0700382 .id(deviceId.toString())
Simon Hunt3a0598f2015-08-04 19:59:04 -0700383
Simon Hunt1911fe42017-05-02 18:25:58 -0700384 .addProp(Properties.URI, deviceId.toString())
385 .addProp(Properties.VENDOR, device.manufacturer())
386 .addProp(Properties.HW_VERSION, device.hwVersion())
387 .addProp(Properties.SW_VERSION, device.swVersion())
388 .addProp(Properties.SERIAL_NUMBER, device.serialNumber())
389 .addProp(Properties.PROTOCOL, annot.value(AnnotationKeys.PROTOCOL))
390 .addSeparator()
Simon Hunt3a0598f2015-08-04 19:59:04 -0700391
Simon Hunt1911fe42017-05-02 18:25:58 -0700392 .addProp(Properties.LATITUDE, annot.value(AnnotationKeys.LATITUDE))
393 .addProp(Properties.LONGITUDE, annot.value(AnnotationKeys.LONGITUDE))
394 .addSeparator()
Simon Hunt3a0598f2015-08-04 19:59:04 -0700395
Simon Hunt1911fe42017-05-02 18:25:58 -0700396 .addProp(Properties.PORTS, portCount)
397 .addProp(Properties.FLOWS, flowCount)
398 .addProp(Properties.TUNNELS, tunnelCount)
Simon Hunt00a27ff2015-07-28 18:53:40 -0700399
Simon Hunt1911fe42017-05-02 18:25:58 -0700400 .addButton(CoreButtons.SHOW_DEVICE_VIEW)
401 .addButton(CoreButtons.SHOW_FLOW_VIEW)
402 .addButton(CoreButtons.SHOW_PORT_VIEW)
403 .addButton(CoreButtons.SHOW_GROUP_VIEW)
404 .addButton(CoreButtons.SHOW_METER_VIEW);
Thomas Vachuska329af532015-03-10 02:08:33 -0700405 }
406
407 protected int getFlowCount(DeviceId deviceId) {
408 int count = 0;
Simon Hunt1911fe42017-05-02 18:25:58 -0700409 for (FlowEntry flowEntry : services.flow().getFlowEntries(deviceId)) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700410 count++;
Thomas Vachuska329af532015-03-10 02:08:33 -0700411 }
412 return count;
413 }
414
cheng fan35dc0f22015-06-10 06:02:47 +0800415 protected int getTunnelCount(DeviceId deviceId) {
416 int count = 0;
Simon Hunt1911fe42017-05-02 18:25:58 -0700417 Collection<Tunnel> tunnels = services.tunnel().queryAllTunnels();
cheng fan35dc0f22015-06-10 06:02:47 +0800418 for (Tunnel tunnel : tunnels) {
chengfanc553c952016-07-22 15:48:23 +0800419 //Only OpticalTunnelEndPoint has a device
420 if (!(tunnel.src() instanceof OpticalTunnelEndPoint) ||
421 !(tunnel.dst() instanceof OpticalTunnelEndPoint)) {
422 continue;
423 }
424
425 Optional<ElementId> srcElementId = ((OpticalTunnelEndPoint) tunnel.src()).elementId();
426 Optional<ElementId> dstElementId = ((OpticalTunnelEndPoint) tunnel.dst()).elementId();
427 if (!srcElementId.isPresent() || !dstElementId.isPresent()) {
428 continue;
429 }
430 DeviceId srcDeviceId = (DeviceId) srcElementId.get();
431 DeviceId dstDeviceId = (DeviceId) dstElementId.get();
432 if (srcDeviceId.equals(deviceId) || dstDeviceId.equals(deviceId)) {
cheng fan35dc0f22015-06-10 06:02:47 +0800433 count++;
434 }
435 }
436 return count;
437 }
438
Simon Hunt10618f62017-06-15 19:30:52 -0700439 private boolean useDefaultName(String annotName) {
440 return isNullOrEmpty(annotName) || DASH.equals(annotName);
441 }
442
443 private String nameForHost(Host host) {
444 String name = host.annotations().value(AnnotationKeys.NAME);
445 return useDefaultName(name) ? ip(host.ipAddresses()) : name;
446 }
447
Thomas Vachuska329af532015-03-10 02:08:33 -0700448 // Returns host details response.
Simon Hunt8a0429a2017-01-06 16:52:47 -0800449 protected PropertyPanel hostDetails(HostId hostId) {
Simon Hunt1911fe42017-05-02 18:25:58 -0700450 Host host = services.host().getHost(hostId);
Thomas Vachuska329af532015-03-10 02:08:33 -0700451 Annotations annot = host.annotations();
452 String type = annot.value(AnnotationKeys.TYPE);
Thomas Vachuska329af532015-03-10 02:08:33 -0700453 String vlan = host.vlan().toString();
Simon Huntb745ca62015-07-28 15:37:11 -0700454 String typeId = isNullOrEmpty(type) ? "endstation" : type;
455
Simon Hunt10618f62017-06-15 19:30:52 -0700456 return new PropertyPanel(nameForHost(host), typeId)
457 .navPath(HOST_NAV_PATH)
Simon Hunt1911fe42017-05-02 18:25:58 -0700458 .id(hostId.toString())
459 .addProp(Properties.MAC, host.mac())
460 .addProp(Properties.IP, host.ipAddresses(), "[\\[\\]]")
461 .addProp(Properties.VLAN, "-1".equals(vlan) ? "none" : vlan)
462 .addSeparator()
463 .addProp(Properties.LATITUDE, annot.value(AnnotationKeys.LATITUDE))
464 .addProp(Properties.LONGITUDE, annot.value(AnnotationKeys.LONGITUDE));
Thomas Vachuska329af532015-03-10 02:08:33 -0700465 }
466
Thomas Vachuska329af532015-03-10 02:08:33 -0700467}