blob: ba4b3d71261034a98ffc391e8a0165f54569e8c7 [file] [log] [blame]
Ray Milkey2b217142014-12-15 09:24:24 -08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2016-present Open Networking Laboratory
Ray Milkey2b217142014-12-15 09:24:24 -08003 *
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 */
Jian Li8ae91202016-03-24 14:36:16 -070016package org.onosproject.rest.resources;
Ray Milkey2b217142014-12-15 09:24:24 -080017
Author Namee252a002016-09-26 22:42:24 +053018import com.eclipsesource.json.Json;
19import com.eclipsesource.json.JsonArray;
20import com.eclipsesource.json.JsonObject;
21import com.eclipsesource.json.JsonValue;
Rafal Szaleckif97e0ed2017-06-01 13:07:18 +020022import com.fasterxml.jackson.databind.node.ObjectNode;
Ray Milkey2b217142014-12-15 09:24:24 -080023import org.hamcrest.Description;
Ray Milkeyb82c42b2015-06-30 09:42:20 -070024import org.hamcrest.Matchers;
Ray Milkey2b217142014-12-15 09:24:24 -080025import org.hamcrest.TypeSafeMatcher;
26import org.junit.After;
27import org.junit.Before;
28import org.junit.Test;
29import org.onlab.osgi.ServiceDirectory;
30import org.onlab.osgi.TestServiceDirectory;
Author Namee252a002016-09-26 22:42:24 +053031import org.onlab.packet.MacAddress;
Ray Milkey2b217142014-12-15 09:24:24 -080032import org.onlab.rest.BaseResource;
33import org.onosproject.codec.CodecService;
34import org.onosproject.codec.impl.CodecManager;
Rafal Szaleckif97e0ed2017-06-01 13:07:18 +020035import org.onosproject.codec.impl.MockCodecContext;
Ray Milkey2b217142014-12-15 09:24:24 -080036import org.onosproject.core.ApplicationId;
Ayaka Koshibec06c89b2015-02-10 19:25:41 -080037import org.onosproject.core.CoreService;
Ray Milkey2b217142014-12-15 09:24:24 -080038import org.onosproject.core.DefaultApplicationId;
Author Namee252a002016-09-26 22:42:24 +053039import org.onosproject.core.GroupId;
Rafal Szaleckif97e0ed2017-06-01 13:07:18 +020040import org.onosproject.net.ConnectPoint;
41import org.onosproject.net.DefaultLink;
Author Namee252a002016-09-26 22:42:24 +053042import org.onosproject.net.DeviceId;
Rafal Szaleckif97e0ed2017-06-01 13:07:18 +020043import org.onosproject.net.Link;
Ray Milkey2b217142014-12-15 09:24:24 -080044import org.onosproject.net.NetworkResource;
Rafal Szaleckif97e0ed2017-06-01 13:07:18 +020045import org.onosproject.net.PortNumber;
Author Namee252a002016-09-26 22:42:24 +053046import org.onosproject.net.flow.DefaultTrafficSelector;
47import org.onosproject.net.flow.DefaultTrafficTreatment;
48import org.onosproject.net.flow.FlowEntry;
Ray Milkey634eb172017-04-05 14:48:50 -070049import org.onosproject.net.flow.FlowEntryAdapter;
Author Namee252a002016-09-26 22:42:24 +053050import org.onosproject.net.flow.FlowId;
51import org.onosproject.net.flow.FlowRule;
52import org.onosproject.net.flow.FlowRuleExtPayLoad;
53import org.onosproject.net.flow.FlowRuleService;
54import org.onosproject.net.flow.TrafficSelector;
55import org.onosproject.net.flow.TrafficTreatment;
56import org.onosproject.net.flow.criteria.Criterion;
57import org.onosproject.net.flow.instructions.Instruction;
Ray Milkey7b158512015-07-21 16:32:43 -070058import org.onosproject.net.intent.FakeIntentManager;
Author Namee252a002016-09-26 22:42:24 +053059import org.onosproject.net.intent.FlowRuleIntent;
Ray Milkey2b217142014-12-15 09:24:24 -080060import org.onosproject.net.intent.Intent;
Ray Milkey2b217142014-12-15 09:24:24 -080061import org.onosproject.net.intent.IntentService;
Ray Milkey1534f8d2015-05-13 15:42:50 -070062import org.onosproject.net.intent.IntentState;
Ray Milkeyf9af43c2015-02-09 16:45:48 -080063import org.onosproject.net.intent.Key;
Thomas Vachuska2048c1f2017-05-10 19:32:22 -070064import org.onosproject.net.intent.MockIdGenerator;
Rafal Szaleckif97e0ed2017-06-01 13:07:18 +020065import org.onosproject.net.provider.ProviderId;
Ray Milkey2b217142014-12-15 09:24:24 -080066
Author Namee252a002016-09-26 22:42:24 +053067import javax.ws.rs.NotFoundException;
68import javax.ws.rs.client.Entity;
69import javax.ws.rs.client.WebTarget;
70import javax.ws.rs.core.MediaType;
71import javax.ws.rs.core.Response;
Rafal Szaleckif97e0ed2017-06-01 13:07:18 +020072import java.io.IOException;
Author Namee252a002016-09-26 22:42:24 +053073import java.io.InputStream;
74import java.net.HttpURLConnection;
75import java.util.ArrayList;
76import java.util.Collections;
77import java.util.HashSet;
78import java.util.List;
Rafal Szaleckif97e0ed2017-06-01 13:07:18 +020079import java.util.Set;
Author Namee252a002016-09-26 22:42:24 +053080import java.util.concurrent.TimeUnit;
Ray Milkey2b217142014-12-15 09:24:24 -080081
Author Namee252a002016-09-26 22:42:24 +053082import static java.util.concurrent.TimeUnit.SECONDS;
83import static org.easymock.EasyMock.*;
84import static org.hamcrest.Matchers.*;
Ray Milkey2b217142014-12-15 09:24:24 -080085import static org.junit.Assert.assertThat;
86import static org.junit.Assert.fail;
Ray Milkey43a28222015-02-23 13:57:58 -080087import static org.onosproject.net.intent.IntentTestsMocks.MockIntent;
Ray Milkey2b217142014-12-15 09:24:24 -080088
89/**
90 * Unit tests for Intents REST APIs.
91 */
Ray Milkey9c3d3362015-01-28 10:39:56 -080092public class IntentsResourceTest extends ResourceTest {
Author Namee252a002016-09-26 22:42:24 +053093
94
95 private static final String APPID = "appId";
96 private static final String CRITERIA = "criteria";
97 private static final String DEVICE_ID = "deviceId";
98 private static final String ID = "id";
99 private static final String INSTRUCTIONS = "instructions";
100 private static final String PATHS = "paths";
Rafal Szaleckif97e0ed2017-06-01 13:07:18 +0200101 private static final String INSTALLABLES = "installables";
102 private static final String RESOURCES = "resources";
103 private static final String DEVICE = "device";
104 private static final String PORT = "port";
105 private static final String SRC = "src";
106 private static final String DST = "dst";
Author Namee252a002016-09-26 22:42:24 +0530107 private static final String SELECTOR = "selector";
108 private static final String SPACE = " ";
109 private static final String TREATMENT = "treatment";
110 private static final String TYPE = "type";
111
Ray Milkey2b217142014-12-15 09:24:24 -0800112 final IntentService mockIntentService = createMock(IntentService.class);
Ayaka Koshibec06c89b2015-02-10 19:25:41 -0800113 final CoreService mockCoreService = createMock(CoreService.class);
Author Namee252a002016-09-26 22:42:24 +0530114 final FlowRuleService mockFlowService = createMock(FlowRuleService.class);
Ray Milkey2b217142014-12-15 09:24:24 -0800115 final HashSet<Intent> intents = new HashSet<>();
Author Namee252a002016-09-26 22:42:24 +0530116 final List<org.onosproject.net.intent.Intent> installableIntents = new ArrayList<>();
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800117 private static final ApplicationId APP_ID = new DefaultApplicationId(1, "test");
Ray Milkey2b217142014-12-15 09:24:24 -0800118
Author Namee252a002016-09-26 22:42:24 +0530119 final DeviceId deviceId1 = DeviceId.deviceId("1");
Rafal Szaleckif97e0ed2017-06-01 13:07:18 +0200120 final DeviceId deviceId2 = DeviceId.deviceId("2");
121 final DeviceId deviceId3 = DeviceId.deviceId("3");
122
123 final ConnectPoint connectPoint1 = new ConnectPoint(deviceId1, PortNumber.portNumber(1L));
124 final ConnectPoint connectPoint2 = new ConnectPoint(deviceId2, PortNumber.portNumber(1L));
125 final ConnectPoint connectPoint3 = new ConnectPoint(deviceId2, PortNumber.portNumber(2L));
126 final ConnectPoint connectPoint4 = new ConnectPoint(deviceId3, PortNumber.portNumber(1L));
Author Namee252a002016-09-26 22:42:24 +0530127
128 final TrafficTreatment treatment1 = DefaultTrafficTreatment.builder()
129 .setEthDst(MacAddress.BROADCAST)
130 .build();
131 final TrafficTreatment treatment2 = DefaultTrafficTreatment.builder()
132 .setEthDst(MacAddress.IPV4_MULTICAST)
133 .build();
134
135 final TrafficSelector selector1 = DefaultTrafficSelector.builder()
136 .matchEthType((short) 3)
137 .matchIPProtocol((byte) 9)
138 .build();
139 final TrafficSelector selector2 = DefaultTrafficSelector.builder()
140 .matchEthType((short) 4)
141 .matchIPProtocol((byte) 10)
142 .build();
143
144 final MockFlowEntry flow1 = new MockFlowEntry(deviceId1, 1, treatment1, selector1);
145 final MockFlowEntry flow2 = new MockFlowEntry(deviceId1, 2, treatment2, selector2);
146
147 final MockFlowRule flowRule1 = new MockFlowRule(deviceId1, 1, treatment1, selector1);
148 final MockFlowRule flowRule2 = new MockFlowRule(deviceId1, 2, treatment2, selector2);
149
Ray Milkey2b217142014-12-15 09:24:24 -0800150 private class MockResource implements NetworkResource {
151 int id;
152
153 MockResource(int id) {
154 this.id = id;
155 }
156
Ayaka Koshibec06c89b2015-02-10 19:25:41 -0800157 @Override
Ray Milkey2b217142014-12-15 09:24:24 -0800158 public String toString() {
159 return "Resource " + Integer.toString(id);
160 }
161 }
162
Ray Milkey2b217142014-12-15 09:24:24 -0800163 /**
Author Namee252a002016-09-26 22:42:24 +0530164 * Mock class for a flow entry.
165 */
Ray Milkey634eb172017-04-05 14:48:50 -0700166 private static class MockFlowEntry extends FlowEntryAdapter {
Author Namee252a002016-09-26 22:42:24 +0530167 final DeviceId deviceId;
168 final long baseValue;
169 TrafficTreatment treatment;
170 TrafficSelector selector;
171
172 public MockFlowEntry(DeviceId deviceId, long id,
173 TrafficTreatment treatment,
174 TrafficSelector selector) {
175 this.deviceId = deviceId;
176 this.baseValue = id * 100;
177 this.treatment = treatment;
178 this.selector = selector;
179 }
180
181 @Override
Author Namee252a002016-09-26 22:42:24 +0530182 public long life() {
183 return life(SECONDS);
184 }
185
186 @Override
kalagesa42019542017-03-14 18:00:47 +0530187 public FlowLiveType liveType() {
188 return FlowLiveType.IMMEDIATE;
189 }
190
191 @Override
Author Namee252a002016-09-26 22:42:24 +0530192 public long life(TimeUnit timeUnit) {
193 return SECONDS.convert(baseValue + 11, timeUnit);
194 }
195
196 @Override
197 public long packets() {
198 return baseValue + 22;
199 }
200
201 @Override
202 public long bytes() {
203 return baseValue + 33;
204 }
205
206 @Override
207 public long lastSeen() {
208 return baseValue + 44;
209 }
210
211 @Override
Author Namee252a002016-09-26 22:42:24 +0530212 public FlowId id() {
213 final long id = baseValue + 55;
214 return FlowId.valueOf(id);
215 }
216
217 @Override
218 public GroupId groupId() {
Yi Tsengfa394de2017-02-01 11:26:40 -0800219 return new GroupId(3);
Author Namee252a002016-09-26 22:42:24 +0530220 }
221
222 @Override
223 public short appId() {
224 return 1;
225 }
226
227 @Override
228 public int priority() {
229 return (int) (baseValue + 66);
230 }
231
232 @Override
233 public DeviceId deviceId() {
234 return deviceId;
235 }
236
237 @Override
238 public TrafficSelector selector() {
239 return selector;
240 }
241
242 @Override
243 public TrafficTreatment treatment() {
244 return treatment;
245 }
246
247 @Override
248 public int timeout() {
249 return (int) (baseValue + 77);
250 }
251
Author Namee252a002016-09-26 22:42:24 +0530252
253 @Override
254 public boolean exactMatch(FlowRule rule) {
255 return this.appId() == rule.appId() &&
256 this.deviceId().equals(rule.deviceId()) &&
257 this.id().equals(rule.id()) &&
258 this.treatment.equals(rule.treatment()) &&
259 this.selector().equals(rule.selector());
260 }
261
262 @Override
Author Namee252a002016-09-26 22:42:24 +0530263 public String toString() {
264 return id().id().toString();
265 }
266 }
267
268 /**
269 * Mock class for a flow rule.
270 */
271 private static class MockFlowRule implements FlowRule {
272
273 final DeviceId deviceId;
274 final long baseValue;
275 TrafficTreatment treatment;
276 TrafficSelector selector;
277
278 public MockFlowRule(DeviceId deviceId,
279 long id,
280 TrafficTreatment treatment,
281 TrafficSelector selector) {
282 this.deviceId = deviceId;
283 this.baseValue = id * 100;
284 this.treatment = treatment;
285 this.selector = selector;
286 }
287
288 @Override
289 public FlowId id() {
290 long id = baseValue + 55;
291 return FlowId.valueOf(id);
292 }
293
294 @Override
295 public short appId() {
296 return 1;
297 }
298
299 @Override
300 public GroupId groupId() {
Yi Tsengfa394de2017-02-01 11:26:40 -0800301 return new GroupId(3);
Author Namee252a002016-09-26 22:42:24 +0530302 }
303
304 @Override
305 public int priority() {
306 return 0;
307 }
308
309 @Override
310 public DeviceId deviceId() {
311 return deviceId;
312 }
313
314 @Override
315 public TrafficSelector selector() {
316 return selector;
317 }
318
319 @Override
320 public TrafficTreatment treatment() {
321 return treatment;
322 }
323
324 @Override
325 public int timeout() {
326 return (int) (baseValue + 77);
327 }
328
329 @Override
330 public int hardTimeout() {
331 return 0;
332 }
333
334 @Override
335 public FlowRemoveReason reason() {
336 return FlowRemoveReason.NO_REASON;
337 }
338
339 @Override
340 public boolean isPermanent() {
341 return false;
342 }
343
344 @Override
345 public int tableId() {
346 return 0;
347 }
348
349 @Override
350 public boolean exactMatch(FlowRule rule) {
351 return false;
352 }
353
354 @Override
355 public FlowRuleExtPayLoad payLoad() {
356 return null;
357 }
358 }
359
360 /**
Ray Milkey2b217142014-12-15 09:24:24 -0800361 * Hamcrest matcher to check that an intent representation in JSON matches
362 * the actual intent.
363 */
364 public static class IntentJsonMatcher extends TypeSafeMatcher<JsonObject> {
365 private final Intent intent;
366 private String reason = "";
367
368 public IntentJsonMatcher(Intent intentValue) {
369 intent = intentValue;
370 }
371
372 @Override
373 public boolean matchesSafely(JsonObject jsonIntent) {
374 // check id
375 final String jsonId = jsonIntent.get("id").asString();
376 if (!jsonId.equals(intent.id().toString())) {
377 reason = "id " + intent.id().toString();
378 return false;
379 }
380
381 // check application id
Ray Milkeyf7cb4012015-07-20 13:01:07 -0700382
Ray Milkey2b217142014-12-15 09:24:24 -0800383 final String jsonAppId = jsonIntent.get("appId").asString();
Ray Milkeyf7cb4012015-07-20 13:01:07 -0700384 final String appId = intent.appId().name();
385 if (!jsonAppId.equals(appId)) {
386 reason = "appId was " + jsonAppId;
Ray Milkey2b217142014-12-15 09:24:24 -0800387 return false;
388 }
389
390 // check intent type
391 final String jsonType = jsonIntent.get("type").asString();
Rafal Szaleckif97e0ed2017-06-01 13:07:18 +0200392 if (!intent.getClass().getSimpleName().equals(jsonType)) {
Ray Milkey2b217142014-12-15 09:24:24 -0800393 reason = "type MockIntent";
394 return false;
395 }
396
Ray Milkey1534f8d2015-05-13 15:42:50 -0700397 // check state field
398 final String jsonState = jsonIntent.get("state").asString();
Jon Halla3fcf672017-03-28 16:53:22 -0700399 if (!"INSTALLED".equals(jsonState)) {
Ray Milkey1534f8d2015-05-13 15:42:50 -0700400 reason = "state INSTALLED";
401 return false;
402 }
403
Ray Milkey2b217142014-12-15 09:24:24 -0800404 // check resources array
405 final JsonArray jsonResources = jsonIntent.get("resources").asArray();
406 if (intent.resources() != null) {
407 if (intent.resources().size() != jsonResources.size()) {
408 reason = "resources array size of " + Integer.toString(intent.resources().size());
409 return false;
410 }
411 for (final NetworkResource resource : intent.resources()) {
412 boolean resourceFound = false;
413 final String resourceString = resource.toString();
Rafal Szaleckif97e0ed2017-06-01 13:07:18 +0200414
415 if (resource instanceof Link) {
416 final Link resourceLink = (Link) resource;
417 MockCodecContext codecContext = new MockCodecContext();
418
419 for (int resourceIndex = 0; resourceIndex < jsonResources.size(); resourceIndex++) {
420 final ObjectNode value;
421 try {
422 value = (ObjectNode) codecContext.mapper()
423 .readTree(jsonResources.get(resourceIndex).toString());
424 } catch (IOException e) {
425 reason = "bad json";
426 return false;
427 }
428 final Link link = codecContext.codec(Link.class).decode(value, codecContext);
429 if (resourceLink.equals(link)) {
430 resourceFound = true;
431 }
432 }
433 } else {
434
435 for (int resourceIndex = 0; resourceIndex < jsonResources.size(); resourceIndex++) {
436 final JsonValue value = jsonResources.get(resourceIndex);
437 if (value.asString().equals(resourceString)) {
438 resourceFound = true;
439 }
Ray Milkey2b217142014-12-15 09:24:24 -0800440 }
441 }
442 if (!resourceFound) {
443 reason = "resource " + resourceString;
444 return false;
445 }
446 }
447 } else if (jsonResources.size() != 0) {
448 reason = "resources array empty";
449 return false;
450 }
451 return true;
452 }
453
454 @Override
455 public void describeTo(Description description) {
456 description.appendText(reason);
457 }
458 }
459
460 /**
461 * Factory to allocate an intent matcher.
462 *
463 * @param intent intent object we are looking for
464 * @return matcher
465 */
466 private static IntentJsonMatcher matchesIntent(Intent intent) {
467 return new IntentJsonMatcher(intent);
468 }
469
470 /**
Author Namee252a002016-09-26 22:42:24 +0530471 * Factory to allocate an IntentRelatedFlows matcher.
472 *
473 * @param pathEntries list of path conatining flow entries of a particular intent
474 * @param expectedAppId expected app id we are looking for
475 * @return matcher
476 */
477 private static IntentStatsJsonMatcher matchesRelatedFlowEntries(
478 List<List<FlowEntry>> pathEntries,
479 final String expectedAppId) {
480 return new IntentStatsJsonMatcher(pathEntries, expectedAppId);
481 }
482
483 /**
484 * Hamcrest matcher to check that an list of flowEntries in JSON matches
485 * the actual list of flow entries.
486 */
487 public static class IntentStatsJsonMatcher extends
488 TypeSafeMatcher<JsonObject> {
489
490 private final List<List<FlowEntry>> pathEntries;
491 private final String expectedAppId;
492 private String reason = "";
493
494 public IntentStatsJsonMatcher(
495 final List<List<FlowEntry>> pathEntries,
496 final String expectedAppId) {
497 this.pathEntries = pathEntries;
498 this.expectedAppId = expectedAppId;
499 }
500
501 @Override
502 public boolean matchesSafely(JsonObject jsonIntent) {
503 int jsonPathIndex = 0;
504 JsonArray jsonPaths = jsonIntent.get(PATHS).asArray();
505
506 if (pathEntries != null) {
507
rohitsharanfd747602017-01-23 21:57:28 +0530508 if (pathEntries.size() == 0) {
509 reason = "pathEntries array empty";
510 return false;
511 }
512
Author Namee252a002016-09-26 22:42:24 +0530513 if (pathEntries.size() != jsonPaths.size()) {
514 reason = "path entries array size of " +
515 Integer.toString(pathEntries.size());
516 return false;
517 }
518
519 for (List<FlowEntry> flowEntries : pathEntries) {
520 JsonArray jsonFlowEntries = jsonPaths.get(
521 jsonPathIndex++).asArray();
522
523 if (flowEntries.size() != jsonFlowEntries.size()) {
524 reason = "flow entries array size of " +
525 Integer.toString(pathEntries.size());
526
527 return false;
528 }
529
530 int jsonFlowEntryIndex = 0;
531 for (FlowEntry flow : flowEntries) {
532
533 JsonObject jsonFlow = jsonFlowEntries.get(
534 jsonFlowEntryIndex++).asObject();
535
536 String jsonId = jsonFlow.get(ID).asString();
537 String flowId = Long.toString(flow.id().value());
538 if (!jsonId.equals(flowId)) {
539 reason = ID + SPACE + flow.id();
540 return false;
541 }
542
543 // check application id
544 String jsonAppId = jsonFlow.get(APPID).asString();
545 if (!jsonAppId.equals(expectedAppId)) {
546 reason = APPID + SPACE + Short.toString(flow.appId());
547 return false;
548 }
549
550 // check device id
551 String jsonDeviceId =
552 jsonFlow.get(DEVICE_ID).asString();
553
554 if (!jsonDeviceId.equals(flow.deviceId().toString())) {
555 reason = DEVICE_ID + SPACE + flow.deviceId();
556 return false;
557 }
558
559 if (!checkFlowTreatment(flow, jsonFlow)) {
560 return false;
561 }
562
563 if (!checkFlowSelector(flow, jsonFlow)) {
564 return false;
565 }
566
567 }
568
569 }
rohitsharanfd747602017-01-23 21:57:28 +0530570 } else {
Author Namee252a002016-09-26 22:42:24 +0530571 reason = "pathEntries array empty";
572 return false;
573 }
574
575 return true;
576 }
577
578 // check treatment and instructions array.
579 private boolean checkFlowTreatment(FlowEntry flow, JsonObject jsonFlow) {
580
581 if (flow.treatment() != null) {
582 JsonObject jsonTreatment =
583 jsonFlow.get(TREATMENT).asObject();
584 JsonArray jsonInstructions =
585 jsonTreatment.get(INSTRUCTIONS).asArray();
586
587 if (flow.treatment().immediate().size() !=
588 jsonInstructions.size()) {
589 reason = "instructions array size of " +
590 flow.treatment().immediate().size();
591
592 return false;
593 }
594 for (Instruction instruction :
595 flow.treatment().immediate()) {
596 boolean instructionFound = false;
597 for (int instructionIndex = 0;
598 instructionIndex < jsonInstructions.size();
599 instructionIndex++) {
600 String jsonType =
601 jsonInstructions.get(instructionIndex)
602 .asObject().get(TYPE).asString();
603
604 String instructionType =
605 instruction.type().name();
606
607 if (jsonType.equals(instructionType)) {
608 instructionFound = true;
609 }
610 }
611 if (!instructionFound) {
612 reason = INSTRUCTIONS + SPACE + instruction;
613 return false;
614 }
615 }
616 }
617 return true;
618 }
619
620 // check selector and criteria array.
621 private boolean checkFlowSelector(FlowEntry flow, JsonObject jsonFlow) {
622
623 if (flow.selector() != null) {
624 JsonObject jsonTreatment =
625 jsonFlow.get(SELECTOR).asObject();
626
627 JsonArray jsonCriteria =
628 jsonTreatment.get(CRITERIA).asArray();
629
630 if (flow.selector().criteria().size() != jsonCriteria.size()) {
631 reason = CRITERIA + " array size of " +
632 Integer.toString(flow.selector().criteria().size());
633 return false;
634 }
635 for (Criterion criterion : flow.selector().criteria()) {
636 boolean criterionFound = false;
637
638 for (int criterionIndex = 0;
639 criterionIndex < jsonCriteria.size();
640 criterionIndex++) {
641 String jsonType =
642 jsonCriteria.get(criterionIndex)
643 .asObject().get(TYPE).asString();
644 String criterionType = criterion.type().name();
645 if (jsonType.equals(criterionType)) {
646 criterionFound = true;
647 }
648 }
649 if (!criterionFound) {
650 reason = "criterion " + criterion;
651 return false;
652 }
653 }
654 }
655 return true;
656 }
657
658 @Override
659 public void describeTo(Description description) {
660 description.appendText(reason);
661 }
662 }
663
664 /**
Ray Milkey2b217142014-12-15 09:24:24 -0800665 * Hamcrest matcher to check that an intent is represented properly in a JSON
666 * array of intents.
667 */
668 public static class IntentJsonArrayMatcher extends TypeSafeMatcher<JsonArray> {
669 private final Intent intent;
670 private String reason = "";
671
672 public IntentJsonArrayMatcher(Intent intentValue) {
673 intent = intentValue;
674 }
675
676 @Override
677 public boolean matchesSafely(JsonArray json) {
678 boolean intentFound = false;
Ray Milkeyf7cb4012015-07-20 13:01:07 -0700679 final int expectedAttributes = 5;
Ray Milkey2b217142014-12-15 09:24:24 -0800680 for (int jsonIntentIndex = 0; jsonIntentIndex < json.size();
681 jsonIntentIndex++) {
682
683 final JsonObject jsonIntent = json.get(jsonIntentIndex).asObject();
684
685 if (jsonIntent.names().size() != expectedAttributes) {
686 reason = "Found an intent with the wrong number of attributes";
687 return false;
688 }
689
690 final String jsonIntentId = jsonIntent.get("id").asString();
691 if (jsonIntentId.equals(intent.id().toString())) {
692 intentFound = true;
693
694 // We found the correct intent, check attribute values
695 assertThat(jsonIntent, matchesIntent(intent));
696 }
697 }
698 if (!intentFound) {
699 reason = "Intent with id " + intent.id().toString() + " not found";
700 return false;
701 } else {
702 return true;
703 }
704 }
705
706 @Override
707 public void describeTo(Description description) {
708 description.appendText(reason);
709 }
710 }
711
712 /**
713 * Factory to allocate an intent array matcher.
714 *
715 * @param intent intent object we are looking for
716 * @return matcher
717 */
718 private static IntentJsonArrayMatcher hasIntent(Intent intent) {
719 return new IntentJsonArrayMatcher(intent);
720 }
Ray Milkey4f5de002014-12-17 19:26:11 -0800721
Ray Milkeyed0b1662015-02-05 09:34:29 -0800722 /**
723 * Initializes test mocks and environment.
724 */
Ray Milkey2b217142014-12-15 09:24:24 -0800725 @Before
Ray Milkeyed0b1662015-02-05 09:34:29 -0800726 public void setUpTest() {
Ray Milkey2b217142014-12-15 09:24:24 -0800727 expect(mockIntentService.getIntents()).andReturn(intents).anyTimes();
Ray Milkey1534f8d2015-05-13 15:42:50 -0700728 expect(mockIntentService.getIntentState(anyObject()))
729 .andReturn(IntentState.INSTALLED)
730 .anyTimes();
Ray Milkey2b217142014-12-15 09:24:24 -0800731 // Register the services needed for the test
732 final CodecManager codecService = new CodecManager();
733 codecService.activate();
734 ServiceDirectory testDirectory =
735 new TestServiceDirectory()
736 .add(IntentService.class, mockIntentService)
Author Namee252a002016-09-26 22:42:24 +0530737 .add(FlowRuleService.class, mockFlowService)
Ayaka Koshibec06c89b2015-02-10 19:25:41 -0800738 .add(CodecService.class, codecService)
739 .add(CoreService.class, mockCoreService);
Ray Milkey2b217142014-12-15 09:24:24 -0800740
741 BaseResource.setServiceDirectory(testDirectory);
742
Thomas Vachuska2048c1f2017-05-10 19:32:22 -0700743 MockIdGenerator.cleanBind();
Ray Milkey2b217142014-12-15 09:24:24 -0800744 }
745
Ray Milkeyed0b1662015-02-05 09:34:29 -0800746 /**
747 * Tears down and verifies test mocks and environment.
748 */
Ray Milkey2b217142014-12-15 09:24:24 -0800749 @After
Ray Milkeyed0b1662015-02-05 09:34:29 -0800750 public void tearDownTest() {
Thomas Vachuska2048c1f2017-05-10 19:32:22 -0700751 MockIdGenerator.unbind();
Ray Milkey2b217142014-12-15 09:24:24 -0800752 verify(mockIntentService);
Ray Milkey2b217142014-12-15 09:24:24 -0800753 }
754
755 /**
756 * Tests the result of the rest api GET when there are no intents.
757 */
758 @Test
759 public void testIntentsEmptyArray() {
760 replay(mockIntentService);
Jian Li9d616492016-03-09 10:52:49 -0800761 final WebTarget wt = target();
762 final String response = wt.path("intents").request().get(String.class);
Ray Milkey2b217142014-12-15 09:24:24 -0800763 assertThat(response, is("{\"intents\":[]}"));
764 }
765
766 /**
767 * Tests the result of the rest api GET when intents are defined.
768 */
769 @Test
770 public void testIntentsArray() {
771 replay(mockIntentService);
772
Ray Milkey43a28222015-02-23 13:57:58 -0800773 final Intent intent1 = new MockIntent(1L, Collections.emptyList());
Ray Milkey2b217142014-12-15 09:24:24 -0800774 final HashSet<NetworkResource> resources = new HashSet<>();
775 resources.add(new MockResource(1));
776 resources.add(new MockResource(2));
777 resources.add(new MockResource(3));
Ray Milkey43a28222015-02-23 13:57:58 -0800778 final Intent intent2 = new MockIntent(2L, resources);
Ray Milkey2b217142014-12-15 09:24:24 -0800779
780 intents.add(intent1);
781 intents.add(intent2);
Jian Li9d616492016-03-09 10:52:49 -0800782 final WebTarget wt = target();
783 final String response = wt.path("intents").request().get(String.class);
Ray Milkey2b217142014-12-15 09:24:24 -0800784 assertThat(response, containsString("{\"intents\":["));
785
Jian Li80cfe452016-01-14 16:04:58 -0800786 final JsonObject result = Json.parse(response).asObject();
Ray Milkey2b217142014-12-15 09:24:24 -0800787 assertThat(result, notNullValue());
788
789 assertThat(result.names(), hasSize(1));
790 assertThat(result.names().get(0), is("intents"));
791
792 final JsonArray jsonIntents = result.get("intents").asArray();
793 assertThat(jsonIntents, notNullValue());
794
795 assertThat(jsonIntents, hasIntent(intent1));
796 assertThat(jsonIntents, hasIntent(intent2));
797 }
798
799 /**
800 * Tests the result of a rest api GET for a single intent.
801 */
802 @Test
803 public void testIntentsSingle() {
804 final HashSet<NetworkResource> resources = new HashSet<>();
805 resources.add(new MockResource(1));
806 resources.add(new MockResource(2));
807 resources.add(new MockResource(3));
Ray Milkey43a28222015-02-23 13:57:58 -0800808 final Intent intent = new MockIntent(3L, resources);
Ray Milkey2b217142014-12-15 09:24:24 -0800809
810 intents.add(intent);
811
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800812 expect(mockIntentService.getIntent(Key.of(0, APP_ID)))
Ray Milkey2b217142014-12-15 09:24:24 -0800813 .andReturn(intent)
814 .anyTimes();
Ayaka Koshibec06c89b2015-02-10 19:25:41 -0800815 expect(mockIntentService.getIntent(Key.of("0", APP_ID)))
816 .andReturn(intent)
817 .anyTimes();
Ray Milkey05b169d2015-08-13 14:33:33 -0700818 expect(mockIntentService.getIntent(Key.of(0, APP_ID)))
819 .andReturn(intent)
820 .anyTimes();
821 expect(mockIntentService.getIntent(Key.of("0x0", APP_ID)))
822 .andReturn(null)
823 .anyTimes();
Ray Milkey2b217142014-12-15 09:24:24 -0800824 replay(mockIntentService);
Ray Milkeyf7cb4012015-07-20 13:01:07 -0700825 expect(mockCoreService.getAppId(APP_ID.name()))
Ayaka Koshibec06c89b2015-02-10 19:25:41 -0800826 .andReturn(APP_ID).anyTimes();
827 replay(mockCoreService);
Jian Li9d616492016-03-09 10:52:49 -0800828 final WebTarget wt = target();
Ray Milkey05b169d2015-08-13 14:33:33 -0700829
830 // Test get using key string
Jian Li9d616492016-03-09 10:52:49 -0800831 final String response = wt.path("intents/" + APP_ID.name()
832 + "/0").request().get(String.class);
Jian Li80cfe452016-01-14 16:04:58 -0800833 final JsonObject result = Json.parse(response).asObject();
Ray Milkey2b217142014-12-15 09:24:24 -0800834 assertThat(result, matchesIntent(intent));
Ray Milkey05b169d2015-08-13 14:33:33 -0700835
836 // Test get using numeric value
Jian Li9d616492016-03-09 10:52:49 -0800837 final String responseNumeric = wt.path("intents/" + APP_ID.name()
838 + "/0x0").request().get(String.class);
Jian Li80cfe452016-01-14 16:04:58 -0800839 final JsonObject resultNumeric = Json.parse(responseNumeric).asObject();
Ray Milkey05b169d2015-08-13 14:33:33 -0700840 assertThat(resultNumeric, matchesIntent(intent));
Ray Milkey2b217142014-12-15 09:24:24 -0800841 }
842
843 /**
Author Namee252a002016-09-26 22:42:24 +0530844 * Tests the result of a rest api GET for related flows for single intent.
845 */
846 @Test
847 public void testRelatedFlowsForIntents() {
848 List<FlowEntry> flowEntries = new ArrayList<>();
849 flowEntries.add(flow1);
850 flowEntries.add(flow2);
851 List<List<FlowEntry>> paths = new ArrayList<>();
852 paths.add(flowEntries);
853 List<FlowRule> flowRules = new ArrayList<>();
854 flowRules.add(flowRule1);
855 flowRules.add(flowRule2);
856 FlowRuleIntent flowRuleIntent = new FlowRuleIntent(
857 APP_ID,
858 flowRules,
859 new HashSet<NetworkResource>());
860 Intent intent = new MockIntent(3L);
861 installableIntents.add(flowRuleIntent);
862 intents.add(intent);
863
864 expect(mockIntentService.getIntent(Key.of(0, APP_ID)))
865 .andReturn(intent)
866 .anyTimes();
867 expect(mockIntentService.getIntent(Key.of("0", APP_ID)))
868 .andReturn(intent)
869 .anyTimes();
870 expect(mockIntentService.getIntent(Key.of(0, APP_ID)))
871 .andReturn(intent)
872 .anyTimes();
873 expect(mockIntentService.getIntent(Key.of("0x0", APP_ID)))
874 .andReturn(null)
875 .anyTimes();
876 expect(mockIntentService.getInstallableIntents(intent.key()))
877 .andReturn(installableIntents)
878 .anyTimes();
879 replay(mockIntentService);
880
881 expect(mockFlowService.getFlowEntries(deviceId1))
882 .andReturn(flowEntries).anyTimes();
883 replay(mockFlowService);
884
885 expect(mockCoreService.getAppId(APP_ID.name()))
886 .andReturn(APP_ID).anyTimes();
887 expect(mockCoreService.getAppId(APP_ID.id()))
888 .andReturn(APP_ID).anyTimes();
889 replay(mockCoreService);
890
891 final WebTarget wt = target();
892
893 // Test get using key string
894 final String response = wt.path("intents/relatedflows/" + APP_ID.name()
895 + "/0").request().get(String.class);
896 final JsonObject result = Json.parse(response).asObject();
897 assertThat(result, matchesRelatedFlowEntries(paths, APP_ID.name()));
898
899 // Test get using numeric value
900 final String responseNumeric = wt.path("intents/relatedflows/" + APP_ID.name()
901 + "/0x0").request().get(String.class);
902 final JsonObject resultNumeric = Json.parse(responseNumeric).asObject();
903 assertThat(resultNumeric, matchesRelatedFlowEntries(paths, APP_ID.name()));
904 }
905
906 /**
Rafal Szaleckif97e0ed2017-06-01 13:07:18 +0200907 * Tests the result of a rest api GET for intent installables.
908 */
909 @Test
910 public void testIntentInstallables() {
911
912 Link link1 = DefaultLink.builder()
913 .type(Link.Type.DIRECT)
914 .providerId(ProviderId.NONE)
915 .src(connectPoint1)
916 .dst(connectPoint2)
917 .build();
918
919 Link link2 = DefaultLink.builder()
920 .type(Link.Type.DIRECT)
921 .providerId(ProviderId.NONE)
922 .src(connectPoint3)
923 .dst(connectPoint4)
924 .build();
925
926 Set<NetworkResource> resources = new HashSet<>();
927 resources.add(link1);
928 resources.add(link2);
929
930 FlowRuleIntent flowRuleIntent = new FlowRuleIntent(
931 APP_ID,
932 new ArrayList<>(),
933 resources);
934
935 Intent intent = new MockIntent(MockIntent.nextId());
936 Long intentId = intent.id().id();
937 installableIntents.add(flowRuleIntent);
938 intents.add(intent);
939
940 expect(mockIntentService.getIntent(Key.of(intentId, APP_ID)))
941 .andReturn(intent)
942 .anyTimes();
943 expect(mockIntentService.getIntent(Key.of(intentId.toString(), APP_ID)))
944 .andReturn(intent)
945 .anyTimes();
946 expect(mockIntentService.getIntent(Key.of(intentId, APP_ID)))
947 .andReturn(intent)
948 .anyTimes();
949 expect(mockIntentService.getIntent(Key.of(Long.toHexString(intentId), APP_ID)))
950 .andReturn(null)
951 .anyTimes();
952 expect(mockIntentService.getInstallableIntents(intent.key()))
953 .andReturn(installableIntents)
954 .anyTimes();
955 replay(mockIntentService);
956
957 replay(mockFlowService);
958
959 expect(mockCoreService.getAppId(APP_ID.name()))
960 .andReturn(APP_ID).anyTimes();
961 expect(mockCoreService.getAppId(APP_ID.id()))
962 .andReturn(APP_ID).anyTimes();
963 replay(mockCoreService);
964
965 final WebTarget wt = target();
966
967 // Test get using key string
968 final String response = wt.path("intents/installables/" + APP_ID.name()
969 + "/" + intentId).request().get(String.class);
970 final JsonObject result = Json.parse(response).asObject();
971 assertThat(result.get(INSTALLABLES).asArray(), hasIntent(flowRuleIntent));
972
973 // Test get using numeric value
974 final String responseNumeric = wt.path("intents/installables/" + APP_ID.name()
975 + "/" + Long.toHexString(intentId)).request().get(String.class);
976 final JsonObject resultNumeric = Json.parse(responseNumeric).asObject();
977 assertThat(resultNumeric.get(INSTALLABLES).asArray(), hasIntent(flowRuleIntent));
978 }
979
980 /**
Ray Milkey2b217142014-12-15 09:24:24 -0800981 * Tests that a fetch of a non-existent intent object throws an exception.
982 */
983 @Test
984 public void testBadGet() {
985
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800986 expect(mockIntentService.getIntent(Key.of(0, APP_ID)))
Ray Milkey2b217142014-12-15 09:24:24 -0800987 .andReturn(null)
988 .anyTimes();
989 replay(mockIntentService);
990
Jian Li9d616492016-03-09 10:52:49 -0800991 WebTarget wt = target();
Ray Milkey2b217142014-12-15 09:24:24 -0800992 try {
Jian Li9d616492016-03-09 10:52:49 -0800993 wt.path("intents/0").request().get(String.class);
Ray Milkey2b217142014-12-15 09:24:24 -0800994 fail("Fetch of non-existent intent did not throw an exception");
Jian Li9d616492016-03-09 10:52:49 -0800995 } catch (NotFoundException ex) {
Ray Milkey2b217142014-12-15 09:24:24 -0800996 assertThat(ex.getMessage(),
Jian Li9d616492016-03-09 10:52:49 -0800997 containsString("HTTP 404 Not Found"));
Ray Milkey2b217142014-12-15 09:24:24 -0800998 }
999 }
Ray Milkeyb82c42b2015-06-30 09:42:20 -07001000
1001 /**
1002 * Tests creating an intent with POST.
1003 */
1004 @Test
1005 public void testPost() {
Ray Milkeyf7cb4012015-07-20 13:01:07 -07001006 ApplicationId testId = new DefaultApplicationId(2, "myApp");
1007 expect(mockCoreService.getAppId("myApp"))
1008 .andReturn(testId);
Ray Milkeyb82c42b2015-06-30 09:42:20 -07001009 replay(mockCoreService);
1010
1011 mockIntentService.submit(anyObject());
1012 expectLastCall();
1013 replay(mockIntentService);
1014
1015 InputStream jsonStream = IntentsResourceTest.class
1016 .getResourceAsStream("post-intent.json");
Jian Li9d616492016-03-09 10:52:49 -08001017 WebTarget wt = target();
Ray Milkeyb82c42b2015-06-30 09:42:20 -07001018
Jian Li9d616492016-03-09 10:52:49 -08001019 Response response = wt.path("intents")
1020 .request(MediaType.APPLICATION_JSON_TYPE)
1021 .post(Entity.json(jsonStream));
Ray Milkeyb82c42b2015-06-30 09:42:20 -07001022 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
1023 String location = response.getLocation().getPath();
Ray Milkey8d076402015-08-31 15:43:18 -07001024 assertThat(location, Matchers.startsWith("/intents/myApp/"));
Ray Milkeyb82c42b2015-06-30 09:42:20 -07001025 }
Ray Milkey7b158512015-07-21 16:32:43 -07001026
1027 /**
Ray Milkey05b169d2015-08-13 14:33:33 -07001028 * Tests creating an intent with POST and illegal JSON.
1029 */
1030 @Test
1031 public void testBadPost() {
1032 replay(mockCoreService);
1033 replay(mockIntentService);
1034
1035 String json = "this is invalid!";
Jian Li9d616492016-03-09 10:52:49 -08001036 WebTarget wt = target();
Ray Milkey05b169d2015-08-13 14:33:33 -07001037
Jian Li9d616492016-03-09 10:52:49 -08001038 Response response = wt.path("intents")
1039 .request(MediaType.APPLICATION_JSON_TYPE)
1040 .post(Entity.json(json));
Ray Milkey05b169d2015-08-13 14:33:33 -07001041 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_BAD_REQUEST));
1042 }
1043
1044 /**
Ray Milkey7b158512015-07-21 16:32:43 -07001045 * Tests removing an intent with DELETE.
1046 */
1047 @Test
1048 public void testRemove() {
1049 final HashSet<NetworkResource> resources = new HashSet<>();
1050 resources.add(new MockResource(1));
1051 resources.add(new MockResource(2));
1052 resources.add(new MockResource(3));
1053 final Intent intent = new MockIntent(3L, resources);
1054 final ApplicationId appId = new DefaultApplicationId(2, "app");
1055 IntentService fakeManager = new FakeIntentManager();
1056
1057 expect(mockCoreService.getAppId("app"))
1058 .andReturn(appId).once();
1059 replay(mockCoreService);
1060
1061 mockIntentService.withdraw(anyObject());
1062 expectLastCall().andDelegateTo(fakeManager).once();
1063 expect(mockIntentService.getIntent(Key.of(2, appId)))
1064 .andReturn(intent)
1065 .once();
1066 expect(mockIntentService.getIntent(Key.of("0x2", appId)))
1067 .andReturn(null)
1068 .once();
1069
1070 mockIntentService.addListener(anyObject());
1071 expectLastCall().andDelegateTo(fakeManager).once();
1072 mockIntentService.removeListener(anyObject());
1073 expectLastCall().andDelegateTo(fakeManager).once();
1074
1075 replay(mockIntentService);
1076
Jian Li9d616492016-03-09 10:52:49 -08001077 WebTarget wt = target();
Ray Milkey7b158512015-07-21 16:32:43 -07001078
Jian Li9d616492016-03-09 10:52:49 -08001079 Response response = wt.path("intents/app/0x2")
Ray Milkey7c251822016-04-06 17:38:25 -07001080 .request(MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN)
Jian Li9d616492016-03-09 10:52:49 -08001081 .delete();
Ray Milkey7b158512015-07-21 16:32:43 -07001082 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_NO_CONTENT));
1083 }
Ray Milkey05b169d2015-08-13 14:33:33 -07001084
1085 /**
1086 * Tests removal of a non existent intent with DELETE.
1087 */
1088 @Test
1089 public void testBadRemove() {
1090 final ApplicationId appId = new DefaultApplicationId(2, "app");
1091
1092 expect(mockCoreService.getAppId("app"))
1093 .andReturn(appId).once();
1094 replay(mockCoreService);
1095
1096 expect(mockIntentService.getIntent(Key.of(2, appId)))
1097 .andReturn(null)
1098 .once();
1099 expect(mockIntentService.getIntent(Key.of("0x2", appId)))
1100 .andReturn(null)
1101 .once();
1102
1103 replay(mockIntentService);
1104
Jian Li9d616492016-03-09 10:52:49 -08001105 WebTarget wt = target();
Ray Milkey05b169d2015-08-13 14:33:33 -07001106
Jian Li9d616492016-03-09 10:52:49 -08001107 Response response = wt.path("intents/app/0x2")
Ray Milkey7c251822016-04-06 17:38:25 -07001108 .request(MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN)
Jian Li9d616492016-03-09 10:52:49 -08001109 .delete();
Ray Milkey05b169d2015-08-13 14:33:33 -07001110 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_NO_CONTENT));
1111 }
1112
Ray Milkey2b217142014-12-15 09:24:24 -08001113}