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
1159 additions
and
194 deletions
... | @@ -87,4 +87,29 @@ public abstract class JsonCodec<T> { | ... | @@ -87,4 +87,29 @@ public abstract class JsonCodec<T> { |
87 | return result; | 87 | return result; |
88 | } | 88 | } |
89 | 89 | ||
90 | + /** | ||
91 | + * Gets a child Object Node from a parent by name. If the child is not found | ||
92 | + * or does nor represent an object, null is returned. | ||
93 | + * | ||
94 | + * @param parent parent object | ||
95 | + * @param childName name of child to query | ||
96 | + * @return child object if found, null if not found or if not an object | ||
97 | + */ | ||
98 | + protected static ObjectNode get(ObjectNode parent, String childName) { | ||
99 | + JsonNode node = parent.path(childName); | ||
100 | + return node.isObject() && !node.isNull() ? (ObjectNode) node : null; | ||
101 | + } | ||
102 | + | ||
103 | + /** | ||
104 | + * Gets a child Object Node from a parent by index. If the child is not found | ||
105 | + * or does nor represent an object, null is returned. | ||
106 | + * | ||
107 | + * @param parent parent object | ||
108 | + * @param childIndex index of child to query | ||
109 | + * @return child object if found, null if not found or if not an object | ||
110 | + */ | ||
111 | + protected static ObjectNode get(JsonNode parent, int childIndex) { | ||
112 | + JsonNode node = parent.path(childIndex); | ||
113 | + return node.isObject() && !node.isNull() ? (ObjectNode) node : null; | ||
114 | + } | ||
90 | } | 115 | } | ... | ... |
... | @@ -55,7 +55,7 @@ public abstract class AnnotatedCodec<T extends Annotated> extends JsonCodec<T> { | ... | @@ -55,7 +55,7 @@ public abstract class AnnotatedCodec<T extends Annotated> extends JsonCodec<T> { |
55 | 55 | ||
56 | JsonCodec<Annotations> codec = context.codec(Annotations.class); | 56 | JsonCodec<Annotations> codec = context.codec(Annotations.class); |
57 | if (objNode.has("annotations") && objNode.isObject()) { | 57 | if (objNode.has("annotations") && objNode.isObject()) { |
58 | - return codec.decode((ObjectNode) objNode.get("annotations"), context); | 58 | + return codec.decode(get(objNode, "annotations"), context); |
59 | } else { | 59 | } else { |
60 | return DefaultAnnotations.EMPTY; | 60 | return DefaultAnnotations.EMPTY; |
61 | } | 61 | } | ... | ... |
... | @@ -15,6 +15,9 @@ | ... | @@ -15,6 +15,9 @@ |
15 | */ | 15 | */ |
16 | package org.onosproject.codec.impl; | 16 | package org.onosproject.codec.impl; |
17 | 17 | ||
18 | +import java.util.ArrayList; | ||
19 | +import java.util.stream.IntStream; | ||
20 | + | ||
18 | import org.onosproject.codec.CodecContext; | 21 | import org.onosproject.codec.CodecContext; |
19 | import org.onosproject.codec.JsonCodec; | 22 | import org.onosproject.codec.JsonCodec; |
20 | import org.onosproject.net.flow.TrafficSelector; | 23 | import org.onosproject.net.flow.TrafficSelector; |
... | @@ -23,6 +26,7 @@ import org.onosproject.net.intent.ConnectivityIntent; | ... | @@ -23,6 +26,7 @@ import org.onosproject.net.intent.ConnectivityIntent; |
23 | import org.onosproject.net.intent.Constraint; | 26 | import org.onosproject.net.intent.Constraint; |
24 | import org.onosproject.net.intent.Intent; | 27 | import org.onosproject.net.intent.Intent; |
25 | 28 | ||
29 | +import com.fasterxml.jackson.databind.JsonNode; | ||
26 | import com.fasterxml.jackson.databind.node.ArrayNode; | 30 | import com.fasterxml.jackson.databind.node.ArrayNode; |
27 | import com.fasterxml.jackson.databind.node.ObjectNode; | 31 | import com.fasterxml.jackson.databind.node.ObjectNode; |
28 | 32 | ||
... | @@ -33,6 +37,10 @@ import static com.google.common.base.Preconditions.checkNotNull; | ... | @@ -33,6 +37,10 @@ import static com.google.common.base.Preconditions.checkNotNull; |
33 | */ | 37 | */ |
34 | public final class ConnectivityIntentCodec extends JsonCodec<ConnectivityIntent> { | 38 | public final class ConnectivityIntentCodec extends JsonCodec<ConnectivityIntent> { |
35 | 39 | ||
40 | + private static final String CONSTRAINTS = "constraints"; | ||
41 | + private static final String SELECTOR = "selector"; | ||
42 | + private static final String TREATMENT = "treatment"; | ||
43 | + | ||
36 | @Override | 44 | @Override |
37 | public ObjectNode encode(ConnectivityIntent intent, CodecContext context) { | 45 | public ObjectNode encode(ConnectivityIntent intent, CodecContext context) { |
38 | checkNotNull(intent, "Connectivity intent cannot be null"); | 46 | checkNotNull(intent, "Connectivity intent cannot be null"); |
... | @@ -43,19 +51,19 @@ public final class ConnectivityIntentCodec extends JsonCodec<ConnectivityIntent> | ... | @@ -43,19 +51,19 @@ public final class ConnectivityIntentCodec extends JsonCodec<ConnectivityIntent> |
43 | if (intent.selector() != null) { | 51 | if (intent.selector() != null) { |
44 | final JsonCodec<TrafficSelector> selectorCodec = | 52 | final JsonCodec<TrafficSelector> selectorCodec = |
45 | context.codec(TrafficSelector.class); | 53 | context.codec(TrafficSelector.class); |
46 | - result.set("selector", selectorCodec.encode(intent.selector(), context)); | 54 | + result.set(SELECTOR, selectorCodec.encode(intent.selector(), context)); |
47 | } | 55 | } |
48 | 56 | ||
49 | if (intent.treatment() != null) { | 57 | if (intent.treatment() != null) { |
50 | final JsonCodec<TrafficTreatment> treatmentCodec = | 58 | final JsonCodec<TrafficTreatment> treatmentCodec = |
51 | context.codec(TrafficTreatment.class); | 59 | context.codec(TrafficTreatment.class); |
52 | - result.set("treatment", treatmentCodec.encode(intent.treatment(), context)); | 60 | + result.set(TREATMENT, treatmentCodec.encode(intent.treatment(), context)); |
53 | } | 61 | } |
54 | 62 | ||
55 | - result.put("priority", intent.priority()); | 63 | + result.put(IntentCodec.PRIORITY, intent.priority()); |
56 | 64 | ||
57 | if (intent.constraints() != null) { | 65 | if (intent.constraints() != null) { |
58 | - final ArrayNode jsonConstraints = result.putArray("constraints"); | 66 | + final ArrayNode jsonConstraints = result.putArray(CONSTRAINTS); |
59 | 67 | ||
60 | if (intent.constraints() != null) { | 68 | if (intent.constraints() != null) { |
61 | final JsonCodec<Constraint> constraintCodec = | 69 | final JsonCodec<Constraint> constraintCodec = |
... | @@ -70,4 +78,41 @@ public final class ConnectivityIntentCodec extends JsonCodec<ConnectivityIntent> | ... | @@ -70,4 +78,41 @@ public final class ConnectivityIntentCodec extends JsonCodec<ConnectivityIntent> |
70 | 78 | ||
71 | return result; | 79 | return result; |
72 | } | 80 | } |
81 | + | ||
82 | + /** | ||
83 | + * Extracts connectivity intent specific attributes from a JSON object | ||
84 | + * and adds them to a builder. | ||
85 | + * | ||
86 | + * @param json root JSON object | ||
87 | + * @param context code context | ||
88 | + * @param builder builder to use for storing the attributes. Constraints, | ||
89 | + * selector and treatment are modified by this call. | ||
90 | + */ | ||
91 | + public static void intentAttributes(ObjectNode json, CodecContext context, | ||
92 | + ConnectivityIntent.Builder builder) { | ||
93 | + JsonNode constraintsJson = json.get(CONSTRAINTS); | ||
94 | + if (constraintsJson != null) { | ||
95 | + JsonCodec<Constraint> constraintsCodec = context.codec(Constraint.class); | ||
96 | + ArrayList<Constraint> constraints = new ArrayList<>(constraintsJson.size()); | ||
97 | + IntStream.range(0, constraintsJson.size()) | ||
98 | + .forEach(i -> constraints.add( | ||
99 | + constraintsCodec.decode(get(constraintsJson, i), | ||
100 | + context))); | ||
101 | + builder.constraints(constraints); | ||
102 | + } | ||
103 | + | ||
104 | + ObjectNode selectorJson = get(json, SELECTOR); | ||
105 | + if (selectorJson != null) { | ||
106 | + JsonCodec<TrafficSelector> selectorCodec = context.codec(TrafficSelector.class); | ||
107 | + TrafficSelector selector = selectorCodec.decode(selectorJson, context); | ||
108 | + builder.selector(selector); | ||
109 | + } | ||
110 | + | ||
111 | + ObjectNode treatmentJson = get(json, TREATMENT); | ||
112 | + if (treatmentJson != null) { | ||
113 | + JsonCodec<TrafficTreatment> treatmentCodec = context.codec(TrafficTreatment.class); | ||
114 | + TrafficTreatment treatment = treatmentCodec.decode(treatmentJson, context); | ||
115 | + builder.treatment(treatment); | ||
116 | + } | ||
117 | + } | ||
73 | } | 118 | } | ... | ... |
... | @@ -17,18 +17,8 @@ package org.onosproject.codec.impl; | ... | @@ -17,18 +17,8 @@ package org.onosproject.codec.impl; |
17 | 17 | ||
18 | import org.onosproject.codec.CodecContext; | 18 | import org.onosproject.codec.CodecContext; |
19 | import org.onosproject.codec.JsonCodec; | 19 | import org.onosproject.codec.JsonCodec; |
20 | -import org.onosproject.net.DeviceId; | ||
21 | -import org.onosproject.net.Link; | ||
22 | import org.onosproject.net.intent.Constraint; | 20 | import org.onosproject.net.intent.Constraint; |
23 | -import org.onosproject.net.intent.constraint.AnnotationConstraint; | ||
24 | -import org.onosproject.net.intent.constraint.BandwidthConstraint; | ||
25 | -import org.onosproject.net.intent.constraint.LambdaConstraint; | ||
26 | -import org.onosproject.net.intent.constraint.LatencyConstraint; | ||
27 | -import org.onosproject.net.intent.constraint.LinkTypeConstraint; | ||
28 | -import org.onosproject.net.intent.constraint.ObstacleConstraint; | ||
29 | -import org.onosproject.net.intent.constraint.WaypointConstraint; | ||
30 | 21 | ||
31 | -import com.fasterxml.jackson.databind.node.ArrayNode; | ||
32 | import com.fasterxml.jackson.databind.node.ObjectNode; | 22 | import com.fasterxml.jackson.databind.node.ObjectNode; |
33 | 23 | ||
34 | import static com.google.common.base.Preconditions.checkNotNull; | 24 | import static com.google.common.base.Preconditions.checkNotNull; |
... | @@ -38,170 +28,36 @@ import static com.google.common.base.Preconditions.checkNotNull; | ... | @@ -38,170 +28,36 @@ import static com.google.common.base.Preconditions.checkNotNull; |
38 | */ | 28 | */ |
39 | public final class ConstraintCodec extends JsonCodec<Constraint> { | 29 | public final class ConstraintCodec extends JsonCodec<Constraint> { |
40 | 30 | ||
41 | - /** | 31 | + protected static final String MISSING_MEMBER_MESSAGE = |
42 | - * Encodes a latency constraint. | 32 | + " member is required in Constraint"; |
43 | - * | 33 | + protected static final String TYPE = "type"; |
44 | - * @param constraint latency constraint to encode | 34 | + protected static final String TYPES = "types"; |
45 | - * @param context code context | 35 | + protected static final String INCLUSIVE = "inclusive"; |
46 | - * @return JSON ObjectNode representing the constraint | 36 | + protected static final String KEY = "key"; |
47 | - */ | 37 | + protected static final String THRESHOLD = "threshold"; |
48 | - private ObjectNode encodeLatencyConstraint(Constraint constraint, | 38 | + protected static final String BANDWIDTH = "bandwidth"; |
49 | - CodecContext context) { | 39 | + protected static final String LAMBDA = "lambda"; |
50 | - checkNotNull(constraint, "Duration constraint cannot be null"); | 40 | + protected static final String LATENCY_MILLIS = "latencyMillis"; |
51 | - final LatencyConstraint latencyConstraint = | 41 | + protected static final String OBSTACLES = "obstacles"; |
52 | - (LatencyConstraint) constraint; | 42 | + protected static final String WAYPOINTS = "waypoints"; |
53 | - return context.mapper().createObjectNode() | ||
54 | - .put("latencyMillis", latencyConstraint.latency().toMillis()); | ||
55 | - } | ||
56 | - | ||
57 | - /** | ||
58 | - * Encodes an obstacle constraint. | ||
59 | - * | ||
60 | - * @param constraint obstacle constraint to encode | ||
61 | - * @param context code context | ||
62 | - * @return JSON ObjectNode representing the constraint | ||
63 | - */ | ||
64 | - private ObjectNode encodeObstacleConstraint(Constraint constraint, | ||
65 | - CodecContext context) { | ||
66 | - checkNotNull(constraint, "Obstacle constraint cannot be null"); | ||
67 | - final ObstacleConstraint obstacleConstraint = | ||
68 | - (ObstacleConstraint) constraint; | ||
69 | - | ||
70 | - final ObjectNode result = context.mapper().createObjectNode(); | ||
71 | - final ArrayNode jsonObstacles = result.putArray("obstacles"); | ||
72 | - | ||
73 | - for (DeviceId did : obstacleConstraint.obstacles()) { | ||
74 | - jsonObstacles.add(did.toString()); | ||
75 | - } | ||
76 | - | ||
77 | - return result; | ||
78 | - } | ||
79 | - | ||
80 | - /** | ||
81 | - * Encodes a waypoint constraint. | ||
82 | - * | ||
83 | - * @param constraint waypoint constraint to encode | ||
84 | - * @param context code context | ||
85 | - * @return JSON ObjectNode representing the constraint | ||
86 | - */ | ||
87 | - private ObjectNode encodeWaypointConstraint(Constraint constraint, | ||
88 | - CodecContext context) { | ||
89 | - checkNotNull(constraint, "Waypoint constraint cannot be null"); | ||
90 | - final WaypointConstraint waypointConstraint = | ||
91 | - (WaypointConstraint) constraint; | ||
92 | - | ||
93 | - final ObjectNode result = context.mapper().createObjectNode(); | ||
94 | - final ArrayNode jsonWaypoints = result.putArray("waypoints"); | ||
95 | - | ||
96 | - for (DeviceId did : waypointConstraint.waypoints()) { | ||
97 | - jsonWaypoints.add(did.toString()); | ||
98 | - } | ||
99 | - | ||
100 | - return result; | ||
101 | - } | ||
102 | - | ||
103 | - /** | ||
104 | - * Encodes a annotation constraint. | ||
105 | - * | ||
106 | - * @param constraint annotation constraint to encode | ||
107 | - * @param context code context | ||
108 | - * @return JSON ObjectNode representing the constraint | ||
109 | - */ | ||
110 | - private ObjectNode encodeAnnotationConstraint(Constraint constraint, | ||
111 | - CodecContext context) { | ||
112 | - checkNotNull(constraint, "Annotation constraint cannot be null"); | ||
113 | - final AnnotationConstraint annotationConstraint = | ||
114 | - (AnnotationConstraint) constraint; | ||
115 | - return context.mapper().createObjectNode() | ||
116 | - .put("key", annotationConstraint.key()) | ||
117 | - .put("threshold", annotationConstraint.threshold()); | ||
118 | - } | ||
119 | 43 | ||
120 | - /** | 44 | + @Override |
121 | - * Encodes a bandwidth constraint. | 45 | + public ObjectNode encode(Constraint constraint, CodecContext context) { |
122 | - * | 46 | + checkNotNull(constraint, "Constraint cannot be null"); |
123 | - * @param constraint bandwidth constraint to encode | ||
124 | - * @param context code context | ||
125 | - * @return JSON ObjectNode representing the constraint | ||
126 | - */ | ||
127 | - private ObjectNode encodeBandwidthConstraint(Constraint constraint, | ||
128 | - CodecContext context) { | ||
129 | - checkNotNull(constraint, "Bandwidth constraint cannot be null"); | ||
130 | - final BandwidthConstraint bandwidthConstraint = | ||
131 | - (BandwidthConstraint) constraint; | ||
132 | - return context.mapper().createObjectNode() | ||
133 | - .put("bandwidth", bandwidthConstraint.bandwidth().toDouble()); | ||
134 | - } | ||
135 | - | ||
136 | - /** | ||
137 | - * Encodes a lambda constraint. | ||
138 | - * | ||
139 | - * @param constraint lambda constraint to encode | ||
140 | - * @param context code context | ||
141 | - * @return JSON ObjectNode representing the constraint | ||
142 | - */ | ||
143 | - private ObjectNode encodeLambdaConstraint(Constraint constraint, | ||
144 | - CodecContext context) { | ||
145 | - checkNotNull(constraint, "Lambda constraint cannot be null"); | ||
146 | - final LambdaConstraint lambdaConstraint = | ||
147 | - (LambdaConstraint) constraint; | ||
148 | - | ||
149 | - return context.mapper().createObjectNode() | ||
150 | - .put("lambda", lambdaConstraint.lambda().toInt()); | ||
151 | - } | ||
152 | - | ||
153 | - /** | ||
154 | - * Encodes a link type constraint. | ||
155 | - * | ||
156 | - * @param constraint link type constraint to encode | ||
157 | - * @param context code context | ||
158 | - * @return JSON ObjectNode representing the constraint | ||
159 | - */ | ||
160 | - private ObjectNode encodeLinkTypeConstraint(Constraint constraint, | ||
161 | - CodecContext context) { | ||
162 | - checkNotNull(constraint, "Link type constraint cannot be null"); | ||
163 | - | ||
164 | - final LinkTypeConstraint linkTypeConstraint = | ||
165 | - (LinkTypeConstraint) constraint; | ||
166 | - | ||
167 | - final ObjectNode result = context.mapper().createObjectNode() | ||
168 | - .put("inclusive", linkTypeConstraint.isInclusive()); | ||
169 | 47 | ||
170 | - final ArrayNode jsonTypes = result.putArray("types"); | 48 | + final EncodeConstraintCodec encodeCodec = |
49 | + new EncodeConstraintCodec(constraint, context); | ||
171 | 50 | ||
172 | - if (linkTypeConstraint.types() != null) { | 51 | + return encodeCodec.encode(); |
173 | - for (Link.Type type : linkTypeConstraint.types()) { | ||
174 | - jsonTypes.add(type.name()); | ||
175 | - } | ||
176 | - } | ||
177 | - | ||
178 | - return result; | ||
179 | } | 52 | } |
180 | 53 | ||
181 | @Override | 54 | @Override |
182 | - public ObjectNode encode(Constraint constraint, CodecContext context) { | 55 | + public Constraint decode(ObjectNode json, CodecContext context) { |
183 | - checkNotNull(constraint, "Constraint cannot be null"); | 56 | + checkNotNull(json, "JSON cannot be null"); |
184 | 57 | ||
185 | - final ObjectNode result; | 58 | + final DecodeConstraintCodec decodeCodec = |
186 | - if (constraint instanceof BandwidthConstraint) { | 59 | + new DecodeConstraintCodec(json); |
187 | - result = encodeBandwidthConstraint(constraint, context); | ||
188 | - } else if (constraint instanceof LambdaConstraint) { | ||
189 | - result = encodeLambdaConstraint(constraint, context); | ||
190 | - } else if (constraint instanceof LinkTypeConstraint) { | ||
191 | - result = encodeLinkTypeConstraint(constraint, context); | ||
192 | - } else if (constraint instanceof AnnotationConstraint) { | ||
193 | - result = encodeAnnotationConstraint(constraint, context); | ||
194 | - } else if (constraint instanceof LatencyConstraint) { | ||
195 | - result = encodeLatencyConstraint(constraint, context); | ||
196 | - } else if (constraint instanceof ObstacleConstraint) { | ||
197 | - result = encodeObstacleConstraint(constraint, context); | ||
198 | - } else if (constraint instanceof WaypointConstraint) { | ||
199 | - result = encodeWaypointConstraint(constraint, context); | ||
200 | - } else { | ||
201 | - result = context.mapper().createObjectNode(); | ||
202 | - } | ||
203 | 60 | ||
204 | - result.put("type", constraint.getClass().getSimpleName()); | 61 | + return decodeCodec.decode(); |
205 | - return result; | ||
206 | } | 62 | } |
207 | } | 63 | } | ... | ... |
1 | +/* | ||
2 | + * Copyright 2015 Open Networking Laboratory | ||
3 | + * | ||
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 | + */ | ||
16 | +package org.onosproject.codec.impl; | ||
17 | + | ||
18 | +import java.time.Duration; | ||
19 | +import java.util.ArrayList; | ||
20 | +import java.util.stream.IntStream; | ||
21 | + | ||
22 | +import org.onlab.util.Bandwidth; | ||
23 | +import org.onosproject.net.DeviceId; | ||
24 | +import org.onosproject.net.IndexedLambda; | ||
25 | +import org.onosproject.net.Link; | ||
26 | +import org.onosproject.net.intent.Constraint; | ||
27 | +import org.onosproject.net.intent.constraint.AnnotationConstraint; | ||
28 | +import org.onosproject.net.intent.constraint.AsymmetricPathConstraint; | ||
29 | +import org.onosproject.net.intent.constraint.BandwidthConstraint; | ||
30 | +import org.onosproject.net.intent.constraint.LambdaConstraint; | ||
31 | +import org.onosproject.net.intent.constraint.LatencyConstraint; | ||
32 | +import org.onosproject.net.intent.constraint.LinkTypeConstraint; | ||
33 | +import org.onosproject.net.intent.constraint.ObstacleConstraint; | ||
34 | +import org.onosproject.net.intent.constraint.WaypointConstraint; | ||
35 | +import org.onosproject.net.resource.link.BandwidthResource; | ||
36 | +import org.onosproject.net.resource.link.LambdaResource; | ||
37 | + | ||
38 | +import com.fasterxml.jackson.databind.JsonNode; | ||
39 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
40 | + | ||
41 | +import static org.onlab.util.Tools.nullIsIllegal; | ||
42 | + | ||
43 | +/** | ||
44 | + * Constraint JSON decoder. | ||
45 | + */ | ||
46 | +public final class DecodeConstraintCodec { | ||
47 | + private final ObjectNode json; | ||
48 | + | ||
49 | + /** | ||
50 | + * Constructs a constraint decoder. | ||
51 | + * | ||
52 | + * @param json object node to decode | ||
53 | + */ | ||
54 | + public DecodeConstraintCodec(ObjectNode json) { | ||
55 | + this.json = json; | ||
56 | + } | ||
57 | + | ||
58 | + /** | ||
59 | + * Decodes a link type constraint. | ||
60 | + * | ||
61 | + * @return link type constraint object. | ||
62 | + */ | ||
63 | + private Constraint decodeLinkTypeConstraint() { | ||
64 | + boolean inclusive = nullIsIllegal(json.get(ConstraintCodec.INCLUSIVE), | ||
65 | + ConstraintCodec.INCLUSIVE + ConstraintCodec.MISSING_MEMBER_MESSAGE).asBoolean(); | ||
66 | + | ||
67 | + JsonNode types = nullIsIllegal(json.get(ConstraintCodec.TYPES), | ||
68 | + ConstraintCodec.TYPES + ConstraintCodec.MISSING_MEMBER_MESSAGE); | ||
69 | + if (types.size() < 1) { | ||
70 | + throw new IllegalArgumentException( | ||
71 | + "types array in link constraint must have at least one value"); | ||
72 | + } | ||
73 | + | ||
74 | + ArrayList<Link.Type> typesEntries = new ArrayList<>(types.size()); | ||
75 | + IntStream.range(0, types.size()) | ||
76 | + .forEach(index -> | ||
77 | + typesEntries.add(Link.Type.valueOf(types.get(index).asText()))); | ||
78 | + | ||
79 | + return new LinkTypeConstraint(inclusive, | ||
80 | + typesEntries.toArray(new Link.Type[types.size()])); | ||
81 | + } | ||
82 | + | ||
83 | + /** | ||
84 | + * Decodes an annotation constraint. | ||
85 | + * | ||
86 | + * @return annotation constraint object. | ||
87 | + */ | ||
88 | + private Constraint decodeAnnotationConstraint() { | ||
89 | + String key = nullIsIllegal(json.get(ConstraintCodec.KEY), | ||
90 | + ConstraintCodec.KEY + ConstraintCodec.MISSING_MEMBER_MESSAGE) | ||
91 | + .asText(); | ||
92 | + double threshold = nullIsIllegal(json.get(ConstraintCodec.THRESHOLD), | ||
93 | + ConstraintCodec.THRESHOLD + ConstraintCodec.MISSING_MEMBER_MESSAGE) | ||
94 | + .asDouble(); | ||
95 | + | ||
96 | + return new AnnotationConstraint(key, threshold); | ||
97 | + } | ||
98 | + | ||
99 | + /** | ||
100 | + * Decodes a lambda constraint. | ||
101 | + * | ||
102 | + * @return lambda constraint object. | ||
103 | + */ | ||
104 | + private Constraint decodeLambdaConstraint() { | ||
105 | + long lambda = nullIsIllegal(json.get(ConstraintCodec.LAMBDA), | ||
106 | + ConstraintCodec.LAMBDA + ConstraintCodec.MISSING_MEMBER_MESSAGE) | ||
107 | + .asLong(); | ||
108 | + | ||
109 | + return new LambdaConstraint(LambdaResource.valueOf(new IndexedLambda(lambda))); | ||
110 | + } | ||
111 | + | ||
112 | + /** | ||
113 | + * Decodes a latency constraint. | ||
114 | + * | ||
115 | + * @return latency constraint object. | ||
116 | + */ | ||
117 | + private Constraint decodeLatencyConstraint() { | ||
118 | + long latencyMillis = nullIsIllegal(json.get(ConstraintCodec.LATENCY_MILLIS), | ||
119 | + ConstraintCodec.LATENCY_MILLIS + ConstraintCodec.MISSING_MEMBER_MESSAGE) | ||
120 | + .asLong(); | ||
121 | + | ||
122 | + return new LatencyConstraint(Duration.ofMillis(latencyMillis)); | ||
123 | + } | ||
124 | + | ||
125 | + /** | ||
126 | + * Decodes an obstacle constraint. | ||
127 | + * | ||
128 | + * @return obstacle constraint object. | ||
129 | + */ | ||
130 | + private Constraint decodeObstacleConstraint() { | ||
131 | + JsonNode obstacles = nullIsIllegal(json.get(ConstraintCodec.OBSTACLES), | ||
132 | + ConstraintCodec.OBSTACLES + ConstraintCodec.MISSING_MEMBER_MESSAGE); | ||
133 | + if (obstacles.size() < 1) { | ||
134 | + throw new IllegalArgumentException( | ||
135 | + "obstacles array in obstacles constraint must have at least one value"); | ||
136 | + } | ||
137 | + | ||
138 | + ArrayList<DeviceId> obstacleEntries = new ArrayList<>(obstacles.size()); | ||
139 | + IntStream.range(0, obstacles.size()) | ||
140 | + .forEach(index -> | ||
141 | + obstacleEntries.add(DeviceId.deviceId(obstacles.get(index).asText()))); | ||
142 | + | ||
143 | + return new ObstacleConstraint( | ||
144 | + obstacleEntries.toArray(new DeviceId[obstacles.size()])); | ||
145 | + } | ||
146 | + | ||
147 | + /** | ||
148 | + * Decodes a waypoint constraint. | ||
149 | + * | ||
150 | + * @return waypoint constraint object. | ||
151 | + */ | ||
152 | + private Constraint decodeWaypointConstraint() { | ||
153 | + JsonNode waypoints = nullIsIllegal(json.get(ConstraintCodec.WAYPOINTS), | ||
154 | + ConstraintCodec.WAYPOINTS + ConstraintCodec.MISSING_MEMBER_MESSAGE); | ||
155 | + if (waypoints.size() < 1) { | ||
156 | + throw new IllegalArgumentException( | ||
157 | + "obstacles array in obstacles constraint must have at least one value"); | ||
158 | + } | ||
159 | + | ||
160 | + ArrayList<DeviceId> waypointEntries = new ArrayList<>(waypoints.size()); | ||
161 | + IntStream.range(0, waypoints.size()) | ||
162 | + .forEach(index -> | ||
163 | + waypointEntries.add(DeviceId.deviceId(waypoints.get(index).asText()))); | ||
164 | + | ||
165 | + return new WaypointConstraint( | ||
166 | + waypointEntries.toArray(new DeviceId[waypoints.size()])); | ||
167 | + } | ||
168 | + | ||
169 | + /** | ||
170 | + * Decodes an asymmetric path constraint. | ||
171 | + * | ||
172 | + * @return asymmetric path constraint object. | ||
173 | + */ | ||
174 | + private Constraint decodeAsymmetricPathConstraint() { | ||
175 | + return new AsymmetricPathConstraint(); | ||
176 | + } | ||
177 | + | ||
178 | + /** | ||
179 | + * Decodes a bandwidth constraint. | ||
180 | + * | ||
181 | + * @return bandwidth constraint object. | ||
182 | + */ | ||
183 | + private Constraint decodeBandwidthConstraint() { | ||
184 | + double bandwidth = nullIsIllegal(json.get(ConstraintCodec.BANDWIDTH), | ||
185 | + ConstraintCodec.BANDWIDTH + ConstraintCodec.MISSING_MEMBER_MESSAGE) | ||
186 | + .asDouble(); | ||
187 | + | ||
188 | + return new BandwidthConstraint(new BandwidthResource(Bandwidth.bps(bandwidth))); | ||
189 | + } | ||
190 | + | ||
191 | + /** | ||
192 | + * Decodes the given constraint. | ||
193 | + * | ||
194 | + * @return constraint object. | ||
195 | + */ | ||
196 | + public Constraint decode() { | ||
197 | + final String type = nullIsIllegal(json.get(ConstraintCodec.TYPE), | ||
198 | + ConstraintCodec.TYPE + ConstraintCodec.MISSING_MEMBER_MESSAGE) | ||
199 | + .asText(); | ||
200 | + | ||
201 | + if (type.equals(BandwidthConstraint.class.getSimpleName())) { | ||
202 | + return decodeBandwidthConstraint(); | ||
203 | + } else if (type.equals(LambdaConstraint.class.getSimpleName())) { | ||
204 | + return decodeLambdaConstraint(); | ||
205 | + } else if (type.equals(LinkTypeConstraint.class.getSimpleName())) { | ||
206 | + return decodeLinkTypeConstraint(); | ||
207 | + } else if (type.equals(AnnotationConstraint.class.getSimpleName())) { | ||
208 | + return decodeAnnotationConstraint(); | ||
209 | + } else if (type.equals(LatencyConstraint.class.getSimpleName())) { | ||
210 | + return decodeLatencyConstraint(); | ||
211 | + } else if (type.equals(ObstacleConstraint.class.getSimpleName())) { | ||
212 | + return decodeObstacleConstraint(); | ||
213 | + } else if (type.equals(WaypointConstraint.class.getSimpleName())) { | ||
214 | + return decodeWaypointConstraint(); | ||
215 | + } else if (type.equals(AsymmetricPathConstraint.class.getSimpleName())) { | ||
216 | + return decodeAsymmetricPathConstraint(); | ||
217 | + } else if (type.equals(LinkTypeConstraint.class.getSimpleName())) { | ||
218 | + return decodeLinkTypeConstraint(); | ||
219 | + } else if (type.equals(AnnotationConstraint.class.getSimpleName())) { | ||
220 | + return decodeAnnotationConstraint(); | ||
221 | + } | ||
222 | + throw new IllegalArgumentException("Instruction type " | ||
223 | + + type + " is not supported"); | ||
224 | + } | ||
225 | +} |
1 | +/* | ||
2 | + * Copyright 2015 Open Networking Laboratory | ||
3 | + * | ||
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 | + */ | ||
16 | +package org.onosproject.codec.impl; | ||
17 | + | ||
18 | +import org.onosproject.codec.CodecContext; | ||
19 | +import org.onosproject.net.DeviceId; | ||
20 | +import org.onosproject.net.Link; | ||
21 | +import org.onosproject.net.intent.Constraint; | ||
22 | +import org.onosproject.net.intent.constraint.AnnotationConstraint; | ||
23 | +import org.onosproject.net.intent.constraint.BandwidthConstraint; | ||
24 | +import org.onosproject.net.intent.constraint.LambdaConstraint; | ||
25 | +import org.onosproject.net.intent.constraint.LatencyConstraint; | ||
26 | +import org.onosproject.net.intent.constraint.LinkTypeConstraint; | ||
27 | +import org.onosproject.net.intent.constraint.ObstacleConstraint; | ||
28 | +import org.onosproject.net.intent.constraint.WaypointConstraint; | ||
29 | + | ||
30 | +import com.fasterxml.jackson.databind.node.ArrayNode; | ||
31 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
32 | + | ||
33 | +import static com.google.common.base.Preconditions.checkNotNull; | ||
34 | + | ||
35 | +/** | ||
36 | + * Implementation of encoder for constraint JSON codec. | ||
37 | + */ | ||
38 | +public final class EncodeConstraintCodec { | ||
39 | + | ||
40 | + private final Constraint constraint; | ||
41 | + private final CodecContext context; | ||
42 | + | ||
43 | + /** | ||
44 | + * Constructs a constraint encoder. | ||
45 | + * | ||
46 | + * @param constraint constraint to encode | ||
47 | + * @param context to use for look ups | ||
48 | + */ | ||
49 | + public EncodeConstraintCodec(Constraint constraint, CodecContext context) { | ||
50 | + this.constraint = constraint; | ||
51 | + this.context = context; | ||
52 | + } | ||
53 | + | ||
54 | + /** | ||
55 | + * Encodes a latency constraint. | ||
56 | + * | ||
57 | + * @return JSON ObjectNode representing the constraint | ||
58 | + */ | ||
59 | + private ObjectNode encodeLatencyConstraint() { | ||
60 | + checkNotNull(constraint, "Duration constraint cannot be null"); | ||
61 | + final LatencyConstraint latencyConstraint = | ||
62 | + (LatencyConstraint) constraint; | ||
63 | + return context.mapper().createObjectNode() | ||
64 | + .put("latencyMillis", latencyConstraint.latency().toMillis()); | ||
65 | + } | ||
66 | + | ||
67 | + /** | ||
68 | + * Encodes an obstacle constraint. | ||
69 | + * | ||
70 | + * @return JSON ObjectNode representing the constraint | ||
71 | + */ | ||
72 | + private ObjectNode encodeObstacleConstraint() { | ||
73 | + checkNotNull(constraint, "Obstacle constraint cannot be null"); | ||
74 | + final ObstacleConstraint obstacleConstraint = | ||
75 | + (ObstacleConstraint) constraint; | ||
76 | + | ||
77 | + final ObjectNode result = context.mapper().createObjectNode(); | ||
78 | + final ArrayNode jsonObstacles = result.putArray("obstacles"); | ||
79 | + | ||
80 | + for (DeviceId did : obstacleConstraint.obstacles()) { | ||
81 | + jsonObstacles.add(did.toString()); | ||
82 | + } | ||
83 | + | ||
84 | + return result; | ||
85 | + } | ||
86 | + | ||
87 | + /** | ||
88 | + * Encodes a waypoint constraint. | ||
89 | + * | ||
90 | + * @return JSON ObjectNode representing the constraint | ||
91 | + */ | ||
92 | + private ObjectNode encodeWaypointConstraint() { | ||
93 | + checkNotNull(constraint, "Waypoint constraint cannot be null"); | ||
94 | + final WaypointConstraint waypointConstraint = | ||
95 | + (WaypointConstraint) constraint; | ||
96 | + | ||
97 | + final ObjectNode result = context.mapper().createObjectNode(); | ||
98 | + final ArrayNode jsonWaypoints = result.putArray("waypoints"); | ||
99 | + | ||
100 | + for (DeviceId did : waypointConstraint.waypoints()) { | ||
101 | + jsonWaypoints.add(did.toString()); | ||
102 | + } | ||
103 | + | ||
104 | + return result; | ||
105 | + } | ||
106 | + | ||
107 | + /** | ||
108 | + * Encodes a annotation constraint. | ||
109 | + * | ||
110 | + * @return JSON ObjectNode representing the constraint | ||
111 | + */ | ||
112 | + private ObjectNode encodeAnnotationConstraint() { | ||
113 | + checkNotNull(constraint, "Annotation constraint cannot be null"); | ||
114 | + final AnnotationConstraint annotationConstraint = | ||
115 | + (AnnotationConstraint) constraint; | ||
116 | + return context.mapper().createObjectNode() | ||
117 | + .put("key", annotationConstraint.key()) | ||
118 | + .put("threshold", annotationConstraint.threshold()); | ||
119 | + } | ||
120 | + | ||
121 | + /** | ||
122 | + * Encodes a bandwidth constraint. | ||
123 | + * | ||
124 | + * @return JSON ObjectNode representing the constraint | ||
125 | + */ | ||
126 | + private ObjectNode encodeBandwidthConstraint() { | ||
127 | + checkNotNull(constraint, "Bandwidth constraint cannot be null"); | ||
128 | + final BandwidthConstraint bandwidthConstraint = | ||
129 | + (BandwidthConstraint) constraint; | ||
130 | + return context.mapper().createObjectNode() | ||
131 | + .put("bandwidth", bandwidthConstraint.bandwidth().toDouble()); | ||
132 | + } | ||
133 | + | ||
134 | + /** | ||
135 | + * Encodes a lambda constraint. | ||
136 | + * | ||
137 | + * @return JSON ObjectNode representing the constraint | ||
138 | + */ | ||
139 | + private ObjectNode encodeLambdaConstraint() { | ||
140 | + checkNotNull(constraint, "Lambda constraint cannot be null"); | ||
141 | + final LambdaConstraint lambdaConstraint = | ||
142 | + (LambdaConstraint) constraint; | ||
143 | + | ||
144 | + return context.mapper().createObjectNode() | ||
145 | + .put("lambda", lambdaConstraint.lambda().toInt()); | ||
146 | + } | ||
147 | + | ||
148 | + /** | ||
149 | + * Encodes a link type constraint. | ||
150 | + * | ||
151 | + * @return JSON ObjectNode representing the constraint | ||
152 | + */ | ||
153 | + private ObjectNode encodeLinkTypeConstraint() { | ||
154 | + checkNotNull(constraint, "Link type constraint cannot be null"); | ||
155 | + | ||
156 | + final LinkTypeConstraint linkTypeConstraint = | ||
157 | + (LinkTypeConstraint) constraint; | ||
158 | + | ||
159 | + final ObjectNode result = context.mapper().createObjectNode() | ||
160 | + .put(ConstraintCodec.INCLUSIVE, linkTypeConstraint.isInclusive()); | ||
161 | + | ||
162 | + final ArrayNode jsonTypes = result.putArray(ConstraintCodec.TYPES); | ||
163 | + | ||
164 | + if (linkTypeConstraint.types() != null) { | ||
165 | + for (Link.Type type : linkTypeConstraint.types()) { | ||
166 | + jsonTypes.add(type.name()); | ||
167 | + } | ||
168 | + } | ||
169 | + | ||
170 | + return result; | ||
171 | + } | ||
172 | + | ||
173 | + /** | ||
174 | + * Encodes the constraint in JSON. | ||
175 | + * | ||
176 | + * @return JSON node | ||
177 | + */ | ||
178 | + public ObjectNode encode() { | ||
179 | + final ObjectNode result; | ||
180 | + if (constraint instanceof BandwidthConstraint) { | ||
181 | + result = encodeBandwidthConstraint(); | ||
182 | + } else if (constraint instanceof LambdaConstraint) { | ||
183 | + result = encodeLambdaConstraint(); | ||
184 | + } else if (constraint instanceof LinkTypeConstraint) { | ||
185 | + result = encodeLinkTypeConstraint(); | ||
186 | + } else if (constraint instanceof AnnotationConstraint) { | ||
187 | + result = encodeAnnotationConstraint(); | ||
188 | + } else if (constraint instanceof LatencyConstraint) { | ||
189 | + result = encodeLatencyConstraint(); | ||
190 | + } else if (constraint instanceof ObstacleConstraint) { | ||
191 | + result = encodeObstacleConstraint(); | ||
192 | + } else if (constraint instanceof WaypointConstraint) { | ||
193 | + result = encodeWaypointConstraint(); | ||
194 | + } else { | ||
195 | + result = context.mapper().createObjectNode(); | ||
196 | + } | ||
197 | + | ||
198 | + result.put(ConstraintCodec.TYPE, constraint.getClass().getSimpleName()); | ||
199 | + return result; | ||
200 | + } | ||
201 | +} |
... | @@ -75,14 +75,14 @@ public final class FlowRuleCodec extends JsonCodec<FlowRule> { | ... | @@ -75,14 +75,14 @@ public final class FlowRuleCodec extends JsonCodec<FlowRule> { |
75 | DEVICE_ID + MISSING_MEMBER_MESSAGE).asText()); | 75 | DEVICE_ID + MISSING_MEMBER_MESSAGE).asText()); |
76 | resultBuilder.forDevice(deviceId); | 76 | resultBuilder.forDevice(deviceId); |
77 | 77 | ||
78 | - ObjectNode treatmentJson = (ObjectNode) json.get(TREATMENT); | 78 | + ObjectNode treatmentJson = get(json, TREATMENT); |
79 | if (treatmentJson != null) { | 79 | if (treatmentJson != null) { |
80 | JsonCodec<TrafficTreatment> treatmentCodec = | 80 | JsonCodec<TrafficTreatment> treatmentCodec = |
81 | context.codec(TrafficTreatment.class); | 81 | context.codec(TrafficTreatment.class); |
82 | resultBuilder.withTreatment(treatmentCodec.decode(treatmentJson, context)); | 82 | resultBuilder.withTreatment(treatmentCodec.decode(treatmentJson, context)); |
83 | } | 83 | } |
84 | 84 | ||
85 | - ObjectNode selectorJson = (ObjectNode) json.get(SELECTOR); | 85 | + ObjectNode selectorJson = get(json, SELECTOR); |
86 | if (selectorJson != null) { | 86 | if (selectorJson != null) { |
87 | JsonCodec<TrafficSelector> selectorCodec = | 87 | JsonCodec<TrafficSelector> selectorCodec = |
88 | context.codec(TrafficSelector.class); | 88 | context.codec(TrafficSelector.class); | ... | ... |
... | @@ -17,18 +17,23 @@ package org.onosproject.codec.impl; | ... | @@ -17,18 +17,23 @@ package org.onosproject.codec.impl; |
17 | 17 | ||
18 | import org.onosproject.codec.CodecContext; | 18 | import org.onosproject.codec.CodecContext; |
19 | import org.onosproject.codec.JsonCodec; | 19 | import org.onosproject.codec.JsonCodec; |
20 | +import org.onosproject.net.HostId; | ||
20 | import org.onosproject.net.intent.ConnectivityIntent; | 21 | import org.onosproject.net.intent.ConnectivityIntent; |
21 | import org.onosproject.net.intent.HostToHostIntent; | 22 | import org.onosproject.net.intent.HostToHostIntent; |
22 | 23 | ||
23 | import com.fasterxml.jackson.databind.node.ObjectNode; | 24 | import com.fasterxml.jackson.databind.node.ObjectNode; |
24 | 25 | ||
25 | import static com.google.common.base.Preconditions.checkNotNull; | 26 | import static com.google.common.base.Preconditions.checkNotNull; |
27 | +import static org.onlab.util.Tools.nullIsIllegal; | ||
26 | 28 | ||
27 | /** | 29 | /** |
28 | * Host to host intent codec. | 30 | * Host to host intent codec. |
29 | */ | 31 | */ |
30 | public final class HostToHostIntentCodec extends JsonCodec<HostToHostIntent> { | 32 | public final class HostToHostIntentCodec extends JsonCodec<HostToHostIntent> { |
31 | 33 | ||
34 | + private static final String ONE = "one"; | ||
35 | + private static final String TWO = "two"; | ||
36 | + | ||
32 | @Override | 37 | @Override |
33 | public ObjectNode encode(HostToHostIntent intent, CodecContext context) { | 38 | public ObjectNode encode(HostToHostIntent intent, CodecContext context) { |
34 | checkNotNull(intent, "Host to host intent cannot be null"); | 39 | checkNotNull(intent, "Host to host intent cannot be null"); |
... | @@ -39,9 +44,27 @@ public final class HostToHostIntentCodec extends JsonCodec<HostToHostIntent> { | ... | @@ -39,9 +44,27 @@ public final class HostToHostIntentCodec extends JsonCodec<HostToHostIntent> { |
39 | 44 | ||
40 | final String one = intent.one().toString(); | 45 | final String one = intent.one().toString(); |
41 | final String two = intent.two().toString(); | 46 | final String two = intent.two().toString(); |
42 | - result.put("one", one); | 47 | + result.put(ONE, one); |
43 | - result.put("two", two); | 48 | + result.put(TWO, two); |
44 | 49 | ||
45 | return result; | 50 | return result; |
46 | } | 51 | } |
52 | + | ||
53 | + @Override | ||
54 | + public HostToHostIntent decode(ObjectNode json, CodecContext context) { | ||
55 | + HostToHostIntent.Builder builder = HostToHostIntent.builder(); | ||
56 | + | ||
57 | + IntentCodec.intentAttributes(json, context, builder); | ||
58 | + ConnectivityIntentCodec.intentAttributes(json, context, builder); | ||
59 | + | ||
60 | + String one = nullIsIllegal(json.get(ONE), | ||
61 | + ONE + IntentCodec.MISSING_MEMBER_MESSAGE).asText(); | ||
62 | + builder.one(HostId.hostId(one)); | ||
63 | + | ||
64 | + String two = nullIsIllegal(json.get(TWO), | ||
65 | + TWO + IntentCodec.MISSING_MEMBER_MESSAGE).asText(); | ||
66 | + builder.two(HostId.hostId(two)); | ||
67 | + | ||
68 | + return builder.build(); | ||
69 | + } | ||
47 | } | 70 | } | ... | ... |
... | @@ -17,31 +17,46 @@ package org.onosproject.codec.impl; | ... | @@ -17,31 +17,46 @@ package org.onosproject.codec.impl; |
17 | 17 | ||
18 | import org.onosproject.codec.CodecContext; | 18 | import org.onosproject.codec.CodecContext; |
19 | import org.onosproject.codec.JsonCodec; | 19 | import org.onosproject.codec.JsonCodec; |
20 | +import org.onosproject.core.CoreService; | ||
20 | import org.onosproject.net.NetworkResource; | 21 | import org.onosproject.net.NetworkResource; |
22 | +import org.onosproject.net.intent.HostToHostIntent; | ||
21 | import org.onosproject.net.intent.Intent; | 23 | import org.onosproject.net.intent.Intent; |
22 | import org.onosproject.net.intent.IntentService; | 24 | import org.onosproject.net.intent.IntentService; |
23 | import org.onosproject.net.intent.IntentState; | 25 | import org.onosproject.net.intent.IntentState; |
26 | +import org.onosproject.net.intent.PointToPointIntent; | ||
24 | 27 | ||
28 | +import com.fasterxml.jackson.databind.JsonNode; | ||
25 | import com.fasterxml.jackson.databind.node.ArrayNode; | 29 | import com.fasterxml.jackson.databind.node.ArrayNode; |
26 | import com.fasterxml.jackson.databind.node.ObjectNode; | 30 | import com.fasterxml.jackson.databind.node.ObjectNode; |
27 | 31 | ||
28 | import static com.google.common.base.Preconditions.checkNotNull; | 32 | import static com.google.common.base.Preconditions.checkNotNull; |
33 | +import static org.onlab.util.Tools.nullIsIllegal; | ||
29 | 34 | ||
30 | /** | 35 | /** |
31 | * Intent JSON codec. | 36 | * Intent JSON codec. |
32 | */ | 37 | */ |
33 | public final class IntentCodec extends JsonCodec<Intent> { | 38 | public final class IntentCodec extends JsonCodec<Intent> { |
34 | 39 | ||
40 | + protected static final String TYPE = "type"; | ||
41 | + protected static final String ID = "id"; | ||
42 | + protected static final String APP_ID = "appId"; | ||
43 | + protected static final String DETAILS = "details"; | ||
44 | + protected static final String STATE = "state"; | ||
45 | + protected static final String PRIORITY = "priority"; | ||
46 | + protected static final String RESOURCES = "resources"; | ||
47 | + protected static final String MISSING_MEMBER_MESSAGE = | ||
48 | + " member is required in Intent"; | ||
49 | + | ||
35 | @Override | 50 | @Override |
36 | public ObjectNode encode(Intent intent, CodecContext context) { | 51 | public ObjectNode encode(Intent intent, CodecContext context) { |
37 | checkNotNull(intent, "Intent cannot be null"); | 52 | checkNotNull(intent, "Intent cannot be null"); |
38 | final ObjectNode result = context.mapper().createObjectNode() | 53 | final ObjectNode result = context.mapper().createObjectNode() |
39 | - .put("type", intent.getClass().getSimpleName()) | 54 | + .put(TYPE, intent.getClass().getSimpleName()) |
40 | - .put("id", intent.id().toString()) | 55 | + .put(ID, intent.id().toString()) |
41 | - .put("appId", intent.appId().toString()) | 56 | + .put(APP_ID, intent.appId().toString()) |
42 | - .put("details", intent.toString()); | 57 | + .put(DETAILS, intent.toString()); |
43 | 58 | ||
44 | - final ArrayNode jsonResources = result.putArray("resources"); | 59 | + final ArrayNode jsonResources = result.putArray(RESOURCES); |
45 | 60 | ||
46 | for (final NetworkResource resource : intent.resources()) { | 61 | for (final NetworkResource resource : intent.resources()) { |
47 | jsonResources.add(resource.toString()); | 62 | jsonResources.add(resource.toString()); |
... | @@ -50,9 +65,47 @@ public final class IntentCodec extends JsonCodec<Intent> { | ... | @@ -50,9 +65,47 @@ public final class IntentCodec extends JsonCodec<Intent> { |
50 | IntentService service = context.getService(IntentService.class); | 65 | IntentService service = context.getService(IntentService.class); |
51 | IntentState state = service.getIntentState(intent.key()); | 66 | IntentState state = service.getIntentState(intent.key()); |
52 | if (state != null) { | 67 | if (state != null) { |
53 | - result.put("state", state.toString()); | 68 | + result.put(STATE, state.toString()); |
54 | } | 69 | } |
55 | 70 | ||
56 | return result; | 71 | return result; |
57 | } | 72 | } |
73 | + | ||
74 | + @Override | ||
75 | + public Intent decode(ObjectNode json, CodecContext context) { | ||
76 | + checkNotNull(json, "JSON cannot be null"); | ||
77 | + | ||
78 | + String type = nullIsIllegal(json.get(TYPE), | ||
79 | + TYPE + MISSING_MEMBER_MESSAGE).asText(); | ||
80 | + | ||
81 | + if (type.equals(PointToPointIntent.class.getSimpleName())) { | ||
82 | + return context.codec(PointToPointIntent.class).decode(json, context); | ||
83 | + } else if (type.equals(HostToHostIntent.class.getSimpleName())) { | ||
84 | + return context.codec(HostToHostIntent.class).decode(json, context); | ||
85 | + } | ||
86 | + | ||
87 | + throw new IllegalArgumentException("Intent type " | ||
88 | + + type + " is not supported"); | ||
89 | + } | ||
90 | + | ||
91 | + /** | ||
92 | + * Extracts base intent specific attributes from a JSON object | ||
93 | + * and adds them to a builder. | ||
94 | + * | ||
95 | + * @param json root JSON object | ||
96 | + * @param context code context | ||
97 | + * @param builder builder to use for storing the attributes | ||
98 | + */ | ||
99 | + public static void intentAttributes(ObjectNode json, CodecContext context, | ||
100 | + Intent.Builder builder) { | ||
101 | + short appId = (short) nullIsIllegal(json.get(IntentCodec.APP_ID), | ||
102 | + IntentCodec.TYPE + IntentCodec.MISSING_MEMBER_MESSAGE).asInt(); | ||
103 | + CoreService service = context.getService(CoreService.class); | ||
104 | + builder.appId(service.getAppId(appId)); | ||
105 | + | ||
106 | + JsonNode priorityJson = json.get(IntentCodec.PRIORITY); | ||
107 | + if (priorityJson != null) { | ||
108 | + builder.priority(priorityJson.asInt()); | ||
109 | + } | ||
110 | + } | ||
58 | } | 111 | } | ... | ... |
... | @@ -70,8 +70,8 @@ public final class LinkCodec extends AnnotatedCodec<Link> { | ... | @@ -70,8 +70,8 @@ public final class LinkCodec extends AnnotatedCodec<Link> { |
70 | // TODO: add providerId to JSON if we need to recover them. | 70 | // TODO: add providerId to JSON if we need to recover them. |
71 | ProviderId pid = new ProviderId("json", "LinkCodec"); | 71 | ProviderId pid = new ProviderId("json", "LinkCodec"); |
72 | 72 | ||
73 | - ConnectPoint src = codec.decode((ObjectNode) json.get(SRC), context); | 73 | + ConnectPoint src = codec.decode(get(json, SRC), context); |
74 | - ConnectPoint dst = codec.decode((ObjectNode) json.get(DST), context); | 74 | + ConnectPoint dst = codec.decode(get(json, DST), context); |
75 | Type type = Type.valueOf(json.get(TYPE).asText()); | 75 | Type type = Type.valueOf(json.get(TYPE).asText()); |
76 | Annotations annotations = extractAnnotations(json, context); | 76 | Annotations annotations = extractAnnotations(json, context); |
77 | 77 | ... | ... |
... | @@ -24,12 +24,16 @@ import org.onosproject.net.intent.PointToPointIntent; | ... | @@ -24,12 +24,16 @@ import org.onosproject.net.intent.PointToPointIntent; |
24 | import com.fasterxml.jackson.databind.node.ObjectNode; | 24 | import com.fasterxml.jackson.databind.node.ObjectNode; |
25 | 25 | ||
26 | import static com.google.common.base.Preconditions.checkNotNull; | 26 | import static com.google.common.base.Preconditions.checkNotNull; |
27 | +import static org.onlab.util.Tools.nullIsIllegal; | ||
27 | 28 | ||
28 | /** | 29 | /** |
29 | * Point to point intent codec. | 30 | * Point to point intent codec. |
30 | */ | 31 | */ |
31 | public final class PointToPointIntentCodec extends JsonCodec<PointToPointIntent> { | 32 | public final class PointToPointIntentCodec extends JsonCodec<PointToPointIntent> { |
32 | 33 | ||
34 | + private static final String INGRESS_POINT = "ingressPoint"; | ||
35 | + private static final String EGRESS_POINT = "egressPoint"; | ||
36 | + | ||
33 | @Override | 37 | @Override |
34 | public ObjectNode encode(PointToPointIntent intent, CodecContext context) { | 38 | public ObjectNode encode(PointToPointIntent intent, CodecContext context) { |
35 | checkNotNull(intent, "Point to point intent cannot be null"); | 39 | checkNotNull(intent, "Point to point intent cannot be null"); |
... | @@ -45,9 +49,32 @@ public final class PointToPointIntentCodec extends JsonCodec<PointToPointIntent> | ... | @@ -45,9 +49,32 @@ public final class PointToPointIntentCodec extends JsonCodec<PointToPointIntent> |
45 | final ObjectNode egress = | 49 | final ObjectNode egress = |
46 | connectPointCodec.encode(intent.egressPoint(), context); | 50 | connectPointCodec.encode(intent.egressPoint(), context); |
47 | 51 | ||
48 | - result.set("ingressPoint", ingress); | 52 | + result.set(INGRESS_POINT, ingress); |
49 | - result.set("egressPoint", egress); | 53 | + result.set(EGRESS_POINT, egress); |
50 | 54 | ||
51 | return result; | 55 | return result; |
52 | } | 56 | } |
57 | + | ||
58 | + | ||
59 | + @Override | ||
60 | + public PointToPointIntent decode(ObjectNode json, CodecContext context) { | ||
61 | + PointToPointIntent.Builder builder = PointToPointIntent.builder(); | ||
62 | + | ||
63 | + IntentCodec.intentAttributes(json, context, builder); | ||
64 | + ConnectivityIntentCodec.intentAttributes(json, context, builder); | ||
65 | + | ||
66 | + ObjectNode ingressJson = nullIsIllegal(get(json, INGRESS_POINT), | ||
67 | + INGRESS_POINT + IntentCodec.MISSING_MEMBER_MESSAGE); | ||
68 | + ConnectPoint ingress = context.codec(ConnectPoint.class) | ||
69 | + .decode(ingressJson, context); | ||
70 | + builder.ingressPoint(ingress); | ||
71 | + | ||
72 | + ObjectNode egressJson = nullIsIllegal(get(json, EGRESS_POINT), | ||
73 | + EGRESS_POINT + IntentCodec.MISSING_MEMBER_MESSAGE); | ||
74 | + ConnectPoint egress = context.codec(ConnectPoint.class) | ||
75 | + .decode(egressJson, context); | ||
76 | + builder.egressPoint(egress); | ||
77 | + | ||
78 | + return builder.build(); | ||
79 | + } | ||
53 | } | 80 | } | ... | ... |
... | @@ -63,7 +63,7 @@ public final class TrafficSelectorCodec extends JsonCodec<TrafficSelector> { | ... | @@ -63,7 +63,7 @@ public final class TrafficSelectorCodec extends JsonCodec<TrafficSelector> { |
63 | if (criteriaJson != null) { | 63 | if (criteriaJson != null) { |
64 | IntStream.range(0, criteriaJson.size()) | 64 | IntStream.range(0, criteriaJson.size()) |
65 | .forEach(i -> builder.add( | 65 | .forEach(i -> builder.add( |
66 | - criterionCodec.decode((ObjectNode) criteriaJson.get(i), | 66 | + criterionCodec.decode(get(criteriaJson, i), |
67 | context))); | 67 | context))); |
68 | } | 68 | } |
69 | return builder.build(); | 69 | return builder.build(); | ... | ... |
... | @@ -68,7 +68,7 @@ public final class TrafficTreatmentCodec extends JsonCodec<TrafficTreatment> { | ... | @@ -68,7 +68,7 @@ public final class TrafficTreatmentCodec extends JsonCodec<TrafficTreatment> { |
68 | if (instructionsJson != null) { | 68 | if (instructionsJson != null) { |
69 | IntStream.range(0, instructionsJson.size()) | 69 | IntStream.range(0, instructionsJson.size()) |
70 | .forEach(i -> builder.add( | 70 | .forEach(i -> builder.add( |
71 | - instructionsCodec.decode((ObjectNode) instructionsJson.get(i), | 71 | + instructionsCodec.decode(get(instructionsJson, i), |
72 | context))); | 72 | context))); |
73 | } | 73 | } |
74 | return builder.build(); | 74 | return builder.build(); | ... | ... |
1 | +/* | ||
2 | + * Copyright 2015 Open Networking Laboratory | ||
3 | + * | ||
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 | + */ | ||
16 | +package org.onosproject.codec.impl; | ||
17 | + | ||
18 | +import java.io.IOException; | ||
19 | +import java.io.InputStream; | ||
20 | + | ||
21 | +import org.junit.Assert; | ||
22 | +import org.junit.Before; | ||
23 | +import org.junit.Test; | ||
24 | +import org.onosproject.codec.JsonCodec; | ||
25 | +import org.onosproject.core.CoreService; | ||
26 | +import org.onosproject.net.Link; | ||
27 | +import org.onosproject.net.intent.Constraint; | ||
28 | +import org.onosproject.net.intent.constraint.AnnotationConstraint; | ||
29 | +import org.onosproject.net.intent.constraint.AsymmetricPathConstraint; | ||
30 | +import org.onosproject.net.intent.constraint.BandwidthConstraint; | ||
31 | +import org.onosproject.net.intent.constraint.LambdaConstraint; | ||
32 | +import org.onosproject.net.intent.constraint.LatencyConstraint; | ||
33 | +import org.onosproject.net.intent.constraint.LinkTypeConstraint; | ||
34 | +import org.onosproject.net.intent.constraint.ObstacleConstraint; | ||
35 | +import org.onosproject.net.intent.constraint.WaypointConstraint; | ||
36 | + | ||
37 | +import com.fasterxml.jackson.databind.JsonNode; | ||
38 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
39 | + | ||
40 | +import static com.google.common.base.Preconditions.checkNotNull; | ||
41 | +import static org.easymock.EasyMock.createMock; | ||
42 | +import static org.easymock.EasyMock.expect; | ||
43 | +import static org.easymock.EasyMock.replay; | ||
44 | +import static org.hamcrest.MatcherAssert.assertThat; | ||
45 | +import static org.hamcrest.Matchers.hasItem; | ||
46 | +import static org.hamcrest.Matchers.hasSize; | ||
47 | +import static org.hamcrest.Matchers.instanceOf; | ||
48 | +import static org.hamcrest.Matchers.is; | ||
49 | +import static org.hamcrest.Matchers.notNullValue; | ||
50 | +import static org.onosproject.net.NetTestTools.APP_ID; | ||
51 | +import static org.onosproject.net.NetTestTools.did; | ||
52 | + | ||
53 | +/** | ||
54 | + * Unit tests for Constraint codec. | ||
55 | + */ | ||
56 | +public class ConstraintCodecTest { | ||
57 | + | ||
58 | + MockCodecContext context; | ||
59 | + JsonCodec<Constraint> constraintCodec; | ||
60 | + final CoreService mockCoreService = createMock(CoreService.class); | ||
61 | + | ||
62 | + /** | ||
63 | + * Sets up for each test. Creates a context and fetches the flow rule | ||
64 | + * codec. | ||
65 | + */ | ||
66 | + @Before | ||
67 | + public void setUp() { | ||
68 | + context = new MockCodecContext(); | ||
69 | + constraintCodec = context.codec(Constraint.class); | ||
70 | + assertThat(constraintCodec, notNullValue()); | ||
71 | + | ||
72 | + expect(mockCoreService.registerApplication(FlowRuleCodec.REST_APP_ID)) | ||
73 | + .andReturn(APP_ID).anyTimes(); | ||
74 | + replay(mockCoreService); | ||
75 | + context.registerService(CoreService.class, mockCoreService); | ||
76 | + } | ||
77 | + | ||
78 | + /** | ||
79 | + * Reads in a constraint from the given resource and decodes it. | ||
80 | + * | ||
81 | + * @param resourceName resource to use to read the JSON for the constraint | ||
82 | + * @return decoded constraint | ||
83 | + */ | ||
84 | + private Constraint getConstraint(String resourceName) { | ||
85 | + InputStream jsonStream = ConstraintCodecTest.class | ||
86 | + .getResourceAsStream(resourceName); | ||
87 | + try { | ||
88 | + JsonNode json = context.mapper().readTree(jsonStream); | ||
89 | + assertThat(json, notNullValue()); | ||
90 | + Constraint constraint = constraintCodec.decode((ObjectNode) json, context); | ||
91 | + assertThat(constraint, notNullValue()); | ||
92 | + return checkNotNull(constraint); | ||
93 | + } catch (IOException ioe) { | ||
94 | + Assert.fail(ioe.getMessage()); | ||
95 | + throw new IllegalStateException("cannot happen"); | ||
96 | + } | ||
97 | + } | ||
98 | + | ||
99 | + | ||
100 | + /** | ||
101 | + * Tests link type constraint. | ||
102 | + */ | ||
103 | + @Test | ||
104 | + public void linkTypeConstraint() { | ||
105 | + Constraint constraint = getConstraint("LinkTypeConstraint.json"); | ||
106 | + assertThat(constraint, instanceOf(LinkTypeConstraint.class)); | ||
107 | + | ||
108 | + LinkTypeConstraint linkTypeConstraint = (LinkTypeConstraint) constraint; | ||
109 | + assertThat(linkTypeConstraint.isInclusive(), is(false)); | ||
110 | + assertThat(linkTypeConstraint.types(), hasSize(2)); | ||
111 | + assertThat(linkTypeConstraint.types(), hasItem(Link.Type.OPTICAL)); | ||
112 | + assertThat(linkTypeConstraint.types(), hasItem(Link.Type.DIRECT)); | ||
113 | + } | ||
114 | + | ||
115 | + /** | ||
116 | + * Tests annotation constraint. | ||
117 | + */ | ||
118 | + @Test | ||
119 | + public void annotationConstraint() { | ||
120 | + Constraint constraint = getConstraint("AnnotationConstraint.json"); | ||
121 | + assertThat(constraint, instanceOf(AnnotationConstraint.class)); | ||
122 | + | ||
123 | + AnnotationConstraint annotationConstraint = (AnnotationConstraint) constraint; | ||
124 | + assertThat(annotationConstraint.key(), is("key")); | ||
125 | + assertThat(annotationConstraint.threshold(), is(123.0D)); | ||
126 | + } | ||
127 | + | ||
128 | + /** | ||
129 | + * Tests bandwidth constraint. | ||
130 | + */ | ||
131 | + @Test | ||
132 | + public void bandwidthConstraint() { | ||
133 | + Constraint constraint = getConstraint("BandwidthConstraint.json"); | ||
134 | + assertThat(constraint, instanceOf(BandwidthConstraint.class)); | ||
135 | + | ||
136 | + BandwidthConstraint bandwidthConstraint = (BandwidthConstraint) constraint; | ||
137 | + assertThat(bandwidthConstraint.bandwidth().toDouble(), is(345.678D)); | ||
138 | + } | ||
139 | + | ||
140 | + /** | ||
141 | + * Tests lambda constraint. | ||
142 | + */ | ||
143 | + @Test | ||
144 | + public void lambdaConstraint() { | ||
145 | + Constraint constraint = getConstraint("LambdaConstraint.json"); | ||
146 | + assertThat(constraint, instanceOf(LambdaConstraint.class)); | ||
147 | + | ||
148 | + LambdaConstraint lambdaConstraint = (LambdaConstraint) constraint; | ||
149 | + assertThat(lambdaConstraint.lambda().toInt(), is(444)); | ||
150 | + } | ||
151 | + | ||
152 | + /** | ||
153 | + * Tests latency constraint. | ||
154 | + */ | ||
155 | + @Test | ||
156 | + public void latencyConstraint() { | ||
157 | + Constraint constraint = getConstraint("LatencyConstraint.json"); | ||
158 | + assertThat(constraint, instanceOf(LatencyConstraint.class)); | ||
159 | + | ||
160 | + LatencyConstraint latencyConstraint = (LatencyConstraint) constraint; | ||
161 | + assertThat(latencyConstraint.latency().toMillis(), is(111L)); | ||
162 | + } | ||
163 | + | ||
164 | + /** | ||
165 | + * Tests obstacle constraint. | ||
166 | + */ | ||
167 | + @Test | ||
168 | + public void obstacleConstraint() { | ||
169 | + Constraint constraint = getConstraint("ObstacleConstraint.json"); | ||
170 | + assertThat(constraint, instanceOf(ObstacleConstraint.class)); | ||
171 | + | ||
172 | + ObstacleConstraint obstacleConstraint = (ObstacleConstraint) constraint; | ||
173 | + | ||
174 | + assertThat(obstacleConstraint.obstacles(), hasItem(did("dev1"))); | ||
175 | + assertThat(obstacleConstraint.obstacles(), hasItem(did("dev2"))); | ||
176 | + assertThat(obstacleConstraint.obstacles(), hasItem(did("dev3"))); | ||
177 | + } | ||
178 | + | ||
179 | + /** | ||
180 | + * Tests waypoint constaint. | ||
181 | + */ | ||
182 | + @Test | ||
183 | + public void waypointConstraint() { | ||
184 | + Constraint constraint = getConstraint("WaypointConstraint.json"); | ||
185 | + assertThat(constraint, instanceOf(WaypointConstraint.class)); | ||
186 | + | ||
187 | + WaypointConstraint waypointConstraint = (WaypointConstraint) constraint; | ||
188 | + | ||
189 | + assertThat(waypointConstraint.waypoints(), hasItem(did("devA"))); | ||
190 | + assertThat(waypointConstraint.waypoints(), hasItem(did("devB"))); | ||
191 | + assertThat(waypointConstraint.waypoints(), hasItem(did("devC"))); | ||
192 | + } | ||
193 | + | ||
194 | + /** | ||
195 | + * Tests asymmetric path constraint. | ||
196 | + */ | ||
197 | + @Test | ||
198 | + public void asymmetricPathConstraint() { | ||
199 | + Constraint constraint = getConstraint("AsymmetricPathConstraint.json"); | ||
200 | + assertThat(constraint, instanceOf(AsymmetricPathConstraint.class)); | ||
201 | + } | ||
202 | +} |
... | @@ -15,6 +15,8 @@ | ... | @@ -15,6 +15,8 @@ |
15 | */ | 15 | */ |
16 | package org.onosproject.codec.impl; | 16 | package org.onosproject.codec.impl; |
17 | 17 | ||
18 | +import java.io.IOException; | ||
19 | +import java.io.InputStream; | ||
18 | import java.time.Duration; | 20 | import java.time.Duration; |
19 | import java.util.List; | 21 | import java.util.List; |
20 | 22 | ||
... | @@ -26,6 +28,7 @@ import org.onlab.packet.MplsLabel; | ... | @@ -26,6 +28,7 @@ import org.onlab.packet.MplsLabel; |
26 | import org.onlab.util.Bandwidth; | 28 | import org.onlab.util.Bandwidth; |
27 | import org.onosproject.codec.JsonCodec; | 29 | import org.onosproject.codec.JsonCodec; |
28 | import org.onosproject.core.ApplicationId; | 30 | import org.onosproject.core.ApplicationId; |
31 | +import org.onosproject.core.CoreService; | ||
29 | import org.onosproject.core.DefaultApplicationId; | 32 | import org.onosproject.core.DefaultApplicationId; |
30 | import org.onosproject.net.ChannelSpacing; | 33 | import org.onosproject.net.ChannelSpacing; |
31 | import org.onosproject.net.ConnectPoint; | 34 | import org.onosproject.net.ConnectPoint; |
... | @@ -43,9 +46,13 @@ import org.onosproject.net.flow.TrafficSelector; | ... | @@ -43,9 +46,13 @@ import org.onosproject.net.flow.TrafficSelector; |
43 | import org.onosproject.net.flow.TrafficTreatment; | 46 | import org.onosproject.net.flow.TrafficTreatment; |
44 | import org.onosproject.net.flow.criteria.Criteria; | 47 | import org.onosproject.net.flow.criteria.Criteria; |
45 | import org.onosproject.net.flow.instructions.Instructions; | 48 | import org.onosproject.net.flow.instructions.Instructions; |
49 | +import org.onosproject.net.flow.criteria.Criterion; | ||
50 | +import org.onosproject.net.flow.criteria.EthCriterion; | ||
51 | +import org.onosproject.net.flow.instructions.Instruction; | ||
46 | import org.onosproject.net.intent.AbstractIntentTest; | 52 | import org.onosproject.net.intent.AbstractIntentTest; |
47 | import org.onosproject.net.intent.Constraint; | 53 | import org.onosproject.net.intent.Constraint; |
48 | import org.onosproject.net.intent.HostToHostIntent; | 54 | import org.onosproject.net.intent.HostToHostIntent; |
55 | +import org.onosproject.net.intent.Intent; | ||
49 | import org.onosproject.net.intent.IntentService; | 56 | import org.onosproject.net.intent.IntentService; |
50 | import org.onosproject.net.intent.IntentServiceAdapter; | 57 | import org.onosproject.net.intent.IntentServiceAdapter; |
51 | import org.onosproject.net.intent.PointToPointIntent; | 58 | import org.onosproject.net.intent.PointToPointIntent; |
... | @@ -59,14 +66,22 @@ import org.onosproject.net.intent.constraint.WaypointConstraint; | ... | @@ -59,14 +66,22 @@ import org.onosproject.net.intent.constraint.WaypointConstraint; |
59 | import org.onosproject.net.resource.link.BandwidthResource; | 66 | import org.onosproject.net.resource.link.BandwidthResource; |
60 | import org.onosproject.net.resource.link.LambdaResource; | 67 | import org.onosproject.net.resource.link.LambdaResource; |
61 | 68 | ||
69 | +import com.fasterxml.jackson.databind.JsonNode; | ||
62 | import com.fasterxml.jackson.databind.node.ObjectNode; | 70 | import com.fasterxml.jackson.databind.node.ObjectNode; |
63 | import com.google.common.collect.ImmutableList; | 71 | import com.google.common.collect.ImmutableList; |
64 | 72 | ||
73 | +import static org.easymock.EasyMock.createMock; | ||
74 | +import static org.easymock.EasyMock.expect; | ||
75 | +import static org.easymock.EasyMock.replay; | ||
65 | import static org.hamcrest.MatcherAssert.assertThat; | 76 | import static org.hamcrest.MatcherAssert.assertThat; |
77 | +import static org.hamcrest.Matchers.hasSize; | ||
78 | +import static org.hamcrest.Matchers.instanceOf; | ||
79 | +import static org.hamcrest.Matchers.is; | ||
66 | import static org.hamcrest.Matchers.notNullValue; | 80 | import static org.hamcrest.Matchers.notNullValue; |
67 | import static org.onosproject.codec.impl.IntentJsonMatcher.matchesIntent; | 81 | import static org.onosproject.codec.impl.IntentJsonMatcher.matchesIntent; |
68 | import static org.onosproject.net.NetTestTools.did; | 82 | import static org.onosproject.net.NetTestTools.did; |
69 | import static org.onosproject.net.NetTestTools.hid; | 83 | import static org.onosproject.net.NetTestTools.hid; |
84 | +import static org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction; | ||
70 | 85 | ||
71 | /** | 86 | /** |
72 | * Unit tests for the host to host intent class codec. | 87 | * Unit tests for the host to host intent class codec. |
... | @@ -81,11 +96,16 @@ public class IntentCodecTest extends AbstractIntentTest { | ... | @@ -81,11 +96,16 @@ public class IntentCodecTest extends AbstractIntentTest { |
81 | final TrafficTreatment emptyTreatment = | 96 | final TrafficTreatment emptyTreatment = |
82 | DefaultTrafficTreatment.emptyTreatment(); | 97 | DefaultTrafficTreatment.emptyTreatment(); |
83 | private final MockCodecContext context = new MockCodecContext(); | 98 | private final MockCodecContext context = new MockCodecContext(); |
99 | + final CoreService mockCoreService = createMock(CoreService.class); | ||
84 | 100 | ||
85 | @Before | 101 | @Before |
86 | public void setUpIntentService() { | 102 | public void setUpIntentService() { |
87 | final IntentService mockIntentService = new IntentServiceAdapter(); | 103 | final IntentService mockIntentService = new IntentServiceAdapter(); |
88 | context.registerService(IntentService.class, mockIntentService); | 104 | context.registerService(IntentService.class, mockIntentService); |
105 | + context.registerService(CoreService.class, mockCoreService); | ||
106 | + expect(mockCoreService.getAppId((short) 2)) | ||
107 | + .andReturn(new DefaultApplicationId(2, "app")); | ||
108 | + replay(mockCoreService); | ||
89 | } | 109 | } |
90 | 110 | ||
91 | /** | 111 | /** |
... | @@ -188,4 +208,81 @@ public class IntentCodecTest extends AbstractIntentTest { | ... | @@ -188,4 +208,81 @@ public class IntentCodecTest extends AbstractIntentTest { |
188 | assertThat(intentJson, matchesIntent(intent)); | 208 | assertThat(intentJson, matchesIntent(intent)); |
189 | 209 | ||
190 | } | 210 | } |
211 | + | ||
212 | + /** | ||
213 | + * Reads in a rule from the given resource and decodes it. | ||
214 | + * | ||
215 | + * @param resourceName resource to use to read the JSON for the rule | ||
216 | + * @return decoded flow rule | ||
217 | + * @throws IOException if processing the resource fails | ||
218 | + */ | ||
219 | + private Intent getIntent(String resourceName, JsonCodec intentCodec) throws IOException { | ||
220 | + InputStream jsonStream = FlowRuleCodecTest.class | ||
221 | + .getResourceAsStream(resourceName); | ||
222 | + JsonNode json = context.mapper().readTree(jsonStream); | ||
223 | + assertThat(json, notNullValue()); | ||
224 | + Intent intent = (Intent) intentCodec.decode((ObjectNode) json, context); | ||
225 | + assertThat(intent, notNullValue()); | ||
226 | + return intent; | ||
227 | + } | ||
228 | + | ||
229 | + /** | ||
230 | + * Tests the point to point intent JSON codec. | ||
231 | + * | ||
232 | + * @throws IOException if JSON processing fails | ||
233 | + */ | ||
234 | + @Test | ||
235 | + public void decodePointToPointIntent() throws IOException { | ||
236 | + JsonCodec<Intent> intentCodec = context.codec(Intent.class); | ||
237 | + assertThat(intentCodec, notNullValue()); | ||
238 | + | ||
239 | + Intent intent = getIntent("PointToPointIntent.json", intentCodec); | ||
240 | + assertThat(intent, notNullValue()); | ||
241 | + assertThat(intent, instanceOf(PointToPointIntent.class)); | ||
242 | + | ||
243 | + PointToPointIntent pointIntent = (PointToPointIntent) intent; | ||
244 | + assertThat(pointIntent.priority(), is(55)); | ||
245 | + assertThat(pointIntent.ingressPoint().deviceId(), is(did("0000000000000001"))); | ||
246 | + assertThat(pointIntent.ingressPoint().port(), is(PortNumber.portNumber(1))); | ||
247 | + assertThat(pointIntent.egressPoint().deviceId(), is(did("0000000000000007"))); | ||
248 | + assertThat(pointIntent.egressPoint().port(), is(PortNumber.portNumber(2))); | ||
249 | + | ||
250 | + assertThat(pointIntent.constraints(), hasSize(1)); | ||
251 | + | ||
252 | + assertThat(pointIntent.selector(), notNullValue()); | ||
253 | + assertThat(pointIntent.selector().criteria(), hasSize(1)); | ||
254 | + Criterion criterion1 = pointIntent.selector().criteria().iterator().next(); | ||
255 | + assertThat(criterion1, instanceOf(EthCriterion.class)); | ||
256 | + EthCriterion ethCriterion = (EthCriterion) criterion1; | ||
257 | + assertThat(ethCriterion.mac().toString(), is("11:22:33:44:55:66")); | ||
258 | + assertThat(ethCriterion.type().name(), is("ETH_DST")); | ||
259 | + | ||
260 | + assertThat(pointIntent.treatment(), notNullValue()); | ||
261 | + assertThat(pointIntent.treatment().allInstructions(), hasSize(1)); | ||
262 | + Instruction instruction1 = pointIntent.treatment().allInstructions().iterator().next(); | ||
263 | + assertThat(instruction1, instanceOf(ModEtherInstruction.class)); | ||
264 | + ModEtherInstruction ethInstruction = (ModEtherInstruction) instruction1; | ||
265 | + assertThat(ethInstruction.mac().toString(), is("22:33:44:55:66:77")); | ||
266 | + assertThat(ethInstruction.type().toString(), is("L2MODIFICATION")); | ||
267 | + assertThat(ethInstruction.subtype().toString(), is("ETH_SRC")); | ||
268 | + } | ||
269 | + | ||
270 | + /** | ||
271 | + * Tests the host to host intent JSON codec. | ||
272 | + * | ||
273 | + * @throws IOException | ||
274 | + */ | ||
275 | + @Test | ||
276 | + public void decodeHostToHostIntent() throws IOException { | ||
277 | + JsonCodec<Intent> intentCodec = context.codec(Intent.class); | ||
278 | + assertThat(intentCodec, notNullValue()); | ||
279 | + | ||
280 | + Intent intent = getIntent("HostToHostIntent.json", intentCodec); | ||
281 | + assertThat(intent, notNullValue()); | ||
282 | + assertThat(intent, instanceOf(HostToHostIntent.class)); | ||
283 | + | ||
284 | + HostToHostIntent hostIntent = (HostToHostIntent) intent; | ||
285 | + assertThat(hostIntent.priority(), is(7)); | ||
286 | + assertThat(hostIntent.constraints(), hasSize(1)); | ||
287 | + } | ||
191 | } | 288 | } | ... | ... |
1 | +{ | ||
2 | + "type": "HostToHostIntent", | ||
3 | + "appId": 2, | ||
4 | + "selector": {"criteria": []}, | ||
5 | + "treatment": { | ||
6 | + "instructions": [], | ||
7 | + "deferred": [] | ||
8 | + }, | ||
9 | + "priority": 7, | ||
10 | + "constraints": [ | ||
11 | + { | ||
12 | + "inclusive": false, | ||
13 | + "types": ["OPTICAL"], | ||
14 | + "type": "LinkTypeConstraint" | ||
15 | + } | ||
16 | + ], | ||
17 | + "one": "00:00:00:00:00:02/-1", | ||
18 | + "two": "00:00:00:00:00:05/-1" | ||
19 | +} |
1 | +{ | ||
2 | + "type": "PointToPointIntent", | ||
3 | + "appId": 2, | ||
4 | + "selector": { | ||
5 | + "criteria": [ | ||
6 | + { | ||
7 | + "type": "ETH_DST", | ||
8 | + "mac": "11:22:33:44:55:66" | ||
9 | + } | ||
10 | + ] | ||
11 | + }, | ||
12 | + "treatment": { | ||
13 | + "instructions": [ | ||
14 | + { | ||
15 | + "type": "L2MODIFICATION", | ||
16 | + "subtype": "ETH_SRC", | ||
17 | + "mac": "22:33:44:55:66:77" | ||
18 | + } | ||
19 | + ], | ||
20 | + "deferred": [] | ||
21 | + }, | ||
22 | + "priority": 55, | ||
23 | + "constraints": [ | ||
24 | + { | ||
25 | + "inclusive": false, | ||
26 | + "types": ["OPTICAL"], | ||
27 | + "type": "LinkTypeConstraint" | ||
28 | + } | ||
29 | + ], | ||
30 | + "ingressPoint": { | ||
31 | + "port": "1", | ||
32 | + "device": "of:0000000000000001" | ||
33 | + }, | ||
34 | + "egressPoint": { | ||
35 | + "port": "2", | ||
36 | + "device": "of:0000000000000007" | ||
37 | + } | ||
38 | +} |
... | @@ -15,12 +15,18 @@ | ... | @@ -15,12 +15,18 @@ |
15 | */ | 15 | */ |
16 | package org.onosproject.rest.resources; | 16 | package org.onosproject.rest.resources; |
17 | 17 | ||
18 | +import java.io.IOException; | ||
19 | +import java.io.InputStream; | ||
20 | +import java.net.URI; | ||
21 | +import java.net.URISyntaxException; | ||
18 | import java.util.Objects; | 22 | import java.util.Objects; |
19 | import java.util.concurrent.CountDownLatch; | 23 | import java.util.concurrent.CountDownLatch; |
20 | import java.util.concurrent.TimeUnit; | 24 | import java.util.concurrent.TimeUnit; |
21 | 25 | ||
26 | +import javax.ws.rs.Consumes; | ||
22 | import javax.ws.rs.DELETE; | 27 | import javax.ws.rs.DELETE; |
23 | import javax.ws.rs.GET; | 28 | import javax.ws.rs.GET; |
29 | +import javax.ws.rs.POST; | ||
24 | import javax.ws.rs.Path; | 30 | import javax.ws.rs.Path; |
25 | import javax.ws.rs.PathParam; | 31 | import javax.ws.rs.PathParam; |
26 | import javax.ws.rs.Produces; | 32 | import javax.ws.rs.Produces; |
... | @@ -177,4 +183,31 @@ public class IntentsWebResource extends AbstractWebResource { | ... | @@ -177,4 +183,31 @@ public class IntentsWebResource extends AbstractWebResource { |
177 | } | 183 | } |
178 | } | 184 | } |
179 | 185 | ||
186 | + /** | ||
187 | + * Creates an intent from a POST of a JSON string and attempts to apply it. | ||
188 | + * | ||
189 | + * @param stream input JSON | ||
190 | + * @return status of the request - CREATED if the JSON is correct, | ||
191 | + * BAD_REQUEST if the JSON is invalid | ||
192 | + */ | ||
193 | + @POST | ||
194 | + @Consumes(MediaType.APPLICATION_JSON) | ||
195 | + @Produces(MediaType.APPLICATION_JSON) | ||
196 | + public Response createIntent(InputStream stream) { | ||
197 | + URI location; | ||
198 | + try { | ||
199 | + IntentService service = get(IntentService.class); | ||
200 | + ObjectNode root = (ObjectNode) mapper().readTree(stream); | ||
201 | + Intent intent = codec(Intent.class).decode(root, this); | ||
202 | + service.submit(intent); | ||
203 | + location = new URI(Short.toString(intent.appId().id()) + "/" | ||
204 | + + Long.toString(intent.id().fingerprint())); | ||
205 | + } catch (IOException | URISyntaxException ex) { | ||
206 | + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); | ||
207 | + } | ||
208 | + return Response | ||
209 | + .created(location) | ||
210 | + .build(); | ||
211 | + } | ||
212 | + | ||
180 | } | 213 | } | ... | ... |
... | @@ -15,6 +15,7 @@ | ... | @@ -15,6 +15,7 @@ |
15 | */ | 15 | */ |
16 | package org.onosproject.rest; | 16 | package org.onosproject.rest; |
17 | 17 | ||
18 | +import java.io.InputStream; | ||
18 | import java.net.HttpURLConnection; | 19 | import java.net.HttpURLConnection; |
19 | import java.util.HashMap; | 20 | import java.util.HashMap; |
20 | import java.util.HashSet; | 21 | import java.util.HashSet; |
... | @@ -107,10 +108,6 @@ public class FlowsResourceTest extends ResourceTest { | ... | @@ -107,10 +108,6 @@ public class FlowsResourceTest extends ResourceTest { |
107 | final MockFlowEntry flow5 = new MockFlowEntry(deviceId2, 5); | 108 | final MockFlowEntry flow5 = new MockFlowEntry(deviceId2, 5); |
108 | final MockFlowEntry flow6 = new MockFlowEntry(deviceId2, 6); | 109 | final MockFlowEntry flow6 = new MockFlowEntry(deviceId2, 6); |
109 | 110 | ||
110 | - private static final String FLOW_JSON = "{\"priority\":1,\"isPermanent\":true," | ||
111 | - + "\"treatment\":{\"instructions\":[ {\"type\":\"OUTPUT\",\"port\":2}]}," | ||
112 | - + "\"selector\":{\"criteria\":[ {\"type\":\"ETH_TYPE\",\"ethType\":2054}]}}"; | ||
113 | - | ||
114 | /** | 111 | /** |
115 | * Mock class for a flow entry. | 112 | * Mock class for a flow entry. |
116 | */ | 113 | */ |
... | @@ -582,11 +579,12 @@ public class FlowsResourceTest extends ResourceTest { | ... | @@ -582,11 +579,12 @@ public class FlowsResourceTest extends ResourceTest { |
582 | replay(mockFlowService); | 579 | replay(mockFlowService); |
583 | 580 | ||
584 | WebResource rs = resource(); | 581 | WebResource rs = resource(); |
585 | - | 582 | + InputStream jsonStream = IntentsResourceTest.class |
583 | + .getResourceAsStream("post-flow.json"); | ||
586 | 584 | ||
587 | ClientResponse response = rs.path("flows/of:0000000000000001") | 585 | ClientResponse response = rs.path("flows/of:0000000000000001") |
588 | .type(MediaType.APPLICATION_JSON_TYPE) | 586 | .type(MediaType.APPLICATION_JSON_TYPE) |
589 | - .post(ClientResponse.class, FLOW_JSON); | 587 | + .post(ClientResponse.class, jsonStream); |
590 | assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED)); | 588 | assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED)); |
591 | String location = response.getLocation().getPath(); | 589 | String location = response.getLocation().getPath(); |
592 | assertThat(location, Matchers.startsWith("/flows/of:0000000000000001/")); | 590 | assertThat(location, Matchers.startsWith("/flows/of:0000000000000001/")); | ... | ... |
... | @@ -15,10 +15,15 @@ | ... | @@ -15,10 +15,15 @@ |
15 | */ | 15 | */ |
16 | package org.onosproject.rest; | 16 | package org.onosproject.rest; |
17 | 17 | ||
18 | +import java.io.InputStream; | ||
19 | +import java.net.HttpURLConnection; | ||
18 | import java.util.Collections; | 20 | import java.util.Collections; |
19 | import java.util.HashSet; | 21 | import java.util.HashSet; |
20 | 22 | ||
23 | +import javax.ws.rs.core.MediaType; | ||
24 | + | ||
21 | import org.hamcrest.Description; | 25 | import org.hamcrest.Description; |
26 | +import org.hamcrest.Matchers; | ||
22 | import org.hamcrest.TypeSafeMatcher; | 27 | import org.hamcrest.TypeSafeMatcher; |
23 | import org.junit.After; | 28 | import org.junit.After; |
24 | import org.junit.Before; | 29 | import org.junit.Before; |
... | @@ -42,12 +47,14 @@ import org.onosproject.net.intent.MockIdGenerator; | ... | @@ -42,12 +47,14 @@ import org.onosproject.net.intent.MockIdGenerator; |
42 | import com.eclipsesource.json.JsonArray; | 47 | import com.eclipsesource.json.JsonArray; |
43 | import com.eclipsesource.json.JsonObject; | 48 | import com.eclipsesource.json.JsonObject; |
44 | import com.eclipsesource.json.JsonValue; | 49 | import com.eclipsesource.json.JsonValue; |
50 | +import com.sun.jersey.api.client.ClientResponse; | ||
45 | import com.sun.jersey.api.client.UniformInterfaceException; | 51 | import com.sun.jersey.api.client.UniformInterfaceException; |
46 | import com.sun.jersey.api.client.WebResource; | 52 | import com.sun.jersey.api.client.WebResource; |
47 | 53 | ||
48 | import static org.easymock.EasyMock.anyObject; | 54 | import static org.easymock.EasyMock.anyObject; |
49 | import static org.easymock.EasyMock.createMock; | 55 | import static org.easymock.EasyMock.createMock; |
50 | import static org.easymock.EasyMock.expect; | 56 | import static org.easymock.EasyMock.expect; |
57 | +import static org.easymock.EasyMock.expectLastCall; | ||
51 | import static org.easymock.EasyMock.replay; | 58 | import static org.easymock.EasyMock.replay; |
52 | import static org.easymock.EasyMock.verify; | 59 | import static org.easymock.EasyMock.verify; |
53 | import static org.hamcrest.Matchers.containsString; | 60 | import static org.hamcrest.Matchers.containsString; |
... | @@ -358,4 +365,29 @@ public class IntentsResourceTest extends ResourceTest { | ... | @@ -358,4 +365,29 @@ public class IntentsResourceTest extends ResourceTest { |
358 | containsString("returned a response status of")); | 365 | containsString("returned a response status of")); |
359 | } | 366 | } |
360 | } | 367 | } |
368 | + | ||
369 | + /** | ||
370 | + * Tests creating an intent with POST. | ||
371 | + */ | ||
372 | + @Test | ||
373 | + public void testPost() { | ||
374 | + expect(mockCoreService.getAppId((short) 2)) | ||
375 | + .andReturn(new DefaultApplicationId(2, "app")); | ||
376 | + replay(mockCoreService); | ||
377 | + | ||
378 | + mockIntentService.submit(anyObject()); | ||
379 | + expectLastCall(); | ||
380 | + replay(mockIntentService); | ||
381 | + | ||
382 | + InputStream jsonStream = IntentsResourceTest.class | ||
383 | + .getResourceAsStream("post-intent.json"); | ||
384 | + WebResource rs = resource(); | ||
385 | + | ||
386 | + ClientResponse response = rs.path("intents") | ||
387 | + .type(MediaType.APPLICATION_JSON_TYPE) | ||
388 | + .post(ClientResponse.class, jsonStream); | ||
389 | + assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED)); | ||
390 | + String location = response.getLocation().getPath(); | ||
391 | + assertThat(location, Matchers.startsWith("/intents/2/")); | ||
392 | + } | ||
361 | } | 393 | } | ... | ... |
1 | +{ | ||
2 | + "type": "PointToPointIntent", | ||
3 | + "appId": 2, | ||
4 | + "selector": { | ||
5 | + "criteria": [ | ||
6 | + { | ||
7 | + "type": "ETH_DST", | ||
8 | + "mac": "11:22:33:44:55:66" | ||
9 | + } | ||
10 | + ] | ||
11 | + }, | ||
12 | + "treatment": { | ||
13 | + "instructions": [ | ||
14 | + { | ||
15 | + "type": "L2MODIFICATION", | ||
16 | + "subtype": "ETH_SRC", | ||
17 | + "mac": "22:33:44:55:66:77" | ||
18 | + } | ||
19 | + ], | ||
20 | + "deferred": [] | ||
21 | + }, | ||
22 | + "priority": 55, | ||
23 | + "constraints": [ | ||
24 | + { | ||
25 | + "inclusive": false, | ||
26 | + "types": ["OPTICAL"], | ||
27 | + "type": "LinkTypeConstraint" | ||
28 | + } | ||
29 | + ], | ||
30 | + "ingressPoint": { | ||
31 | + "port": "1", | ||
32 | + "device": "of:0000000000000001" | ||
33 | + }, | ||
34 | + "egressPoint": { | ||
35 | + "port": "2", | ||
36 | + "device": "of:0000000000000007" | ||
37 | + } | ||
38 | +} |
-
Please register or login to post a comment