Simon Hunt

ONOS-4970: Device data for topology view -- WIP.

Change-Id: Ie5a0c65f38b32672570919c50c1f53b14d293d3f
...@@ -50,7 +50,7 @@ public final class AnnotationKeys { ...@@ -50,7 +50,7 @@ public final class AnnotationKeys {
50 public static final String LATITUDE = "latitude"; 50 public static final String LATITUDE = "latitude";
51 51
52 /** 52 /**
53 - * Annotation key for longitute (e.g. longitude of device). 53 + * Annotation key for longitude (e.g. longitude of device).
54 */ 54 */
55 public static final String LONGITUDE = "longitude"; 55 public static final String LONGITUDE = "longitude";
56 56
......
...@@ -26,6 +26,9 @@ import org.onosproject.cluster.NodeId; ...@@ -26,6 +26,9 @@ import org.onosproject.cluster.NodeId;
26 import org.onosproject.incubator.net.PortStatisticsService; 26 import org.onosproject.incubator.net.PortStatisticsService;
27 import org.onosproject.incubator.net.tunnel.TunnelService; 27 import org.onosproject.incubator.net.tunnel.TunnelService;
28 import org.onosproject.mastership.MastershipService; 28 import org.onosproject.mastership.MastershipService;
29 +import org.onosproject.net.Annotated;
30 +import org.onosproject.net.Annotations;
31 +import org.onosproject.net.Device;
29 import org.onosproject.net.device.DeviceService; 32 import org.onosproject.net.device.DeviceService;
30 import org.onosproject.net.flow.FlowRuleService; 33 import org.onosproject.net.flow.FlowRuleService;
31 import org.onosproject.net.host.HostService; 34 import org.onosproject.net.host.HostService;
...@@ -51,8 +54,11 @@ import java.util.HashSet; ...@@ -51,8 +54,11 @@ import java.util.HashSet;
51 import java.util.List; 54 import java.util.List;
52 import java.util.Map; 55 import java.util.Map;
53 import java.util.Set; 56 import java.util.Set;
57 +import java.util.concurrent.ConcurrentHashMap;
54 58
55 import static com.google.common.base.Preconditions.checkNotNull; 59 import static com.google.common.base.Preconditions.checkNotNull;
60 +import static org.onosproject.net.AnnotationKeys.LATITUDE;
61 +import static org.onosproject.net.AnnotationKeys.LONGITUDE;
56 import static org.onosproject.ui.model.topo.UiNode.LAYER_DEFAULT; 62 import static org.onosproject.ui.model.topo.UiNode.LAYER_DEFAULT;
57 63
58 /** 64 /**
...@@ -88,6 +94,11 @@ class Topo2Jsonifier { ...@@ -88,6 +94,11 @@ class Topo2Jsonifier {
88 private TunnelService tunnelService; 94 private TunnelService tunnelService;
89 95
90 96
97 + // NOTE: we'll stick this here for now, but maybe there is a better home?
98 + // (this is not distributed across the cluster)
99 + private static Map<String, ObjectNode> metaUi = new ConcurrentHashMap<>();
100 +
101 +
91 /** 102 /**
92 * Creates an instance with a reference to the services directory, so that 103 * Creates an instance with a reference to the services directory, so that
93 * additional information about network elements may be looked up on 104 * additional information about network elements may be looked up on
...@@ -263,17 +274,73 @@ class Topo2Jsonifier { ...@@ -263,17 +274,73 @@ class Topo2Jsonifier {
263 .put("master", nullIsEmpty(device.master())) 274 .put("master", nullIsEmpty(device.master()))
264 .put("layer", device.layer()); 275 .put("layer", device.layer());
265 276
266 - // TODO: complete device details 277 + Device d = device.backingDevice();
267 -// addLabels(node, device); 278 +
268 -// addProps(node, device); 279 + addProps(node, d);
269 -// addGeoLocation(node, device); 280 + addGeoLocation(node, d);
270 -// addMetaUi(node, device); 281 + addMetaUi(node, device.idAsString());
271 282
272 return node; 283 return node;
273 } 284 }
274 285
275 - private void addLabels(ObjectNode node, UiDevice device) { 286 + private void addProps(ObjectNode node, Device dev) {
287 + Annotations annot = dev.annotations();
288 + ObjectNode props = objectNode();
289 + if (annot != null) {
290 + annot.keys().forEach(k -> props.put(k, annot.value(k)));
291 + }
292 + node.set("props", props);
293 + }
294 +
295 + private void addMetaUi(ObjectNode node, String metaInstanceId) {
296 + ObjectNode meta = metaUi.get(metaInstanceId);
297 + if (meta != null) {
298 + node.set("metaUi", meta);
299 + }
300 + }
301 +
302 + private void addGeoLocation(ObjectNode node, Annotated a) {
303 + List<String> lngLat = getAnnotValues(a, LONGITUDE, LATITUDE);
304 + if (lngLat != null) {
305 + try {
306 + double lng = Double.parseDouble(lngLat.get(0));
307 + double lat = Double.parseDouble(lngLat.get(1));
308 + ObjectNode loc = objectNode()
309 + .put("type", "lnglat")
310 + .put("lng", lng)
311 + .put("lat", lat);
312 + node.set("location", loc);
313 +
314 + } catch (NumberFormatException e) {
315 + log.warn("Invalid geo data: longitude={}, latitude={}",
316 + lngLat.get(0), lngLat.get(1));
317 + }
318 + } else {
319 + log.debug("No geo lng/lat for {}", a);
320 + }
321 + }
322 +
323 + // return list of string values from annotated instance, for given keys
324 + // return null if any keys are not present
325 + List<String> getAnnotValues(Annotated a, String... annotKeys) {
326 + List<String> result = new ArrayList<>(annotKeys.length);
327 + for (String k : annotKeys) {
328 + String v = a.annotations().value(k);
329 + if (v == null) {
330 + return null;
331 + }
332 + result.add(v);
333 + }
334 + return result;
335 + }
276 336
337 + // derive JSON object from annotations
338 + private ObjectNode props(Annotations annotations) {
339 + ObjectNode p = objectNode();
340 + if (annotations != null) {
341 + annotations.keys().forEach(k -> p.put(k, annotations.value(k)));
342 + }
343 + return p;
277 } 344 }
278 345
279 private ObjectNode json(UiHost host) { 346 private ObjectNode json(UiHost host) {
......
...@@ -19,6 +19,8 @@ package org.onosproject.ui.impl.topo; ...@@ -19,6 +19,8 @@ package org.onosproject.ui.impl.topo;
19 import com.google.common.collect.ImmutableList; 19 import com.google.common.collect.ImmutableList;
20 import com.google.common.collect.ImmutableSet; 20 import com.google.common.collect.ImmutableSet;
21 import org.junit.Test; 21 import org.junit.Test;
22 +import org.onosproject.net.Annotated;
23 +import org.onosproject.net.Annotations;
22 import org.onosproject.ui.impl.AbstractUiImplTest; 24 import org.onosproject.ui.impl.AbstractUiImplTest;
23 import org.onosproject.ui.model.topo.UiNode; 25 import org.onosproject.ui.model.topo.UiNode;
24 26
...@@ -26,6 +28,7 @@ import java.util.List; ...@@ -26,6 +28,7 @@ import java.util.List;
26 import java.util.Set; 28 import java.util.Set;
27 29
28 import static org.junit.Assert.assertEquals; 30 import static org.junit.Assert.assertEquals;
31 +import static org.junit.Assert.assertNull;
29 import static org.onosproject.ui.model.topo.UiNode.LAYER_DEFAULT; 32 import static org.onosproject.ui.model.topo.UiNode.LAYER_DEFAULT;
30 import static org.onosproject.ui.model.topo.UiNode.LAYER_OPTICAL; 33 import static org.onosproject.ui.model.topo.UiNode.LAYER_OPTICAL;
31 import static org.onosproject.ui.model.topo.UiNode.LAYER_PACKET; 34 import static org.onosproject.ui.model.topo.UiNode.LAYER_PACKET;
...@@ -145,4 +148,59 @@ public class Topo2JsonifierTest extends AbstractUiImplTest { ...@@ -145,4 +148,59 @@ public class Topo2JsonifierTest extends AbstractUiImplTest {
145 assertEquals("missing node B", true, def.contains(NODE_B)); 148 assertEquals("missing node B", true, def.contains(NODE_B));
146 assertEquals("missing node E", true, def.contains(NODE_E)); 149 assertEquals("missing node E", true, def.contains(NODE_E));
147 } 150 }
151 +
152 + private static final String K1 = "K1";
153 + private static final String K2 = "K2";
154 + private static final String K3 = "K3";
155 + private static final String K4 = "K4";
156 +
157 + private static final String V1 = "V1";
158 + private static final String V2 = "V2";
159 + private static final String V3 = "V3";
160 +
161 + private static final Annotations ANNOTS = new Annotations() {
162 + @Override
163 + public Set<String> keys() {
164 + return ImmutableSet.of(K1, K2, K3);
165 + }
166 +
167 + @Override
168 + public String value(String key) {
169 + switch (key) {
170 + case K1:
171 + return V1;
172 + case K2:
173 + return V2;
174 + case K3:
175 + return V3;
176 + default:
177 + return null;
178 + }
179 + }
180 + };
181 +
182 + private static final Annotated THING = () -> ANNOTS;
183 +
184 + private void verifyValues(List<String> vals, String... exp) {
185 + print(vals);
186 + if (exp.length == 0) {
187 + // don't expect any results
188 + assertNull("huh?", vals);
189 + } else {
190 + assertEquals("wrong list len", exp.length, vals.size());
191 +
192 + for (int i = 0; i < exp.length; i++) {
193 + assertEquals("wrong value " + i, exp[i], vals.get(i));
194 + }
195 + }
196 + }
197 +
198 + @Test
199 + public void annotValues() {
200 + print("annotValues()");
201 + verifyValues(t2.getAnnotValues(THING, K1), V1);
202 + verifyValues(t2.getAnnotValues(THING, K3, K1), V3, V1);
203 + verifyValues(t2.getAnnotValues(THING, K1, K2, K3), V1, V2, V3);
204 + verifyValues(t2.getAnnotValues(THING, K1, K4));
205 + }
148 } 206 }
......