blob: 7483cd734d6c6928dfde790e95ed0e47ef14604a [file] [log] [blame]
Ray Milkeydb358082015-01-13 16:34:38 -08001/*
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 */
16package org.onosproject.codec.impl;
17
18import java.util.List;
19import java.util.Set;
20
21import org.hamcrest.Description;
22import org.hamcrest.TypeSafeDiagnosingMatcher;
23import org.onosproject.net.ConnectPoint;
Ray Milkeyb9a8a182015-01-20 14:15:35 -080024import org.onosproject.net.DeviceId;
25import org.onosproject.net.Link;
Ray Milkeydb358082015-01-13 16:34:38 -080026import org.onosproject.net.NetworkResource;
27import org.onosproject.net.flow.TrafficSelector;
28import org.onosproject.net.flow.TrafficTreatment;
29import org.onosproject.net.flow.criteria.Criterion;
30import org.onosproject.net.flow.instructions.Instruction;
31import org.onosproject.net.intent.ConnectivityIntent;
32import org.onosproject.net.intent.Constraint;
33import org.onosproject.net.intent.HostToHostIntent;
34import org.onosproject.net.intent.Intent;
35import org.onosproject.net.intent.PointToPointIntent;
Ray Milkeyb9a8a182015-01-20 14:15:35 -080036import org.onosproject.net.intent.constraint.AnnotationConstraint;
37import org.onosproject.net.intent.constraint.BandwidthConstraint;
Ray Milkeyb9a8a182015-01-20 14:15:35 -080038import org.onosproject.net.intent.constraint.LatencyConstraint;
39import org.onosproject.net.intent.constraint.LinkTypeConstraint;
40import org.onosproject.net.intent.constraint.ObstacleConstraint;
41import org.onosproject.net.intent.constraint.WaypointConstraint;
Ray Milkeydb358082015-01-13 16:34:38 -080042
43import com.fasterxml.jackson.databind.JsonNode;
44
45/**
46 * Hamcrest matcher to check that an intent representation in JSON matches
47 * the actual intent.
48 */
49public final class IntentJsonMatcher extends TypeSafeDiagnosingMatcher<JsonNode> {
50
51 private final Intent intent;
52
53 /**
54 * Constructor is private, use factory method.
55 *
56 * @param intentValue the intent object to compare against
57 */
58 private IntentJsonMatcher(Intent intentValue) {
59 intent = intentValue;
60 }
61
62 /**
63 * Matches the JSON representation of a host to host intent.
64 *
65 * @param jsonIntent JSON representation of the intent
66 * @param description Description object used for recording errors
67 * @return true if the JSON matches the intent, false otherwise
68 */
69 private boolean matchHostToHostIntent(JsonNode jsonIntent, Description description) {
70 final HostToHostIntent hostToHostIntent = (HostToHostIntent) intent;
71
72 // check host one
73 final String host1 = hostToHostIntent.one().toString();
74 final String jsonHost1 = jsonIntent.get("one").asText();
75 if (!host1.equals(jsonHost1)) {
76 description.appendText("host one was " + jsonHost1);
77 return false;
78 }
79
80 // check host 2
81 final String host2 = hostToHostIntent.two().toString();
82 final String jsonHost2 = jsonIntent.get("two").asText();
83 if (!host2.equals(jsonHost2)) {
84 description.appendText("host two was " + jsonHost2);
85 return false;
86 }
87 return true;
88 }
89
90 /**
91 * Matches the JSON representation of a point to point intent.
92 *
93 * @param jsonIntent JSON representation of the intent
94 * @param description Description object used for recording errors
95 * @return true if the JSON matches the intent, false otherwise
96 */
97 private boolean matchPointToPointIntent(JsonNode jsonIntent, Description description) {
98 final PointToPointIntent pointToPointIntent = (PointToPointIntent) intent;
99
100 // check ingress connection
101 final ConnectPoint ingress = pointToPointIntent.ingressPoint();
102 final ConnectPointJsonMatcher ingressMatcher =
103 ConnectPointJsonMatcher.matchesConnectPoint(ingress);
104 final JsonNode jsonIngress = jsonIntent.get("ingressPoint");
105 final boolean ingressMatches =
106 ingressMatcher.matchesSafely(jsonIngress, description);
107
108 if (!ingressMatches) {
109 description.appendText("ingress was " + jsonIngress);
110 return false;
111 }
112
113 // check egress connection
114 final ConnectPoint egress = pointToPointIntent.egressPoint();
115 final ConnectPointJsonMatcher egressMatcher =
116 ConnectPointJsonMatcher.matchesConnectPoint(egress);
117 final JsonNode jsonEgress = jsonIntent.get("egressPoint");
118 final boolean egressMatches =
119 egressMatcher.matchesSafely(jsonEgress, description);
120
121 if (!egressMatches) {
122 description.appendText("egress was " + jsonEgress);
123 return false;
124 }
125
126 return true;
127 }
128
Ray Milkeyb9a8a182015-01-20 14:15:35 -0800129
130 /**
131 * Matches a bandwidth constraint against a JSON representation of the
132 * constraint.
133 *
134 * @param bandwidthConstraint constraint object to match
135 * @param constraintJson JSON representation of the constraint
136 * @return true if the constraint and JSON match, false otherwise.
137 */
138 private boolean matchBandwidthConstraint(BandwidthConstraint bandwidthConstraint,
139 JsonNode constraintJson) {
140 final JsonNode bandwidthJson = constraintJson.get("bandwidth");
141 return bandwidthJson != null
142 && constraintJson.get("bandwidth").asDouble()
Sho SHIMIZUa88db492015-11-23 13:21:04 -0800143 == bandwidthConstraint.bandwidth().bps();
Ray Milkeyb9a8a182015-01-20 14:15:35 -0800144 }
145
146 /**
Ray Milkeyb9a8a182015-01-20 14:15:35 -0800147 * Matches a link type constraint against a JSON representation of the
148 * constraint.
149 *
150 * @param linkTypeConstraint constraint object to match
151 * @param constraintJson JSON representation of the constraint
152 * @return true if the constraint and JSON match, false otherwise.
153 */
154 private boolean matchLinkTypeConstraint(LinkTypeConstraint linkTypeConstraint,
155 JsonNode constraintJson) {
156 final JsonNode inclusiveJson = constraintJson.get("inclusive");
157 final JsonNode typesJson = constraintJson.get("types");
158
159 if (typesJson.size() != linkTypeConstraint.types().size()) {
160 return false;
161 }
162
163 int foundType = 0;
164 for (Link.Type type : linkTypeConstraint.types()) {
165 for (int jsonIndex = 0; jsonIndex < typesJson.size(); jsonIndex++) {
166 if (type.name().equals(typesJson.get(jsonIndex).asText())) {
167 foundType++;
168 break;
169 }
170 }
171 }
172 return (inclusiveJson != null &&
173 inclusiveJson.asBoolean() == linkTypeConstraint.isInclusive()) &&
174 foundType == typesJson.size();
175 }
176
177 /**
178 * Matches an annotation constraint against a JSON representation of the
179 * constraint.
180 *
181 * @param annotationConstraint constraint object to match
182 * @param constraintJson JSON representation of the constraint
183 * @return true if the constraint and JSON match, false otherwise.
184 */
185 private boolean matchAnnotationConstraint(AnnotationConstraint annotationConstraint,
186 JsonNode constraintJson) {
187 final JsonNode keyJson = constraintJson.get("key");
188 final JsonNode thresholdJson = constraintJson.get("threshold");
189 return (keyJson != null
190 && keyJson.asText().equals(annotationConstraint.key())) &&
191 (thresholdJson != null
192 && thresholdJson.asDouble() == annotationConstraint.threshold());
193 }
194
195 /**
196 * Matches a latency constraint against a JSON representation of the
197 * constraint.
198 *
199 * @param latencyConstraint constraint object to match
200 * @param constraintJson JSON representation of the constraint
201 * @return true if the constraint and JSON match, false otherwise.
202 */
203 private boolean matchLatencyConstraint(LatencyConstraint latencyConstraint,
204 JsonNode constraintJson) {
205 final JsonNode latencyJson = constraintJson.get("latencyMillis");
206 return (latencyJson != null
207 && latencyJson.asInt() == latencyConstraint.latency().toMillis());
208 }
209
210 /**
211 * Matches an obstacle constraint against a JSON representation of the
212 * constraint.
213 *
214 * @param obstacleConstraint constraint object to match
215 * @param constraintJson JSON representation of the constraint
216 * @return true if the constraint and JSON match, false otherwise.
217 */
218 private boolean matchObstacleConstraint(ObstacleConstraint obstacleConstraint,
219 JsonNode constraintJson) {
220 final JsonNode obstaclesJson = constraintJson.get("obstacles");
221
222 if (obstaclesJson.size() != obstacleConstraint.obstacles().size()) {
223 return false;
224 }
225
226 for (int obstaclesIndex = 0; obstaclesIndex < obstaclesJson.size();
227 obstaclesIndex++) {
228 boolean obstacleFound = false;
229 final String obstacleJson = obstaclesJson.get(obstaclesIndex)
230 .asText();
231 for (DeviceId obstacle : obstacleConstraint.obstacles()) {
232 if (obstacle.toString().equals(obstacleJson)) {
233 obstacleFound = true;
234 }
235 }
236 if (!obstacleFound) {
237 return false;
238 }
239 }
240 return true;
241 }
242
243 /**
244 * Matches a waypoint constraint against a JSON representation of the
245 * constraint.
246 *
247 * @param waypointConstraint constraint object to match
248 * @param constraintJson JSON representation of the constraint
249 * @return true if the constraint and JSON match, false otherwise.
250 */
251 private boolean matchWaypointConstraint(WaypointConstraint waypointConstraint,
252 JsonNode constraintJson) {
253 final JsonNode waypointsJson = constraintJson.get("waypoints");
254
255 if (waypointsJson.size() != waypointConstraint.waypoints().size()) {
256 return false;
257 }
258
259 for (int waypointsIndex = 0; waypointsIndex < waypointsJson.size();
260 waypointsIndex++) {
261 boolean waypointFound = false;
262 final String waypointJson = waypointsJson.get(waypointsIndex)
263 .asText();
264 for (DeviceId waypoint : waypointConstraint.waypoints()) {
265 if (waypoint.toString().equals(waypointJson)) {
266 waypointFound = true;
267 }
268 }
269 if (!waypointFound) {
270 return false;
271 }
272 }
273 return true;
274 }
275
276
277 /**
278 * Matches a constraint against a JSON representation of the
279 * constraint.
280 *
281 * @param constraint constraint object to match
282 * @param constraintJson JSON representation of the constraint
283 * @return true if the constraint and JSON match, false otherwise.
284 */
285 private boolean matchConstraint(Constraint constraint, JsonNode constraintJson) {
286 final JsonNode typeJson = constraintJson.get("type");
287 if (!typeJson.asText().equals(constraint.getClass().getSimpleName())) {
288 return false;
289 }
290 if (constraint instanceof BandwidthConstraint) {
291 return matchBandwidthConstraint((BandwidthConstraint) constraint,
292 constraintJson);
Ray Milkeyb9a8a182015-01-20 14:15:35 -0800293 } else if (constraint instanceof LinkTypeConstraint) {
294 return matchLinkTypeConstraint((LinkTypeConstraint) constraint,
295 constraintJson);
296 } else if (constraint instanceof AnnotationConstraint) {
297 return matchAnnotationConstraint((AnnotationConstraint) constraint,
298 constraintJson);
299 } else if (constraint instanceof LatencyConstraint) {
300 return matchLatencyConstraint((LatencyConstraint) constraint,
301 constraintJson);
302 } else if (constraint instanceof ObstacleConstraint) {
303 return matchObstacleConstraint((ObstacleConstraint) constraint,
304 constraintJson);
305 } else if (constraint instanceof WaypointConstraint) {
306 return matchWaypointConstraint((WaypointConstraint) constraint,
307 constraintJson);
308 }
309 return true;
310 }
311
Ray Milkeydb358082015-01-13 16:34:38 -0800312 /**
313 * Matches the JSON representation of a connectivity intent. Calls the
314 * matcher for the connectivity intent subtype.
315 *
316 * @param jsonIntent JSON representation of the intent
317 * @param description Description object used for recording errors
318 * @return true if the JSON matches the intent, false otherwise
319 */
320 private boolean matchConnectivityIntent(JsonNode jsonIntent, Description description) {
321 final ConnectivityIntent connectivityIntent = (ConnectivityIntent) intent;
322
323 // check selector
324 final JsonNode jsonSelector = jsonIntent.get("selector");
325 final TrafficSelector selector = connectivityIntent.selector();
326 final Set<Criterion> criteria = selector.criteria();
327 final JsonNode jsonCriteria = jsonSelector.get("criteria");
328 if (jsonCriteria.size() != criteria.size()) {
329 description.appendText("size of criteria array is "
330 + Integer.toString(jsonCriteria.size()));
331 return false;
332 }
333
334 for (Criterion criterion : criteria) {
335 boolean criterionFound = false;
336 for (int criterionIndex = 0; criterionIndex < jsonCriteria.size(); criterionIndex++) {
337 final CriterionJsonMatcher criterionMatcher =
338 CriterionJsonMatcher.matchesCriterion(criterion);
339 if (criterionMatcher.matches(jsonCriteria.get(criterionIndex))) {
340 criterionFound = true;
341 break;
342 }
343 }
344 if (!criterionFound) {
345 description.appendText("criterion not found " + criterion.toString());
346 return false;
347 }
348 }
349
350 // check treatment
351 final JsonNode jsonTreatment = jsonIntent.get("treatment");
352 final TrafficTreatment treatment = connectivityIntent.treatment();
Ray Milkey42507352015-03-20 15:16:10 -0700353 final List<Instruction> instructions = treatment.immediate();
Ray Milkeydb358082015-01-13 16:34:38 -0800354 final JsonNode jsonInstructions = jsonTreatment.get("instructions");
355 if (jsonInstructions.size() != instructions.size()) {
356 description.appendText("size of instructions array is "
357 + Integer.toString(jsonInstructions.size()));
358 return false;
359 }
360
361 for (Instruction instruction : instructions) {
362 boolean instructionFound = false;
alshabib346b5b32015-03-06 00:42:16 -0800363 for (int instructionIndex = 0; instructionIndex < jsonInstructions.size(); instructionIndex++) {
Ray Milkeydb358082015-01-13 16:34:38 -0800364 final InstructionJsonMatcher instructionMatcher =
365 InstructionJsonMatcher.matchesInstruction(instruction);
366 if (instructionMatcher.matches(jsonInstructions.get(instructionIndex))) {
367 instructionFound = true;
368 break;
369 }
370 }
371 if (!instructionFound) {
372 description.appendText("instruction not found " + instruction.toString());
373 return false;
374 }
375 }
376
377 // Check constraints
378 final JsonNode jsonConstraints = jsonIntent.get("constraints");
379 if (connectivityIntent.constraints() != null) {
380 if (connectivityIntent.constraints().size() != jsonConstraints.size()) {
381 description.appendText("constraints array size was "
382 + Integer.toString(jsonConstraints.size()));
383 return false;
384 }
385 for (final Constraint constraint : connectivityIntent.constraints()) {
386 boolean constraintFound = false;
Ray Milkeydb358082015-01-13 16:34:38 -0800387 for (int constraintIndex = 0; constraintIndex < jsonConstraints.size();
388 constraintIndex++) {
389 final JsonNode value = jsonConstraints.get(constraintIndex);
Ray Milkeyb9a8a182015-01-20 14:15:35 -0800390 if (matchConstraint(constraint, value)) {
Ray Milkeydb358082015-01-13 16:34:38 -0800391 constraintFound = true;
392 }
393 }
394 if (!constraintFound) {
Ray Milkeyb9a8a182015-01-20 14:15:35 -0800395 final String constraintString = constraint.toString();
396 description.appendText("constraint missing " + constraintString);
Ray Milkeydb358082015-01-13 16:34:38 -0800397 return false;
398 }
399 }
400 } else if (jsonConstraints.size() != 0) {
401 description.appendText("constraint array not empty");
402 return false;
403 }
404
405 if (connectivityIntent instanceof HostToHostIntent) {
406 return matchHostToHostIntent(jsonIntent, description);
407 } else if (connectivityIntent instanceof PointToPointIntent) {
408 return matchPointToPointIntent(jsonIntent, description);
409 } else {
410 description.appendText("class of connectivity intent is unknown");
411 return false;
412 }
413 }
414
415 @Override
416 public boolean matchesSafely(JsonNode jsonIntent, Description description) {
417 // check id
418 final String jsonId = jsonIntent.get("id").asText();
419 final String id = intent.id().toString();
420 if (!jsonId.equals(id)) {
421 description.appendText("id was " + jsonId);
422 return false;
423 }
424
425 // check application id
Ray Milkeyf7cb4012015-07-20 13:01:07 -0700426 final JsonNode jsonAppIdNode = jsonIntent.get("appId");
427
428 final String jsonAppId = jsonAppIdNode.asText();
429 final String appId = intent.appId().name();
Ray Milkeydb358082015-01-13 16:34:38 -0800430 if (!jsonAppId.equals(appId)) {
431 description.appendText("appId was " + jsonAppId);
432 return false;
433 }
434
435 // check intent type
436 final String jsonType = jsonIntent.get("type").asText();
437 final String type = intent.getClass().getSimpleName();
438 if (!jsonType.equals(type)) {
439 description.appendText("type was " + jsonType);
440 return false;
441 }
442
Ray Milkeydb358082015-01-13 16:34:38 -0800443 // check resources array
444 final JsonNode jsonResources = jsonIntent.get("resources");
445 if (intent.resources() != null) {
446 if (intent.resources().size() != jsonResources.size()) {
447 description.appendText("resources array size was "
448 + Integer.toString(jsonResources.size()));
449 return false;
450 }
451 for (final NetworkResource resource : intent.resources()) {
452 boolean resourceFound = false;
453 final String resourceString = resource.toString();
454 for (int resourceIndex = 0; resourceIndex < jsonResources.size(); resourceIndex++) {
455 final JsonNode value = jsonResources.get(resourceIndex);
456 if (value.asText().equals(resourceString)) {
457 resourceFound = true;
458 }
459 }
460 if (!resourceFound) {
461 description.appendText("resource missing " + resourceString);
462 return false;
463 }
464 }
465 } else if (jsonResources.size() != 0) {
466 description.appendText("resources array empty");
467 return false;
468 }
469
470 if (intent instanceof ConnectivityIntent) {
471 return matchConnectivityIntent(jsonIntent, description);
472 } else {
473 description.appendText("class of intent is unknown");
474 return false;
475 }
476 }
477
478 @Override
479 public void describeTo(Description description) {
480 description.appendText(intent.toString());
481 }
482
483 /**
484 * Factory to allocate an intent matcher.
485 *
486 * @param intent intent object we are looking for
487 * @return matcher
488 */
489 public static IntentJsonMatcher matchesIntent(Intent intent) {
490 return new IntentJsonMatcher(intent);
491 }
492}