blob: 68dc39391f14a7d87ac6f39ed8249a381fda4796 [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
Jian Li80cfe452016-01-14 16:04:58 -080018import com.eclipsesource.json.Json;
Jonathan Hartf44e42c2015-08-04 09:58:46 -070019import com.eclipsesource.json.JsonArray;
20import com.eclipsesource.json.JsonObject;
21import com.google.common.collect.ImmutableSet;
Jian Li2907ad22016-05-12 23:08:54 -070022import com.google.common.collect.Sets;
Ray Milkey4f5de002014-12-17 19:26:11 -080023import org.hamcrest.Description;
Ray Milkeyeb5c7172015-06-23 14:59:27 -070024import org.hamcrest.Matchers;
Ray Milkey4f5de002014-12-17 19:26:11 -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;
31import org.onlab.packet.MacAddress;
32import org.onlab.rest.BaseResource;
Jian Li2907ad22016-05-12 23:08:54 -070033import org.onosproject.app.ApplicationService;
Ray Milkey4f5de002014-12-17 19:26:11 -080034import org.onosproject.codec.CodecService;
35import org.onosproject.codec.impl.CodecManager;
Ray Milkeyeb5c7172015-06-23 14:59:27 -070036import org.onosproject.codec.impl.FlowRuleCodec;
Ray Milkeyd43fe452015-05-29 09:35:12 -070037import org.onosproject.core.CoreService;
Ray Milkey4f5de002014-12-17 19:26:11 -080038import org.onosproject.core.DefaultGroupId;
39import org.onosproject.core.GroupId;
40import org.onosproject.net.DefaultDevice;
41import org.onosproject.net.Device;
42import org.onosproject.net.DeviceId;
Ray Milkeyd43fe452015-05-29 09:35:12 -070043import org.onosproject.net.NetTestTools;
Ray Milkey4f5de002014-12-17 19:26:11 -080044import org.onosproject.net.device.DeviceService;
45import org.onosproject.net.flow.DefaultTrafficSelector;
46import org.onosproject.net.flow.DefaultTrafficTreatment;
47import org.onosproject.net.flow.FlowEntry;
48import org.onosproject.net.flow.FlowId;
Jonathan Hartf44e42c2015-08-04 09:58:46 -070049import org.onosproject.net.flow.FlowRule;
jcc3d4e14a2015-04-21 11:32:05 +080050import org.onosproject.net.flow.FlowRuleExtPayLoad;
Ray Milkey4f5de002014-12-17 19:26:11 -080051import org.onosproject.net.flow.FlowRuleService;
52import org.onosproject.net.flow.TrafficSelector;
53import org.onosproject.net.flow.TrafficTreatment;
54import org.onosproject.net.flow.criteria.Criterion;
55import org.onosproject.net.flow.instructions.Instruction;
jcc3d4e14a2015-04-21 11:32:05 +080056
Jian Li9d616492016-03-09 10:52:49 -080057import javax.ws.rs.NotFoundException;
58import javax.ws.rs.client.Entity;
59import javax.ws.rs.client.WebTarget;
Jonathan Hartf44e42c2015-08-04 09:58:46 -070060import javax.ws.rs.core.MediaType;
Jian Li9d616492016-03-09 10:52:49 -080061import javax.ws.rs.core.Response;
Jonathan Hartf44e42c2015-08-04 09:58:46 -070062import java.io.InputStream;
63import java.net.HttpURLConnection;
64import java.util.HashMap;
65import java.util.HashSet;
66import java.util.Set;
Thiago Santos877914d2016-07-20 18:29:29 -030067import java.util.concurrent.TimeUnit;
Ray Milkey4f5de002014-12-17 19:26:11 -080068
Thiago Santos877914d2016-07-20 18:29:29 -030069import static java.util.concurrent.TimeUnit.SECONDS;
Ray Milkey4f5de002014-12-17 19:26:11 -080070import static org.easymock.EasyMock.anyObject;
Ray Milkeyd43fe452015-05-29 09:35:12 -070071import static org.easymock.EasyMock.anyShort;
Jian Li2907ad22016-05-12 23:08:54 -070072import static org.easymock.EasyMock.anyString;
Ray Milkey4f5de002014-12-17 19:26:11 -080073import static org.easymock.EasyMock.createMock;
74import static org.easymock.EasyMock.expect;
Ray Milkeyd43fe452015-05-29 09:35:12 -070075import static org.easymock.EasyMock.expectLastCall;
Ray Milkey4f5de002014-12-17 19:26:11 -080076import static org.easymock.EasyMock.replay;
77import static org.easymock.EasyMock.verify;
78import static org.hamcrest.Matchers.containsString;
79import static org.hamcrest.Matchers.hasSize;
80import static org.hamcrest.Matchers.is;
81import static org.hamcrest.Matchers.not;
82import static org.hamcrest.Matchers.notNullValue;
83import static org.junit.Assert.assertThat;
84import static org.junit.Assert.fail;
Ray Milkeyeb5c7172015-06-23 14:59:27 -070085import static org.onosproject.net.NetTestTools.APP_ID;
Ray Milkey4f5de002014-12-17 19:26:11 -080086
87/**
88 * Unit tests for Flows REST APIs.
89 */
Ray Milkey9c3d3362015-01-28 10:39:56 -080090public class FlowsResourceTest extends ResourceTest {
Ray Milkey4f5de002014-12-17 19:26:11 -080091 final FlowRuleService mockFlowService = createMock(FlowRuleService.class);
Ray Milkeyd43fe452015-05-29 09:35:12 -070092 CoreService mockCoreService = createMock(CoreService.class);
93
Ray Milkey4f5de002014-12-17 19:26:11 -080094 final HashMap<DeviceId, Set<FlowEntry>> rules = new HashMap<>();
95
96 final DeviceService mockDeviceService = createMock(DeviceService.class);
97
98 final DeviceId deviceId1 = DeviceId.deviceId("1");
99 final DeviceId deviceId2 = DeviceId.deviceId("2");
100 final DeviceId deviceId3 = DeviceId.deviceId("3");
101 final Device device1 = new DefaultDevice(null, deviceId1, Device.Type.OTHER,
102 "", "", "", "", null);
103 final Device device2 = new DefaultDevice(null, deviceId2, Device.Type.OTHER,
104 "", "", "", "", null);
105
Jian Li2907ad22016-05-12 23:08:54 -0700106 final ApplicationService mockApplicationService = createMock(ApplicationService.class);
107
Ray Milkey4f5de002014-12-17 19:26:11 -0800108 final MockFlowEntry flow1 = new MockFlowEntry(deviceId1, 1);
109 final MockFlowEntry flow2 = new MockFlowEntry(deviceId1, 2);
110
111 final MockFlowEntry flow3 = new MockFlowEntry(deviceId2, 3);
112 final MockFlowEntry flow4 = new MockFlowEntry(deviceId2, 4);
113
114 final MockFlowEntry flow5 = new MockFlowEntry(deviceId2, 5);
115 final MockFlowEntry flow6 = new MockFlowEntry(deviceId2, 6);
116
Jian Li2907ad22016-05-12 23:08:54 -0700117 final MockFlowRule flowRule1 = new MockFlowRule(deviceId1, 1);
118 final MockFlowRule flowRule2 = new MockFlowRule(deviceId1, 2);
119
120 final MockFlowRule flowRule3 = new MockFlowRule(deviceId2, 3);
121 final MockFlowRule flowRule4 = new MockFlowRule(deviceId2, 4);
122
123 final Set<FlowRule> flowRules = Sets.newHashSet();
124
Ray Milkey4f5de002014-12-17 19:26:11 -0800125 /**
126 * Mock class for a flow entry.
127 */
128 private static class MockFlowEntry implements FlowEntry {
129 final DeviceId deviceId;
130 final long baseValue;
131 TrafficTreatment treatment;
132 TrafficSelector selector;
133
134 public MockFlowEntry(DeviceId deviceId, long id) {
135 this.deviceId = deviceId;
136 this.baseValue = id * 100;
137 }
138
139 @Override
140 public FlowEntryState state() {
141 return FlowEntryState.ADDED;
142 }
143
144 @Override
145 public long life() {
Thiago Santos877914d2016-07-20 18:29:29 -0300146 return life(SECONDS);
147 }
148
149 @Override
150 public long life(TimeUnit timeUnit) {
151 return SECONDS.convert(baseValue + 11, timeUnit);
Ray Milkey4f5de002014-12-17 19:26:11 -0800152 }
153
154 @Override
155 public long packets() {
156 return baseValue + 22;
157 }
158
159 @Override
160 public long bytes() {
161 return baseValue + 33;
162 }
163
164 @Override
165 public long lastSeen() {
166 return baseValue + 44;
167 }
168
169 @Override
170 public int errType() {
171 return 0;
172 }
173
174 @Override
175 public int errCode() {
176 return 0;
177 }
178
179 @Override
180 public FlowId id() {
181 final long id = baseValue + 55;
182 return FlowId.valueOf(id);
183 }
184
185 @Override
alshabib08d98982015-04-21 16:25:50 -0700186 public GroupId groupId() {
187 return new DefaultGroupId(3);
Ray Milkey4f5de002014-12-17 19:26:11 -0800188 }
189
190 @Override
alshabib08d98982015-04-21 16:25:50 -0700191 public short appId() {
192 return 2;
Ray Milkey4f5de002014-12-17 19:26:11 -0800193 }
194
195 @Override
196 public int priority() {
197 return (int) (baseValue + 66);
198 }
199
200 @Override
201 public DeviceId deviceId() {
202 return deviceId;
203 }
204
205 @Override
206 public TrafficSelector selector() {
207 return selector;
208 }
209
210 @Override
211 public TrafficTreatment treatment() {
212 return treatment;
213 }
214
215 @Override
216 public int timeout() {
217 return (int) (baseValue + 77);
218 }
219
220 @Override
221 public boolean isPermanent() {
222 return false;
223 }
sangho11c30ac2015-01-22 14:30:55 -0800224
Yuta HIGUCHI32a53c52015-02-08 01:25:40 -0800225 @Override
alshabibdb774072015-04-20 13:13:51 -0700226 public int tableId() {
227 return 0;
228 }
jcc3d4e14a2015-04-21 11:32:05 +0800229
230 @Override
Jonathan Hartf44e42c2015-08-04 09:58:46 -0700231 public boolean exactMatch(FlowRule rule) {
232 return false;
233 }
234
235 @Override
jcc3d4e14a2015-04-21 11:32:05 +0800236 public FlowRuleExtPayLoad payLoad() {
237 return null;
238 }
Ray Milkey4f5de002014-12-17 19:26:11 -0800239 }
240
Ray Milkey4f5de002014-12-17 19:26:11 -0800241 /**
Jian Li2907ad22016-05-12 23:08:54 -0700242 * Mock class for a flow rule.
243 */
244 private static class MockFlowRule implements FlowRule {
245
246 final DeviceId deviceId;
247 final long baseValue;
248 TrafficTreatment treatment;
249 TrafficSelector selector;
250
251 public MockFlowRule(DeviceId deviceId, long id) {
252 this.deviceId = deviceId;
253 this.baseValue = id * 100;
254 }
255
256 @Override
257 public FlowId id() {
258 final long id = baseValue + 55;
259 return FlowId.valueOf(id);
260 }
261
262 @Override
263 public short appId() {
264 return 4;
265 }
266
267 @Override
268 public GroupId groupId() {
269 return new DefaultGroupId(3);
270 }
271
272 @Override
273 public int priority() {
274 return 0;
275 }
276
277 @Override
278 public DeviceId deviceId() {
279 return deviceId;
280 }
281
282 @Override
283 public TrafficSelector selector() {
284 return selector;
285 }
286
287 @Override
288 public TrafficTreatment treatment() {
289 return treatment;
290 }
291
292 @Override
293 public int timeout() {
294 return (int) (baseValue + 77);
295 }
296
297 @Override
298 public boolean isPermanent() {
299 return false;
300 }
301
302 @Override
303 public int tableId() {
304 return 0;
305 }
306
307 @Override
308 public boolean exactMatch(FlowRule rule) {
309 return false;
310 }
311
312 @Override
313 public FlowRuleExtPayLoad payLoad() {
314 return null;
315 }
316 }
317
318 /**
Ray Milkey4f5de002014-12-17 19:26:11 -0800319 * Populates some flows used as testing data.
320 */
321 private void setupMockFlows() {
322 flow2.treatment = DefaultTrafficTreatment.builder()
Ray Milkey4f5de002014-12-17 19:26:11 -0800323 .setEthDst(MacAddress.BROADCAST)
324 .build();
325 flow2.selector = DefaultTrafficSelector.builder()
326 .matchEthType((short) 3)
327 .matchIPProtocol((byte) 9)
328 .build();
329 flow4.treatment = DefaultTrafficTreatment.builder()
Ray Milkey4f5de002014-12-17 19:26:11 -0800330 .build();
331 final Set<FlowEntry> flows1 = new HashSet<>();
332 flows1.add(flow1);
333 flows1.add(flow2);
334
335 final Set<FlowEntry> flows2 = new HashSet<>();
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700336 flows2.add(flow3);
337 flows2.add(flow4);
Ray Milkey4f5de002014-12-17 19:26:11 -0800338
339 rules.put(deviceId1, flows1);
340 rules.put(deviceId2, flows2);
341
342 expect(mockFlowService.getFlowEntries(deviceId1))
343 .andReturn(rules.get(deviceId1)).anyTimes();
344 expect(mockFlowService.getFlowEntries(deviceId2))
345 .andReturn(rules.get(deviceId2)).anyTimes();
346 }
347
348 /**
Jian Li2907ad22016-05-12 23:08:54 -0700349 * Populates some flow rules used as testing data.
350 */
351 private void setupMockFlowRules() {
352 flowRule2.treatment = DefaultTrafficTreatment.builder()
353 .setEthDst(MacAddress.BROADCAST)
354 .build();
355 flowRule2.selector = DefaultTrafficSelector.builder()
356 .matchEthType((short) 3)
357 .matchIPProtocol((byte) 9)
358 .build();
359 flowRule4.treatment = DefaultTrafficTreatment.builder()
360 .build();
361
362 flowRules.add(flowRule1);
363 flowRules.add(flowRule2);
364 flowRules.add(flowRule3);
365 flowRules.add(flowRule4);
366 }
367
368 /**
Ray Milkey4f5de002014-12-17 19:26:11 -0800369 * Sets up the global values for all the tests.
370 */
371 @Before
Ray Milkeyed0b1662015-02-05 09:34:29 -0800372 public void setUpTest() {
Ray Milkey4f5de002014-12-17 19:26:11 -0800373 // Mock device service
374 expect(mockDeviceService.getDevice(deviceId1))
375 .andReturn(device1);
376 expect(mockDeviceService.getDevice(deviceId2))
377 .andReturn(device2);
378 expect(mockDeviceService.getDevices())
379 .andReturn(ImmutableSet.of(device1, device2));
380
Ray Milkeyd43fe452015-05-29 09:35:12 -0700381 // Mock Core Service
382 expect(mockCoreService.getAppId(anyShort()))
383 .andReturn(NetTestTools.APP_ID).anyTimes();
Jian Li2907ad22016-05-12 23:08:54 -0700384 expect(mockCoreService.getAppId(anyString()))
385 .andReturn(NetTestTools.APP_ID).anyTimes();
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700386 expect(mockCoreService.registerApplication(FlowRuleCodec.REST_APP_ID))
387 .andReturn(APP_ID).anyTimes();
Ray Milkeyd43fe452015-05-29 09:35:12 -0700388 replay(mockCoreService);
389
Ray Milkey4f5de002014-12-17 19:26:11 -0800390 // Register the services needed for the test
391 final CodecManager codecService = new CodecManager();
392 codecService.activate();
393 ServiceDirectory testDirectory =
394 new TestServiceDirectory()
395 .add(FlowRuleService.class, mockFlowService)
396 .add(DeviceService.class, mockDeviceService)
Ray Milkeyd43fe452015-05-29 09:35:12 -0700397 .add(CodecService.class, codecService)
Jian Li2907ad22016-05-12 23:08:54 -0700398 .add(CoreService.class, mockCoreService)
399 .add(ApplicationService.class, mockApplicationService);
Ray Milkey4f5de002014-12-17 19:26:11 -0800400
401 BaseResource.setServiceDirectory(testDirectory);
402 }
403
404 /**
405 * Cleans up and verifies the mocks.
Ray Milkey4f5de002014-12-17 19:26:11 -0800406 */
407 @After
Ray Milkeyed0b1662015-02-05 09:34:29 -0800408 public void tearDownTest() {
Ray Milkey4f5de002014-12-17 19:26:11 -0800409 verify(mockFlowService);
Ray Milkeyd43fe452015-05-29 09:35:12 -0700410 verify(mockCoreService);
Ray Milkey4f5de002014-12-17 19:26:11 -0800411 }
412
413 /**
414 * Hamcrest matcher to check that a flow representation in JSON matches
415 * the actual flow entry.
416 */
Jian Li2907ad22016-05-12 23:08:54 -0700417 public static class FlowEntryJsonMatcher extends TypeSafeMatcher<JsonObject> {
Ray Milkey4f5de002014-12-17 19:26:11 -0800418 private final FlowEntry flow;
Ray Milkeybe539db2015-09-04 11:00:43 -0700419 private final String expectedAppId;
Ray Milkey4f5de002014-12-17 19:26:11 -0800420 private String reason = "";
421
Jian Li2907ad22016-05-12 23:08:54 -0700422 public FlowEntryJsonMatcher(FlowEntry flowValue, String expectedAppIdValue) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800423 flow = flowValue;
Ray Milkeybe539db2015-09-04 11:00:43 -0700424 expectedAppId = expectedAppIdValue;
Ray Milkey4f5de002014-12-17 19:26:11 -0800425 }
426
427 @Override
428 public boolean matchesSafely(JsonObject jsonFlow) {
429 // check id
430 final String jsonId = jsonFlow.get("id").asString();
431 final String flowId = Long.toString(flow.id().value());
432 if (!jsonId.equals(flowId)) {
433 reason = "id " + flow.id().toString();
434 return false;
435 }
436
437 // check application id
Ray Milkeybe539db2015-09-04 11:00:43 -0700438 final String jsonAppId = jsonFlow.get("appId").asString();
439 if (!jsonAppId.equals(expectedAppId)) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800440 reason = "appId " + Short.toString(flow.appId());
441 return false;
442 }
443
444 // check device id
445 final String jsonDeviceId = jsonFlow.get("deviceId").asString();
446 if (!jsonDeviceId.equals(flow.deviceId().toString())) {
447 reason = "deviceId " + flow.deviceId();
448 return false;
449 }
450
451 // check treatment and instructions array
452 if (flow.treatment() != null) {
453 final JsonObject jsonTreatment = jsonFlow.get("treatment").asObject();
454 final JsonArray jsonInstructions = jsonTreatment.get("instructions").asArray();
Ray Milkey42507352015-03-20 15:16:10 -0700455 if (flow.treatment().immediate().size() != jsonInstructions.size()) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800456 reason = "instructions array size of " +
Ray Milkey42507352015-03-20 15:16:10 -0700457 Integer.toString(flow.treatment().immediate().size());
Ray Milkey4f5de002014-12-17 19:26:11 -0800458 return false;
459 }
Ray Milkey42507352015-03-20 15:16:10 -0700460 for (final Instruction instruction : flow.treatment().immediate()) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800461 boolean instructionFound = false;
Ray Milkey4f5de002014-12-17 19:26:11 -0800462 for (int instructionIndex = 0; instructionIndex < jsonInstructions.size(); instructionIndex++) {
Ray Milkeyc95bb9d2015-01-06 10:28:24 -0800463 final String jsonType =
464 jsonInstructions.get(instructionIndex)
465 .asObject().get("type").asString();
466 final String instructionType = instruction.type().name();
467 if (jsonType.equals(instructionType)) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800468 instructionFound = true;
469 }
470 }
471 if (!instructionFound) {
Ray Milkeyc95bb9d2015-01-06 10:28:24 -0800472 reason = "instruction " + instruction.toString();
Ray Milkey4f5de002014-12-17 19:26:11 -0800473 return false;
474 }
475 }
476 }
477
478 // check selector and criteria array
479 if (flow.selector() != null) {
480 final JsonObject jsonTreatment = jsonFlow.get("selector").asObject();
481 final JsonArray jsonCriteria = jsonTreatment.get("criteria").asArray();
482 if (flow.selector().criteria().size() != jsonCriteria.size()) {
483 reason = "criteria array size of " +
484 Integer.toString(flow.selector().criteria().size());
485 return false;
486 }
487 for (final Criterion criterion : flow.selector().criteria()) {
488 boolean criterionFound = false;
Ray Milkeyc95bb9d2015-01-06 10:28:24 -0800489
Ray Milkey4f5de002014-12-17 19:26:11 -0800490 for (int criterionIndex = 0; criterionIndex < jsonCriteria.size(); criterionIndex++) {
Ray Milkeyc95bb9d2015-01-06 10:28:24 -0800491 final String jsonType =
492 jsonCriteria.get(criterionIndex)
493 .asObject().get("type").asString();
494 final String criterionType = criterion.type().name();
495 if (jsonType.equals(criterionType)) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800496 criterionFound = true;
497 }
498 }
499 if (!criterionFound) {
Ray Milkeyc95bb9d2015-01-06 10:28:24 -0800500 reason = "criterion " + criterion.toString();
Ray Milkey4f5de002014-12-17 19:26:11 -0800501 return false;
502 }
503 }
504 }
505
506 return true;
507 }
508
509 @Override
510 public void describeTo(Description description) {
511 description.appendText(reason);
512 }
513 }
514
515 /**
516 * Factory to allocate a flow matcher.
517 *
518 * @param flow flow object we are looking for
519 * @return matcher
520 */
Jian Li2907ad22016-05-12 23:08:54 -0700521 private static FlowEntryJsonMatcher matchesFlow(FlowEntry flow, String expectedAppName) {
522 return new FlowEntryJsonMatcher(flow, expectedAppName);
Ray Milkey4f5de002014-12-17 19:26:11 -0800523 }
524
525 /**
526 * Hamcrest matcher to check that a flow is represented properly in a JSON
527 * array of flows.
528 */
Jian Li2907ad22016-05-12 23:08:54 -0700529 public static class FlowEntryJsonArrayMatcher extends TypeSafeMatcher<JsonArray> {
Ray Milkey4f5de002014-12-17 19:26:11 -0800530 private final FlowEntry flow;
531 private String reason = "";
532
Jian Li2907ad22016-05-12 23:08:54 -0700533 public FlowEntryJsonArrayMatcher(FlowEntry flowValue) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800534 flow = flowValue;
535 }
536
537 @Override
538 public boolean matchesSafely(JsonArray json) {
539 boolean flowFound = false;
540
541 for (int jsonFlowIndex = 0; jsonFlowIndex < json.size();
542 jsonFlowIndex++) {
543
544 final JsonObject jsonFlow = json.get(jsonFlowIndex).asObject();
545
546 final String flowId = Long.toString(flow.id().value());
547 final String jsonFlowId = jsonFlow.get("id").asString();
548 if (jsonFlowId.equals(flowId)) {
549 flowFound = true;
550
551 // We found the correct flow, check attribute values
Ray Milkeybe539db2015-09-04 11:00:43 -0700552 assertThat(jsonFlow, matchesFlow(flow, APP_ID.name()));
Ray Milkey4f5de002014-12-17 19:26:11 -0800553 }
554 }
555 if (!flowFound) {
556 reason = "Flow with id " + flow.id().toString() + " not found";
557 return false;
558 } else {
559 return true;
560 }
561 }
562
563 @Override
564 public void describeTo(Description description) {
565 description.appendText(reason);
566 }
567 }
568
569 /**
570 * Factory to allocate a flow array matcher.
571 *
572 * @param flow flow object we are looking for
573 * @return matcher
574 */
Jian Li2907ad22016-05-12 23:08:54 -0700575 private static FlowEntryJsonArrayMatcher hasFlow(FlowEntry flow) {
576 return new FlowEntryJsonArrayMatcher(flow);
577 }
578
579 /**
580 * Hamcrest matcher to check that a flow representation in JSON matches
581 * the actual flow rule.
582 */
583 public static class FlowRuleJsonMatcher extends TypeSafeMatcher<JsonObject> {
584 private final FlowRule flow;
585 private final String expectedAppId;
586 private String reason = "";
587
588 public FlowRuleJsonMatcher(FlowRule flowValue, String expectedAppIdValue) {
589 flow = flowValue;
590 expectedAppId = expectedAppIdValue;
591 }
592
593 @Override
594 public boolean matchesSafely(JsonObject jsonFlow) {
595 // check id
596 final String jsonId = jsonFlow.get("id").asString();
597 final String flowId = Long.toString(flow.id().value());
598 if (!jsonId.equals(flowId)) {
599 reason = "id " + flow.id().toString();
600 return false;
601 }
602
603 // check application id
604 final String jsonAppId = jsonFlow.get("appId").asString();
605 if (!jsonAppId.equals(expectedAppId)) {
606 reason = "appId " + Short.toString(flow.appId());
607 return false;
608 }
609
610 // check device id
611 final String jsonDeviceId = jsonFlow.get("deviceId").asString();
612 if (!jsonDeviceId.equals(flow.deviceId().toString())) {
613 reason = "deviceId " + flow.deviceId();
614 return false;
615 }
616
617 // check treatment and instructions array
618 if (flow.treatment() != null) {
619 final JsonObject jsonTreatment = jsonFlow.get("treatment").asObject();
620 final JsonArray jsonInstructions = jsonTreatment.get("instructions").asArray();
621 if (flow.treatment().immediate().size() != jsonInstructions.size()) {
622 reason = "instructions array size of " +
623 Integer.toString(flow.treatment().immediate().size());
624 return false;
625 }
626 for (final Instruction instruction : flow.treatment().immediate()) {
627 boolean instructionFound = false;
628 for (int instructionIndex = 0; instructionIndex < jsonInstructions.size(); instructionIndex++) {
629 final String jsonType =
630 jsonInstructions.get(instructionIndex)
631 .asObject().get("type").asString();
632 final String instructionType = instruction.type().name();
633 if (jsonType.equals(instructionType)) {
634 instructionFound = true;
635 }
636 }
637 if (!instructionFound) {
638 reason = "instruction " + instruction.toString();
639 return false;
640 }
641 }
642 }
643
644 // check selector and criteria array
645 if (flow.selector() != null) {
646 final JsonObject jsonTreatment = jsonFlow.get("selector").asObject();
647 final JsonArray jsonCriteria = jsonTreatment.get("criteria").asArray();
648 if (flow.selector().criteria().size() != jsonCriteria.size()) {
649 reason = "criteria array size of " +
650 Integer.toString(flow.selector().criteria().size());
651 return false;
652 }
653 for (final Criterion criterion : flow.selector().criteria()) {
654 boolean criterionFound = false;
655
656 for (int criterionIndex = 0; criterionIndex < jsonCriteria.size(); criterionIndex++) {
657 final String jsonType =
658 jsonCriteria.get(criterionIndex)
659 .asObject().get("type").asString();
660 final String criterionType = criterion.type().name();
661 if (jsonType.equals(criterionType)) {
662 criterionFound = true;
663 }
664 }
665 if (!criterionFound) {
666 reason = "criterion " + criterion.toString();
667 return false;
668 }
669 }
670 }
671
672 return true;
673 }
674
675 @Override
676 public void describeTo(Description description) {
677 description.appendText(reason);
678 }
679 }
680
681 /**
682 * Factory to allocate a flow matcher.
683 *
684 * @param flow flow rule object we are looking for
685 * @return matcher
686 */
687 private static FlowRuleJsonMatcher matchesFlowRule(FlowRule flow, String expectedAppName) {
688 return new FlowRuleJsonMatcher(flow, expectedAppName);
689 }
690
691 /**
692 * Hamcrest matcher to check that a flow is represented properly in a JSON
693 * array of flow rules.
694 */
695 public static class FlowRuleJsonArrayMatcher extends TypeSafeMatcher<JsonArray> {
696 private final FlowRule flow;
697 private String reason = "";
698
699 public FlowRuleJsonArrayMatcher(FlowRule flowValue) {
700 flow = flowValue;
701 }
702
703 @Override
704 public boolean matchesSafely(JsonArray json) {
705 boolean flowFound = false;
706
707 for (int jsonFlowIndex = 0; jsonFlowIndex < json.size();
708 jsonFlowIndex++) {
709
710 final JsonObject jsonFlow = json.get(jsonFlowIndex).asObject();
711
712 final String flowId = Long.toString(flow.id().value());
713 final String jsonFlowId = jsonFlow.get("id").asString();
714 if (jsonFlowId.equals(flowId)) {
715 flowFound = true;
716
717 // We found the correct flow, check attribute values
718 assertThat(jsonFlow, matchesFlowRule(flow, APP_ID.name()));
719 }
720 }
721 if (!flowFound) {
722 reason = "Flow with id " + flow.id().toString() + " not found";
723 return false;
724 } else {
725 return true;
726 }
727 }
728
729 @Override
730 public void describeTo(Description description) {
731 description.appendText(reason);
732 }
733 }
734
735 /**
736 * Factory to allocate a flow array matcher.
737 *
738 * @param flow flow rule object we are looking for
739 * @return matcher
740 */
741 private static FlowRuleJsonArrayMatcher hasFlowRule(FlowRule flow) {
742 return new FlowRuleJsonArrayMatcher(flow);
Ray Milkey4f5de002014-12-17 19:26:11 -0800743 }
744
745 /**
746 * Tests the result of the rest api GET when there are no flows.
747 */
748 @Test
749 public void testFlowsEmptyArray() {
750 expect(mockFlowService.getFlowEntries(deviceId1))
751 .andReturn(null).anyTimes();
752 expect(mockFlowService.getFlowEntries(deviceId2))
753 .andReturn(null).anyTimes();
754 replay(mockFlowService);
755 replay(mockDeviceService);
Jian Li9d616492016-03-09 10:52:49 -0800756 final WebTarget wt = target();
757 final String response = wt.path("flows").request().get(String.class);
Ray Milkey4f5de002014-12-17 19:26:11 -0800758 assertThat(response, is("{\"flows\":[]}"));
759 }
760
761 /**
762 * Tests the result of the rest api GET when there are active flows.
763 */
764 @Test
765 public void testFlowsPopulatedArray() {
766 setupMockFlows();
767 replay(mockFlowService);
768 replay(mockDeviceService);
Jian Li9d616492016-03-09 10:52:49 -0800769 final WebTarget wt = target();
770 final String response = wt.path("flows").request().get(String.class);
Jian Li80cfe452016-01-14 16:04:58 -0800771 final JsonObject result = Json.parse(response).asObject();
Ray Milkey4f5de002014-12-17 19:26:11 -0800772 assertThat(result, notNullValue());
773
774 assertThat(result.names(), hasSize(1));
775 assertThat(result.names().get(0), is("flows"));
776 final JsonArray jsonFlows = result.get("flows").asArray();
777 assertThat(jsonFlows, notNullValue());
778 assertThat(jsonFlows, hasFlow(flow1));
779 assertThat(jsonFlows, hasFlow(flow2));
780 assertThat(jsonFlows, hasFlow(flow3));
781 assertThat(jsonFlows, hasFlow(flow4));
782 }
783
784 /**
785 * Tests the result of a rest api GET for a device.
786 */
787 @Test
788 public void testFlowsSingleDevice() {
789 setupMockFlows();
790 final Set<FlowEntry> flows = new HashSet<>();
791 flows.add(flow5);
792 flows.add(flow6);
793 expect(mockFlowService.getFlowEntries(anyObject()))
794 .andReturn(flows).anyTimes();
795 replay(mockFlowService);
796 replay(mockDeviceService);
Jian Li9d616492016-03-09 10:52:49 -0800797 final WebTarget wt = target();
798 final String response = wt.path("flows/" + deviceId3).request().get(String.class);
Jian Li80cfe452016-01-14 16:04:58 -0800799 final JsonObject result = Json.parse(response).asObject();
Ray Milkey4f5de002014-12-17 19:26:11 -0800800 assertThat(result, notNullValue());
801
802 assertThat(result.names(), hasSize(1));
803 assertThat(result.names().get(0), is("flows"));
804 final JsonArray jsonFlows = result.get("flows").asArray();
805 assertThat(jsonFlows, notNullValue());
806 assertThat(jsonFlows, hasFlow(flow5));
807 assertThat(jsonFlows, hasFlow(flow6));
808 }
809
810 /**
811 * Tests the result of a rest api GET for a device.
812 */
813 @Test
814 public void testFlowsSingleDeviceWithFlowId() {
815 setupMockFlows();
816 final Set<FlowEntry> flows = new HashSet<>();
817 flows.add(flow5);
818 flows.add(flow6);
819 expect(mockFlowService.getFlowEntries(anyObject()))
820 .andReturn(flows).anyTimes();
821 replay(mockFlowService);
822 replay(mockDeviceService);
Jian Li9d616492016-03-09 10:52:49 -0800823 final WebTarget wt = target();
824 final String response = wt.path("flows/" + deviceId3 + "/"
825 + Long.toString(flow5.id().value())).request().get(String.class);
Jian Li80cfe452016-01-14 16:04:58 -0800826 final JsonObject result = Json.parse(response).asObject();
Ray Milkey4f5de002014-12-17 19:26:11 -0800827 assertThat(result, notNullValue());
828
829 assertThat(result.names(), hasSize(1));
830 assertThat(result.names().get(0), is("flows"));
831 final JsonArray jsonFlows = result.get("flows").asArray();
832 assertThat(jsonFlows, notNullValue());
833 assertThat(jsonFlows, hasFlow(flow5));
834 assertThat(jsonFlows, not(hasFlow(flow6)));
835 }
836
837 /**
838 * Tests that a fetch of a non-existent device object throws an exception.
839 */
840 @Test
841 public void testBadGet() {
Jian Li9d616492016-03-09 10:52:49 -0800842 expect(mockFlowService.getFlowEntries(anyObject()))
Ray Milkey4f5de002014-12-17 19:26:11 -0800843 .andReturn(null).anyTimes();
844 replay(mockFlowService);
845 replay(mockDeviceService);
846
Jian Li9d616492016-03-09 10:52:49 -0800847 WebTarget wt = target();
Ray Milkey4f5de002014-12-17 19:26:11 -0800848 try {
Jian Li9d616492016-03-09 10:52:49 -0800849 wt.path("flows/0").request().get(String.class);
Ray Milkey4f5de002014-12-17 19:26:11 -0800850 fail("Fetch of non-existent device did not throw an exception");
Jian Li9d616492016-03-09 10:52:49 -0800851 } catch (NotFoundException ex) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800852 assertThat(ex.getMessage(),
Jian Li9d616492016-03-09 10:52:49 -0800853 containsString("HTTP 404 Not Found"));
Ray Milkey4f5de002014-12-17 19:26:11 -0800854 }
855 }
Ray Milkeyd43fe452015-05-29 09:35:12 -0700856
857 /**
858 * Tests creating a flow with POST.
859 */
860 @Test
Jian Li2907ad22016-05-12 23:08:54 -0700861 public void testPostWithoutAppId() {
Ray Milkeyd43fe452015-05-29 09:35:12 -0700862 mockFlowService.applyFlowRules(anyObject());
863 expectLastCall();
864 replay(mockFlowService);
865
Jian Li9d616492016-03-09 10:52:49 -0800866 WebTarget wt = target();
Phaneendra Manda2be2f882015-11-11 15:23:40 +0530867 InputStream jsonStream = FlowsResourceTest.class
Ray Milkeyb82c42b2015-06-30 09:42:20 -0700868 .getResourceAsStream("post-flow.json");
Ray Milkeyd43fe452015-05-29 09:35:12 -0700869
Jian Li9d616492016-03-09 10:52:49 -0800870 Response response = wt.path("flows/of:0000000000000001")
871 .request(MediaType.APPLICATION_JSON_TYPE)
872 .post(Entity.json(jsonStream));
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700873 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
874 String location = response.getLocation().getPath();
875 assertThat(location, Matchers.startsWith("/flows/of:0000000000000001/"));
876 }
877
878 /**
Jian Li2907ad22016-05-12 23:08:54 -0700879 * Tests creating a flow with POST while specifying application identifier.
880 */
881 @Test
882 public void testPostWithAppId() {
883 mockFlowService.applyFlowRules(anyObject());
884 expectLastCall();
885 replay(mockFlowService);
886
887 WebTarget wt = target();
888 InputStream jsonStream = FlowsResourceTest.class
889 .getResourceAsStream("post-flow.json");
890
891 Response response = wt.path("flows/of:0000000000000001")
892 .queryParam("appId", "org.onosproject.rest")
893 .request(MediaType.APPLICATION_JSON_TYPE)
894 .post(Entity.json(jsonStream));
895 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
896 String location = response.getLocation().getPath();
897 assertThat(location, Matchers.startsWith("/flows/of:0000000000000001/"));
898 }
899
900 /**
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700901 * Tests deleting a flow.
902 */
903 @Test
904 public void testDelete() {
905 setupMockFlows();
906 mockFlowService.removeFlowRules(anyObject());
907 expectLastCall();
908 replay(mockFlowService);
909
Jian Li9d616492016-03-09 10:52:49 -0800910 WebTarget wt = target();
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700911
912 String location = "/flows/1/155";
913
Jian Li9d616492016-03-09 10:52:49 -0800914 Response deleteResponse = wt.path(location)
915 .request(MediaType.APPLICATION_JSON_TYPE)
916 .delete();
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700917 assertThat(deleteResponse.getStatus(),
918 is(HttpURLConnection.HTTP_NO_CONTENT));
Ray Milkeyd43fe452015-05-29 09:35:12 -0700919 }
Jian Li2907ad22016-05-12 23:08:54 -0700920
921 /**
922 * Tests the result of a rest api GET for an application.
923 */
924 @Test
925 public void testGetFlowByAppId() {
926 setupMockFlowRules();
927
928 expect(mockApplicationService.getId(anyObject())).andReturn(APP_ID).anyTimes();
929 replay(mockApplicationService);
930
931 expect(mockFlowService.getFlowRulesById(APP_ID)).andReturn(flowRules).anyTimes();
932 replay(mockFlowService);
933
934 final WebTarget wt = target();
935 final String response = wt.path("flows/application/1").request().get(String.class);
936 final JsonObject result = Json.parse(response).asObject();
937 assertThat(result, notNullValue());
938
939 assertThat(result.names(), hasSize(1));
940 assertThat(result.names().get(0), is("flows"));
941 final JsonArray jsonFlows = result.get("flows").asArray();
942 assertThat(jsonFlows, notNullValue());
943 assertThat(jsonFlows, hasFlowRule(flowRule1));
944 assertThat(jsonFlows, hasFlowRule(flowRule2));
945 assertThat(jsonFlows, hasFlowRule(flowRule3));
946 assertThat(jsonFlows, hasFlowRule(flowRule4));
947 }
948
949 /**
950 * Tests the result of a rest api DELETE for an application.
951 */
952 @Test
953 public void testRemoveFlowByAppId() {
954 expect(mockApplicationService.getId(anyObject())).andReturn(APP_ID).anyTimes();
955 replay(mockApplicationService);
956
957 mockFlowService.removeFlowRulesById(APP_ID);
958 expectLastCall();
959 replay(mockFlowService);
960
961 WebTarget wt = target();
962
963 String location = "/flows/application/1";
964
965 Response deleteResponse = wt.path(location)
966 .request()
967 .delete();
968 assertThat(deleteResponse.getStatus(),
969 is(HttpURLConnection.HTTP_NO_CONTENT));
970 }
Ray Milkey4f5de002014-12-17 19:26:11 -0800971}