blob: c3eae02e18ec7fcf8fb404f74f165e3bcc3508a6 [file] [log] [blame]
Thomas Vachuska329af532015-03-10 02:08:33 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
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;
Simon Hunt879ce452017-08-10 23:32:00 -070021import com.google.common.collect.ImmutableSet;
Thomas Vachuska329af532015-03-10 02:08:33 -070022import org.onlab.osgi.ServiceDirectory;
23import org.onlab.packet.IpAddress;
Simon Hunta58d8942017-08-11 12:51:14 -070024import org.onlab.packet.VlanId;
Simon Hunt95d56fd2015-11-12 11:06:44 -080025import org.onlab.util.DefaultHashMap;
Thomas Vachuska329af532015-03-10 02:08:33 -070026import org.onosproject.cluster.ClusterEvent;
Thomas Vachuska329af532015-03-10 02:08:33 -070027import org.onosproject.cluster.ControllerNode;
28import org.onosproject.cluster.NodeId;
29import org.onosproject.core.CoreService;
cheng fan35dc0f22015-06-10 06:02:47 +080030import org.onosproject.incubator.net.tunnel.OpticalTunnelEndPoint;
31import org.onosproject.incubator.net.tunnel.Tunnel;
Thomas Vachuska329af532015-03-10 02:08:33 -070032import org.onosproject.net.Annotated;
33import org.onosproject.net.AnnotationKeys;
34import org.onosproject.net.Annotations;
35import org.onosproject.net.ConnectPoint;
36import org.onosproject.net.DefaultEdgeLink;
37import org.onosproject.net.Device;
38import org.onosproject.net.DeviceId;
39import org.onosproject.net.EdgeLink;
Simon Huntf4fd2a22016-08-10 15:41:09 -070040import org.onosproject.net.ElementId;
Thomas Vachuska329af532015-03-10 02:08:33 -070041import 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.device.DeviceEvent;
Thomas Vachuska329af532015-03-10 02:08:33 -070046import org.onosproject.net.flow.FlowEntry;
Thomas Vachuska329af532015-03-10 02:08:33 -070047import org.onosproject.net.host.HostEvent;
Thomas Vachuska329af532015-03-10 02:08:33 -070048import org.onosproject.net.link.LinkEvent;
Thomas Vachuska329af532015-03-10 02:08:33 -070049import org.onosproject.net.provider.ProviderId;
Thomas Vachuska329af532015-03-10 02:08:33 -070050import org.onosproject.net.topology.Topology;
Simon Huntd2747a02015-04-30 22:41:16 -070051import org.onosproject.ui.JsonUtils;
Thomas Vachuska329af532015-03-10 02:08:33 -070052import org.onosproject.ui.UiConnection;
Simon Hunta0ddb022015-05-01 09:53:01 -070053import org.onosproject.ui.UiMessageHandler;
Simon Hunted804d52016-03-30 09:51:40 -070054import org.onosproject.ui.impl.topo.util.ServicesBundle;
Simon Hunt879ce452017-08-10 23:32:00 -070055import org.onosproject.ui.lion.LionBundle;
Simon Hunt0af1ec32015-07-24 12:17:55 -070056import org.onosproject.ui.topo.PropertyPanel;
Thomas Vachuska329af532015-03-10 02:08:33 -070057import org.slf4j.Logger;
58import org.slf4j.LoggerFactory;
59
Thomas Vachuska329af532015-03-10 02:08:33 -070060import java.util.Collection;
61import java.util.Collections;
Thomas Vachuska329af532015-03-10 02:08:33 -070062import java.util.Iterator;
Thomas Vachuska329af532015-03-10 02:08:33 -070063import java.util.Map;
chengfanc553c952016-07-22 15:48:23 +080064import java.util.Optional;
Simon Huntf4fd2a22016-08-10 15:41:09 -070065import java.util.Set;
Thomas Vachuska329af532015-03-10 02:08:33 -070066import java.util.concurrent.ConcurrentHashMap;
67
Thomas Vachuska329af532015-03-10 02:08:33 -070068import static com.google.common.base.Strings.isNullOrEmpty;
Thomas Vachuska329af532015-03-10 02:08:33 -070069import static org.onosproject.net.PortNumber.portNumber;
Simon Hunt3a0598f2015-08-04 19:59:04 -070070import static org.onosproject.ui.topo.TopoConstants.CoreButtons;
Simon Hunt879ce452017-08-10 23:32:00 -070071import static org.onosproject.ui.topo.TopoConstants.Properties.DEVICES;
72import static org.onosproject.ui.topo.TopoConstants.Properties.FLOWS;
Simon Hunta58d8942017-08-11 12:51:14 -070073import static org.onosproject.ui.topo.TopoConstants.Properties.GRID_X;
74import static org.onosproject.ui.topo.TopoConstants.Properties.GRID_Y;
Simon Hunt879ce452017-08-10 23:32:00 -070075import static org.onosproject.ui.topo.TopoConstants.Properties.HOSTS;
Simon Hunta58d8942017-08-11 12:51:14 -070076import static org.onosproject.ui.topo.TopoConstants.Properties.HW_VERSION;
Simon Hunt879ce452017-08-10 23:32:00 -070077import static org.onosproject.ui.topo.TopoConstants.Properties.INTENTS;
Simon Hunta58d8942017-08-11 12:51:14 -070078import static org.onosproject.ui.topo.TopoConstants.Properties.IP;
79import static org.onosproject.ui.topo.TopoConstants.Properties.LATITUDE;
Simon Hunt879ce452017-08-10 23:32:00 -070080import static org.onosproject.ui.topo.TopoConstants.Properties.LINKS;
Simon Hunta58d8942017-08-11 12:51:14 -070081import static org.onosproject.ui.topo.TopoConstants.Properties.LONGITUDE;
82import static org.onosproject.ui.topo.TopoConstants.Properties.MAC;
83import static org.onosproject.ui.topo.TopoConstants.Properties.PORTS;
84import static org.onosproject.ui.topo.TopoConstants.Properties.PROTOCOL;
85import static org.onosproject.ui.topo.TopoConstants.Properties.SERIAL_NUMBER;
86import static org.onosproject.ui.topo.TopoConstants.Properties.SW_VERSION;
Simon Hunt879ce452017-08-10 23:32:00 -070087import static org.onosproject.ui.topo.TopoConstants.Properties.TOPOLOGY_SSCS;
88import static org.onosproject.ui.topo.TopoConstants.Properties.TUNNELS;
Simon Hunta58d8942017-08-11 12:51:14 -070089import static org.onosproject.ui.topo.TopoConstants.Properties.URI;
90import static org.onosproject.ui.topo.TopoConstants.Properties.VENDOR;
Simon Hunt879ce452017-08-10 23:32:00 -070091import static org.onosproject.ui.topo.TopoConstants.Properties.VERSION;
Simon Hunta58d8942017-08-11 12:51:14 -070092import static org.onosproject.ui.topo.TopoConstants.Properties.VLAN;
93import static org.onosproject.ui.topo.TopoConstants.Properties.VLAN_NONE;
Simon Huntd3ceffa2015-08-25 12:44:35 -070094import static org.onosproject.ui.topo.TopoUtils.compactLinkString;
Thomas Vachuska329af532015-03-10 02:08:33 -070095
96/**
97 * Facility for creating messages bound for the topology viewer.
98 */
Simon Hunta0ddb022015-05-01 09:53:01 -070099public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
Thomas Vachuska329af532015-03-10 02:08:33 -0700100
Simon Huntf4fd2a22016-08-10 15:41:09 -0700101 private static final String NO_GEO_VALUE = "0.0";
Simon Hunt10618f62017-06-15 19:30:52 -0700102 private static final String DASH = "-";
Simon Hunta58d8942017-08-11 12:51:14 -0700103 private static final String SLASH = " / ";
Simon Hunt10618f62017-06-15 19:30:52 -0700104
105 // nav paths are the view names for hot-link navigation from topo view...
106 private static final String DEVICE_NAV_PATH = "device";
107 private static final String HOST_NAV_PATH = "host";
Simon Huntf4fd2a22016-08-10 15:41:09 -0700108
Simon Hunta58d8942017-08-11 12:51:14 -0700109 // link panel label keys
110 private static final String LPL_FRIENDLY = "lp_label_friendly";
111 private static final String LPL_A_TYPE = "lp_label_a_type";
112 private static final String LPL_A_ID = "lp_label_a_id";
113 private static final String LPL_A_FRIENDLY = "lp_label_a_friendly";
114 private static final String LPL_A_PORT = "lp_label_a_port";
115 private static final String LPL_B_TYPE = "lp_label_b_type";
116 private static final String LPL_B_ID = "lp_label_b_id";
117 private static final String LPL_B_FRIENDLY = "lp_label_b_friendly";
118 private static final String LPL_B_PORT = "lp_label_b_port";
119 private static final String LPL_A2B = "lp_label_a2b";
120 private static final String LPL_B2A = "lp_label_b2a";
121 private static final String LPV_NO_LINK = "lp_value_no_link";
122
123 // other Lion keys
124 private static final String HOST = "host";
125 private static final String DEVICE = "device";
126 private static final String EXPECTED = "expected";
127 private static final String NOT_EXPECTED = "not_expected";
128
Simon Hunt95d56fd2015-11-12 11:06:44 -0800129 // default to an "add" event...
130 private static final DefaultHashMap<ClusterEvent.Type, String> CLUSTER_EVENT =
131 new DefaultHashMap<>("addInstance");
132
133 // default to an "update" event...
134 private static final DefaultHashMap<DeviceEvent.Type, String> DEVICE_EVENT =
135 new DefaultHashMap<>("updateDevice");
136 private static final DefaultHashMap<LinkEvent.Type, String> LINK_EVENT =
137 new DefaultHashMap<>("updateLink");
138 private static final DefaultHashMap<HostEvent.Type, String> HOST_EVENT =
139 new DefaultHashMap<>("updateHost");
140
141 // but call out specific events that we care to differentiate...
142 static {
143 CLUSTER_EVENT.put(ClusterEvent.Type.INSTANCE_REMOVED, "removeInstance");
144
145 DEVICE_EVENT.put(DeviceEvent.Type.DEVICE_ADDED, "addDevice");
146 DEVICE_EVENT.put(DeviceEvent.Type.DEVICE_REMOVED, "removeDevice");
147
148 LINK_EVENT.put(LinkEvent.Type.LINK_ADDED, "addLink");
149 LINK_EVENT.put(LinkEvent.Type.LINK_REMOVED, "removeLink");
150
151 HOST_EVENT.put(HostEvent.Type.HOST_ADDED, "addHost");
152 HOST_EVENT.put(HostEvent.Type.HOST_REMOVED, "removeHost");
153 HOST_EVENT.put(HostEvent.Type.HOST_MOVED, "moveHost");
154 }
155
Simon Hunta58d8942017-08-11 12:51:14 -0700156 private static final DefaultHashMap<Device.Type, String> DEVICE_GLYPHS =
157 new DefaultHashMap<>("m_unknown");
158
159 static {
160 DEVICE_GLYPHS.put(Device.Type.SWITCH, "m_switch");
161 DEVICE_GLYPHS.put(Device.Type.ROUTER, "m_router");
162 DEVICE_GLYPHS.put(Device.Type.ROADM, "m_roadm");
163 DEVICE_GLYPHS.put(Device.Type.OTN, "m_otn");
164 DEVICE_GLYPHS.put(Device.Type.ROADM_OTN, "m_roadm_otn");
165 DEVICE_GLYPHS.put(Device.Type.BALANCER, "m_balancer");
166 DEVICE_GLYPHS.put(Device.Type.IPS, "m_ips");
167 DEVICE_GLYPHS.put(Device.Type.IDS, "m_ids");
168 DEVICE_GLYPHS.put(Device.Type.CONTROLLER, "m_controller");
169 DEVICE_GLYPHS.put(Device.Type.VIRTUAL, "m_virtual");
170 DEVICE_GLYPHS.put(Device.Type.FIBER_SWITCH, "m_fiberSwitch");
171 DEVICE_GLYPHS.put(Device.Type.MICROWAVE, "m_microwave");
172 DEVICE_GLYPHS.put(Device.Type.OLT, "m_olt");
173 DEVICE_GLYPHS.put(Device.Type.ONU, "m_onu");
174 DEVICE_GLYPHS.put(Device.Type.OPTICAL_AMPLIFIER, "unknown"); // TODO glyph needed
175 DEVICE_GLYPHS.put(Device.Type.OTHER, "m_other");
176 }
177
178 private static final String DEFAULT_HOST_GLYPH = "m_endstation";
179 private static final String LINK_GLYPH = "m_ports";
180
181
Simon Huntd2747a02015-04-30 22:41:16 -0700182 protected static final Logger log =
183 LoggerFactory.getLogger(TopologyViewMessageHandlerBase.class);
Thomas Vachuska329af532015-03-10 02:08:33 -0700184
Simon Huntd2747a02015-04-30 22:41:16 -0700185 private static final ProviderId PID =
186 new ProviderId("core", "org.onosproject.core", true);
Thomas Vachuska329af532015-03-10 02:08:33 -0700187
Thomas Vachuska329af532015-03-10 02:08:33 -0700188 // TODO: extract into an external & durable state; good enough for now and demo
189 private static Map<String, ObjectNode> metaUi = new ConcurrentHashMap<>();
190
191 /**
Thomas Vachuska329af532015-03-10 02:08:33 -0700192 * Returns read-only view of the meta-ui information.
193 *
194 * @return map of id to meta-ui mementos
195 */
196 static Map<String, ObjectNode> getMetaUi() {
197 return Collections.unmodifiableMap(metaUi);
198 }
199
Simon Hunt879ce452017-08-10 23:32:00 -0700200 private static final String LION_TOPO = "core.view.Topo";
201
202 private static final Set<String> REQ_LION_BUNDLES = ImmutableSet.of(
203 LION_TOPO
204 );
Simon Hunt1911fe42017-05-02 18:25:58 -0700205
206 protected ServicesBundle services;
207
208 private String version;
209
210
Thomas Vachuska329af532015-03-10 02:08:33 -0700211 @Override
212 public void init(UiConnection connection, ServiceDirectory directory) {
213 super.init(connection, directory);
Simon Hunt1911fe42017-05-02 18:25:58 -0700214 services = new ServicesBundle(directory);
215 setVersionString(directory);
216 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700217
Simon Hunt1911fe42017-05-02 18:25:58 -0700218 // Creates a palatable version string to display on the summary panel
219 private void setVersionString(ServiceDirectory directory) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700220 String ver = directory.get(CoreService.class).version().toString();
221 version = ver.replace(".SNAPSHOT", "*").replaceFirst("~.*$", "");
222 }
223
Simon Hunt879ce452017-08-10 23:32:00 -0700224 @Override
225 public Set<String> requiredLionBundles() {
226 return REQ_LION_BUNDLES;
227 }
228
Simon Hunt10618f62017-06-15 19:30:52 -0700229 // Returns the first of the given set of IP addresses as a string.
Thomas Vachuska329af532015-03-10 02:08:33 -0700230 private String ip(Set<IpAddress> ipAddresses) {
231 Iterator<IpAddress> it = ipAddresses.iterator();
232 return it.hasNext() ? it.next().toString() : "unknown";
233 }
234
235 // Produces JSON structure from annotations.
236 private JsonNode props(Annotations annotations) {
Simon Huntda580882015-05-12 20:58:18 -0700237 ObjectNode props = objectNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700238 if (annotations != null) {
239 for (String key : annotations.keys()) {
240 props.put(key, annotations.value(key));
241 }
242 }
243 return props;
244 }
245
246 // Produces an informational log message event bound to the client.
Simon Hunt36740d02017-06-07 11:25:51 -0700247 protected ObjectNode info(String message) {
248 return message("info", message);
Thomas Vachuska329af532015-03-10 02:08:33 -0700249 }
250
251 // Produces a warning log message event bound to the client.
Simon Hunt36740d02017-06-07 11:25:51 -0700252 protected ObjectNode warning(String message) {
253 return message("warning", message);
Thomas Vachuska329af532015-03-10 02:08:33 -0700254 }
255
256 // Produces an error log message event bound to the client.
Simon Hunt36740d02017-06-07 11:25:51 -0700257 protected ObjectNode error(String message) {
258 return message("error", message);
Thomas Vachuska329af532015-03-10 02:08:33 -0700259 }
260
261 // Produces a log message event bound to the client.
Simon Hunt36740d02017-06-07 11:25:51 -0700262 private ObjectNode message(String severity, String message) {
Simon Huntda580882015-05-12 20:58:18 -0700263 ObjectNode payload = objectNode()
Simon Huntd2747a02015-04-30 22:41:16 -0700264 .put("severity", severity)
265 .put("message", message);
Thomas Vachuska329af532015-03-10 02:08:33 -0700266
Simon Hunt36740d02017-06-07 11:25:51 -0700267 return JsonUtils.envelope("message", payload);
Thomas Vachuska329af532015-03-10 02:08:33 -0700268 }
269
Thomas Vachuska329af532015-03-10 02:08:33 -0700270 // Produces a cluster instance message to the client.
Simon Hunt95d56fd2015-11-12 11:06:44 -0800271 protected ObjectNode instanceMessage(ClusterEvent event, String msgType) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700272 ControllerNode node = event.subject();
Simon Hunt1911fe42017-05-02 18:25:58 -0700273 int switchCount = services.mastership().getDevicesOf(node.id()).size();
Simon Huntda580882015-05-12 20:58:18 -0700274 ObjectNode payload = objectNode()
Thomas Vachuska329af532015-03-10 02:08:33 -0700275 .put("id", node.id().toString())
276 .put("ip", node.ip().toString())
Simon Hunt1911fe42017-05-02 18:25:58 -0700277 .put("online", services.cluster().getState(node.id()).isActive())
278 .put("ready", services.cluster().getState(node.id()).isReady())
279 .put("uiAttached", node.equals(services.cluster().getLocalNode()))
Thomas Vachuska329af532015-03-10 02:08:33 -0700280 .put("switches", switchCount);
281
Simon Huntda580882015-05-12 20:58:18 -0700282 ArrayNode labels = arrayNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700283 labels.add(node.id().toString());
284 labels.add(node.ip().toString());
285
286 // Add labels, props and stuff the payload into envelope.
287 payload.set("labels", labels);
288 addMetaUi(node.id().toString(), payload);
289
Simon Hunt95d56fd2015-11-12 11:06:44 -0800290 String type = msgType != null ? msgType : CLUSTER_EVENT.get(event.type());
Simon Hunt36740d02017-06-07 11:25:51 -0700291 return JsonUtils.envelope(type, payload);
Thomas Vachuska329af532015-03-10 02:08:33 -0700292 }
293
294 // Produces a device event message to the client.
295 protected ObjectNode deviceMessage(DeviceEvent event) {
296 Device device = event.subject();
Simon Hunt1e20dae2016-10-28 11:26:26 -0700297 String uiType = device.annotations().value(AnnotationKeys.UI_TYPE);
298 String devType = uiType != null ? uiType :
299 device.type().toString().toLowerCase();
Simon Hunt10618f62017-06-15 19:30:52 -0700300 String name = device.annotations().value(AnnotationKeys.NAME);
301 name = isNullOrEmpty(name) ? device.id().toString() : name;
Simon Hunt1e20dae2016-10-28 11:26:26 -0700302
Simon Huntda580882015-05-12 20:58:18 -0700303 ObjectNode payload = objectNode()
Thomas Vachuska329af532015-03-10 02:08:33 -0700304 .put("id", device.id().toString())
Simon Hunt1e20dae2016-10-28 11:26:26 -0700305 .put("type", devType)
Simon Hunt1911fe42017-05-02 18:25:58 -0700306 .put("online", services.device().isAvailable(device.id()))
Thomas Vachuska329af532015-03-10 02:08:33 -0700307 .put("master", master(device.id()));
308
Simon Hunt10618f62017-06-15 19:30:52 -0700309 payload.set("labels", labels("", name, device.id().toString()));
Thomas Vachuska329af532015-03-10 02:08:33 -0700310 payload.set("props", props(device.annotations()));
311 addGeoLocation(device, payload);
312 addMetaUi(device.id().toString(), payload);
313
Simon Hunt95d56fd2015-11-12 11:06:44 -0800314 String type = DEVICE_EVENT.get(event.type());
Simon Hunt36740d02017-06-07 11:25:51 -0700315 return JsonUtils.envelope(type, payload);
Thomas Vachuska329af532015-03-10 02:08:33 -0700316 }
317
318 // Produces a link event message to the client.
319 protected ObjectNode linkMessage(LinkEvent event) {
320 Link link = event.subject();
Simon Huntda580882015-05-12 20:58:18 -0700321 ObjectNode payload = objectNode()
Thomas Vachuska329af532015-03-10 02:08:33 -0700322 .put("id", compactLinkString(link))
323 .put("type", link.type().toString().toLowerCase())
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800324 .put("expected", link.isExpected())
Thomas Vachuska329af532015-03-10 02:08:33 -0700325 .put("online", link.state() == Link.State.ACTIVE)
326 .put("linkWidth", 1.2)
327 .put("src", link.src().deviceId().toString())
328 .put("srcPort", link.src().port().toString())
329 .put("dst", link.dst().deviceId().toString())
330 .put("dstPort", link.dst().port().toString());
Simon Hunt95d56fd2015-11-12 11:06:44 -0800331 String type = LINK_EVENT.get(event.type());
Simon Hunt36740d02017-06-07 11:25:51 -0700332 return JsonUtils.envelope(type, payload);
Thomas Vachuska329af532015-03-10 02:08:33 -0700333 }
334
335 // Produces a host event message to the client.
336 protected ObjectNode hostMessage(HostEvent event) {
337 Host host = event.subject();
Charles Chan33f28a92015-11-13 13:12:38 -0800338 Host prevHost = event.prevSubject();
Simon Hunt1e20dae2016-10-28 11:26:26 -0700339 String hostType = host.annotations().value(AnnotationKeys.UI_TYPE);
Simon Hunt10618f62017-06-15 19:30:52 -0700340 String ip = ip(host.ipAddresses());
Simon Hunt95d56fd2015-11-12 11:06:44 -0800341
Simon Huntda580882015-05-12 20:58:18 -0700342 ObjectNode payload = objectNode()
Thomas Vachuska329af532015-03-10 02:08:33 -0700343 .put("id", host.id().toString())
344 .put("type", isNullOrEmpty(hostType) ? "endstation" : hostType)
345 .put("ingress", compactLinkString(edgeLink(host, true)))
346 .put("egress", compactLinkString(edgeLink(host, false)));
Simon Hunt10618f62017-06-15 19:30:52 -0700347
Simon Huntda580882015-05-12 20:58:18 -0700348 payload.set("cp", hostConnect(host.location()));
Charles Chan33f28a92015-11-13 13:12:38 -0800349 if (prevHost != null && prevHost.location() != null) {
350 payload.set("prevCp", hostConnect(prevHost.location()));
Simon Hunt95d56fd2015-11-12 11:06:44 -0800351 }
Simon Hunt10618f62017-06-15 19:30:52 -0700352 payload.set("labels", labels(nameForHost(host), ip, host.mac().toString()));
Thomas Vachuska329af532015-03-10 02:08:33 -0700353 payload.set("props", props(host.annotations()));
354 addGeoLocation(host, payload);
355 addMetaUi(host.id().toString(), payload);
356
Simon Hunt95d56fd2015-11-12 11:06:44 -0800357 String type = HOST_EVENT.get(event.type());
Simon Hunt36740d02017-06-07 11:25:51 -0700358 return JsonUtils.envelope(type, payload);
Thomas Vachuska329af532015-03-10 02:08:33 -0700359 }
360
361 // Encodes the specified host location into a JSON object.
Simon Huntda580882015-05-12 20:58:18 -0700362 private ObjectNode hostConnect(HostLocation location) {
363 return objectNode()
Thomas Vachuska329af532015-03-10 02:08:33 -0700364 .put("device", location.deviceId().toString())
365 .put("port", location.port().toLong());
366 }
367
368 // Encodes the specified list of labels a JSON array.
Simon Huntda580882015-05-12 20:58:18 -0700369 private ArrayNode labels(String... labels) {
370 ArrayNode json = arrayNode();
Thomas Vachuska329af532015-03-10 02:08:33 -0700371 for (String label : labels) {
372 json.add(label);
373 }
374 return json;
375 }
376
377 // Returns the name of the master node for the specified device id.
378 private String master(DeviceId deviceId) {
Simon Hunt1911fe42017-05-02 18:25:58 -0700379 NodeId master = services.mastership().getMasterFor(deviceId);
Thomas Vachuska329af532015-03-10 02:08:33 -0700380 return master != null ? master.toString() : "";
381 }
382
383 // Generates an edge link from the specified host location.
384 private EdgeLink edgeLink(Host host, boolean ingress) {
385 return new DefaultEdgeLink(PID, new ConnectPoint(host.id(), portNumber(0)),
386 host.location(), ingress);
387 }
388
389 // Adds meta UI information for the specified object.
390 private void addMetaUi(String id, ObjectNode payload) {
391 ObjectNode meta = metaUi.get(id);
392 if (meta != null) {
393 payload.set("metaUi", meta);
394 }
395 }
396
397 // Adds a geo location JSON to the specified payload object.
398 private void addGeoLocation(Annotated annotated, ObjectNode payload) {
399 Annotations annotations = annotated.annotations();
400 if (annotations == null) {
401 return;
402 }
403
Simon Huntfd7106c2016-02-09 15:05:26 -0800404 String slat = annotations.value(AnnotationKeys.LATITUDE);
Simon Huntf27a9292017-05-04 17:36:26 -0700405 String slng = annotations.value(AnnotationKeys.LONGITUDE);
Simon Huntf4fd2a22016-08-10 15:41:09 -0700406 boolean validLat = slat != null && !slat.equals(NO_GEO_VALUE);
Simon Huntf27a9292017-05-04 17:36:26 -0700407 boolean validLng = slng != null && !slng.equals(NO_GEO_VALUE);
Simon Huntf4fd2a22016-08-10 15:41:09 -0700408 if (validLat && validLng) {
409 try {
Simon Huntfd7106c2016-02-09 15:05:26 -0800410 double lat = Double.parseDouble(slat);
Simon Huntf27a9292017-05-04 17:36:26 -0700411 double lng = Double.parseDouble(slng);
Simon Huntda580882015-05-12 20:58:18 -0700412 ObjectNode loc = objectNode()
Simon Huntf27a9292017-05-04 17:36:26 -0700413 .put("locType", "geo")
414 .put("latOrY", lat)
415 .put("longOrX", lng);
Thomas Vachuska329af532015-03-10 02:08:33 -0700416 payload.set("location", loc);
Simon Huntf4fd2a22016-08-10 15:41:09 -0700417 } catch (NumberFormatException e) {
Simon Huntf27a9292017-05-04 17:36:26 -0700418 log.warn("Invalid geo data: latitude={}, longitude={}", slat, slng);
Thomas Vachuska329af532015-03-10 02:08:33 -0700419 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700420 }
421 }
422
423 // Updates meta UI information for the specified object.
Simon Huntd2747a02015-04-30 22:41:16 -0700424 protected void updateMetaUi(ObjectNode payload) {
425 metaUi.put(JsonUtils.string(payload, "id"),
426 JsonUtils.node(payload, "memento"));
Thomas Vachuska329af532015-03-10 02:08:33 -0700427 }
428
Simon Hunta17fa672015-08-19 18:42:22 -0700429
Simon Huntb745ca62015-07-28 15:37:11 -0700430 // -----------------------------------------------------------------------
431 // Create models of the data to return, that overlays can adjust / augment
432
Simon Hunta58d8942017-08-11 12:51:14 -0700433 private String lookupGlyph(Device device) {
434 return DEVICE_GLYPHS.get(device.type());
435 }
436
437
Simon Huntb745ca62015-07-28 15:37:11 -0700438 // Returns property panel model for summary response.
Simon Hunt8a0429a2017-01-06 16:52:47 -0800439 protected PropertyPanel summmaryMessage() {
Simon Hunta58d8942017-08-11 12:51:14 -0700440 // chose NOT to add debug messages, since this is called every few seconds
Simon Hunt1911fe42017-05-02 18:25:58 -0700441 Topology topology = services.topology().currentTopology();
Simon Hunt879ce452017-08-10 23:32:00 -0700442 LionBundle lion = getLionBundle(LION_TOPO);
443 String panelTitle = lion.getSafe("title_panel_summary");
Simon Hunt0af1ec32015-07-24 12:17:55 -0700444
Simon Hunta58d8942017-08-11 12:51:14 -0700445 return new PropertyPanel(panelTitle, "bird")
Simon Hunt879ce452017-08-10 23:32:00 -0700446 .addProp(VERSION, lion.getSafe(VERSION), version)
Simon Hunt1911fe42017-05-02 18:25:58 -0700447 .addSeparator()
Simon Hunt879ce452017-08-10 23:32:00 -0700448 .addProp(DEVICES, lion.getSafe(DEVICES), services.device().getDeviceCount())
449 .addProp(LINKS, lion.getSafe(LINKS), topology.linkCount())
450 .addProp(HOSTS, lion.getSafe(HOSTS), services.host().getHostCount())
451 .addProp(TOPOLOGY_SSCS, lion.getSafe(TOPOLOGY_SSCS), topology.clusterCount())
Simon Hunt1911fe42017-05-02 18:25:58 -0700452 .addSeparator()
Simon Hunt879ce452017-08-10 23:32:00 -0700453 .addProp(INTENTS, lion.getSafe(INTENTS), services.intent().getIntentCount())
454 .addProp(TUNNELS, lion.getSafe(TUNNELS), services.tunnel().tunnelCount())
455 .addProp(FLOWS, lion.getSafe(FLOWS), services.flow().getFlowRuleCount());
Thomas Vachuska329af532015-03-10 02:08:33 -0700456 }
457
Simon Hunta58d8942017-08-11 12:51:14 -0700458
459 private String friendlyDevice(DeviceId deviceId) {
Simon Hunt1911fe42017-05-02 18:25:58 -0700460 Device device = services.device().getDevice(deviceId);
Thomas Vachuska329af532015-03-10 02:08:33 -0700461 Annotations annot = device.annotations();
462 String name = annot.value(AnnotationKeys.NAME);
Simon Hunta58d8942017-08-11 12:51:14 -0700463 return isNullOrEmpty(name) ? deviceId.toString() : name;
464 }
465
466 // Generates a property panel model for device details response
467 protected PropertyPanel deviceDetails(DeviceId deviceId) {
468 log.debug("generate prop panel data for device {}", deviceId);
469 Device device = services.device().getDevice(deviceId);
470 Annotations annot = device.annotations();
471 String proto = annot.value(AnnotationKeys.PROTOCOL);
472 String title = friendlyDevice(deviceId);
473 LionBundle lion = getLionBundle(LION_TOPO);
474
475 PropertyPanel pp = new PropertyPanel(title, lookupGlyph(device))
476 .navPath(DEVICE_NAV_PATH)
477 .id(deviceId.toString());
478 addDeviceBasicProps(pp, deviceId, device, proto, lion);
479 addLocationProps(pp, annot, lion);
480 addDeviceCountStats(pp, deviceId, lion);
481 addDeviceCoreButtons(pp);
482 return pp;
483 }
484
485 private void addDeviceBasicProps(PropertyPanel pp, DeviceId deviceId,
486 Device device, String proto, LionBundle lion) {
487 pp.addProp(URI, lion.getSafe(URI), deviceId.toString())
488 .addProp(VENDOR, lion.getSafe(VENDOR), device.manufacturer())
489 .addProp(HW_VERSION, lion.getSafe(HW_VERSION), device.hwVersion())
490 .addProp(SW_VERSION, lion.getSafe(SW_VERSION), device.swVersion())
491 .addProp(SERIAL_NUMBER, lion.getSafe(SERIAL_NUMBER), device.serialNumber())
492 .addProp(PROTOCOL, lion.getSafe(PROTOCOL), proto)
493 .addSeparator();
494 }
495
496 // only add location properties if we have them
497 private void addLocationProps(PropertyPanel pp, Annotations annot,
498 LionBundle lion) {
499 String slat = annot.value(AnnotationKeys.LATITUDE);
500 String slng = annot.value(AnnotationKeys.LONGITUDE);
501 String sgrY = annot.value(AnnotationKeys.GRID_Y);
502 String sgrX = annot.value(AnnotationKeys.GRID_X);
503
504 boolean validLat = slat != null && !slat.equals(NO_GEO_VALUE);
505 boolean validLng = slng != null && !slng.equals(NO_GEO_VALUE);
506 if (validLat && validLng) {
507 pp.addProp(LATITUDE, lion.getSafe(LATITUDE), slat)
508 .addProp(LONGITUDE, lion.getSafe(LONGITUDE), slng)
509 .addSeparator();
510
511 } else if (sgrY != null && sgrX != null) {
512 pp.addProp(GRID_Y, lion.getSafe(GRID_Y), sgrY)
513 .addProp(GRID_X, lion.getSafe(GRID_X), sgrX)
514 .addSeparator();
515 }
516 // else, no location
517 }
518
519 private void addDeviceCountStats(PropertyPanel pp, DeviceId deviceId, LionBundle lion) {
Simon Hunt1911fe42017-05-02 18:25:58 -0700520 int portCount = services.device().getPorts(deviceId).size();
Thomas Vachuska329af532015-03-10 02:08:33 -0700521 int flowCount = getFlowCount(deviceId);
cheng fan35dc0f22015-06-10 06:02:47 +0800522 int tunnelCount = getTunnelCount(deviceId);
Simon Huntb745ca62015-07-28 15:37:11 -0700523
Simon Hunta58d8942017-08-11 12:51:14 -0700524 pp.addProp(PORTS, lion.getSafe(PORTS), portCount)
525 .addProp(FLOWS, lion.getSafe(FLOWS), flowCount)
526 .addProp(TUNNELS, lion.getSafe(TUNNELS), tunnelCount);
527 }
Simon Huntb745ca62015-07-28 15:37:11 -0700528
Simon Hunta58d8942017-08-11 12:51:14 -0700529 private void addDeviceCoreButtons(PropertyPanel pp) {
530 pp.addButton(CoreButtons.SHOW_DEVICE_VIEW)
Simon Hunt1911fe42017-05-02 18:25:58 -0700531 .addButton(CoreButtons.SHOW_FLOW_VIEW)
532 .addButton(CoreButtons.SHOW_PORT_VIEW)
533 .addButton(CoreButtons.SHOW_GROUP_VIEW)
534 .addButton(CoreButtons.SHOW_METER_VIEW);
Thomas Vachuska329af532015-03-10 02:08:33 -0700535 }
536
537 protected int getFlowCount(DeviceId deviceId) {
538 int count = 0;
Simon Hunt1911fe42017-05-02 18:25:58 -0700539 for (FlowEntry flowEntry : services.flow().getFlowEntries(deviceId)) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700540 count++;
Thomas Vachuska329af532015-03-10 02:08:33 -0700541 }
542 return count;
543 }
544
cheng fan35dc0f22015-06-10 06:02:47 +0800545 protected int getTunnelCount(DeviceId deviceId) {
546 int count = 0;
Simon Hunt1911fe42017-05-02 18:25:58 -0700547 Collection<Tunnel> tunnels = services.tunnel().queryAllTunnels();
cheng fan35dc0f22015-06-10 06:02:47 +0800548 for (Tunnel tunnel : tunnels) {
chengfanc553c952016-07-22 15:48:23 +0800549 //Only OpticalTunnelEndPoint has a device
550 if (!(tunnel.src() instanceof OpticalTunnelEndPoint) ||
551 !(tunnel.dst() instanceof OpticalTunnelEndPoint)) {
552 continue;
553 }
554
555 Optional<ElementId> srcElementId = ((OpticalTunnelEndPoint) tunnel.src()).elementId();
556 Optional<ElementId> dstElementId = ((OpticalTunnelEndPoint) tunnel.dst()).elementId();
557 if (!srcElementId.isPresent() || !dstElementId.isPresent()) {
558 continue;
559 }
560 DeviceId srcDeviceId = (DeviceId) srcElementId.get();
561 DeviceId dstDeviceId = (DeviceId) dstElementId.get();
562 if (srcDeviceId.equals(deviceId) || dstDeviceId.equals(deviceId)) {
cheng fan35dc0f22015-06-10 06:02:47 +0800563 count++;
564 }
565 }
566 return count;
567 }
568
Simon Hunt10618f62017-06-15 19:30:52 -0700569 private boolean useDefaultName(String annotName) {
570 return isNullOrEmpty(annotName) || DASH.equals(annotName);
571 }
572
573 private String nameForHost(Host host) {
574 String name = host.annotations().value(AnnotationKeys.NAME);
575 return useDefaultName(name) ? ip(host.ipAddresses()) : name;
576 }
577
Simon Hunta58d8942017-08-11 12:51:14 -0700578 private String glyphForHost(Annotations annot) {
579 String uiType = annot.value(AnnotationKeys.UI_TYPE);
580 return isNullOrEmpty(uiType) ? DEFAULT_HOST_GLYPH : uiType;
Thomas Vachuska329af532015-03-10 02:08:33 -0700581 }
582
Simon Hunta58d8942017-08-11 12:51:14 -0700583 // Generates a property panel model for a host details response
584 protected PropertyPanel hostDetails(HostId hostId) {
585 log.debug("generate prop panel data for host {}", hostId);
586 Host host = services.host().getHost(hostId);
587 Annotations annot = host.annotations();
588 String glyphId = glyphForHost(annot);
589 LionBundle lion = getLionBundle(LION_TOPO);
590
591 PropertyPanel pp = new PropertyPanel(nameForHost(host), glyphId)
592 .navPath(HOST_NAV_PATH)
593 .id(hostId.toString());
594 addHostBasicProps(pp, host, lion);
595 addLocationProps(pp, annot, lion);
596 return pp;
597 }
598
599 private void addHostBasicProps(PropertyPanel pp, Host host, LionBundle lion) {
600 pp.addProp(LPL_FRIENDLY, lion.getSafe(LPL_FRIENDLY), nameForHost(host))
601 .addProp(MAC, lion.getSafe(MAC), host.mac())
602 .addProp(IP, lion.getSafe(IP), host.ipAddresses(), "[\\[\\]]")
603 .addProp(VLAN, lion.getSafe(VLAN), displayVlan(host.vlan(), lion))
604 .addSeparator();
605 }
606
607 private String displayVlan(VlanId vlan, LionBundle lion) {
608 return VlanId.NONE.equals(vlan) ? lion.getSafe(VLAN_NONE) : vlan.toString();
609 }
610
611 // Generates a property panel model for a link details response (edge-link)
612 protected PropertyPanel edgeLinkDetails(HostId hid, ConnectPoint cp) {
613 log.debug("generate prop panel data for edgelink {} {}", hid, cp);
614 LionBundle lion = getLionBundle(LION_TOPO);
615 String title = lion.getSafe("title_edge_link");
616
617 PropertyPanel pp = new PropertyPanel(title, LINK_GLYPH);
618 addLinkHostProps(pp, hid, lion);
619 addLinkCpBProps(pp, cp, lion);
620 return pp;
621 }
622
623 // Generates a property panel model for a link details response (infra-link)
624 protected PropertyPanel infraLinkDetails(ConnectPoint cpA, ConnectPoint cpB) {
625 log.debug("generate prop panel data for infralink {} {}", cpA, cpB);
626 LionBundle lion = getLionBundle(LION_TOPO);
627 String title = lion.getSafe("title_infra_link");
628
629 PropertyPanel pp = new PropertyPanel(title, LINK_GLYPH);
630 addLinkCpAProps(pp, cpA, lion);
631 addLinkCpBProps(pp, cpB, lion);
632 addLinkBackingProps(pp, cpA, cpB, lion);
633 return pp;
634 }
635
636 private void addLinkHostProps(PropertyPanel pp, HostId hostId, LionBundle lion) {
637 Host host = services.host().getHost(hostId);
638
639 pp.addProp(LPL_A_TYPE, lion.getSafe(LPL_A_TYPE), lion.getSafe(HOST))
640 .addProp(LPL_A_ID, lion.getSafe(LPL_A_ID), hostId.toString())
641 .addProp(LPL_A_FRIENDLY, lion.getSafe(LPL_A_FRIENDLY), nameForHost(host))
642 .addSeparator();
643 }
644
645 private void addLinkCpAProps(PropertyPanel pp, ConnectPoint cp, LionBundle lion) {
646 DeviceId did = cp.deviceId();
647
648 pp.addProp(LPL_A_TYPE, lion.getSafe(LPL_A_TYPE), lion.getSafe(DEVICE))
649 .addProp(LPL_A_ID, lion.getSafe(LPL_A_ID), did.toString())
650 .addProp(LPL_A_FRIENDLY, lion.getSafe(LPL_A_FRIENDLY), friendlyDevice(did))
651 .addProp(LPL_A_PORT, lion.getSafe(LPL_A_PORT), cp.port().toLong())
652 .addSeparator();
653 }
654
655 private void addLinkCpBProps(PropertyPanel pp, ConnectPoint cp, LionBundle lion) {
656 DeviceId did = cp.deviceId();
657
658 pp.addProp(LPL_B_TYPE, lion.getSafe(LPL_B_TYPE), lion.getSafe(DEVICE))
659 .addProp(LPL_B_ID, lion.getSafe(LPL_B_ID), did.toString())
660 .addProp(LPL_B_FRIENDLY, lion.getSafe(LPL_B_FRIENDLY), friendlyDevice(did))
661 .addProp(LPL_B_PORT, lion.getSafe(LPL_B_PORT), cp.port().toLong())
662 .addSeparator();
663 }
664
665 private void addLinkBackingProps(PropertyPanel pp, ConnectPoint cpA,
666 ConnectPoint cpB, LionBundle lion) {
667 Link a2b = services.link().getLink(cpA, cpB);
668 Link b2a = services.link().getLink(cpB, cpA);
669
670 pp.addProp(LPL_A2B, lion.getSafe(LPL_A2B), linkPropString(a2b, lion))
671 .addProp(LPL_B2A, lion.getSafe(LPL_B2A), linkPropString(b2a, lion));
672 }
673
674 private String linkPropString(Link link, LionBundle lion) {
675 if (link == null) {
676 return lion.getSafe(LPV_NO_LINK);
677 }
678 return lion.getSafe(link.type()) + SLASH +
679 lion.getSafe(link.state()) + SLASH +
680 lion.getSafe(link.isExpected() ? EXPECTED : NOT_EXPECTED);
681 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700682}