blob: 2ba847e28717ac4ca3b7f34711681ac2c90c6415 [file] [log] [blame]
Ray Milkey4f5de002014-12-17 19:26:11 -08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2016-present Open Networking Laboratory
Ray Milkey4f5de002014-12-17 19:26:11 -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 Milkey4f5de002014-12-17 19:26:11 -080017
Bharath Thiruveedula99849dc2016-11-17 22:04:38 +053018import java.io.InputStream;
19import java.net.HttpURLConnection;
20import java.util.HashMap;
21import java.util.HashSet;
22import java.util.Set;
23import java.util.concurrent.TimeUnit;
24
25import javax.ws.rs.NotFoundException;
26import javax.ws.rs.client.Entity;
27import javax.ws.rs.client.WebTarget;
28import javax.ws.rs.core.MediaType;
29import javax.ws.rs.core.Response;
30
Ray Milkey4f5de002014-12-17 19:26:11 -080031import org.hamcrest.Description;
Ray Milkeyeb5c7172015-06-23 14:59:27 -070032import org.hamcrest.Matchers;
Ray Milkey4f5de002014-12-17 19:26:11 -080033import org.hamcrest.TypeSafeMatcher;
34import org.junit.After;
35import org.junit.Before;
36import org.junit.Test;
37import org.onlab.osgi.ServiceDirectory;
38import org.onlab.osgi.TestServiceDirectory;
39import org.onlab.packet.MacAddress;
40import org.onlab.rest.BaseResource;
Jian Li2907ad22016-05-12 23:08:54 -070041import org.onosproject.app.ApplicationService;
Ray Milkey4f5de002014-12-17 19:26:11 -080042import org.onosproject.codec.CodecService;
43import org.onosproject.codec.impl.CodecManager;
Jonathan Hart9b80da82016-11-10 21:46:15 +000044import org.onosproject.codec.impl.FlowRuleCodec;
Ray Milkeyd43fe452015-05-29 09:35:12 -070045import org.onosproject.core.CoreService;
Ray Milkey4f5de002014-12-17 19:26:11 -080046import org.onosproject.core.DefaultGroupId;
47import org.onosproject.core.GroupId;
48import org.onosproject.net.DefaultDevice;
49import org.onosproject.net.Device;
50import org.onosproject.net.DeviceId;
Ray Milkeyd43fe452015-05-29 09:35:12 -070051import org.onosproject.net.NetTestTools;
Ray Milkey4f5de002014-12-17 19:26:11 -080052import org.onosproject.net.device.DeviceService;
53import org.onosproject.net.flow.DefaultTrafficSelector;
54import org.onosproject.net.flow.DefaultTrafficTreatment;
55import org.onosproject.net.flow.FlowEntry;
56import org.onosproject.net.flow.FlowId;
Jonathan Hartf44e42c2015-08-04 09:58:46 -070057import org.onosproject.net.flow.FlowRule;
jcc3d4e14a2015-04-21 11:32:05 +080058import org.onosproject.net.flow.FlowRuleExtPayLoad;
Ray Milkey4f5de002014-12-17 19:26:11 -080059import org.onosproject.net.flow.FlowRuleService;
60import org.onosproject.net.flow.TrafficSelector;
61import org.onosproject.net.flow.TrafficTreatment;
62import org.onosproject.net.flow.criteria.Criterion;
63import org.onosproject.net.flow.instructions.Instruction;
jcc3d4e14a2015-04-21 11:32:05 +080064
Bharath Thiruveedula99849dc2016-11-17 22:04:38 +053065import com.eclipsesource.json.Json;
66import com.eclipsesource.json.JsonArray;
67import com.eclipsesource.json.JsonObject;
68import com.google.common.collect.ImmutableSet;
Ray Milkey4f5de002014-12-17 19:26:11 -080069
Thiago Santos877914d2016-07-20 18:29:29 -030070import static java.util.concurrent.TimeUnit.SECONDS;
Ray Milkey4f5de002014-12-17 19:26:11 -080071import static org.easymock.EasyMock.anyObject;
Ray Milkeyd43fe452015-05-29 09:35:12 -070072import static org.easymock.EasyMock.anyShort;
Jian Li2907ad22016-05-12 23:08:54 -070073import static org.easymock.EasyMock.anyString;
Ray Milkey4f5de002014-12-17 19:26:11 -080074import static org.easymock.EasyMock.createMock;
75import static org.easymock.EasyMock.expect;
Ray Milkeyd43fe452015-05-29 09:35:12 -070076import static org.easymock.EasyMock.expectLastCall;
Ray Milkey4f5de002014-12-17 19:26:11 -080077import static org.easymock.EasyMock.replay;
78import static org.easymock.EasyMock.verify;
79import static org.hamcrest.Matchers.containsString;
80import static org.hamcrest.Matchers.hasSize;
81import static org.hamcrest.Matchers.is;
82import static org.hamcrest.Matchers.not;
83import static org.hamcrest.Matchers.notNullValue;
84import static org.junit.Assert.assertThat;
85import static org.junit.Assert.fail;
Ray Milkeyeb5c7172015-06-23 14:59:27 -070086import static org.onosproject.net.NetTestTools.APP_ID;
Ray Milkey4f5de002014-12-17 19:26:11 -080087
88/**
89 * Unit tests for Flows REST APIs.
90 */
Ray Milkey9c3d3362015-01-28 10:39:56 -080091public class FlowsResourceTest extends ResourceTest {
Ray Milkey4f5de002014-12-17 19:26:11 -080092 final FlowRuleService mockFlowService = createMock(FlowRuleService.class);
Ray Milkeyd43fe452015-05-29 09:35:12 -070093 CoreService mockCoreService = createMock(CoreService.class);
94
Ray Milkey4f5de002014-12-17 19:26:11 -080095 final HashMap<DeviceId, Set<FlowEntry>> rules = new HashMap<>();
96
97 final DeviceService mockDeviceService = createMock(DeviceService.class);
98
99 final DeviceId deviceId1 = DeviceId.deviceId("1");
100 final DeviceId deviceId2 = DeviceId.deviceId("2");
101 final DeviceId deviceId3 = DeviceId.deviceId("3");
102 final Device device1 = new DefaultDevice(null, deviceId1, Device.Type.OTHER,
103 "", "", "", "", null);
104 final Device device2 = new DefaultDevice(null, deviceId2, Device.Type.OTHER,
105 "", "", "", "", null);
106
Jian Li2907ad22016-05-12 23:08:54 -0700107 final ApplicationService mockApplicationService = createMock(ApplicationService.class);
108
Ray Milkey4f5de002014-12-17 19:26:11 -0800109 final MockFlowEntry flow1 = new MockFlowEntry(deviceId1, 1);
110 final MockFlowEntry flow2 = new MockFlowEntry(deviceId1, 2);
111
112 final MockFlowEntry flow3 = new MockFlowEntry(deviceId2, 3);
113 final MockFlowEntry flow4 = new MockFlowEntry(deviceId2, 4);
114
115 final MockFlowEntry flow5 = new MockFlowEntry(deviceId2, 5);
116 final MockFlowEntry flow6 = new MockFlowEntry(deviceId2, 6);
117
Bharath Thiruveedula99849dc2016-11-17 22:04:38 +0530118 final Set<FlowEntry> flowEntries = ImmutableSet.of(flow1, flow2, flow3, flow4, flow5, flow6);
Jian Li2907ad22016-05-12 23:08:54 -0700119
Ray Milkey4f5de002014-12-17 19:26:11 -0800120 /**
121 * Mock class for a flow entry.
122 */
123 private static class MockFlowEntry implements FlowEntry {
124 final DeviceId deviceId;
125 final long baseValue;
126 TrafficTreatment treatment;
127 TrafficSelector selector;
128
129 public MockFlowEntry(DeviceId deviceId, long id) {
130 this.deviceId = deviceId;
131 this.baseValue = id * 100;
132 }
133
134 @Override
135 public FlowEntryState state() {
136 return FlowEntryState.ADDED;
137 }
138
139 @Override
140 public long life() {
Thiago Santos877914d2016-07-20 18:29:29 -0300141 return life(SECONDS);
142 }
143
144 @Override
145 public long life(TimeUnit timeUnit) {
146 return SECONDS.convert(baseValue + 11, timeUnit);
Ray Milkey4f5de002014-12-17 19:26:11 -0800147 }
148
149 @Override
150 public long packets() {
151 return baseValue + 22;
152 }
153
154 @Override
155 public long bytes() {
156 return baseValue + 33;
157 }
158
159 @Override
160 public long lastSeen() {
161 return baseValue + 44;
162 }
163
164 @Override
165 public int errType() {
166 return 0;
167 }
168
169 @Override
170 public int errCode() {
171 return 0;
172 }
173
174 @Override
175 public FlowId id() {
176 final long id = baseValue + 55;
177 return FlowId.valueOf(id);
178 }
179
180 @Override
alshabib08d98982015-04-21 16:25:50 -0700181 public GroupId groupId() {
182 return new DefaultGroupId(3);
Ray Milkey4f5de002014-12-17 19:26:11 -0800183 }
184
185 @Override
alshabib08d98982015-04-21 16:25:50 -0700186 public short appId() {
187 return 2;
Ray Milkey4f5de002014-12-17 19:26:11 -0800188 }
189
190 @Override
191 public int priority() {
192 return (int) (baseValue + 66);
193 }
194
195 @Override
196 public DeviceId deviceId() {
197 return deviceId;
198 }
199
200 @Override
201 public TrafficSelector selector() {
202 return selector;
203 }
204
205 @Override
206 public TrafficTreatment treatment() {
207 return treatment;
208 }
209
210 @Override
211 public int timeout() {
212 return (int) (baseValue + 77);
213 }
214
215 @Override
Murat Parlakisikc6759e82016-06-29 03:22:22 -0700216 public int hardTimeout() {
217 return 0;
218 }
219
220 @Override
221 public FlowRemoveReason reason() {
222 return FlowRemoveReason.NO_REASON;
223 }
224
225 @Override
Ray Milkey4f5de002014-12-17 19:26:11 -0800226 public boolean isPermanent() {
227 return false;
228 }
sangho11c30ac2015-01-22 14:30:55 -0800229
Yuta HIGUCHI32a53c52015-02-08 01:25:40 -0800230 @Override
alshabibdb774072015-04-20 13:13:51 -0700231 public int tableId() {
232 return 0;
233 }
jcc3d4e14a2015-04-21 11:32:05 +0800234
235 @Override
Jonathan Hartf44e42c2015-08-04 09:58:46 -0700236 public boolean exactMatch(FlowRule rule) {
237 return false;
238 }
239
240 @Override
jcc3d4e14a2015-04-21 11:32:05 +0800241 public FlowRuleExtPayLoad payLoad() {
242 return null;
243 }
Ray Milkey4f5de002014-12-17 19:26:11 -0800244 }
245
Jian Li2907ad22016-05-12 23:08:54 -0700246
247 /**
Ray Milkey4f5de002014-12-17 19:26:11 -0800248 * Populates some flows used as testing data.
249 */
250 private void setupMockFlows() {
251 flow2.treatment = DefaultTrafficTreatment.builder()
Ray Milkey4f5de002014-12-17 19:26:11 -0800252 .setEthDst(MacAddress.BROADCAST)
253 .build();
254 flow2.selector = DefaultTrafficSelector.builder()
255 .matchEthType((short) 3)
256 .matchIPProtocol((byte) 9)
257 .build();
258 flow4.treatment = DefaultTrafficTreatment.builder()
Ray Milkey4f5de002014-12-17 19:26:11 -0800259 .build();
260 final Set<FlowEntry> flows1 = new HashSet<>();
261 flows1.add(flow1);
262 flows1.add(flow2);
263
264 final Set<FlowEntry> flows2 = new HashSet<>();
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700265 flows2.add(flow3);
266 flows2.add(flow4);
Ray Milkey4f5de002014-12-17 19:26:11 -0800267
268 rules.put(deviceId1, flows1);
269 rules.put(deviceId2, flows2);
270
271 expect(mockFlowService.getFlowEntries(deviceId1))
272 .andReturn(rules.get(deviceId1)).anyTimes();
273 expect(mockFlowService.getFlowEntries(deviceId2))
274 .andReturn(rules.get(deviceId2)).anyTimes();
275 }
276
277 /**
278 * Sets up the global values for all the tests.
279 */
280 @Before
Ray Milkeyed0b1662015-02-05 09:34:29 -0800281 public void setUpTest() {
Ray Milkey4f5de002014-12-17 19:26:11 -0800282 // Mock device service
283 expect(mockDeviceService.getDevice(deviceId1))
284 .andReturn(device1);
285 expect(mockDeviceService.getDevice(deviceId2))
286 .andReturn(device2);
287 expect(mockDeviceService.getDevices())
288 .andReturn(ImmutableSet.of(device1, device2));
289
Ray Milkeyd43fe452015-05-29 09:35:12 -0700290 // Mock Core Service
291 expect(mockCoreService.getAppId(anyShort()))
292 .andReturn(NetTestTools.APP_ID).anyTimes();
Jian Li2907ad22016-05-12 23:08:54 -0700293 expect(mockCoreService.getAppId(anyString()))
294 .andReturn(NetTestTools.APP_ID).anyTimes();
Jonathan Hart9b80da82016-11-10 21:46:15 +0000295 expect(mockCoreService.registerApplication(FlowRuleCodec.REST_APP_ID))
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700296 .andReturn(APP_ID).anyTimes();
Ray Milkeyd43fe452015-05-29 09:35:12 -0700297 replay(mockCoreService);
298
Ray Milkey4f5de002014-12-17 19:26:11 -0800299 // Register the services needed for the test
300 final CodecManager codecService = new CodecManager();
301 codecService.activate();
302 ServiceDirectory testDirectory =
303 new TestServiceDirectory()
304 .add(FlowRuleService.class, mockFlowService)
305 .add(DeviceService.class, mockDeviceService)
Ray Milkeyd43fe452015-05-29 09:35:12 -0700306 .add(CodecService.class, codecService)
Jian Li2907ad22016-05-12 23:08:54 -0700307 .add(CoreService.class, mockCoreService)
308 .add(ApplicationService.class, mockApplicationService);
Ray Milkey4f5de002014-12-17 19:26:11 -0800309
310 BaseResource.setServiceDirectory(testDirectory);
311 }
312
313 /**
314 * Cleans up and verifies the mocks.
Ray Milkey4f5de002014-12-17 19:26:11 -0800315 */
316 @After
Ray Milkeyed0b1662015-02-05 09:34:29 -0800317 public void tearDownTest() {
Ray Milkey4f5de002014-12-17 19:26:11 -0800318 verify(mockFlowService);
Ray Milkeyd43fe452015-05-29 09:35:12 -0700319 verify(mockCoreService);
Ray Milkey4f5de002014-12-17 19:26:11 -0800320 }
321
322 /**
323 * Hamcrest matcher to check that a flow representation in JSON matches
324 * the actual flow entry.
325 */
Jian Li2907ad22016-05-12 23:08:54 -0700326 public static class FlowEntryJsonMatcher extends TypeSafeMatcher<JsonObject> {
Ray Milkey4f5de002014-12-17 19:26:11 -0800327 private final FlowEntry flow;
Ray Milkeybe539db2015-09-04 11:00:43 -0700328 private final String expectedAppId;
Ray Milkey4f5de002014-12-17 19:26:11 -0800329 private String reason = "";
330
Jian Li2907ad22016-05-12 23:08:54 -0700331 public FlowEntryJsonMatcher(FlowEntry flowValue, String expectedAppIdValue) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800332 flow = flowValue;
Ray Milkeybe539db2015-09-04 11:00:43 -0700333 expectedAppId = expectedAppIdValue;
Ray Milkey4f5de002014-12-17 19:26:11 -0800334 }
335
336 @Override
337 public boolean matchesSafely(JsonObject jsonFlow) {
338 // check id
339 final String jsonId = jsonFlow.get("id").asString();
340 final String flowId = Long.toString(flow.id().value());
341 if (!jsonId.equals(flowId)) {
342 reason = "id " + flow.id().toString();
343 return false;
344 }
345
346 // check application id
Ray Milkeybe539db2015-09-04 11:00:43 -0700347 final String jsonAppId = jsonFlow.get("appId").asString();
348 if (!jsonAppId.equals(expectedAppId)) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800349 reason = "appId " + Short.toString(flow.appId());
350 return false;
351 }
352
353 // check device id
354 final String jsonDeviceId = jsonFlow.get("deviceId").asString();
355 if (!jsonDeviceId.equals(flow.deviceId().toString())) {
356 reason = "deviceId " + flow.deviceId();
357 return false;
358 }
359
360 // check treatment and instructions array
361 if (flow.treatment() != null) {
362 final JsonObject jsonTreatment = jsonFlow.get("treatment").asObject();
363 final JsonArray jsonInstructions = jsonTreatment.get("instructions").asArray();
Ray Milkey42507352015-03-20 15:16:10 -0700364 if (flow.treatment().immediate().size() != jsonInstructions.size()) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800365 reason = "instructions array size of " +
Ray Milkey42507352015-03-20 15:16:10 -0700366 Integer.toString(flow.treatment().immediate().size());
Ray Milkey4f5de002014-12-17 19:26:11 -0800367 return false;
368 }
Ray Milkey42507352015-03-20 15:16:10 -0700369 for (final Instruction instruction : flow.treatment().immediate()) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800370 boolean instructionFound = false;
Ray Milkey4f5de002014-12-17 19:26:11 -0800371 for (int instructionIndex = 0; instructionIndex < jsonInstructions.size(); instructionIndex++) {
Ray Milkeyc95bb9d2015-01-06 10:28:24 -0800372 final String jsonType =
373 jsonInstructions.get(instructionIndex)
374 .asObject().get("type").asString();
375 final String instructionType = instruction.type().name();
376 if (jsonType.equals(instructionType)) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800377 instructionFound = true;
378 }
379 }
380 if (!instructionFound) {
Ray Milkeyc95bb9d2015-01-06 10:28:24 -0800381 reason = "instruction " + instruction.toString();
Ray Milkey4f5de002014-12-17 19:26:11 -0800382 return false;
383 }
384 }
385 }
386
387 // check selector and criteria array
388 if (flow.selector() != null) {
389 final JsonObject jsonTreatment = jsonFlow.get("selector").asObject();
390 final JsonArray jsonCriteria = jsonTreatment.get("criteria").asArray();
391 if (flow.selector().criteria().size() != jsonCriteria.size()) {
392 reason = "criteria array size of " +
393 Integer.toString(flow.selector().criteria().size());
394 return false;
395 }
396 for (final Criterion criterion : flow.selector().criteria()) {
397 boolean criterionFound = false;
Ray Milkeyc95bb9d2015-01-06 10:28:24 -0800398
Ray Milkey4f5de002014-12-17 19:26:11 -0800399 for (int criterionIndex = 0; criterionIndex < jsonCriteria.size(); criterionIndex++) {
Ray Milkeyc95bb9d2015-01-06 10:28:24 -0800400 final String jsonType =
401 jsonCriteria.get(criterionIndex)
402 .asObject().get("type").asString();
403 final String criterionType = criterion.type().name();
404 if (jsonType.equals(criterionType)) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800405 criterionFound = true;
406 }
407 }
408 if (!criterionFound) {
Ray Milkeyc95bb9d2015-01-06 10:28:24 -0800409 reason = "criterion " + criterion.toString();
Ray Milkey4f5de002014-12-17 19:26:11 -0800410 return false;
411 }
412 }
413 }
414
415 return true;
416 }
417
418 @Override
419 public void describeTo(Description description) {
420 description.appendText(reason);
421 }
422 }
423
424 /**
425 * Factory to allocate a flow matcher.
426 *
427 * @param flow flow object we are looking for
428 * @return matcher
429 */
Jian Li2907ad22016-05-12 23:08:54 -0700430 private static FlowEntryJsonMatcher matchesFlow(FlowEntry flow, String expectedAppName) {
431 return new FlowEntryJsonMatcher(flow, expectedAppName);
Ray Milkey4f5de002014-12-17 19:26:11 -0800432 }
433
434 /**
435 * Hamcrest matcher to check that a flow is represented properly in a JSON
436 * array of flows.
437 */
Jian Li2907ad22016-05-12 23:08:54 -0700438 public static class FlowEntryJsonArrayMatcher extends TypeSafeMatcher<JsonArray> {
Ray Milkey4f5de002014-12-17 19:26:11 -0800439 private final FlowEntry flow;
440 private String reason = "";
441
Jian Li2907ad22016-05-12 23:08:54 -0700442 public FlowEntryJsonArrayMatcher(FlowEntry flowValue) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800443 flow = flowValue;
444 }
445
446 @Override
447 public boolean matchesSafely(JsonArray json) {
448 boolean flowFound = false;
449
450 for (int jsonFlowIndex = 0; jsonFlowIndex < json.size();
451 jsonFlowIndex++) {
452
453 final JsonObject jsonFlow = json.get(jsonFlowIndex).asObject();
454
455 final String flowId = Long.toString(flow.id().value());
456 final String jsonFlowId = jsonFlow.get("id").asString();
457 if (jsonFlowId.equals(flowId)) {
458 flowFound = true;
459
460 // We found the correct flow, check attribute values
Ray Milkeybe539db2015-09-04 11:00:43 -0700461 assertThat(jsonFlow, matchesFlow(flow, APP_ID.name()));
Ray Milkey4f5de002014-12-17 19:26:11 -0800462 }
463 }
464 if (!flowFound) {
465 reason = "Flow with id " + flow.id().toString() + " not found";
466 return false;
467 } else {
468 return true;
469 }
470 }
471
472 @Override
473 public void describeTo(Description description) {
474 description.appendText(reason);
475 }
476 }
477
478 /**
479 * Factory to allocate a flow array matcher.
480 *
481 * @param flow flow object we are looking for
482 * @return matcher
483 */
Jian Li2907ad22016-05-12 23:08:54 -0700484 private static FlowEntryJsonArrayMatcher hasFlow(FlowEntry flow) {
485 return new FlowEntryJsonArrayMatcher(flow);
486 }
487
488 /**
489 * Hamcrest matcher to check that a flow representation in JSON matches
490 * the actual flow rule.
491 */
492 public static class FlowRuleJsonMatcher extends TypeSafeMatcher<JsonObject> {
493 private final FlowRule flow;
494 private final String expectedAppId;
495 private String reason = "";
496
497 public FlowRuleJsonMatcher(FlowRule flowValue, String expectedAppIdValue) {
498 flow = flowValue;
499 expectedAppId = expectedAppIdValue;
500 }
501
502 @Override
503 public boolean matchesSafely(JsonObject jsonFlow) {
504 // check id
505 final String jsonId = jsonFlow.get("id").asString();
506 final String flowId = Long.toString(flow.id().value());
507 if (!jsonId.equals(flowId)) {
508 reason = "id " + flow.id().toString();
509 return false;
510 }
511
512 // check application id
513 final String jsonAppId = jsonFlow.get("appId").asString();
514 if (!jsonAppId.equals(expectedAppId)) {
515 reason = "appId " + Short.toString(flow.appId());
516 return false;
517 }
518
519 // check device id
520 final String jsonDeviceId = jsonFlow.get("deviceId").asString();
521 if (!jsonDeviceId.equals(flow.deviceId().toString())) {
522 reason = "deviceId " + flow.deviceId();
523 return false;
524 }
525
526 // check treatment and instructions array
527 if (flow.treatment() != null) {
528 final JsonObject jsonTreatment = jsonFlow.get("treatment").asObject();
529 final JsonArray jsonInstructions = jsonTreatment.get("instructions").asArray();
530 if (flow.treatment().immediate().size() != jsonInstructions.size()) {
531 reason = "instructions array size of " +
532 Integer.toString(flow.treatment().immediate().size());
533 return false;
534 }
535 for (final Instruction instruction : flow.treatment().immediate()) {
536 boolean instructionFound = false;
537 for (int instructionIndex = 0; instructionIndex < jsonInstructions.size(); instructionIndex++) {
538 final String jsonType =
539 jsonInstructions.get(instructionIndex)
540 .asObject().get("type").asString();
541 final String instructionType = instruction.type().name();
542 if (jsonType.equals(instructionType)) {
543 instructionFound = true;
544 }
545 }
546 if (!instructionFound) {
547 reason = "instruction " + instruction.toString();
548 return false;
549 }
550 }
551 }
552
553 // check selector and criteria array
554 if (flow.selector() != null) {
555 final JsonObject jsonTreatment = jsonFlow.get("selector").asObject();
556 final JsonArray jsonCriteria = jsonTreatment.get("criteria").asArray();
557 if (flow.selector().criteria().size() != jsonCriteria.size()) {
558 reason = "criteria array size of " +
559 Integer.toString(flow.selector().criteria().size());
560 return false;
561 }
562 for (final Criterion criterion : flow.selector().criteria()) {
563 boolean criterionFound = false;
564
565 for (int criterionIndex = 0; criterionIndex < jsonCriteria.size(); criterionIndex++) {
566 final String jsonType =
567 jsonCriteria.get(criterionIndex)
568 .asObject().get("type").asString();
569 final String criterionType = criterion.type().name();
570 if (jsonType.equals(criterionType)) {
571 criterionFound = true;
572 }
573 }
574 if (!criterionFound) {
575 reason = "criterion " + criterion.toString();
576 return false;
577 }
578 }
579 }
580
581 return true;
582 }
583
584 @Override
585 public void describeTo(Description description) {
586 description.appendText(reason);
587 }
588 }
589
590 /**
591 * Factory to allocate a flow matcher.
592 *
593 * @param flow flow rule object we are looking for
594 * @return matcher
595 */
596 private static FlowRuleJsonMatcher matchesFlowRule(FlowRule flow, String expectedAppName) {
597 return new FlowRuleJsonMatcher(flow, expectedAppName);
598 }
599
600 /**
601 * Hamcrest matcher to check that a flow is represented properly in a JSON
602 * array of flow rules.
603 */
604 public static class FlowRuleJsonArrayMatcher extends TypeSafeMatcher<JsonArray> {
605 private final FlowRule flow;
606 private String reason = "";
607
608 public FlowRuleJsonArrayMatcher(FlowRule flowValue) {
609 flow = flowValue;
610 }
611
612 @Override
613 public boolean matchesSafely(JsonArray json) {
614 boolean flowFound = false;
615
616 for (int jsonFlowIndex = 0; jsonFlowIndex < json.size();
617 jsonFlowIndex++) {
618
619 final JsonObject jsonFlow = json.get(jsonFlowIndex).asObject();
620
621 final String flowId = Long.toString(flow.id().value());
622 final String jsonFlowId = jsonFlow.get("id").asString();
623 if (jsonFlowId.equals(flowId)) {
624 flowFound = true;
625
626 // We found the correct flow, check attribute values
627 assertThat(jsonFlow, matchesFlowRule(flow, APP_ID.name()));
628 }
629 }
630 if (!flowFound) {
631 reason = "Flow with id " + flow.id().toString() + " not found";
632 return false;
633 } else {
634 return true;
635 }
636 }
637
638 @Override
639 public void describeTo(Description description) {
640 description.appendText(reason);
641 }
642 }
643
644 /**
645 * Factory to allocate a flow array matcher.
646 *
647 * @param flow flow rule object we are looking for
648 * @return matcher
649 */
650 private static FlowRuleJsonArrayMatcher hasFlowRule(FlowRule flow) {
651 return new FlowRuleJsonArrayMatcher(flow);
Ray Milkey4f5de002014-12-17 19:26:11 -0800652 }
653
654 /**
655 * Tests the result of the rest api GET when there are no flows.
656 */
657 @Test
658 public void testFlowsEmptyArray() {
659 expect(mockFlowService.getFlowEntries(deviceId1))
660 .andReturn(null).anyTimes();
661 expect(mockFlowService.getFlowEntries(deviceId2))
662 .andReturn(null).anyTimes();
663 replay(mockFlowService);
664 replay(mockDeviceService);
Jian Li9d616492016-03-09 10:52:49 -0800665 final WebTarget wt = target();
666 final String response = wt.path("flows").request().get(String.class);
Ray Milkey4f5de002014-12-17 19:26:11 -0800667 assertThat(response, is("{\"flows\":[]}"));
668 }
669
670 /**
671 * Tests the result of the rest api GET when there are active flows.
672 */
673 @Test
674 public void testFlowsPopulatedArray() {
675 setupMockFlows();
676 replay(mockFlowService);
677 replay(mockDeviceService);
Jian Li9d616492016-03-09 10:52:49 -0800678 final WebTarget wt = target();
679 final String response = wt.path("flows").request().get(String.class);
Jian Li80cfe452016-01-14 16:04:58 -0800680 final JsonObject result = Json.parse(response).asObject();
Ray Milkey4f5de002014-12-17 19:26:11 -0800681 assertThat(result, notNullValue());
682
683 assertThat(result.names(), hasSize(1));
684 assertThat(result.names().get(0), is("flows"));
685 final JsonArray jsonFlows = result.get("flows").asArray();
686 assertThat(jsonFlows, notNullValue());
687 assertThat(jsonFlows, hasFlow(flow1));
688 assertThat(jsonFlows, hasFlow(flow2));
689 assertThat(jsonFlows, hasFlow(flow3));
690 assertThat(jsonFlows, hasFlow(flow4));
691 }
692
693 /**
694 * Tests the result of a rest api GET for a device.
695 */
696 @Test
697 public void testFlowsSingleDevice() {
698 setupMockFlows();
699 final Set<FlowEntry> flows = new HashSet<>();
700 flows.add(flow5);
701 flows.add(flow6);
702 expect(mockFlowService.getFlowEntries(anyObject()))
703 .andReturn(flows).anyTimes();
704 replay(mockFlowService);
705 replay(mockDeviceService);
Jian Li9d616492016-03-09 10:52:49 -0800706 final WebTarget wt = target();
707 final String response = wt.path("flows/" + deviceId3).request().get(String.class);
Jian Li80cfe452016-01-14 16:04:58 -0800708 final JsonObject result = Json.parse(response).asObject();
Ray Milkey4f5de002014-12-17 19:26:11 -0800709 assertThat(result, notNullValue());
710
711 assertThat(result.names(), hasSize(1));
712 assertThat(result.names().get(0), is("flows"));
713 final JsonArray jsonFlows = result.get("flows").asArray();
714 assertThat(jsonFlows, notNullValue());
715 assertThat(jsonFlows, hasFlow(flow5));
716 assertThat(jsonFlows, hasFlow(flow6));
717 }
718
719 /**
720 * Tests the result of a rest api GET for a device.
721 */
722 @Test
723 public void testFlowsSingleDeviceWithFlowId() {
724 setupMockFlows();
725 final Set<FlowEntry> flows = new HashSet<>();
726 flows.add(flow5);
727 flows.add(flow6);
728 expect(mockFlowService.getFlowEntries(anyObject()))
729 .andReturn(flows).anyTimes();
730 replay(mockFlowService);
731 replay(mockDeviceService);
Jian Li9d616492016-03-09 10:52:49 -0800732 final WebTarget wt = target();
733 final String response = wt.path("flows/" + deviceId3 + "/"
734 + Long.toString(flow5.id().value())).request().get(String.class);
Jian Li80cfe452016-01-14 16:04:58 -0800735 final JsonObject result = Json.parse(response).asObject();
Ray Milkey4f5de002014-12-17 19:26:11 -0800736 assertThat(result, notNullValue());
737
738 assertThat(result.names(), hasSize(1));
739 assertThat(result.names().get(0), is("flows"));
740 final JsonArray jsonFlows = result.get("flows").asArray();
741 assertThat(jsonFlows, notNullValue());
742 assertThat(jsonFlows, hasFlow(flow5));
743 assertThat(jsonFlows, not(hasFlow(flow6)));
744 }
745
746 /**
747 * Tests that a fetch of a non-existent device object throws an exception.
748 */
749 @Test
750 public void testBadGet() {
Jian Li9d616492016-03-09 10:52:49 -0800751 expect(mockFlowService.getFlowEntries(anyObject()))
Ray Milkey4f5de002014-12-17 19:26:11 -0800752 .andReturn(null).anyTimes();
753 replay(mockFlowService);
754 replay(mockDeviceService);
755
Jian Li9d616492016-03-09 10:52:49 -0800756 WebTarget wt = target();
Ray Milkey4f5de002014-12-17 19:26:11 -0800757 try {
Jian Li9d616492016-03-09 10:52:49 -0800758 wt.path("flows/0").request().get(String.class);
Ray Milkey4f5de002014-12-17 19:26:11 -0800759 fail("Fetch of non-existent device did not throw an exception");
Jian Li9d616492016-03-09 10:52:49 -0800760 } catch (NotFoundException ex) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800761 assertThat(ex.getMessage(),
Jian Li9d616492016-03-09 10:52:49 -0800762 containsString("HTTP 404 Not Found"));
Ray Milkey4f5de002014-12-17 19:26:11 -0800763 }
764 }
Ray Milkeyd43fe452015-05-29 09:35:12 -0700765
766 /**
767 * Tests creating a flow with POST.
768 */
769 @Test
Jian Li2907ad22016-05-12 23:08:54 -0700770 public void testPostWithoutAppId() {
Jonathan Hart9b80da82016-11-10 21:46:15 +0000771 mockFlowService.applyFlowRules(anyObject());
772 expectLastCall();
Ray Milkeyd43fe452015-05-29 09:35:12 -0700773 replay(mockFlowService);
774
Jian Li9d616492016-03-09 10:52:49 -0800775 WebTarget wt = target();
Phaneendra Manda2be2f882015-11-11 15:23:40 +0530776 InputStream jsonStream = FlowsResourceTest.class
Ray Milkeyb82c42b2015-06-30 09:42:20 -0700777 .getResourceAsStream("post-flow.json");
Ray Milkeyd43fe452015-05-29 09:35:12 -0700778
Jian Li9d616492016-03-09 10:52:49 -0800779 Response response = wt.path("flows/of:0000000000000001")
780 .request(MediaType.APPLICATION_JSON_TYPE)
781 .post(Entity.json(jsonStream));
Jonathan Hart9b80da82016-11-10 21:46:15 +0000782 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
783 String location = response.getLocation().getPath();
784 assertThat(location, Matchers.startsWith("/flows/of:0000000000000001/"));
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700785 }
786
787 /**
Jian Li2907ad22016-05-12 23:08:54 -0700788 * Tests creating a flow with POST while specifying application identifier.
789 */
790 @Test
791 public void testPostWithAppId() {
792 mockFlowService.applyFlowRules(anyObject());
793 expectLastCall();
794 replay(mockFlowService);
795
796 WebTarget wt = target();
797 InputStream jsonStream = FlowsResourceTest.class
798 .getResourceAsStream("post-flow.json");
799
800 Response response = wt.path("flows/of:0000000000000001")
801 .queryParam("appId", "org.onosproject.rest")
802 .request(MediaType.APPLICATION_JSON_TYPE)
803 .post(Entity.json(jsonStream));
804 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
805 String location = response.getLocation().getPath();
806 assertThat(location, Matchers.startsWith("/flows/of:0000000000000001/"));
807 }
808
809 /**
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700810 * Tests deleting a flow.
811 */
812 @Test
813 public void testDelete() {
814 setupMockFlows();
815 mockFlowService.removeFlowRules(anyObject());
816 expectLastCall();
817 replay(mockFlowService);
818
Jian Li9d616492016-03-09 10:52:49 -0800819 WebTarget wt = target();
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700820
821 String location = "/flows/1/155";
822
Jian Li9d616492016-03-09 10:52:49 -0800823 Response deleteResponse = wt.path(location)
824 .request(MediaType.APPLICATION_JSON_TYPE)
825 .delete();
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700826 assertThat(deleteResponse.getStatus(),
827 is(HttpURLConnection.HTTP_NO_CONTENT));
Ray Milkeyd43fe452015-05-29 09:35:12 -0700828 }
Jian Li2907ad22016-05-12 23:08:54 -0700829
830 /**
831 * Tests the result of a rest api GET for an application.
832 */
833 @Test
834 public void testGetFlowByAppId() {
Bharath Thiruveedula99849dc2016-11-17 22:04:38 +0530835 setupMockFlows();
Jian Li2907ad22016-05-12 23:08:54 -0700836
837 expect(mockApplicationService.getId(anyObject())).andReturn(APP_ID).anyTimes();
838 replay(mockApplicationService);
839
Bharath Thiruveedula99849dc2016-11-17 22:04:38 +0530840 expect(mockFlowService.getFlowEntriesById(APP_ID)).andReturn(flowEntries).anyTimes();
Jian Li2907ad22016-05-12 23:08:54 -0700841 replay(mockFlowService);
842
843 final WebTarget wt = target();
Bharath Thiruveedula99849dc2016-11-17 22:04:38 +0530844 final String response = wt.path("/flows/application/1").request().get(String.class);
Jian Li2907ad22016-05-12 23:08:54 -0700845 final JsonObject result = Json.parse(response).asObject();
846 assertThat(result, notNullValue());
847
848 assertThat(result.names(), hasSize(1));
849 assertThat(result.names().get(0), is("flows"));
850 final JsonArray jsonFlows = result.get("flows").asArray();
851 assertThat(jsonFlows, notNullValue());
Bharath Thiruveedula99849dc2016-11-17 22:04:38 +0530852 assertThat(jsonFlows, hasFlowRule(flow1));
853 assertThat(jsonFlows, hasFlowRule(flow2));
854 assertThat(jsonFlows, hasFlowRule(flow3));
855 assertThat(jsonFlows, hasFlowRule(flow4));
Jian Li2907ad22016-05-12 23:08:54 -0700856 }
857
858 /**
859 * Tests the result of a rest api DELETE for an application.
860 */
861 @Test
862 public void testRemoveFlowByAppId() {
863 expect(mockApplicationService.getId(anyObject())).andReturn(APP_ID).anyTimes();
864 replay(mockApplicationService);
865
866 mockFlowService.removeFlowRulesById(APP_ID);
867 expectLastCall();
868 replay(mockFlowService);
869
870 WebTarget wt = target();
871
872 String location = "/flows/application/1";
873
874 Response deleteResponse = wt.path(location)
875 .request()
876 .delete();
877 assertThat(deleteResponse.getStatus(),
878 is(HttpURLConnection.HTTP_NO_CONTENT));
879 }
Ray Milkey4f5de002014-12-17 19:26:11 -0800880}