Add support to decode Device, Port, Link JSON.
- Device, Port, Link can now be encoded and decoded back to Java Object,
which will be Object#equals to the original.
- Modified DeviceServiceAdapter to be null-safe when possible
- Modified JSON assertion/matcher not to check for exact number of attributes
Change-Id: I7cf02e2254cf17f6265fb15847912519e564b14f
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/AnnotatedCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/AnnotatedCodec.java
index a3d0663..6883fe0 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/AnnotatedCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/AnnotatedCodec.java
@@ -16,10 +16,12 @@
package org.onosproject.codec.impl;
import com.fasterxml.jackson.databind.node.ObjectNode;
+
import org.onosproject.codec.CodecContext;
import org.onosproject.codec.JsonCodec;
import org.onosproject.net.Annotated;
import org.onosproject.net.Annotations;
+import org.onosproject.net.DefaultAnnotations;
/**
* Base JSON codec for annotated entities.
@@ -42,4 +44,21 @@
return node;
}
+ /**
+ * Extracts annotations of given Object.
+ *
+ * @param objNode annotated JSON object node
+ * @param context decode context
+ * @return extracted Annotations
+ */
+ protected Annotations extractAnnotations(ObjectNode objNode, CodecContext context) {
+
+ JsonCodec<Annotations> codec = context.codec(Annotations.class);
+ if (objNode.has("annotations") && objNode.isObject()) {
+ return codec.decode((ObjectNode) objNode.get("annotations"), context);
+ } else {
+ return DefaultAnnotations.EMPTY;
+ }
+ }
+
}
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/AnnotationsCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/AnnotationsCodec.java
index 5516f23..d71446d 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/AnnotationsCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/AnnotationsCodec.java
@@ -16,9 +16,12 @@
package org.onosproject.codec.impl;
import com.fasterxml.jackson.databind.node.ObjectNode;
+
import org.onosproject.codec.CodecContext;
import org.onosproject.codec.JsonCodec;
import org.onosproject.net.Annotations;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DefaultAnnotations.Builder;
/**
* Annotations JSON codec.
@@ -34,4 +37,13 @@
return result;
}
+ @Override
+ public Annotations decode(ObjectNode json, CodecContext context) {
+ Builder builder = DefaultAnnotations.builder();
+
+ json.fields().forEachRemaining(e ->
+ builder.set(e.getKey(), e.getValue().asText()));
+
+ return builder.build();
+ }
}
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/ConnectPointCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/ConnectPointCodec.java
index 7d12308..dd733cd 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/ConnectPointCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/ConnectPointCodec.java
@@ -16,32 +16,59 @@
package org.onosproject.codec.impl;
import com.fasterxml.jackson.databind.node.ObjectNode;
+
import org.onosproject.codec.CodecContext;
import org.onosproject.codec.JsonCodec;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DeviceId;
+import org.onosproject.net.ElementId;
import org.onosproject.net.HostId;
+import org.onosproject.net.PortNumber;
import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.PortNumber.portNumber;
/**
* Connection point JSON codec.
*/
public final class ConnectPointCodec extends JsonCodec<ConnectPoint> {
+ // JSON field names
+ private static final String ELEMENT_HOST = "host";
+ private static final String ELEMENT_DEVICE = "device";
+ private static final String PORT = "port";
+
@Override
public ObjectNode encode(ConnectPoint point, CodecContext context) {
checkNotNull(point, "Connect point cannot be null");
ObjectNode root = context.mapper().createObjectNode()
- .put("port", point.port().toString());
+ .put(PORT, point.port().toString());
if (point.elementId() instanceof DeviceId) {
- root.put("device", point.deviceId().toString());
+ root.put(ELEMENT_DEVICE, point.deviceId().toString());
} else if (point.elementId() instanceof HostId) {
- root.put("host", point.hostId().toString());
+ root.put(ELEMENT_HOST, point.hostId().toString());
}
return root;
}
+ @Override
+ public ConnectPoint decode(ObjectNode json, CodecContext context) {
+ if (json == null || !json.isObject()) {
+ return null;
+ }
+
+ ElementId elementId;
+ if (json.has(ELEMENT_DEVICE)) {
+ elementId = DeviceId.deviceId(json.get(ELEMENT_DEVICE).asText());
+ } else if (json.has(ELEMENT_HOST)) {
+ elementId = HostId.hostId(json.get(ELEMENT_HOST).asText());
+ } else {
+ // invalid JSON
+ return null;
+ }
+ PortNumber portNumber = portNumber(json.get(PORT).asText());
+ return new ConnectPoint(elementId, portNumber);
+ }
}
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/DeviceCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/DeviceCodec.java
index fcb720c..b032329 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/DeviceCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/DeviceCodec.java
@@ -16,32 +16,78 @@
package org.onosproject.codec.impl;
import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import org.onlab.packet.ChassisId;
import org.onosproject.codec.CodecContext;
+import org.onosproject.net.Annotations;
+import org.onosproject.net.DefaultDevice;
import org.onosproject.net.Device;
+import org.onosproject.net.Device.Type;
+import org.onosproject.net.DeviceId;
import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.provider.ProviderId;
import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.DeviceId.deviceId;
/**
* Device JSON codec.
*/
public final class DeviceCodec extends AnnotatedCodec<Device> {
+ // JSON fieldNames
+ private static final String ID = "id";
+ private static final String TYPE = "type";
+ private static final String MFR = "mfr";
+ private static final String HW = "hw";
+ private static final String SW = "sw";
+ private static final String SERIAL = "serial";
+ private static final String CHASSIS_ID = "chassisId";
+
+
@Override
public ObjectNode encode(Device device, CodecContext context) {
checkNotNull(device, "Device cannot be null");
DeviceService service = context.get(DeviceService.class);
ObjectNode result = context.mapper().createObjectNode()
- .put("id", device.id().toString())
- .put("type", device.type().name())
+ .put(ID, device.id().toString())
+ .put(TYPE, device.type().name())
.put("available", service.isAvailable(device.id()))
.put("role", service.getRole(device.id()).toString())
- .put("mfr", device.manufacturer())
- .put("hw", device.hwVersion())
- .put("sw", device.swVersion())
- .put("serial", device.serialNumber())
- .put("chassisId", device.chassisId().toString());
+ .put(MFR, device.manufacturer())
+ .put(HW, device.hwVersion())
+ .put(SW, device.swVersion())
+ .put(SERIAL, device.serialNumber())
+ .put(CHASSIS_ID, device.chassisId().toString());
return annotate(result, device, context);
}
+
+ /**
+ * {@inheritDoc}
+ *
+ * Note: ProviderId is not part of JSON representation.
+ * Returned object will have random ProviderId set.
+ */
+ @Override
+ public Device decode(ObjectNode json, CodecContext context) {
+ if (json == null || !json.isObject()) {
+ return null;
+ }
+
+ DeviceId id = deviceId(json.get(ID).asText());
+ // TODO: add providerId to JSON if we need to recover them.
+ ProviderId pid = new ProviderId(id.uri().getScheme(), "DeviceCodec");
+
+ Type type = Type.valueOf(json.get(TYPE).asText());
+ String mfr = json.get(MFR).asText();
+ String hw = json.get(HW).asText();
+ String sw = json.get(SW).asText();
+ String serial = json.get(SERIAL).asText();
+ ChassisId chassisId = new ChassisId(json.get(CHASSIS_ID).asText());
+ Annotations annotations = extractAnnotations(json, context);
+
+ return new DefaultDevice(pid, id, type, mfr, hw, sw, serial,
+ chassisId, annotations);
+ }
}
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/LinkCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/LinkCodec.java
index 84018b8..d63b4e3 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/LinkCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/LinkCodec.java
@@ -16,11 +16,15 @@
package org.onosproject.codec.impl;
import com.fasterxml.jackson.databind.node.ObjectNode;
+
import org.onosproject.codec.CodecContext;
import org.onosproject.codec.JsonCodec;
+import org.onosproject.net.Annotations;
import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultLink;
import org.onosproject.net.Link;
-import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.Link.Type;
+import org.onosproject.net.provider.ProviderId;
import static com.google.common.base.Preconditions.checkNotNull;
@@ -29,15 +33,44 @@
*/
public final class LinkCodec extends AnnotatedCodec<Link> {
+ // JSON field names
+ private static final String SRC = "src";
+ private static final String DST = "dst";
+ private static final String TYPE = "type";
+
@Override
public ObjectNode encode(Link link, CodecContext context) {
checkNotNull(link, "Link cannot be null");
- DeviceService service = context.get(DeviceService.class);
JsonCodec<ConnectPoint> codec = context.codec(ConnectPoint.class);
ObjectNode result = context.mapper().createObjectNode();
- result.set("src", codec.encode(link.src(), context));
- result.set("dst", codec.encode(link.dst(), context));
+ result.set(SRC, codec.encode(link.src(), context));
+ result.set(DST, codec.encode(link.dst(), context));
+ result.put(TYPE, link.type().toString());
return annotate(result, link, context);
}
+
+ /**
+ * {@inheritDoc}
+ *
+ * Note: ProviderId is not part of JSON representation.
+ * Returned object will have random ProviderId set.
+ */
+ @Override
+ public Link decode(ObjectNode json, CodecContext context) {
+ if (json == null || !json.isObject()) {
+ return null;
+ }
+
+ JsonCodec<ConnectPoint> codec = context.codec(ConnectPoint.class);
+ // TODO: add providerId to JSON if we need to recover them.
+ ProviderId pid = new ProviderId("json", "LinkCodec");
+
+ ConnectPoint src = codec.decode((ObjectNode) json.get(SRC), context);
+ ConnectPoint dst = codec.decode((ObjectNode) json.get(DST), context);
+ Type type = Type.valueOf(json.get(TYPE).asText());
+ Annotations annotations = extractAnnotations(json, context);
+
+ return new DefaultLink(pid, src, dst, type, annotations);
+ }
}
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/PortCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/PortCodec.java
index fb15a45..59e46d1 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/PortCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/PortCodec.java
@@ -16,9 +16,18 @@
package org.onosproject.codec.impl;
import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import org.onlab.packet.ChassisId;
import org.onosproject.codec.CodecContext;
+import org.onosproject.net.Annotations;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DefaultPort;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
import org.onosproject.net.Port;
+import org.onosproject.net.Port.Type;
import org.onosproject.net.PortNumber;
+import org.onosproject.net.provider.ProviderId;
import static com.google.common.base.Preconditions.checkNotNull;
@@ -27,19 +36,125 @@
*/
public final class PortCodec extends AnnotatedCodec<Port> {
+ // JSON field names
+ private static final String ELEMENT = "element"; // DeviceId
+ private static final String PORT_NAME = "port";
+ private static final String IS_ENABLED = "isEnabled";
+ private static final String TYPE = "type";
+ private static final String PORT_SPEED = "portSpeed";
+
+ // Special port name alias
+ private static final String PORT_NAME_LOCAL = "local";
+
@Override
public ObjectNode encode(Port port, CodecContext context) {
checkNotNull(port, "Port cannot be null");
ObjectNode result = context.mapper().createObjectNode()
- .put("port", portName(port.number()))
- .put("isEnabled", port.isEnabled())
- .put("type", port.type().toString().toLowerCase())
- .put("portSpeed", port.portSpeed());
+ .put(ELEMENT, port.element().id().toString())
+ .put(PORT_NAME, portName(port.number()))
+ .put(IS_ENABLED, port.isEnabled())
+ .put(TYPE, port.type().toString().toLowerCase())
+ .put(PORT_SPEED, port.portSpeed());
return annotate(result, port, context);
}
private String portName(PortNumber port) {
- return port.equals(PortNumber.LOCAL) ? "local" : port.toString();
+ return port.equals(PortNumber.LOCAL) ? PORT_NAME_LOCAL : port.toString();
}
+ private static PortNumber portNumber(String portName) {
+ if (portName.equalsIgnoreCase(PORT_NAME_LOCAL)) {
+ return PortNumber.LOCAL;
+ }
+
+ return PortNumber.portNumber(portName);
+ }
+
+
+ /**
+ * {@inheritDoc}
+ *
+ * Note: Result of {@link Port#element()} returned Port object,
+ * is not a full Device object.
+ * Only it's DeviceId can be used.
+ */
+ @Override
+ public Port decode(ObjectNode json, CodecContext context) {
+ if (json == null || !json.isObject()) {
+ return null;
+ }
+
+ DeviceId did = DeviceId.deviceId(json.get(ELEMENT).asText());
+ Device device = new DummyDevice(did);
+ PortNumber number = portNumber(json.get(PORT_NAME).asText());
+ boolean isEnabled = json.get(IS_ENABLED).asBoolean();
+ Type type = Type.valueOf(json.get(TYPE).asText().toUpperCase());
+ long portSpeed = json.get(PORT_SPEED).asLong();
+ Annotations annotations = extractAnnotations(json, context);
+
+ return new DefaultPort(device, number, isEnabled, type, portSpeed, annotations);
+ }
+
+
+ /**
+ * Dummy Device which only holds DeviceId.
+ */
+ private static final class DummyDevice implements Device {
+
+ private final DeviceId did;
+
+ /**
+ * Constructs Dummy Device which only holds DeviceId.
+ *
+ * @param did device Id
+ */
+ public DummyDevice(DeviceId did) {
+ this.did = did;
+ }
+
+ @Override
+ public Annotations annotations() {
+ return DefaultAnnotations.EMPTY;
+ }
+
+ @Override
+ public ProviderId providerId() {
+ return new ProviderId(did.uri().getScheme(), "PortCodec");
+ }
+
+ @Override
+ public DeviceId id() {
+ return did;
+ }
+
+ @Override
+ public Type type() {
+ return Type.SWITCH;
+ }
+
+ @Override
+ public String manufacturer() {
+ return "dummy";
+ }
+
+ @Override
+ public String hwVersion() {
+ return "0";
+ }
+
+ @Override
+ public String swVersion() {
+ return "0";
+ }
+
+ @Override
+ public String serialNumber() {
+ return "0";
+ }
+
+ @Override
+ public ChassisId chassisId() {
+ return new ChassisId();
+ }
+ }
}