Simon Hunt

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

Change-Id: Ie5a0c65f38b32672570919c50c1f53b14d293d3f
......@@ -50,7 +50,7 @@ public final class AnnotationKeys {
public static final String LATITUDE = "latitude";
/**
* Annotation key for longitute (e.g. longitude of device).
* Annotation key for longitude (e.g. longitude of device).
*/
public static final String LONGITUDE = "longitude";
......
......@@ -26,6 +26,9 @@ import org.onosproject.cluster.NodeId;
import org.onosproject.incubator.net.PortStatisticsService;
import org.onosproject.incubator.net.tunnel.TunnelService;
import org.onosproject.mastership.MastershipService;
import org.onosproject.net.Annotated;
import org.onosproject.net.Annotations;
import org.onosproject.net.Device;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.flow.FlowRuleService;
import org.onosproject.net.host.HostService;
......@@ -51,8 +54,11 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.onosproject.net.AnnotationKeys.LATITUDE;
import static org.onosproject.net.AnnotationKeys.LONGITUDE;
import static org.onosproject.ui.model.topo.UiNode.LAYER_DEFAULT;
/**
......@@ -88,6 +94,11 @@ class Topo2Jsonifier {
private TunnelService tunnelService;
// NOTE: we'll stick this here for now, but maybe there is a better home?
// (this is not distributed across the cluster)
private static Map<String, ObjectNode> metaUi = new ConcurrentHashMap<>();
/**
* Creates an instance with a reference to the services directory, so that
* additional information about network elements may be looked up on
......@@ -263,17 +274,73 @@ class Topo2Jsonifier {
.put("master", nullIsEmpty(device.master()))
.put("layer", device.layer());
// TODO: complete device details
// addLabels(node, device);
// addProps(node, device);
// addGeoLocation(node, device);
// addMetaUi(node, device);
Device d = device.backingDevice();
addProps(node, d);
addGeoLocation(node, d);
addMetaUi(node, device.idAsString());
return node;
}
private void addLabels(ObjectNode node, UiDevice device) {
private void addProps(ObjectNode node, Device dev) {
Annotations annot = dev.annotations();
ObjectNode props = objectNode();
if (annot != null) {
annot.keys().forEach(k -> props.put(k, annot.value(k)));
}
node.set("props", props);
}
private void addMetaUi(ObjectNode node, String metaInstanceId) {
ObjectNode meta = metaUi.get(metaInstanceId);
if (meta != null) {
node.set("metaUi", meta);
}
}
private void addGeoLocation(ObjectNode node, Annotated a) {
List<String> lngLat = getAnnotValues(a, LONGITUDE, LATITUDE);
if (lngLat != null) {
try {
double lng = Double.parseDouble(lngLat.get(0));
double lat = Double.parseDouble(lngLat.get(1));
ObjectNode loc = objectNode()
.put("type", "lnglat")
.put("lng", lng)
.put("lat", lat);
node.set("location", loc);
} catch (NumberFormatException e) {
log.warn("Invalid geo data: longitude={}, latitude={}",
lngLat.get(0), lngLat.get(1));
}
} else {
log.debug("No geo lng/lat for {}", a);
}
}
// return list of string values from annotated instance, for given keys
// return null if any keys are not present
List<String> getAnnotValues(Annotated a, String... annotKeys) {
List<String> result = new ArrayList<>(annotKeys.length);
for (String k : annotKeys) {
String v = a.annotations().value(k);
if (v == null) {
return null;
}
result.add(v);
}
return result;
}
// derive JSON object from annotations
private ObjectNode props(Annotations annotations) {
ObjectNode p = objectNode();
if (annotations != null) {
annotations.keys().forEach(k -> p.put(k, annotations.value(k)));
}
return p;
}
private ObjectNode json(UiHost host) {
......
......@@ -19,6 +19,8 @@ package org.onosproject.ui.impl.topo;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import org.junit.Test;
import org.onosproject.net.Annotated;
import org.onosproject.net.Annotations;
import org.onosproject.ui.impl.AbstractUiImplTest;
import org.onosproject.ui.model.topo.UiNode;
......@@ -26,6 +28,7 @@ import java.util.List;
import java.util.Set;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.onosproject.ui.model.topo.UiNode.LAYER_DEFAULT;
import static org.onosproject.ui.model.topo.UiNode.LAYER_OPTICAL;
import static org.onosproject.ui.model.topo.UiNode.LAYER_PACKET;
......@@ -145,4 +148,59 @@ public class Topo2JsonifierTest extends AbstractUiImplTest {
assertEquals("missing node B", true, def.contains(NODE_B));
assertEquals("missing node E", true, def.contains(NODE_E));
}
private static final String K1 = "K1";
private static final String K2 = "K2";
private static final String K3 = "K3";
private static final String K4 = "K4";
private static final String V1 = "V1";
private static final String V2 = "V2";
private static final String V3 = "V3";
private static final Annotations ANNOTS = new Annotations() {
@Override
public Set<String> keys() {
return ImmutableSet.of(K1, K2, K3);
}
@Override
public String value(String key) {
switch (key) {
case K1:
return V1;
case K2:
return V2;
case K3:
return V3;
default:
return null;
}
}
};
private static final Annotated THING = () -> ANNOTS;
private void verifyValues(List<String> vals, String... exp) {
print(vals);
if (exp.length == 0) {
// don't expect any results
assertNull("huh?", vals);
} else {
assertEquals("wrong list len", exp.length, vals.size());
for (int i = 0; i < exp.length; i++) {
assertEquals("wrong value " + i, exp[i], vals.get(i));
}
}
}
@Test
public void annotValues() {
print("annotValues()");
verifyValues(t2.getAnnotValues(THING, K1), V1);
verifyValues(t2.getAnnotValues(THING, K3, K1), V3, V1);
verifyValues(t2.getAnnotValues(THING, K1, K2, K3), V1, V2, V3);
verifyValues(t2.getAnnotValues(THING, K1, K4));
}
}
......