blob: 6e8255c833725877fffd71870e45854933aa7f17 [file] [log] [blame]
Ray Milkey4f5de002014-12-17 19:26:11 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
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.GroupId;
47import org.onosproject.net.DefaultDevice;
48import org.onosproject.net.Device;
49import org.onosproject.net.DeviceId;
Ray Milkeyd43fe452015-05-29 09:35:12 -070050import org.onosproject.net.NetTestTools;
Ray Milkey4f5de002014-12-17 19:26:11 -080051import org.onosproject.net.device.DeviceService;
52import org.onosproject.net.flow.DefaultTrafficSelector;
53import org.onosproject.net.flow.DefaultTrafficTreatment;
54import org.onosproject.net.flow.FlowEntry;
Ray Milkey634eb172017-04-05 14:48:50 -070055import org.onosproject.net.flow.FlowEntryAdapter;
Ray Milkey4f5de002014-12-17 19:26:11 -080056import org.onosproject.net.flow.FlowId;
Jonathan Hartf44e42c2015-08-04 09:58:46 -070057import org.onosproject.net.flow.FlowRule;
Ray Milkey4f5de002014-12-17 19:26:11 -080058import org.onosproject.net.flow.FlowRuleService;
59import org.onosproject.net.flow.TrafficSelector;
60import org.onosproject.net.flow.TrafficTreatment;
61import org.onosproject.net.flow.criteria.Criterion;
62import org.onosproject.net.flow.instructions.Instruction;
jcc3d4e14a2015-04-21 11:32:05 +080063
Bharath Thiruveedula99849dc2016-11-17 22:04:38 +053064import com.eclipsesource.json.Json;
65import com.eclipsesource.json.JsonArray;
66import com.eclipsesource.json.JsonObject;
67import com.google.common.collect.ImmutableSet;
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
Bharath Thiruveedula99849dc2016-11-17 22:04:38 +0530117 final Set<FlowEntry> flowEntries = ImmutableSet.of(flow1, flow2, flow3, flow4, flow5, flow6);
Jian Li2907ad22016-05-12 23:08:54 -0700118
Ray Milkey4f5de002014-12-17 19:26:11 -0800119 /**
120 * Mock class for a flow entry.
121 */
Ray Milkey634eb172017-04-05 14:48:50 -0700122 private static class MockFlowEntry extends FlowEntryAdapter {
Ray Milkey4f5de002014-12-17 19:26:11 -0800123 final DeviceId deviceId;
124 final long baseValue;
125 TrafficTreatment treatment;
126 TrafficSelector selector;
127
128 public MockFlowEntry(DeviceId deviceId, long id) {
129 this.deviceId = deviceId;
130 this.baseValue = id * 100;
131 }
132
133 @Override
134 public FlowEntryState state() {
135 return FlowEntryState.ADDED;
136 }
137
138 @Override
139 public long life() {
Thiago Santos877914d2016-07-20 18:29:29 -0300140 return life(SECONDS);
141 }
142
143 @Override
144 public long life(TimeUnit timeUnit) {
145 return SECONDS.convert(baseValue + 11, timeUnit);
Ray Milkey4f5de002014-12-17 19:26:11 -0800146 }
147
148 @Override
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900149 public FlowLiveType liveType() {
150 return FlowLiveType.IMMEDIATE;
151 }
152
153 @Override
Ray Milkey4f5de002014-12-17 19:26:11 -0800154 public long packets() {
155 return baseValue + 22;
156 }
157
158 @Override
159 public long bytes() {
160 return baseValue + 33;
161 }
162
163 @Override
164 public long lastSeen() {
165 return baseValue + 44;
166 }
167
168 @Override
Ray Milkey4f5de002014-12-17 19:26:11 -0800169 public FlowId id() {
170 final long id = baseValue + 55;
171 return FlowId.valueOf(id);
172 }
173
174 @Override
alshabib08d98982015-04-21 16:25:50 -0700175 public GroupId groupId() {
Yi Tsengfa394de2017-02-01 11:26:40 -0800176 return new GroupId(3);
Ray Milkey4f5de002014-12-17 19:26:11 -0800177 }
178
179 @Override
alshabib08d98982015-04-21 16:25:50 -0700180 public short appId() {
181 return 2;
Ray Milkey4f5de002014-12-17 19:26:11 -0800182 }
183
184 @Override
185 public int priority() {
186 return (int) (baseValue + 66);
187 }
188
189 @Override
190 public DeviceId deviceId() {
191 return deviceId;
192 }
193
194 @Override
195 public TrafficSelector selector() {
196 return selector;
197 }
198
199 @Override
200 public TrafficTreatment treatment() {
201 return treatment;
202 }
203
204 @Override
205 public int timeout() {
206 return (int) (baseValue + 77);
207 }
208
209 @Override
Murat Parlakisikc6759e82016-06-29 03:22:22 -0700210 public FlowRemoveReason reason() {
211 return FlowRemoveReason.NO_REASON;
212 }
Ray Milkey4f5de002014-12-17 19:26:11 -0800213 }
214
Jian Li2907ad22016-05-12 23:08:54 -0700215
216 /**
Ray Milkey4f5de002014-12-17 19:26:11 -0800217 * Populates some flows used as testing data.
218 */
219 private void setupMockFlows() {
220 flow2.treatment = DefaultTrafficTreatment.builder()
Ray Milkey4f5de002014-12-17 19:26:11 -0800221 .setEthDst(MacAddress.BROADCAST)
222 .build();
223 flow2.selector = DefaultTrafficSelector.builder()
224 .matchEthType((short) 3)
225 .matchIPProtocol((byte) 9)
226 .build();
227 flow4.treatment = DefaultTrafficTreatment.builder()
Ray Milkey4f5de002014-12-17 19:26:11 -0800228 .build();
229 final Set<FlowEntry> flows1 = new HashSet<>();
230 flows1.add(flow1);
231 flows1.add(flow2);
232
233 final Set<FlowEntry> flows2 = new HashSet<>();
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700234 flows2.add(flow3);
235 flows2.add(flow4);
Ray Milkey4f5de002014-12-17 19:26:11 -0800236
237 rules.put(deviceId1, flows1);
238 rules.put(deviceId2, flows2);
239
240 expect(mockFlowService.getFlowEntries(deviceId1))
241 .andReturn(rules.get(deviceId1)).anyTimes();
242 expect(mockFlowService.getFlowEntries(deviceId2))
243 .andReturn(rules.get(deviceId2)).anyTimes();
244 }
245
246 /**
247 * Sets up the global values for all the tests.
248 */
249 @Before
Ray Milkeyed0b1662015-02-05 09:34:29 -0800250 public void setUpTest() {
Ray Milkey4f5de002014-12-17 19:26:11 -0800251 // Mock device service
252 expect(mockDeviceService.getDevice(deviceId1))
253 .andReturn(device1);
254 expect(mockDeviceService.getDevice(deviceId2))
255 .andReturn(device2);
256 expect(mockDeviceService.getDevices())
257 .andReturn(ImmutableSet.of(device1, device2));
258
Ray Milkeyd43fe452015-05-29 09:35:12 -0700259 // Mock Core Service
260 expect(mockCoreService.getAppId(anyShort()))
261 .andReturn(NetTestTools.APP_ID).anyTimes();
Jian Li2907ad22016-05-12 23:08:54 -0700262 expect(mockCoreService.getAppId(anyString()))
263 .andReturn(NetTestTools.APP_ID).anyTimes();
Jonathan Hart9b80da82016-11-10 21:46:15 +0000264 expect(mockCoreService.registerApplication(FlowRuleCodec.REST_APP_ID))
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700265 .andReturn(APP_ID).anyTimes();
Ray Milkeyd43fe452015-05-29 09:35:12 -0700266 replay(mockCoreService);
267
Ray Milkey4f5de002014-12-17 19:26:11 -0800268 // Register the services needed for the test
269 final CodecManager codecService = new CodecManager();
270 codecService.activate();
271 ServiceDirectory testDirectory =
272 new TestServiceDirectory()
273 .add(FlowRuleService.class, mockFlowService)
274 .add(DeviceService.class, mockDeviceService)
Ray Milkeyd43fe452015-05-29 09:35:12 -0700275 .add(CodecService.class, codecService)
Jian Li2907ad22016-05-12 23:08:54 -0700276 .add(CoreService.class, mockCoreService)
277 .add(ApplicationService.class, mockApplicationService);
Ray Milkey4f5de002014-12-17 19:26:11 -0800278
279 BaseResource.setServiceDirectory(testDirectory);
280 }
281
282 /**
283 * Cleans up and verifies the mocks.
Ray Milkey4f5de002014-12-17 19:26:11 -0800284 */
285 @After
Ray Milkeyed0b1662015-02-05 09:34:29 -0800286 public void tearDownTest() {
Ray Milkey4f5de002014-12-17 19:26:11 -0800287 verify(mockFlowService);
Ray Milkeyd43fe452015-05-29 09:35:12 -0700288 verify(mockCoreService);
Ray Milkey4f5de002014-12-17 19:26:11 -0800289 }
290
291 /**
292 * Hamcrest matcher to check that a flow representation in JSON matches
293 * the actual flow entry.
294 */
Jian Li2907ad22016-05-12 23:08:54 -0700295 public static class FlowEntryJsonMatcher extends TypeSafeMatcher<JsonObject> {
Ray Milkey4f5de002014-12-17 19:26:11 -0800296 private final FlowEntry flow;
Ray Milkeybe539db2015-09-04 11:00:43 -0700297 private final String expectedAppId;
Ray Milkey4f5de002014-12-17 19:26:11 -0800298 private String reason = "";
299
Jian Li2907ad22016-05-12 23:08:54 -0700300 public FlowEntryJsonMatcher(FlowEntry flowValue, String expectedAppIdValue) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800301 flow = flowValue;
Ray Milkeybe539db2015-09-04 11:00:43 -0700302 expectedAppId = expectedAppIdValue;
Ray Milkey4f5de002014-12-17 19:26:11 -0800303 }
304
305 @Override
306 public boolean matchesSafely(JsonObject jsonFlow) {
307 // check id
308 final String jsonId = jsonFlow.get("id").asString();
309 final String flowId = Long.toString(flow.id().value());
310 if (!jsonId.equals(flowId)) {
311 reason = "id " + flow.id().toString();
312 return false;
313 }
314
315 // check application id
Ray Milkeybe539db2015-09-04 11:00:43 -0700316 final String jsonAppId = jsonFlow.get("appId").asString();
317 if (!jsonAppId.equals(expectedAppId)) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800318 reason = "appId " + Short.toString(flow.appId());
319 return false;
320 }
321
322 // check device id
323 final String jsonDeviceId = jsonFlow.get("deviceId").asString();
324 if (!jsonDeviceId.equals(flow.deviceId().toString())) {
325 reason = "deviceId " + flow.deviceId();
326 return false;
327 }
328
329 // check treatment and instructions array
330 if (flow.treatment() != null) {
331 final JsonObject jsonTreatment = jsonFlow.get("treatment").asObject();
332 final JsonArray jsonInstructions = jsonTreatment.get("instructions").asArray();
Ray Milkey42507352015-03-20 15:16:10 -0700333 if (flow.treatment().immediate().size() != jsonInstructions.size()) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800334 reason = "instructions array size of " +
Ray Milkey42507352015-03-20 15:16:10 -0700335 Integer.toString(flow.treatment().immediate().size());
Ray Milkey4f5de002014-12-17 19:26:11 -0800336 return false;
337 }
Ray Milkey42507352015-03-20 15:16:10 -0700338 for (final Instruction instruction : flow.treatment().immediate()) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800339 boolean instructionFound = false;
Ray Milkey4f5de002014-12-17 19:26:11 -0800340 for (int instructionIndex = 0; instructionIndex < jsonInstructions.size(); instructionIndex++) {
Ray Milkeyc95bb9d2015-01-06 10:28:24 -0800341 final String jsonType =
342 jsonInstructions.get(instructionIndex)
343 .asObject().get("type").asString();
344 final String instructionType = instruction.type().name();
345 if (jsonType.equals(instructionType)) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800346 instructionFound = true;
347 }
348 }
349 if (!instructionFound) {
Ray Milkeyc95bb9d2015-01-06 10:28:24 -0800350 reason = "instruction " + instruction.toString();
Ray Milkey4f5de002014-12-17 19:26:11 -0800351 return false;
352 }
353 }
354 }
355
356 // check selector and criteria array
357 if (flow.selector() != null) {
358 final JsonObject jsonTreatment = jsonFlow.get("selector").asObject();
359 final JsonArray jsonCriteria = jsonTreatment.get("criteria").asArray();
360 if (flow.selector().criteria().size() != jsonCriteria.size()) {
361 reason = "criteria array size of " +
362 Integer.toString(flow.selector().criteria().size());
363 return false;
364 }
365 for (final Criterion criterion : flow.selector().criteria()) {
366 boolean criterionFound = false;
Ray Milkeyc95bb9d2015-01-06 10:28:24 -0800367
Ray Milkey4f5de002014-12-17 19:26:11 -0800368 for (int criterionIndex = 0; criterionIndex < jsonCriteria.size(); criterionIndex++) {
Ray Milkeyc95bb9d2015-01-06 10:28:24 -0800369 final String jsonType =
370 jsonCriteria.get(criterionIndex)
371 .asObject().get("type").asString();
372 final String criterionType = criterion.type().name();
373 if (jsonType.equals(criterionType)) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800374 criterionFound = true;
375 }
376 }
377 if (!criterionFound) {
Ray Milkeyc95bb9d2015-01-06 10:28:24 -0800378 reason = "criterion " + criterion.toString();
Ray Milkey4f5de002014-12-17 19:26:11 -0800379 return false;
380 }
381 }
382 }
383
384 return true;
385 }
386
387 @Override
388 public void describeTo(Description description) {
389 description.appendText(reason);
390 }
391 }
392
393 /**
394 * Factory to allocate a flow matcher.
395 *
396 * @param flow flow object we are looking for
397 * @return matcher
398 */
Jian Li2907ad22016-05-12 23:08:54 -0700399 private static FlowEntryJsonMatcher matchesFlow(FlowEntry flow, String expectedAppName) {
400 return new FlowEntryJsonMatcher(flow, expectedAppName);
Ray Milkey4f5de002014-12-17 19:26:11 -0800401 }
402
403 /**
404 * Hamcrest matcher to check that a flow is represented properly in a JSON
405 * array of flows.
406 */
Jian Li2907ad22016-05-12 23:08:54 -0700407 public static class FlowEntryJsonArrayMatcher extends TypeSafeMatcher<JsonArray> {
Ray Milkey4f5de002014-12-17 19:26:11 -0800408 private final FlowEntry flow;
409 private String reason = "";
410
Jian Li2907ad22016-05-12 23:08:54 -0700411 public FlowEntryJsonArrayMatcher(FlowEntry flowValue) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800412 flow = flowValue;
413 }
414
415 @Override
416 public boolean matchesSafely(JsonArray json) {
417 boolean flowFound = false;
418
419 for (int jsonFlowIndex = 0; jsonFlowIndex < json.size();
420 jsonFlowIndex++) {
421
422 final JsonObject jsonFlow = json.get(jsonFlowIndex).asObject();
423
424 final String flowId = Long.toString(flow.id().value());
425 final String jsonFlowId = jsonFlow.get("id").asString();
426 if (jsonFlowId.equals(flowId)) {
427 flowFound = true;
428
429 // We found the correct flow, check attribute values
Ray Milkeybe539db2015-09-04 11:00:43 -0700430 assertThat(jsonFlow, matchesFlow(flow, APP_ID.name()));
Ray Milkey4f5de002014-12-17 19:26:11 -0800431 }
432 }
433 if (!flowFound) {
434 reason = "Flow with id " + flow.id().toString() + " not found";
435 return false;
436 } else {
437 return true;
438 }
439 }
440
441 @Override
442 public void describeTo(Description description) {
443 description.appendText(reason);
444 }
445 }
446
447 /**
448 * Factory to allocate a flow array matcher.
449 *
450 * @param flow flow object we are looking for
451 * @return matcher
452 */
Jian Li2907ad22016-05-12 23:08:54 -0700453 private static FlowEntryJsonArrayMatcher hasFlow(FlowEntry flow) {
454 return new FlowEntryJsonArrayMatcher(flow);
455 }
456
457 /**
458 * Hamcrest matcher to check that a flow representation in JSON matches
459 * the actual flow rule.
460 */
461 public static class FlowRuleJsonMatcher extends TypeSafeMatcher<JsonObject> {
462 private final FlowRule flow;
463 private final String expectedAppId;
464 private String reason = "";
465
466 public FlowRuleJsonMatcher(FlowRule flowValue, String expectedAppIdValue) {
467 flow = flowValue;
468 expectedAppId = expectedAppIdValue;
469 }
470
471 @Override
472 public boolean matchesSafely(JsonObject jsonFlow) {
473 // check id
474 final String jsonId = jsonFlow.get("id").asString();
475 final String flowId = Long.toString(flow.id().value());
476 if (!jsonId.equals(flowId)) {
477 reason = "id " + flow.id().toString();
478 return false;
479 }
480
481 // check application id
482 final String jsonAppId = jsonFlow.get("appId").asString();
483 if (!jsonAppId.equals(expectedAppId)) {
484 reason = "appId " + Short.toString(flow.appId());
485 return false;
486 }
487
488 // check device id
489 final String jsonDeviceId = jsonFlow.get("deviceId").asString();
490 if (!jsonDeviceId.equals(flow.deviceId().toString())) {
491 reason = "deviceId " + flow.deviceId();
492 return false;
493 }
494
495 // check treatment and instructions array
496 if (flow.treatment() != null) {
497 final JsonObject jsonTreatment = jsonFlow.get("treatment").asObject();
498 final JsonArray jsonInstructions = jsonTreatment.get("instructions").asArray();
499 if (flow.treatment().immediate().size() != jsonInstructions.size()) {
500 reason = "instructions array size of " +
501 Integer.toString(flow.treatment().immediate().size());
502 return false;
503 }
504 for (final Instruction instruction : flow.treatment().immediate()) {
505 boolean instructionFound = false;
506 for (int instructionIndex = 0; instructionIndex < jsonInstructions.size(); instructionIndex++) {
507 final String jsonType =
508 jsonInstructions.get(instructionIndex)
509 .asObject().get("type").asString();
510 final String instructionType = instruction.type().name();
511 if (jsonType.equals(instructionType)) {
512 instructionFound = true;
513 }
514 }
515 if (!instructionFound) {
516 reason = "instruction " + instruction.toString();
517 return false;
518 }
519 }
520 }
521
522 // check selector and criteria array
523 if (flow.selector() != null) {
524 final JsonObject jsonTreatment = jsonFlow.get("selector").asObject();
525 final JsonArray jsonCriteria = jsonTreatment.get("criteria").asArray();
526 if (flow.selector().criteria().size() != jsonCriteria.size()) {
527 reason = "criteria array size of " +
528 Integer.toString(flow.selector().criteria().size());
529 return false;
530 }
531 for (final Criterion criterion : flow.selector().criteria()) {
532 boolean criterionFound = false;
533
534 for (int criterionIndex = 0; criterionIndex < jsonCriteria.size(); criterionIndex++) {
535 final String jsonType =
536 jsonCriteria.get(criterionIndex)
537 .asObject().get("type").asString();
538 final String criterionType = criterion.type().name();
539 if (jsonType.equals(criterionType)) {
540 criterionFound = true;
541 }
542 }
543 if (!criterionFound) {
544 reason = "criterion " + criterion.toString();
545 return false;
546 }
547 }
548 }
549
550 return true;
551 }
552
553 @Override
554 public void describeTo(Description description) {
555 description.appendText(reason);
556 }
557 }
558
559 /**
560 * Factory to allocate a flow matcher.
561 *
562 * @param flow flow rule object we are looking for
563 * @return matcher
564 */
565 private static FlowRuleJsonMatcher matchesFlowRule(FlowRule flow, String expectedAppName) {
566 return new FlowRuleJsonMatcher(flow, expectedAppName);
567 }
568
569 /**
570 * Hamcrest matcher to check that a flow is represented properly in a JSON
571 * array of flow rules.
572 */
573 public static class FlowRuleJsonArrayMatcher extends TypeSafeMatcher<JsonArray> {
574 private final FlowRule flow;
575 private String reason = "";
576
577 public FlowRuleJsonArrayMatcher(FlowRule flowValue) {
578 flow = flowValue;
579 }
580
581 @Override
582 public boolean matchesSafely(JsonArray json) {
583 boolean flowFound = false;
584
585 for (int jsonFlowIndex = 0; jsonFlowIndex < json.size();
586 jsonFlowIndex++) {
587
588 final JsonObject jsonFlow = json.get(jsonFlowIndex).asObject();
589
590 final String flowId = Long.toString(flow.id().value());
591 final String jsonFlowId = jsonFlow.get("id").asString();
592 if (jsonFlowId.equals(flowId)) {
593 flowFound = true;
594
595 // We found the correct flow, check attribute values
596 assertThat(jsonFlow, matchesFlowRule(flow, APP_ID.name()));
597 }
598 }
599 if (!flowFound) {
600 reason = "Flow with id " + flow.id().toString() + " not found";
601 return false;
602 } else {
603 return true;
604 }
605 }
606
607 @Override
608 public void describeTo(Description description) {
609 description.appendText(reason);
610 }
611 }
612
613 /**
614 * Factory to allocate a flow array matcher.
615 *
616 * @param flow flow rule object we are looking for
617 * @return matcher
618 */
619 private static FlowRuleJsonArrayMatcher hasFlowRule(FlowRule flow) {
620 return new FlowRuleJsonArrayMatcher(flow);
Ray Milkey4f5de002014-12-17 19:26:11 -0800621 }
622
623 /**
624 * Tests the result of the rest api GET when there are no flows.
625 */
626 @Test
627 public void testFlowsEmptyArray() {
628 expect(mockFlowService.getFlowEntries(deviceId1))
629 .andReturn(null).anyTimes();
630 expect(mockFlowService.getFlowEntries(deviceId2))
631 .andReturn(null).anyTimes();
632 replay(mockFlowService);
633 replay(mockDeviceService);
Jian Li9d616492016-03-09 10:52:49 -0800634 final WebTarget wt = target();
635 final String response = wt.path("flows").request().get(String.class);
Ray Milkey4f5de002014-12-17 19:26:11 -0800636 assertThat(response, is("{\"flows\":[]}"));
637 }
638
639 /**
640 * Tests the result of the rest api GET when there are active flows.
641 */
642 @Test
643 public void testFlowsPopulatedArray() {
644 setupMockFlows();
645 replay(mockFlowService);
646 replay(mockDeviceService);
Jian Li9d616492016-03-09 10:52:49 -0800647 final WebTarget wt = target();
648 final String response = wt.path("flows").request().get(String.class);
Jian Li80cfe452016-01-14 16:04:58 -0800649 final JsonObject result = Json.parse(response).asObject();
Ray Milkey4f5de002014-12-17 19:26:11 -0800650 assertThat(result, notNullValue());
651
652 assertThat(result.names(), hasSize(1));
653 assertThat(result.names().get(0), is("flows"));
654 final JsonArray jsonFlows = result.get("flows").asArray();
655 assertThat(jsonFlows, notNullValue());
656 assertThat(jsonFlows, hasFlow(flow1));
657 assertThat(jsonFlows, hasFlow(flow2));
658 assertThat(jsonFlows, hasFlow(flow3));
659 assertThat(jsonFlows, hasFlow(flow4));
660 }
661
662 /**
663 * Tests the result of a rest api GET for a device.
664 */
665 @Test
666 public void testFlowsSingleDevice() {
667 setupMockFlows();
668 final Set<FlowEntry> flows = new HashSet<>();
669 flows.add(flow5);
670 flows.add(flow6);
671 expect(mockFlowService.getFlowEntries(anyObject()))
672 .andReturn(flows).anyTimes();
673 replay(mockFlowService);
674 replay(mockDeviceService);
Jian Li9d616492016-03-09 10:52:49 -0800675 final WebTarget wt = target();
676 final String response = wt.path("flows/" + deviceId3).request().get(String.class);
Jian Li80cfe452016-01-14 16:04:58 -0800677 final JsonObject result = Json.parse(response).asObject();
Ray Milkey4f5de002014-12-17 19:26:11 -0800678 assertThat(result, notNullValue());
679
680 assertThat(result.names(), hasSize(1));
681 assertThat(result.names().get(0), is("flows"));
682 final JsonArray jsonFlows = result.get("flows").asArray();
683 assertThat(jsonFlows, notNullValue());
684 assertThat(jsonFlows, hasFlow(flow5));
685 assertThat(jsonFlows, hasFlow(flow6));
686 }
687
688 /**
689 * Tests the result of a rest api GET for a device.
690 */
691 @Test
692 public void testFlowsSingleDeviceWithFlowId() {
693 setupMockFlows();
694 final Set<FlowEntry> flows = new HashSet<>();
695 flows.add(flow5);
696 flows.add(flow6);
697 expect(mockFlowService.getFlowEntries(anyObject()))
698 .andReturn(flows).anyTimes();
699 replay(mockFlowService);
700 replay(mockDeviceService);
Jian Li9d616492016-03-09 10:52:49 -0800701 final WebTarget wt = target();
702 final String response = wt.path("flows/" + deviceId3 + "/"
703 + Long.toString(flow5.id().value())).request().get(String.class);
Jian Li80cfe452016-01-14 16:04:58 -0800704 final JsonObject result = Json.parse(response).asObject();
Ray Milkey4f5de002014-12-17 19:26:11 -0800705 assertThat(result, notNullValue());
706
707 assertThat(result.names(), hasSize(1));
708 assertThat(result.names().get(0), is("flows"));
709 final JsonArray jsonFlows = result.get("flows").asArray();
710 assertThat(jsonFlows, notNullValue());
711 assertThat(jsonFlows, hasFlow(flow5));
712 assertThat(jsonFlows, not(hasFlow(flow6)));
713 }
714
715 /**
716 * Tests that a fetch of a non-existent device object throws an exception.
717 */
718 @Test
719 public void testBadGet() {
Jian Li9d616492016-03-09 10:52:49 -0800720 expect(mockFlowService.getFlowEntries(anyObject()))
Ray Milkey4f5de002014-12-17 19:26:11 -0800721 .andReturn(null).anyTimes();
722 replay(mockFlowService);
723 replay(mockDeviceService);
724
Jian Li9d616492016-03-09 10:52:49 -0800725 WebTarget wt = target();
Ray Milkey4f5de002014-12-17 19:26:11 -0800726 try {
Jian Li9d616492016-03-09 10:52:49 -0800727 wt.path("flows/0").request().get(String.class);
Ray Milkey4f5de002014-12-17 19:26:11 -0800728 fail("Fetch of non-existent device did not throw an exception");
Jian Li9d616492016-03-09 10:52:49 -0800729 } catch (NotFoundException ex) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800730 assertThat(ex.getMessage(),
Jian Li9d616492016-03-09 10:52:49 -0800731 containsString("HTTP 404 Not Found"));
Ray Milkey4f5de002014-12-17 19:26:11 -0800732 }
733 }
Ray Milkeyd43fe452015-05-29 09:35:12 -0700734
735 /**
736 * Tests creating a flow with POST.
737 */
738 @Test
Jian Li2907ad22016-05-12 23:08:54 -0700739 public void testPostWithoutAppId() {
Jonathan Hart9b80da82016-11-10 21:46:15 +0000740 mockFlowService.applyFlowRules(anyObject());
741 expectLastCall();
Ray Milkeyd43fe452015-05-29 09:35:12 -0700742 replay(mockFlowService);
743
Jian Li9d616492016-03-09 10:52:49 -0800744 WebTarget wt = target();
Phaneendra Manda2be2f882015-11-11 15:23:40 +0530745 InputStream jsonStream = FlowsResourceTest.class
Ray Milkeyb82c42b2015-06-30 09:42:20 -0700746 .getResourceAsStream("post-flow.json");
Ray Milkeyd43fe452015-05-29 09:35:12 -0700747
Jian Li9d616492016-03-09 10:52:49 -0800748 Response response = wt.path("flows/of:0000000000000001")
749 .request(MediaType.APPLICATION_JSON_TYPE)
750 .post(Entity.json(jsonStream));
Jonathan Hart9b80da82016-11-10 21:46:15 +0000751 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
752 String location = response.getLocation().getPath();
753 assertThat(location, Matchers.startsWith("/flows/of:0000000000000001/"));
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700754 }
755
756 /**
Jian Li2907ad22016-05-12 23:08:54 -0700757 * Tests creating a flow with POST while specifying application identifier.
758 */
759 @Test
760 public void testPostWithAppId() {
761 mockFlowService.applyFlowRules(anyObject());
762 expectLastCall();
763 replay(mockFlowService);
764
765 WebTarget wt = target();
766 InputStream jsonStream = FlowsResourceTest.class
767 .getResourceAsStream("post-flow.json");
768
769 Response response = wt.path("flows/of:0000000000000001")
770 .queryParam("appId", "org.onosproject.rest")
771 .request(MediaType.APPLICATION_JSON_TYPE)
772 .post(Entity.json(jsonStream));
773 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
774 String location = response.getLocation().getPath();
775 assertThat(location, Matchers.startsWith("/flows/of:0000000000000001/"));
776 }
777
778 /**
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700779 * Tests deleting a flow.
780 */
781 @Test
782 public void testDelete() {
783 setupMockFlows();
784 mockFlowService.removeFlowRules(anyObject());
785 expectLastCall();
786 replay(mockFlowService);
787
Jian Li9d616492016-03-09 10:52:49 -0800788 WebTarget wt = target();
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700789
790 String location = "/flows/1/155";
791
Jian Li9d616492016-03-09 10:52:49 -0800792 Response deleteResponse = wt.path(location)
793 .request(MediaType.APPLICATION_JSON_TYPE)
794 .delete();
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700795 assertThat(deleteResponse.getStatus(),
796 is(HttpURLConnection.HTTP_NO_CONTENT));
Ray Milkeyd43fe452015-05-29 09:35:12 -0700797 }
Jian Li2907ad22016-05-12 23:08:54 -0700798
799 /**
800 * Tests the result of a rest api GET for an application.
801 */
802 @Test
803 public void testGetFlowByAppId() {
Bharath Thiruveedula99849dc2016-11-17 22:04:38 +0530804 setupMockFlows();
Jian Li2907ad22016-05-12 23:08:54 -0700805
806 expect(mockApplicationService.getId(anyObject())).andReturn(APP_ID).anyTimes();
807 replay(mockApplicationService);
808
Bharath Thiruveedula99849dc2016-11-17 22:04:38 +0530809 expect(mockFlowService.getFlowEntriesById(APP_ID)).andReturn(flowEntries).anyTimes();
Jian Li2907ad22016-05-12 23:08:54 -0700810 replay(mockFlowService);
811
812 final WebTarget wt = target();
Bharath Thiruveedula99849dc2016-11-17 22:04:38 +0530813 final String response = wt.path("/flows/application/1").request().get(String.class);
Jian Li2907ad22016-05-12 23:08:54 -0700814 final JsonObject result = Json.parse(response).asObject();
815 assertThat(result, notNullValue());
816
817 assertThat(result.names(), hasSize(1));
818 assertThat(result.names().get(0), is("flows"));
819 final JsonArray jsonFlows = result.get("flows").asArray();
820 assertThat(jsonFlows, notNullValue());
Bharath Thiruveedula99849dc2016-11-17 22:04:38 +0530821 assertThat(jsonFlows, hasFlowRule(flow1));
822 assertThat(jsonFlows, hasFlowRule(flow2));
823 assertThat(jsonFlows, hasFlowRule(flow3));
824 assertThat(jsonFlows, hasFlowRule(flow4));
Jian Li2907ad22016-05-12 23:08:54 -0700825 }
826
827 /**
828 * Tests the result of a rest api DELETE for an application.
829 */
830 @Test
831 public void testRemoveFlowByAppId() {
832 expect(mockApplicationService.getId(anyObject())).andReturn(APP_ID).anyTimes();
833 replay(mockApplicationService);
834
835 mockFlowService.removeFlowRulesById(APP_ID);
836 expectLastCall();
837 replay(mockFlowService);
838
839 WebTarget wt = target();
840
841 String location = "/flows/application/1";
842
843 Response deleteResponse = wt.path(location)
844 .request()
845 .delete();
846 assertThat(deleteResponse.getStatus(),
847 is(HttpURLConnection.HTTP_NO_CONTENT));
848 }
Ray Milkey4f5de002014-12-17 19:26:11 -0800849}