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