Ray Milkey
Committed by Gerrit Code Review

Implementation of REST POST API for creating intents

- codec for constraint decode
- codec for intent decode
- POST method for intents
- unit tests for codecs and POST method

Change-Id: Ibc0ef8f99a0c0664710a733985424c77010c49b5
Showing 30 changed files with 1166 additions and 201 deletions
......@@ -87,4 +87,29 @@ public abstract class JsonCodec<T> {
return result;
}
/**
* Gets a child Object Node from a parent by name. If the child is not found
* or does nor represent an object, null is returned.
*
* @param parent parent object
* @param childName name of child to query
* @return child object if found, null if not found or if not an object
*/
protected static ObjectNode get(ObjectNode parent, String childName) {
JsonNode node = parent.path(childName);
return node.isObject() && !node.isNull() ? (ObjectNode) node : null;
}
/**
* Gets a child Object Node from a parent by index. If the child is not found
* or does nor represent an object, null is returned.
*
* @param parent parent object
* @param childIndex index of child to query
* @return child object if found, null if not found or if not an object
*/
protected static ObjectNode get(JsonNode parent, int childIndex) {
JsonNode node = parent.path(childIndex);
return node.isObject() && !node.isNull() ? (ObjectNode) node : null;
}
}
......
......@@ -55,7 +55,7 @@ public abstract class AnnotatedCodec<T extends Annotated> extends JsonCodec<T> {
JsonCodec<Annotations> codec = context.codec(Annotations.class);
if (objNode.has("annotations") && objNode.isObject()) {
return codec.decode((ObjectNode) objNode.get("annotations"), context);
return codec.decode(get(objNode, "annotations"), context);
} else {
return DefaultAnnotations.EMPTY;
}
......
......@@ -15,6 +15,9 @@
*/
package org.onosproject.codec.impl;
import java.util.ArrayList;
import java.util.stream.IntStream;
import org.onosproject.codec.CodecContext;
import org.onosproject.codec.JsonCodec;
import org.onosproject.net.flow.TrafficSelector;
......@@ -23,6 +26,7 @@ import org.onosproject.net.intent.ConnectivityIntent;
import org.onosproject.net.intent.Constraint;
import org.onosproject.net.intent.Intent;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
......@@ -33,6 +37,10 @@ import static com.google.common.base.Preconditions.checkNotNull;
*/
public final class ConnectivityIntentCodec extends JsonCodec<ConnectivityIntent> {
private static final String CONSTRAINTS = "constraints";
private static final String SELECTOR = "selector";
private static final String TREATMENT = "treatment";
@Override
public ObjectNode encode(ConnectivityIntent intent, CodecContext context) {
checkNotNull(intent, "Connectivity intent cannot be null");
......@@ -43,19 +51,19 @@ public final class ConnectivityIntentCodec extends JsonCodec<ConnectivityIntent>
if (intent.selector() != null) {
final JsonCodec<TrafficSelector> selectorCodec =
context.codec(TrafficSelector.class);
result.set("selector", selectorCodec.encode(intent.selector(), context));
result.set(SELECTOR, selectorCodec.encode(intent.selector(), context));
}
if (intent.treatment() != null) {
final JsonCodec<TrafficTreatment> treatmentCodec =
context.codec(TrafficTreatment.class);
result.set("treatment", treatmentCodec.encode(intent.treatment(), context));
result.set(TREATMENT, treatmentCodec.encode(intent.treatment(), context));
}
result.put("priority", intent.priority());
result.put(IntentCodec.PRIORITY, intent.priority());
if (intent.constraints() != null) {
final ArrayNode jsonConstraints = result.putArray("constraints");
final ArrayNode jsonConstraints = result.putArray(CONSTRAINTS);
if (intent.constraints() != null) {
final JsonCodec<Constraint> constraintCodec =
......@@ -70,4 +78,41 @@ public final class ConnectivityIntentCodec extends JsonCodec<ConnectivityIntent>
return result;
}
/**
* Extracts connectivity intent specific attributes from a JSON object
* and adds them to a builder.
*
* @param json root JSON object
* @param context code context
* @param builder builder to use for storing the attributes. Constraints,
* selector and treatment are modified by this call.
*/
public static void intentAttributes(ObjectNode json, CodecContext context,
ConnectivityIntent.Builder builder) {
JsonNode constraintsJson = json.get(CONSTRAINTS);
if (constraintsJson != null) {
JsonCodec<Constraint> constraintsCodec = context.codec(Constraint.class);
ArrayList<Constraint> constraints = new ArrayList<>(constraintsJson.size());
IntStream.range(0, constraintsJson.size())
.forEach(i -> constraints.add(
constraintsCodec.decode(get(constraintsJson, i),
context)));
builder.constraints(constraints);
}
ObjectNode selectorJson = get(json, SELECTOR);
if (selectorJson != null) {
JsonCodec<TrafficSelector> selectorCodec = context.codec(TrafficSelector.class);
TrafficSelector selector = selectorCodec.decode(selectorJson, context);
builder.selector(selector);
}
ObjectNode treatmentJson = get(json, TREATMENT);
if (treatmentJson != null) {
JsonCodec<TrafficTreatment> treatmentCodec = context.codec(TrafficTreatment.class);
TrafficTreatment treatment = treatmentCodec.decode(treatmentJson, context);
builder.treatment(treatment);
}
}
}
......
......@@ -17,18 +17,8 @@ package org.onosproject.codec.impl;
import org.onosproject.codec.CodecContext;
import org.onosproject.codec.JsonCodec;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Link;
import org.onosproject.net.intent.Constraint;
import org.onosproject.net.intent.constraint.AnnotationConstraint;
import org.onosproject.net.intent.constraint.BandwidthConstraint;
import org.onosproject.net.intent.constraint.LambdaConstraint;
import org.onosproject.net.intent.constraint.LatencyConstraint;
import org.onosproject.net.intent.constraint.LinkTypeConstraint;
import org.onosproject.net.intent.constraint.ObstacleConstraint;
import org.onosproject.net.intent.constraint.WaypointConstraint;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import static com.google.common.base.Preconditions.checkNotNull;
......@@ -38,170 +28,36 @@ import static com.google.common.base.Preconditions.checkNotNull;
*/
public final class ConstraintCodec extends JsonCodec<Constraint> {
/**
* Encodes a latency constraint.
*
* @param constraint latency constraint to encode
* @param context code context
* @return JSON ObjectNode representing the constraint
*/
private ObjectNode encodeLatencyConstraint(Constraint constraint,
CodecContext context) {
checkNotNull(constraint, "Duration constraint cannot be null");
final LatencyConstraint latencyConstraint =
(LatencyConstraint) constraint;
return context.mapper().createObjectNode()
.put("latencyMillis", latencyConstraint.latency().toMillis());
}
/**
* Encodes an obstacle constraint.
*
* @param constraint obstacle constraint to encode
* @param context code context
* @return JSON ObjectNode representing the constraint
*/
private ObjectNode encodeObstacleConstraint(Constraint constraint,
CodecContext context) {
checkNotNull(constraint, "Obstacle constraint cannot be null");
final ObstacleConstraint obstacleConstraint =
(ObstacleConstraint) constraint;
final ObjectNode result = context.mapper().createObjectNode();
final ArrayNode jsonObstacles = result.putArray("obstacles");
for (DeviceId did : obstacleConstraint.obstacles()) {
jsonObstacles.add(did.toString());
}
return result;
}
/**
* Encodes a waypoint constraint.
*
* @param constraint waypoint constraint to encode
* @param context code context
* @return JSON ObjectNode representing the constraint
*/
private ObjectNode encodeWaypointConstraint(Constraint constraint,
CodecContext context) {
checkNotNull(constraint, "Waypoint constraint cannot be null");
final WaypointConstraint waypointConstraint =
(WaypointConstraint) constraint;
final ObjectNode result = context.mapper().createObjectNode();
final ArrayNode jsonWaypoints = result.putArray("waypoints");
for (DeviceId did : waypointConstraint.waypoints()) {
jsonWaypoints.add(did.toString());
}
return result;
}
/**
* Encodes a annotation constraint.
*
* @param constraint annotation constraint to encode
* @param context code context
* @return JSON ObjectNode representing the constraint
*/
private ObjectNode encodeAnnotationConstraint(Constraint constraint,
CodecContext context) {
checkNotNull(constraint, "Annotation constraint cannot be null");
final AnnotationConstraint annotationConstraint =
(AnnotationConstraint) constraint;
return context.mapper().createObjectNode()
.put("key", annotationConstraint.key())
.put("threshold", annotationConstraint.threshold());
}
protected static final String MISSING_MEMBER_MESSAGE =
" member is required in Constraint";
protected static final String TYPE = "type";
protected static final String TYPES = "types";
protected static final String INCLUSIVE = "inclusive";
protected static final String KEY = "key";
protected static final String THRESHOLD = "threshold";
protected static final String BANDWIDTH = "bandwidth";
protected static final String LAMBDA = "lambda";
protected static final String LATENCY_MILLIS = "latencyMillis";
protected static final String OBSTACLES = "obstacles";
protected static final String WAYPOINTS = "waypoints";
/**
* Encodes a bandwidth constraint.
*
* @param constraint bandwidth constraint to encode
* @param context code context
* @return JSON ObjectNode representing the constraint
*/
private ObjectNode encodeBandwidthConstraint(Constraint constraint,
CodecContext context) {
checkNotNull(constraint, "Bandwidth constraint cannot be null");
final BandwidthConstraint bandwidthConstraint =
(BandwidthConstraint) constraint;
return context.mapper().createObjectNode()
.put("bandwidth", bandwidthConstraint.bandwidth().toDouble());
}
/**
* Encodes a lambda constraint.
*
* @param constraint lambda constraint to encode
* @param context code context
* @return JSON ObjectNode representing the constraint
*/
private ObjectNode encodeLambdaConstraint(Constraint constraint,
CodecContext context) {
checkNotNull(constraint, "Lambda constraint cannot be null");
final LambdaConstraint lambdaConstraint =
(LambdaConstraint) constraint;
return context.mapper().createObjectNode()
.put("lambda", lambdaConstraint.lambda().toInt());
}
/**
* Encodes a link type constraint.
*
* @param constraint link type constraint to encode
* @param context code context
* @return JSON ObjectNode representing the constraint
*/
private ObjectNode encodeLinkTypeConstraint(Constraint constraint,
CodecContext context) {
checkNotNull(constraint, "Link type constraint cannot be null");
final LinkTypeConstraint linkTypeConstraint =
(LinkTypeConstraint) constraint;
final ObjectNode result = context.mapper().createObjectNode()
.put("inclusive", linkTypeConstraint.isInclusive());
final ArrayNode jsonTypes = result.putArray("types");
@Override
public ObjectNode encode(Constraint constraint, CodecContext context) {
checkNotNull(constraint, "Constraint cannot be null");
if (linkTypeConstraint.types() != null) {
for (Link.Type type : linkTypeConstraint.types()) {
jsonTypes.add(type.name());
}
}
final EncodeConstraintCodec encodeCodec =
new EncodeConstraintCodec(constraint, context);
return result;
return encodeCodec.encode();
}
@Override
public ObjectNode encode(Constraint constraint, CodecContext context) {
checkNotNull(constraint, "Constraint cannot be null");
public Constraint decode(ObjectNode json, CodecContext context) {
checkNotNull(json, "JSON cannot be null");
final ObjectNode result;
if (constraint instanceof BandwidthConstraint) {
result = encodeBandwidthConstraint(constraint, context);
} else if (constraint instanceof LambdaConstraint) {
result = encodeLambdaConstraint(constraint, context);
} else if (constraint instanceof LinkTypeConstraint) {
result = encodeLinkTypeConstraint(constraint, context);
} else if (constraint instanceof AnnotationConstraint) {
result = encodeAnnotationConstraint(constraint, context);
} else if (constraint instanceof LatencyConstraint) {
result = encodeLatencyConstraint(constraint, context);
} else if (constraint instanceof ObstacleConstraint) {
result = encodeObstacleConstraint(constraint, context);
} else if (constraint instanceof WaypointConstraint) {
result = encodeWaypointConstraint(constraint, context);
} else {
result = context.mapper().createObjectNode();
}
final DecodeConstraintCodec decodeCodec =
new DecodeConstraintCodec(json);
result.put("type", constraint.getClass().getSimpleName());
return result;
return decodeCodec.decode();
}
}
......
/*
* Copyright 2015 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.codec.impl;
import java.time.Duration;
import java.util.ArrayList;
import java.util.stream.IntStream;
import org.onlab.util.Bandwidth;
import org.onosproject.net.DeviceId;
import org.onosproject.net.IndexedLambda;
import org.onosproject.net.Link;
import org.onosproject.net.intent.Constraint;
import org.onosproject.net.intent.constraint.AnnotationConstraint;
import org.onosproject.net.intent.constraint.AsymmetricPathConstraint;
import org.onosproject.net.intent.constraint.BandwidthConstraint;
import org.onosproject.net.intent.constraint.LambdaConstraint;
import org.onosproject.net.intent.constraint.LatencyConstraint;
import org.onosproject.net.intent.constraint.LinkTypeConstraint;
import org.onosproject.net.intent.constraint.ObstacleConstraint;
import org.onosproject.net.intent.constraint.WaypointConstraint;
import org.onosproject.net.resource.link.BandwidthResource;
import org.onosproject.net.resource.link.LambdaResource;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import static org.onlab.util.Tools.nullIsIllegal;
/**
* Constraint JSON decoder.
*/
public final class DecodeConstraintCodec {
private final ObjectNode json;
/**
* Constructs a constraint decoder.
*
* @param json object node to decode
*/
public DecodeConstraintCodec(ObjectNode json) {
this.json = json;
}
/**
* Decodes a link type constraint.
*
* @return link type constraint object.
*/
private Constraint decodeLinkTypeConstraint() {
boolean inclusive = nullIsIllegal(json.get(ConstraintCodec.INCLUSIVE),
ConstraintCodec.INCLUSIVE + ConstraintCodec.MISSING_MEMBER_MESSAGE).asBoolean();
JsonNode types = nullIsIllegal(json.get(ConstraintCodec.TYPES),
ConstraintCodec.TYPES + ConstraintCodec.MISSING_MEMBER_MESSAGE);
if (types.size() < 1) {
throw new IllegalArgumentException(
"types array in link constraint must have at least one value");
}
ArrayList<Link.Type> typesEntries = new ArrayList<>(types.size());
IntStream.range(0, types.size())
.forEach(index ->
typesEntries.add(Link.Type.valueOf(types.get(index).asText())));
return new LinkTypeConstraint(inclusive,
typesEntries.toArray(new Link.Type[types.size()]));
}
/**
* Decodes an annotation constraint.
*
* @return annotation constraint object.
*/
private Constraint decodeAnnotationConstraint() {
String key = nullIsIllegal(json.get(ConstraintCodec.KEY),
ConstraintCodec.KEY + ConstraintCodec.MISSING_MEMBER_MESSAGE)
.asText();
double threshold = nullIsIllegal(json.get(ConstraintCodec.THRESHOLD),
ConstraintCodec.THRESHOLD + ConstraintCodec.MISSING_MEMBER_MESSAGE)
.asDouble();
return new AnnotationConstraint(key, threshold);
}
/**
* Decodes a lambda constraint.
*
* @return lambda constraint object.
*/
private Constraint decodeLambdaConstraint() {
long lambda = nullIsIllegal(json.get(ConstraintCodec.LAMBDA),
ConstraintCodec.LAMBDA + ConstraintCodec.MISSING_MEMBER_MESSAGE)
.asLong();
return new LambdaConstraint(LambdaResource.valueOf(new IndexedLambda(lambda)));
}
/**
* Decodes a latency constraint.
*
* @return latency constraint object.
*/
private Constraint decodeLatencyConstraint() {
long latencyMillis = nullIsIllegal(json.get(ConstraintCodec.LATENCY_MILLIS),
ConstraintCodec.LATENCY_MILLIS + ConstraintCodec.MISSING_MEMBER_MESSAGE)
.asLong();
return new LatencyConstraint(Duration.ofMillis(latencyMillis));
}
/**
* Decodes an obstacle constraint.
*
* @return obstacle constraint object.
*/
private Constraint decodeObstacleConstraint() {
JsonNode obstacles = nullIsIllegal(json.get(ConstraintCodec.OBSTACLES),
ConstraintCodec.OBSTACLES + ConstraintCodec.MISSING_MEMBER_MESSAGE);
if (obstacles.size() < 1) {
throw new IllegalArgumentException(
"obstacles array in obstacles constraint must have at least one value");
}
ArrayList<DeviceId> obstacleEntries = new ArrayList<>(obstacles.size());
IntStream.range(0, obstacles.size())
.forEach(index ->
obstacleEntries.add(DeviceId.deviceId(obstacles.get(index).asText())));
return new ObstacleConstraint(
obstacleEntries.toArray(new DeviceId[obstacles.size()]));
}
/**
* Decodes a waypoint constraint.
*
* @return waypoint constraint object.
*/
private Constraint decodeWaypointConstraint() {
JsonNode waypoints = nullIsIllegal(json.get(ConstraintCodec.WAYPOINTS),
ConstraintCodec.WAYPOINTS + ConstraintCodec.MISSING_MEMBER_MESSAGE);
if (waypoints.size() < 1) {
throw new IllegalArgumentException(
"obstacles array in obstacles constraint must have at least one value");
}
ArrayList<DeviceId> waypointEntries = new ArrayList<>(waypoints.size());
IntStream.range(0, waypoints.size())
.forEach(index ->
waypointEntries.add(DeviceId.deviceId(waypoints.get(index).asText())));
return new WaypointConstraint(
waypointEntries.toArray(new DeviceId[waypoints.size()]));
}
/**
* Decodes an asymmetric path constraint.
*
* @return asymmetric path constraint object.
*/
private Constraint decodeAsymmetricPathConstraint() {
return new AsymmetricPathConstraint();
}
/**
* Decodes a bandwidth constraint.
*
* @return bandwidth constraint object.
*/
private Constraint decodeBandwidthConstraint() {
double bandwidth = nullIsIllegal(json.get(ConstraintCodec.BANDWIDTH),
ConstraintCodec.BANDWIDTH + ConstraintCodec.MISSING_MEMBER_MESSAGE)
.asDouble();
return new BandwidthConstraint(new BandwidthResource(Bandwidth.bps(bandwidth)));
}
/**
* Decodes the given constraint.
*
* @return constraint object.
*/
public Constraint decode() {
final String type = nullIsIllegal(json.get(ConstraintCodec.TYPE),
ConstraintCodec.TYPE + ConstraintCodec.MISSING_MEMBER_MESSAGE)
.asText();
if (type.equals(BandwidthConstraint.class.getSimpleName())) {
return decodeBandwidthConstraint();
} else if (type.equals(LambdaConstraint.class.getSimpleName())) {
return decodeLambdaConstraint();
} else if (type.equals(LinkTypeConstraint.class.getSimpleName())) {
return decodeLinkTypeConstraint();
} else if (type.equals(AnnotationConstraint.class.getSimpleName())) {
return decodeAnnotationConstraint();
} else if (type.equals(LatencyConstraint.class.getSimpleName())) {
return decodeLatencyConstraint();
} else if (type.equals(ObstacleConstraint.class.getSimpleName())) {
return decodeObstacleConstraint();
} else if (type.equals(WaypointConstraint.class.getSimpleName())) {
return decodeWaypointConstraint();
} else if (type.equals(AsymmetricPathConstraint.class.getSimpleName())) {
return decodeAsymmetricPathConstraint();
} else if (type.equals(LinkTypeConstraint.class.getSimpleName())) {
return decodeLinkTypeConstraint();
} else if (type.equals(AnnotationConstraint.class.getSimpleName())) {
return decodeAnnotationConstraint();
}
throw new IllegalArgumentException("Instruction type "
+ type + " is not supported");
}
}
/*
* Copyright 2015 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.codec.impl;
import org.onosproject.codec.CodecContext;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Link;
import org.onosproject.net.intent.Constraint;
import org.onosproject.net.intent.constraint.AnnotationConstraint;
import org.onosproject.net.intent.constraint.BandwidthConstraint;
import org.onosproject.net.intent.constraint.LambdaConstraint;
import org.onosproject.net.intent.constraint.LatencyConstraint;
import org.onosproject.net.intent.constraint.LinkTypeConstraint;
import org.onosproject.net.intent.constraint.ObstacleConstraint;
import org.onosproject.net.intent.constraint.WaypointConstraint;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Implementation of encoder for constraint JSON codec.
*/
public final class EncodeConstraintCodec {
private final Constraint constraint;
private final CodecContext context;
/**
* Constructs a constraint encoder.
*
* @param constraint constraint to encode
* @param context to use for look ups
*/
public EncodeConstraintCodec(Constraint constraint, CodecContext context) {
this.constraint = constraint;
this.context = context;
}
/**
* Encodes a latency constraint.
*
* @return JSON ObjectNode representing the constraint
*/
private ObjectNode encodeLatencyConstraint() {
checkNotNull(constraint, "Duration constraint cannot be null");
final LatencyConstraint latencyConstraint =
(LatencyConstraint) constraint;
return context.mapper().createObjectNode()
.put("latencyMillis", latencyConstraint.latency().toMillis());
}
/**
* Encodes an obstacle constraint.
*
* @return JSON ObjectNode representing the constraint
*/
private ObjectNode encodeObstacleConstraint() {
checkNotNull(constraint, "Obstacle constraint cannot be null");
final ObstacleConstraint obstacleConstraint =
(ObstacleConstraint) constraint;
final ObjectNode result = context.mapper().createObjectNode();
final ArrayNode jsonObstacles = result.putArray("obstacles");
for (DeviceId did : obstacleConstraint.obstacles()) {
jsonObstacles.add(did.toString());
}
return result;
}
/**
* Encodes a waypoint constraint.
*
* @return JSON ObjectNode representing the constraint
*/
private ObjectNode encodeWaypointConstraint() {
checkNotNull(constraint, "Waypoint constraint cannot be null");
final WaypointConstraint waypointConstraint =
(WaypointConstraint) constraint;
final ObjectNode result = context.mapper().createObjectNode();
final ArrayNode jsonWaypoints = result.putArray("waypoints");
for (DeviceId did : waypointConstraint.waypoints()) {
jsonWaypoints.add(did.toString());
}
return result;
}
/**
* Encodes a annotation constraint.
*
* @return JSON ObjectNode representing the constraint
*/
private ObjectNode encodeAnnotationConstraint() {
checkNotNull(constraint, "Annotation constraint cannot be null");
final AnnotationConstraint annotationConstraint =
(AnnotationConstraint) constraint;
return context.mapper().createObjectNode()
.put("key", annotationConstraint.key())
.put("threshold", annotationConstraint.threshold());
}
/**
* Encodes a bandwidth constraint.
*
* @return JSON ObjectNode representing the constraint
*/
private ObjectNode encodeBandwidthConstraint() {
checkNotNull(constraint, "Bandwidth constraint cannot be null");
final BandwidthConstraint bandwidthConstraint =
(BandwidthConstraint) constraint;
return context.mapper().createObjectNode()
.put("bandwidth", bandwidthConstraint.bandwidth().toDouble());
}
/**
* Encodes a lambda constraint.
*
* @return JSON ObjectNode representing the constraint
*/
private ObjectNode encodeLambdaConstraint() {
checkNotNull(constraint, "Lambda constraint cannot be null");
final LambdaConstraint lambdaConstraint =
(LambdaConstraint) constraint;
return context.mapper().createObjectNode()
.put("lambda", lambdaConstraint.lambda().toInt());
}
/**
* Encodes a link type constraint.
*
* @return JSON ObjectNode representing the constraint
*/
private ObjectNode encodeLinkTypeConstraint() {
checkNotNull(constraint, "Link type constraint cannot be null");
final LinkTypeConstraint linkTypeConstraint =
(LinkTypeConstraint) constraint;
final ObjectNode result = context.mapper().createObjectNode()
.put(ConstraintCodec.INCLUSIVE, linkTypeConstraint.isInclusive());
final ArrayNode jsonTypes = result.putArray(ConstraintCodec.TYPES);
if (linkTypeConstraint.types() != null) {
for (Link.Type type : linkTypeConstraint.types()) {
jsonTypes.add(type.name());
}
}
return result;
}
/**
* Encodes the constraint in JSON.
*
* @return JSON node
*/
public ObjectNode encode() {
final ObjectNode result;
if (constraint instanceof BandwidthConstraint) {
result = encodeBandwidthConstraint();
} else if (constraint instanceof LambdaConstraint) {
result = encodeLambdaConstraint();
} else if (constraint instanceof LinkTypeConstraint) {
result = encodeLinkTypeConstraint();
} else if (constraint instanceof AnnotationConstraint) {
result = encodeAnnotationConstraint();
} else if (constraint instanceof LatencyConstraint) {
result = encodeLatencyConstraint();
} else if (constraint instanceof ObstacleConstraint) {
result = encodeObstacleConstraint();
} else if (constraint instanceof WaypointConstraint) {
result = encodeWaypointConstraint();
} else {
result = context.mapper().createObjectNode();
}
result.put(ConstraintCodec.TYPE, constraint.getClass().getSimpleName());
return result;
}
}
......@@ -75,14 +75,14 @@ public final class FlowRuleCodec extends JsonCodec<FlowRule> {
DEVICE_ID + MISSING_MEMBER_MESSAGE).asText());
resultBuilder.forDevice(deviceId);
ObjectNode treatmentJson = (ObjectNode) json.get(TREATMENT);
ObjectNode treatmentJson = get(json, TREATMENT);
if (treatmentJson != null) {
JsonCodec<TrafficTreatment> treatmentCodec =
context.codec(TrafficTreatment.class);
resultBuilder.withTreatment(treatmentCodec.decode(treatmentJson, context));
}
ObjectNode selectorJson = (ObjectNode) json.get(SELECTOR);
ObjectNode selectorJson = get(json, SELECTOR);
if (selectorJson != null) {
JsonCodec<TrafficSelector> selectorCodec =
context.codec(TrafficSelector.class);
......
......@@ -17,18 +17,23 @@ package org.onosproject.codec.impl;
import org.onosproject.codec.CodecContext;
import org.onosproject.codec.JsonCodec;
import org.onosproject.net.HostId;
import org.onosproject.net.intent.ConnectivityIntent;
import org.onosproject.net.intent.HostToHostIntent;
import com.fasterxml.jackson.databind.node.ObjectNode;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.onlab.util.Tools.nullIsIllegal;
/**
* Host to host intent codec.
*/
public final class HostToHostIntentCodec extends JsonCodec<HostToHostIntent> {
private static final String ONE = "one";
private static final String TWO = "two";
@Override
public ObjectNode encode(HostToHostIntent intent, CodecContext context) {
checkNotNull(intent, "Host to host intent cannot be null");
......@@ -39,9 +44,27 @@ public final class HostToHostIntentCodec extends JsonCodec<HostToHostIntent> {
final String one = intent.one().toString();
final String two = intent.two().toString();
result.put("one", one);
result.put("two", two);
result.put(ONE, one);
result.put(TWO, two);
return result;
}
@Override
public HostToHostIntent decode(ObjectNode json, CodecContext context) {
HostToHostIntent.Builder builder = HostToHostIntent.builder();
IntentCodec.intentAttributes(json, context, builder);
ConnectivityIntentCodec.intentAttributes(json, context, builder);
String one = nullIsIllegal(json.get(ONE),
ONE + IntentCodec.MISSING_MEMBER_MESSAGE).asText();
builder.one(HostId.hostId(one));
String two = nullIsIllegal(json.get(TWO),
TWO + IntentCodec.MISSING_MEMBER_MESSAGE).asText();
builder.two(HostId.hostId(two));
return builder.build();
}
}
......
......@@ -17,31 +17,46 @@ package org.onosproject.codec.impl;
import org.onosproject.codec.CodecContext;
import org.onosproject.codec.JsonCodec;
import org.onosproject.core.CoreService;
import org.onosproject.net.NetworkResource;
import org.onosproject.net.intent.HostToHostIntent;
import org.onosproject.net.intent.Intent;
import org.onosproject.net.intent.IntentService;
import org.onosproject.net.intent.IntentState;
import org.onosproject.net.intent.PointToPointIntent;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.onlab.util.Tools.nullIsIllegal;
/**
* Intent JSON codec.
*/
public final class IntentCodec extends JsonCodec<Intent> {
protected static final String TYPE = "type";
protected static final String ID = "id";
protected static final String APP_ID = "appId";
protected static final String DETAILS = "details";
protected static final String STATE = "state";
protected static final String PRIORITY = "priority";
protected static final String RESOURCES = "resources";
protected static final String MISSING_MEMBER_MESSAGE =
" member is required in Intent";
@Override
public ObjectNode encode(Intent intent, CodecContext context) {
checkNotNull(intent, "Intent cannot be null");
final ObjectNode result = context.mapper().createObjectNode()
.put("type", intent.getClass().getSimpleName())
.put("id", intent.id().toString())
.put("appId", intent.appId().toString())
.put("details", intent.toString());
.put(TYPE, intent.getClass().getSimpleName())
.put(ID, intent.id().toString())
.put(APP_ID, intent.appId().toString())
.put(DETAILS, intent.toString());
final ArrayNode jsonResources = result.putArray("resources");
final ArrayNode jsonResources = result.putArray(RESOURCES);
for (final NetworkResource resource : intent.resources()) {
jsonResources.add(resource.toString());
......@@ -50,9 +65,47 @@ public final class IntentCodec extends JsonCodec<Intent> {
IntentService service = context.getService(IntentService.class);
IntentState state = service.getIntentState(intent.key());
if (state != null) {
result.put("state", state.toString());
result.put(STATE, state.toString());
}
return result;
}
@Override
public Intent decode(ObjectNode json, CodecContext context) {
checkNotNull(json, "JSON cannot be null");
String type = nullIsIllegal(json.get(TYPE),
TYPE + MISSING_MEMBER_MESSAGE).asText();
if (type.equals(PointToPointIntent.class.getSimpleName())) {
return context.codec(PointToPointIntent.class).decode(json, context);
} else if (type.equals(HostToHostIntent.class.getSimpleName())) {
return context.codec(HostToHostIntent.class).decode(json, context);
}
throw new IllegalArgumentException("Intent type "
+ type + " is not supported");
}
/**
* Extracts base intent specific attributes from a JSON object
* and adds them to a builder.
*
* @param json root JSON object
* @param context code context
* @param builder builder to use for storing the attributes
*/
public static void intentAttributes(ObjectNode json, CodecContext context,
Intent.Builder builder) {
short appId = (short) nullIsIllegal(json.get(IntentCodec.APP_ID),
IntentCodec.TYPE + IntentCodec.MISSING_MEMBER_MESSAGE).asInt();
CoreService service = context.getService(CoreService.class);
builder.appId(service.getAppId(appId));
JsonNode priorityJson = json.get(IntentCodec.PRIORITY);
if (priorityJson != null) {
builder.priority(priorityJson.asInt());
}
}
}
......
......@@ -70,8 +70,8 @@ public final class LinkCodec extends AnnotatedCodec<Link> {
// 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);
ConnectPoint src = codec.decode(get(json, SRC), context);
ConnectPoint dst = codec.decode(get(json, DST), context);
Type type = Type.valueOf(json.get(TYPE).asText());
Annotations annotations = extractAnnotations(json, context);
......
......@@ -24,12 +24,16 @@ import org.onosproject.net.intent.PointToPointIntent;
import com.fasterxml.jackson.databind.node.ObjectNode;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.onlab.util.Tools.nullIsIllegal;
/**
* Point to point intent codec.
*/
public final class PointToPointIntentCodec extends JsonCodec<PointToPointIntent> {
private static final String INGRESS_POINT = "ingressPoint";
private static final String EGRESS_POINT = "egressPoint";
@Override
public ObjectNode encode(PointToPointIntent intent, CodecContext context) {
checkNotNull(intent, "Point to point intent cannot be null");
......@@ -45,9 +49,32 @@ public final class PointToPointIntentCodec extends JsonCodec<PointToPointIntent>
final ObjectNode egress =
connectPointCodec.encode(intent.egressPoint(), context);
result.set("ingressPoint", ingress);
result.set("egressPoint", egress);
result.set(INGRESS_POINT, ingress);
result.set(EGRESS_POINT, egress);
return result;
}
@Override
public PointToPointIntent decode(ObjectNode json, CodecContext context) {
PointToPointIntent.Builder builder = PointToPointIntent.builder();
IntentCodec.intentAttributes(json, context, builder);
ConnectivityIntentCodec.intentAttributes(json, context, builder);
ObjectNode ingressJson = nullIsIllegal(get(json, INGRESS_POINT),
INGRESS_POINT + IntentCodec.MISSING_MEMBER_MESSAGE);
ConnectPoint ingress = context.codec(ConnectPoint.class)
.decode(ingressJson, context);
builder.ingressPoint(ingress);
ObjectNode egressJson = nullIsIllegal(get(json, EGRESS_POINT),
EGRESS_POINT + IntentCodec.MISSING_MEMBER_MESSAGE);
ConnectPoint egress = context.codec(ConnectPoint.class)
.decode(egressJson, context);
builder.egressPoint(egress);
return builder.build();
}
}
......
......@@ -63,7 +63,7 @@ public final class TrafficSelectorCodec extends JsonCodec<TrafficSelector> {
if (criteriaJson != null) {
IntStream.range(0, criteriaJson.size())
.forEach(i -> builder.add(
criterionCodec.decode((ObjectNode) criteriaJson.get(i),
criterionCodec.decode(get(criteriaJson, i),
context)));
}
return builder.build();
......
......@@ -68,7 +68,7 @@ public final class TrafficTreatmentCodec extends JsonCodec<TrafficTreatment> {
if (instructionsJson != null) {
IntStream.range(0, instructionsJson.size())
.forEach(i -> builder.add(
instructionsCodec.decode((ObjectNode) instructionsJson.get(i),
instructionsCodec.decode(get(instructionsJson, i),
context)));
}
return builder.build();
......
/*
* Copyright 2015 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.codec.impl;
import java.io.IOException;
import java.io.InputStream;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.onosproject.codec.JsonCodec;
import org.onosproject.core.CoreService;
import org.onosproject.net.Link;
import org.onosproject.net.intent.Constraint;
import org.onosproject.net.intent.constraint.AnnotationConstraint;
import org.onosproject.net.intent.constraint.AsymmetricPathConstraint;
import org.onosproject.net.intent.constraint.BandwidthConstraint;
import org.onosproject.net.intent.constraint.LambdaConstraint;
import org.onosproject.net.intent.constraint.LatencyConstraint;
import org.onosproject.net.intent.constraint.LinkTypeConstraint;
import org.onosproject.net.intent.constraint.ObstacleConstraint;
import org.onosproject.net.intent.constraint.WaypointConstraint;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.onosproject.net.NetTestTools.APP_ID;
import static org.onosproject.net.NetTestTools.did;
/**
* Unit tests for Constraint codec.
*/
public class ConstraintCodecTest {
MockCodecContext context;
JsonCodec<Constraint> constraintCodec;
final CoreService mockCoreService = createMock(CoreService.class);
/**
* Sets up for each test. Creates a context and fetches the flow rule
* codec.
*/
@Before
public void setUp() {
context = new MockCodecContext();
constraintCodec = context.codec(Constraint.class);
assertThat(constraintCodec, notNullValue());
expect(mockCoreService.registerApplication(FlowRuleCodec.REST_APP_ID))
.andReturn(APP_ID).anyTimes();
replay(mockCoreService);
context.registerService(CoreService.class, mockCoreService);
}
/**
* Reads in a constraint from the given resource and decodes it.
*
* @param resourceName resource to use to read the JSON for the constraint
* @return decoded constraint
*/
private Constraint getConstraint(String resourceName) {
InputStream jsonStream = ConstraintCodecTest.class
.getResourceAsStream(resourceName);
try {
JsonNode json = context.mapper().readTree(jsonStream);
assertThat(json, notNullValue());
Constraint constraint = constraintCodec.decode((ObjectNode) json, context);
assertThat(constraint, notNullValue());
return checkNotNull(constraint);
} catch (IOException ioe) {
Assert.fail(ioe.getMessage());
throw new IllegalStateException("cannot happen");
}
}
/**
* Tests link type constraint.
*/
@Test
public void linkTypeConstraint() {
Constraint constraint = getConstraint("LinkTypeConstraint.json");
assertThat(constraint, instanceOf(LinkTypeConstraint.class));
LinkTypeConstraint linkTypeConstraint = (LinkTypeConstraint) constraint;
assertThat(linkTypeConstraint.isInclusive(), is(false));
assertThat(linkTypeConstraint.types(), hasSize(2));
assertThat(linkTypeConstraint.types(), hasItem(Link.Type.OPTICAL));
assertThat(linkTypeConstraint.types(), hasItem(Link.Type.DIRECT));
}
/**
* Tests annotation constraint.
*/
@Test
public void annotationConstraint() {
Constraint constraint = getConstraint("AnnotationConstraint.json");
assertThat(constraint, instanceOf(AnnotationConstraint.class));
AnnotationConstraint annotationConstraint = (AnnotationConstraint) constraint;
assertThat(annotationConstraint.key(), is("key"));
assertThat(annotationConstraint.threshold(), is(123.0D));
}
/**
* Tests bandwidth constraint.
*/
@Test
public void bandwidthConstraint() {
Constraint constraint = getConstraint("BandwidthConstraint.json");
assertThat(constraint, instanceOf(BandwidthConstraint.class));
BandwidthConstraint bandwidthConstraint = (BandwidthConstraint) constraint;
assertThat(bandwidthConstraint.bandwidth().toDouble(), is(345.678D));
}
/**
* Tests lambda constraint.
*/
@Test
public void lambdaConstraint() {
Constraint constraint = getConstraint("LambdaConstraint.json");
assertThat(constraint, instanceOf(LambdaConstraint.class));
LambdaConstraint lambdaConstraint = (LambdaConstraint) constraint;
assertThat(lambdaConstraint.lambda().toInt(), is(444));
}
/**
* Tests latency constraint.
*/
@Test
public void latencyConstraint() {
Constraint constraint = getConstraint("LatencyConstraint.json");
assertThat(constraint, instanceOf(LatencyConstraint.class));
LatencyConstraint latencyConstraint = (LatencyConstraint) constraint;
assertThat(latencyConstraint.latency().toMillis(), is(111L));
}
/**
* Tests obstacle constraint.
*/
@Test
public void obstacleConstraint() {
Constraint constraint = getConstraint("ObstacleConstraint.json");
assertThat(constraint, instanceOf(ObstacleConstraint.class));
ObstacleConstraint obstacleConstraint = (ObstacleConstraint) constraint;
assertThat(obstacleConstraint.obstacles(), hasItem(did("dev1")));
assertThat(obstacleConstraint.obstacles(), hasItem(did("dev2")));
assertThat(obstacleConstraint.obstacles(), hasItem(did("dev3")));
}
/**
* Tests waypoint constaint.
*/
@Test
public void waypointConstraint() {
Constraint constraint = getConstraint("WaypointConstraint.json");
assertThat(constraint, instanceOf(WaypointConstraint.class));
WaypointConstraint waypointConstraint = (WaypointConstraint) constraint;
assertThat(waypointConstraint.waypoints(), hasItem(did("devA")));
assertThat(waypointConstraint.waypoints(), hasItem(did("devB")));
assertThat(waypointConstraint.waypoints(), hasItem(did("devC")));
}
/**
* Tests asymmetric path constraint.
*/
@Test
public void asymmetricPathConstraint() {
Constraint constraint = getConstraint("AsymmetricPathConstraint.json");
assertThat(constraint, instanceOf(AsymmetricPathConstraint.class));
}
}
......@@ -15,6 +15,8 @@
*/
package org.onosproject.codec.impl;
import java.io.IOException;
import java.io.InputStream;
import java.time.Duration;
import java.util.List;
......@@ -26,6 +28,7 @@ import org.onlab.packet.MplsLabel;
import org.onlab.util.Bandwidth;
import org.onosproject.codec.JsonCodec;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.core.DefaultApplicationId;
import org.onosproject.net.ChannelSpacing;
import org.onosproject.net.ConnectPoint;
......@@ -43,9 +46,13 @@ import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.flow.criteria.Criteria;
import org.onosproject.net.flow.instructions.Instructions;
import org.onosproject.net.flow.criteria.Criterion;
import org.onosproject.net.flow.criteria.EthCriterion;
import org.onosproject.net.flow.instructions.Instruction;
import org.onosproject.net.intent.AbstractIntentTest;
import org.onosproject.net.intent.Constraint;
import org.onosproject.net.intent.HostToHostIntent;
import org.onosproject.net.intent.Intent;
import org.onosproject.net.intent.IntentService;
import org.onosproject.net.intent.IntentServiceAdapter;
import org.onosproject.net.intent.PointToPointIntent;
......@@ -59,14 +66,22 @@ import org.onosproject.net.intent.constraint.WaypointConstraint;
import org.onosproject.net.resource.link.BandwidthResource;
import org.onosproject.net.resource.link.LambdaResource;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.ImmutableList;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.onosproject.codec.impl.IntentJsonMatcher.matchesIntent;
import static org.onosproject.net.NetTestTools.did;
import static org.onosproject.net.NetTestTools.hid;
import static org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
/**
* Unit tests for the host to host intent class codec.
......@@ -81,11 +96,16 @@ public class IntentCodecTest extends AbstractIntentTest {
final TrafficTreatment emptyTreatment =
DefaultTrafficTreatment.emptyTreatment();
private final MockCodecContext context = new MockCodecContext();
final CoreService mockCoreService = createMock(CoreService.class);
@Before
public void setUpIntentService() {
final IntentService mockIntentService = new IntentServiceAdapter();
context.registerService(IntentService.class, mockIntentService);
context.registerService(CoreService.class, mockCoreService);
expect(mockCoreService.getAppId((short) 2))
.andReturn(new DefaultApplicationId(2, "app"));
replay(mockCoreService);
}
/**
......@@ -161,13 +181,13 @@ public class IntentCodecTest extends AbstractIntentTest {
final List<Constraint> constraints =
ImmutableList.of(
new BandwidthConstraint(new BandwidthResource(Bandwidth.bps(1.0))),
new LambdaConstraint(LambdaResource.valueOf(3)),
new AnnotationConstraint("key", 33.0),
new AsymmetricPathConstraint(),
new LatencyConstraint(Duration.ofSeconds(2)),
new ObstacleConstraint(did1, did2),
new WaypointConstraint(did3));
new BandwidthConstraint(new BandwidthResource(Bandwidth.bps(1.0))),
new LambdaConstraint(LambdaResource.valueOf(3)),
new AnnotationConstraint("key", 33.0),
new AsymmetricPathConstraint(),
new LatencyConstraint(Duration.ofSeconds(2)),
new ObstacleConstraint(did1, did2),
new WaypointConstraint(did3));
final PointToPointIntent intent =
PointToPointIntent.builder()
......@@ -188,4 +208,81 @@ public class IntentCodecTest extends AbstractIntentTest {
assertThat(intentJson, matchesIntent(intent));
}
/**
* Reads in a rule from the given resource and decodes it.
*
* @param resourceName resource to use to read the JSON for the rule
* @return decoded flow rule
* @throws IOException if processing the resource fails
*/
private Intent getIntent(String resourceName, JsonCodec intentCodec) throws IOException {
InputStream jsonStream = FlowRuleCodecTest.class
.getResourceAsStream(resourceName);
JsonNode json = context.mapper().readTree(jsonStream);
assertThat(json, notNullValue());
Intent intent = (Intent) intentCodec.decode((ObjectNode) json, context);
assertThat(intent, notNullValue());
return intent;
}
/**
* Tests the point to point intent JSON codec.
*
* @throws IOException if JSON processing fails
*/
@Test
public void decodePointToPointIntent() throws IOException {
JsonCodec<Intent> intentCodec = context.codec(Intent.class);
assertThat(intentCodec, notNullValue());
Intent intent = getIntent("PointToPointIntent.json", intentCodec);
assertThat(intent, notNullValue());
assertThat(intent, instanceOf(PointToPointIntent.class));
PointToPointIntent pointIntent = (PointToPointIntent) intent;
assertThat(pointIntent.priority(), is(55));
assertThat(pointIntent.ingressPoint().deviceId(), is(did("0000000000000001")));
assertThat(pointIntent.ingressPoint().port(), is(PortNumber.portNumber(1)));
assertThat(pointIntent.egressPoint().deviceId(), is(did("0000000000000007")));
assertThat(pointIntent.egressPoint().port(), is(PortNumber.portNumber(2)));
assertThat(pointIntent.constraints(), hasSize(1));
assertThat(pointIntent.selector(), notNullValue());
assertThat(pointIntent.selector().criteria(), hasSize(1));
Criterion criterion1 = pointIntent.selector().criteria().iterator().next();
assertThat(criterion1, instanceOf(EthCriterion.class));
EthCriterion ethCriterion = (EthCriterion) criterion1;
assertThat(ethCriterion.mac().toString(), is("11:22:33:44:55:66"));
assertThat(ethCriterion.type().name(), is("ETH_DST"));
assertThat(pointIntent.treatment(), notNullValue());
assertThat(pointIntent.treatment().allInstructions(), hasSize(1));
Instruction instruction1 = pointIntent.treatment().allInstructions().iterator().next();
assertThat(instruction1, instanceOf(ModEtherInstruction.class));
ModEtherInstruction ethInstruction = (ModEtherInstruction) instruction1;
assertThat(ethInstruction.mac().toString(), is("22:33:44:55:66:77"));
assertThat(ethInstruction.type().toString(), is("L2MODIFICATION"));
assertThat(ethInstruction.subtype().toString(), is("ETH_SRC"));
}
/**
* Tests the host to host intent JSON codec.
*
* @throws IOException
*/
@Test
public void decodeHostToHostIntent() throws IOException {
JsonCodec<Intent> intentCodec = context.codec(Intent.class);
assertThat(intentCodec, notNullValue());
Intent intent = getIntent("HostToHostIntent.json", intentCodec);
assertThat(intent, notNullValue());
assertThat(intent, instanceOf(HostToHostIntent.class));
HostToHostIntent hostIntent = (HostToHostIntent) intent;
assertThat(hostIntent.priority(), is(7));
assertThat(hostIntent.constraints(), hasSize(1));
}
}
......
{
"type":"AnnotationConstraint",
"key":"key",
"threshold":123.0
}
{
"type": "HostToHostIntent",
"appId": 2,
"selector": {"criteria": []},
"treatment": {
"instructions": [],
"deferred": []
},
"priority": 7,
"constraints": [
{
"inclusive": false,
"types": ["OPTICAL"],
"type": "LinkTypeConstraint"
}
],
"one": "00:00:00:00:00:02/-1",
"two": "00:00:00:00:00:05/-1"
}
{
"inclusive":false,
"types":["DIRECT","OPTICAL"],
"type":"LinkTypeConstraint"
}
{
"type":"ObstacleConstraint",
"obstacles":["of:dev1","of:dev2","of:dev3"]
}
{
"type": "PointToPointIntent",
"appId": 2,
"selector": {
"criteria": [
{
"type": "ETH_DST",
"mac": "11:22:33:44:55:66"
}
]
},
"treatment": {
"instructions": [
{
"type": "L2MODIFICATION",
"subtype": "ETH_SRC",
"mac": "22:33:44:55:66:77"
}
],
"deferred": []
},
"priority": 55,
"constraints": [
{
"inclusive": false,
"types": ["OPTICAL"],
"type": "LinkTypeConstraint"
}
],
"ingressPoint": {
"port": "1",
"device": "of:0000000000000001"
},
"egressPoint": {
"port": "2",
"device": "of:0000000000000007"
}
}
{
"type":"WaypointConstraint",
"waypoints":["of:devA","of:devB","of:devC"]
}
......@@ -15,12 +15,18 @@
*/
package org.onosproject.rest.resources;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
......@@ -177,4 +183,31 @@ public class IntentsWebResource extends AbstractWebResource {
}
}
/**
* Creates an intent from a POST of a JSON string and attempts to apply it.
*
* @param stream input JSON
* @return status of the request - CREATED if the JSON is correct,
* BAD_REQUEST if the JSON is invalid
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response createIntent(InputStream stream) {
URI location;
try {
IntentService service = get(IntentService.class);
ObjectNode root = (ObjectNode) mapper().readTree(stream);
Intent intent = codec(Intent.class).decode(root, this);
service.submit(intent);
location = new URI(Short.toString(intent.appId().id()) + "/"
+ Long.toString(intent.id().fingerprint()));
} catch (IOException | URISyntaxException ex) {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
}
return Response
.created(location)
.build();
}
}
......
......@@ -15,6 +15,7 @@
*/
package org.onosproject.rest;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.util.HashMap;
import java.util.HashSet;
......@@ -107,10 +108,6 @@ public class FlowsResourceTest extends ResourceTest {
final MockFlowEntry flow5 = new MockFlowEntry(deviceId2, 5);
final MockFlowEntry flow6 = new MockFlowEntry(deviceId2, 6);
private static final String FLOW_JSON = "{\"priority\":1,\"isPermanent\":true,"
+ "\"treatment\":{\"instructions\":[ {\"type\":\"OUTPUT\",\"port\":2}]},"
+ "\"selector\":{\"criteria\":[ {\"type\":\"ETH_TYPE\",\"ethType\":2054}]}}";
/**
* Mock class for a flow entry.
*/
......@@ -582,11 +579,12 @@ public class FlowsResourceTest extends ResourceTest {
replay(mockFlowService);
WebResource rs = resource();
InputStream jsonStream = IntentsResourceTest.class
.getResourceAsStream("post-flow.json");
ClientResponse response = rs.path("flows/of:0000000000000001")
.type(MediaType.APPLICATION_JSON_TYPE)
.post(ClientResponse.class, FLOW_JSON);
.post(ClientResponse.class, jsonStream);
assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
String location = response.getLocation().getPath();
assertThat(location, Matchers.startsWith("/flows/of:0000000000000001/"));
......
......@@ -15,10 +15,15 @@
*/
package org.onosproject.rest;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.util.Collections;
import java.util.HashSet;
import javax.ws.rs.core.MediaType;
import org.hamcrest.Description;
import org.hamcrest.Matchers;
import org.hamcrest.TypeSafeMatcher;
import org.junit.After;
import org.junit.Before;
......@@ -42,12 +47,14 @@ import org.onosproject.net.intent.MockIdGenerator;
import com.eclipsesource.json.JsonArray;
import com.eclipsesource.json.JsonObject;
import com.eclipsesource.json.JsonValue;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.UniformInterfaceException;
import com.sun.jersey.api.client.WebResource;
import static org.easymock.EasyMock.anyObject;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.expectLastCall;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import static org.hamcrest.Matchers.containsString;
......@@ -358,4 +365,29 @@ public class IntentsResourceTest extends ResourceTest {
containsString("returned a response status of"));
}
}
/**
* Tests creating an intent with POST.
*/
@Test
public void testPost() {
expect(mockCoreService.getAppId((short) 2))
.andReturn(new DefaultApplicationId(2, "app"));
replay(mockCoreService);
mockIntentService.submit(anyObject());
expectLastCall();
replay(mockIntentService);
InputStream jsonStream = IntentsResourceTest.class
.getResourceAsStream("post-intent.json");
WebResource rs = resource();
ClientResponse response = rs.path("intents")
.type(MediaType.APPLICATION_JSON_TYPE)
.post(ClientResponse.class, jsonStream);
assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
String location = response.getLocation().getPath();
assertThat(location, Matchers.startsWith("/intents/2/"));
}
}
......
{
"priority": 1,
"isPermanent": true,
"treatment": {
"instructions": [
{
"type": "OUTPUT",
"port": 2
}
]
},
"selector": {
"criteria": [
{
"type": "ETH_TYPE",
"ethType": 2054
}
]
}
}
{
"type": "PointToPointIntent",
"appId": 2,
"selector": {
"criteria": [
{
"type": "ETH_DST",
"mac": "11:22:33:44:55:66"
}
]
},
"treatment": {
"instructions": [
{
"type": "L2MODIFICATION",
"subtype": "ETH_SRC",
"mac": "22:33:44:55:66:77"
}
],
"deferred": []
},
"priority": 55,
"constraints": [
{
"inclusive": false,
"types": ["OPTICAL"],
"type": "LinkTypeConstraint"
}
],
"ingressPoint": {
"port": "1",
"device": "of:0000000000000001"
},
"egressPoint": {
"port": "2",
"device": "of:0000000000000007"
}
}