blob: 4687e449a8805e35478206dcb185e35177331e94 [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;
Ray Milkey4f5de002014-12-17 19:26:11 -080067
68import static org.easymock.EasyMock.anyObject;
Ray Milkeyd43fe452015-05-29 09:35:12 -070069import static org.easymock.EasyMock.anyShort;
Jian Li2907ad22016-05-12 23:08:54 -070070import static org.easymock.EasyMock.anyString;
Ray Milkey4f5de002014-12-17 19:26:11 -080071import static org.easymock.EasyMock.createMock;
72import static org.easymock.EasyMock.expect;
Ray Milkeyd43fe452015-05-29 09:35:12 -070073import static org.easymock.EasyMock.expectLastCall;
Ray Milkey4f5de002014-12-17 19:26:11 -080074import static org.easymock.EasyMock.replay;
75import static org.easymock.EasyMock.verify;
76import static org.hamcrest.Matchers.containsString;
77import static org.hamcrest.Matchers.hasSize;
78import static org.hamcrest.Matchers.is;
79import static org.hamcrest.Matchers.not;
80import static org.hamcrest.Matchers.notNullValue;
81import static org.junit.Assert.assertThat;
82import static org.junit.Assert.fail;
Ray Milkeyeb5c7172015-06-23 14:59:27 -070083import static org.onosproject.net.NetTestTools.APP_ID;
Ray Milkey4f5de002014-12-17 19:26:11 -080084
85/**
86 * Unit tests for Flows REST APIs.
87 */
Ray Milkey9c3d3362015-01-28 10:39:56 -080088public class FlowsResourceTest extends ResourceTest {
Ray Milkey4f5de002014-12-17 19:26:11 -080089 final FlowRuleService mockFlowService = createMock(FlowRuleService.class);
Ray Milkeyd43fe452015-05-29 09:35:12 -070090 CoreService mockCoreService = createMock(CoreService.class);
91
Ray Milkey4f5de002014-12-17 19:26:11 -080092 final HashMap<DeviceId, Set<FlowEntry>> rules = new HashMap<>();
93
94 final DeviceService mockDeviceService = createMock(DeviceService.class);
95
96 final DeviceId deviceId1 = DeviceId.deviceId("1");
97 final DeviceId deviceId2 = DeviceId.deviceId("2");
98 final DeviceId deviceId3 = DeviceId.deviceId("3");
99 final Device device1 = new DefaultDevice(null, deviceId1, Device.Type.OTHER,
100 "", "", "", "", null);
101 final Device device2 = new DefaultDevice(null, deviceId2, Device.Type.OTHER,
102 "", "", "", "", null);
103
Jian Li2907ad22016-05-12 23:08:54 -0700104 final ApplicationService mockApplicationService = createMock(ApplicationService.class);
105
Ray Milkey4f5de002014-12-17 19:26:11 -0800106 final MockFlowEntry flow1 = new MockFlowEntry(deviceId1, 1);
107 final MockFlowEntry flow2 = new MockFlowEntry(deviceId1, 2);
108
109 final MockFlowEntry flow3 = new MockFlowEntry(deviceId2, 3);
110 final MockFlowEntry flow4 = new MockFlowEntry(deviceId2, 4);
111
112 final MockFlowEntry flow5 = new MockFlowEntry(deviceId2, 5);
113 final MockFlowEntry flow6 = new MockFlowEntry(deviceId2, 6);
114
Jian Li2907ad22016-05-12 23:08:54 -0700115 final MockFlowRule flowRule1 = new MockFlowRule(deviceId1, 1);
116 final MockFlowRule flowRule2 = new MockFlowRule(deviceId1, 2);
117
118 final MockFlowRule flowRule3 = new MockFlowRule(deviceId2, 3);
119 final MockFlowRule flowRule4 = new MockFlowRule(deviceId2, 4);
120
121 final Set<FlowRule> flowRules = Sets.newHashSet();
122
Ray Milkey4f5de002014-12-17 19:26:11 -0800123 /**
124 * Mock class for a flow entry.
125 */
126 private static class MockFlowEntry implements FlowEntry {
127 final DeviceId deviceId;
128 final long baseValue;
129 TrafficTreatment treatment;
130 TrafficSelector selector;
131
132 public MockFlowEntry(DeviceId deviceId, long id) {
133 this.deviceId = deviceId;
134 this.baseValue = id * 100;
135 }
136
137 @Override
138 public FlowEntryState state() {
139 return FlowEntryState.ADDED;
140 }
141
142 @Override
143 public long life() {
144 return baseValue + 11;
145 }
146
147 @Override
148 public long packets() {
149 return baseValue + 22;
150 }
151
152 @Override
153 public long bytes() {
154 return baseValue + 33;
155 }
156
157 @Override
158 public long lastSeen() {
159 return baseValue + 44;
160 }
161
162 @Override
163 public int errType() {
164 return 0;
165 }
166
167 @Override
168 public int errCode() {
169 return 0;
170 }
171
172 @Override
173 public FlowId id() {
174 final long id = baseValue + 55;
175 return FlowId.valueOf(id);
176 }
177
178 @Override
alshabib08d98982015-04-21 16:25:50 -0700179 public GroupId groupId() {
180 return new DefaultGroupId(3);
Ray Milkey4f5de002014-12-17 19:26:11 -0800181 }
182
183 @Override
alshabib08d98982015-04-21 16:25:50 -0700184 public short appId() {
185 return 2;
Ray Milkey4f5de002014-12-17 19:26:11 -0800186 }
187
188 @Override
189 public int priority() {
190 return (int) (baseValue + 66);
191 }
192
193 @Override
194 public DeviceId deviceId() {
195 return deviceId;
196 }
197
198 @Override
199 public TrafficSelector selector() {
200 return selector;
201 }
202
203 @Override
204 public TrafficTreatment treatment() {
205 return treatment;
206 }
207
208 @Override
209 public int timeout() {
210 return (int) (baseValue + 77);
211 }
212
213 @Override
214 public boolean isPermanent() {
215 return false;
216 }
sangho11c30ac2015-01-22 14:30:55 -0800217
Yuta HIGUCHI32a53c52015-02-08 01:25:40 -0800218 @Override
alshabibdb774072015-04-20 13:13:51 -0700219 public int tableId() {
220 return 0;
221 }
jcc3d4e14a2015-04-21 11:32:05 +0800222
223 @Override
Jonathan Hartf44e42c2015-08-04 09:58:46 -0700224 public boolean exactMatch(FlowRule rule) {
225 return false;
226 }
227
228 @Override
jcc3d4e14a2015-04-21 11:32:05 +0800229 public FlowRuleExtPayLoad payLoad() {
230 return null;
231 }
Ray Milkey4f5de002014-12-17 19:26:11 -0800232 }
233
Ray Milkey4f5de002014-12-17 19:26:11 -0800234 /**
Jian Li2907ad22016-05-12 23:08:54 -0700235 * Mock class for a flow rule.
236 */
237 private static class MockFlowRule implements FlowRule {
238
239 final DeviceId deviceId;
240 final long baseValue;
241 TrafficTreatment treatment;
242 TrafficSelector selector;
243
244 public MockFlowRule(DeviceId deviceId, long id) {
245 this.deviceId = deviceId;
246 this.baseValue = id * 100;
247 }
248
249 @Override
250 public FlowId id() {
251 final long id = baseValue + 55;
252 return FlowId.valueOf(id);
253 }
254
255 @Override
256 public short appId() {
257 return 4;
258 }
259
260 @Override
261 public GroupId groupId() {
262 return new DefaultGroupId(3);
263 }
264
265 @Override
266 public int priority() {
267 return 0;
268 }
269
270 @Override
271 public DeviceId deviceId() {
272 return deviceId;
273 }
274
275 @Override
276 public TrafficSelector selector() {
277 return selector;
278 }
279
280 @Override
281 public TrafficTreatment treatment() {
282 return treatment;
283 }
284
285 @Override
286 public int timeout() {
287 return (int) (baseValue + 77);
288 }
289
290 @Override
291 public boolean isPermanent() {
292 return false;
293 }
294
295 @Override
296 public int tableId() {
297 return 0;
298 }
299
300 @Override
301 public boolean exactMatch(FlowRule rule) {
302 return false;
303 }
304
305 @Override
306 public FlowRuleExtPayLoad payLoad() {
307 return null;
308 }
309 }
310
311 /**
Ray Milkey4f5de002014-12-17 19:26:11 -0800312 * Populates some flows used as testing data.
313 */
314 private void setupMockFlows() {
315 flow2.treatment = DefaultTrafficTreatment.builder()
Ray Milkey4f5de002014-12-17 19:26:11 -0800316 .setEthDst(MacAddress.BROADCAST)
317 .build();
318 flow2.selector = DefaultTrafficSelector.builder()
319 .matchEthType((short) 3)
320 .matchIPProtocol((byte) 9)
321 .build();
322 flow4.treatment = DefaultTrafficTreatment.builder()
Ray Milkey4f5de002014-12-17 19:26:11 -0800323 .build();
324 final Set<FlowEntry> flows1 = new HashSet<>();
325 flows1.add(flow1);
326 flows1.add(flow2);
327
328 final Set<FlowEntry> flows2 = new HashSet<>();
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700329 flows2.add(flow3);
330 flows2.add(flow4);
Ray Milkey4f5de002014-12-17 19:26:11 -0800331
332 rules.put(deviceId1, flows1);
333 rules.put(deviceId2, flows2);
334
335 expect(mockFlowService.getFlowEntries(deviceId1))
336 .andReturn(rules.get(deviceId1)).anyTimes();
337 expect(mockFlowService.getFlowEntries(deviceId2))
338 .andReturn(rules.get(deviceId2)).anyTimes();
339 }
340
341 /**
Jian Li2907ad22016-05-12 23:08:54 -0700342 * Populates some flow rules used as testing data.
343 */
344 private void setupMockFlowRules() {
345 flowRule2.treatment = DefaultTrafficTreatment.builder()
346 .setEthDst(MacAddress.BROADCAST)
347 .build();
348 flowRule2.selector = DefaultTrafficSelector.builder()
349 .matchEthType((short) 3)
350 .matchIPProtocol((byte) 9)
351 .build();
352 flowRule4.treatment = DefaultTrafficTreatment.builder()
353 .build();
354
355 flowRules.add(flowRule1);
356 flowRules.add(flowRule2);
357 flowRules.add(flowRule3);
358 flowRules.add(flowRule4);
359 }
360
361 /**
Ray Milkey4f5de002014-12-17 19:26:11 -0800362 * Sets up the global values for all the tests.
363 */
364 @Before
Ray Milkeyed0b1662015-02-05 09:34:29 -0800365 public void setUpTest() {
Ray Milkey4f5de002014-12-17 19:26:11 -0800366 // Mock device service
367 expect(mockDeviceService.getDevice(deviceId1))
368 .andReturn(device1);
369 expect(mockDeviceService.getDevice(deviceId2))
370 .andReturn(device2);
371 expect(mockDeviceService.getDevices())
372 .andReturn(ImmutableSet.of(device1, device2));
373
Ray Milkeyd43fe452015-05-29 09:35:12 -0700374 // Mock Core Service
375 expect(mockCoreService.getAppId(anyShort()))
376 .andReturn(NetTestTools.APP_ID).anyTimes();
Jian Li2907ad22016-05-12 23:08:54 -0700377 expect(mockCoreService.getAppId(anyString()))
378 .andReturn(NetTestTools.APP_ID).anyTimes();
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700379 expect(mockCoreService.registerApplication(FlowRuleCodec.REST_APP_ID))
380 .andReturn(APP_ID).anyTimes();
Ray Milkeyd43fe452015-05-29 09:35:12 -0700381 replay(mockCoreService);
382
Ray Milkey4f5de002014-12-17 19:26:11 -0800383 // Register the services needed for the test
384 final CodecManager codecService = new CodecManager();
385 codecService.activate();
386 ServiceDirectory testDirectory =
387 new TestServiceDirectory()
388 .add(FlowRuleService.class, mockFlowService)
389 .add(DeviceService.class, mockDeviceService)
Ray Milkeyd43fe452015-05-29 09:35:12 -0700390 .add(CodecService.class, codecService)
Jian Li2907ad22016-05-12 23:08:54 -0700391 .add(CoreService.class, mockCoreService)
392 .add(ApplicationService.class, mockApplicationService);
Ray Milkey4f5de002014-12-17 19:26:11 -0800393
394 BaseResource.setServiceDirectory(testDirectory);
395 }
396
397 /**
398 * Cleans up and verifies the mocks.
Ray Milkey4f5de002014-12-17 19:26:11 -0800399 */
400 @After
Ray Milkeyed0b1662015-02-05 09:34:29 -0800401 public void tearDownTest() {
Ray Milkey4f5de002014-12-17 19:26:11 -0800402 verify(mockFlowService);
Ray Milkeyd43fe452015-05-29 09:35:12 -0700403 verify(mockCoreService);
Ray Milkey4f5de002014-12-17 19:26:11 -0800404 }
405
406 /**
407 * Hamcrest matcher to check that a flow representation in JSON matches
408 * the actual flow entry.
409 */
Jian Li2907ad22016-05-12 23:08:54 -0700410 public static class FlowEntryJsonMatcher extends TypeSafeMatcher<JsonObject> {
Ray Milkey4f5de002014-12-17 19:26:11 -0800411 private final FlowEntry flow;
Ray Milkeybe539db2015-09-04 11:00:43 -0700412 private final String expectedAppId;
Ray Milkey4f5de002014-12-17 19:26:11 -0800413 private String reason = "";
414
Jian Li2907ad22016-05-12 23:08:54 -0700415 public FlowEntryJsonMatcher(FlowEntry flowValue, String expectedAppIdValue) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800416 flow = flowValue;
Ray Milkeybe539db2015-09-04 11:00:43 -0700417 expectedAppId = expectedAppIdValue;
Ray Milkey4f5de002014-12-17 19:26:11 -0800418 }
419
420 @Override
421 public boolean matchesSafely(JsonObject jsonFlow) {
422 // check id
423 final String jsonId = jsonFlow.get("id").asString();
424 final String flowId = Long.toString(flow.id().value());
425 if (!jsonId.equals(flowId)) {
426 reason = "id " + flow.id().toString();
427 return false;
428 }
429
430 // check application id
Ray Milkeybe539db2015-09-04 11:00:43 -0700431 final String jsonAppId = jsonFlow.get("appId").asString();
432 if (!jsonAppId.equals(expectedAppId)) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800433 reason = "appId " + Short.toString(flow.appId());
434 return false;
435 }
436
437 // check device id
438 final String jsonDeviceId = jsonFlow.get("deviceId").asString();
439 if (!jsonDeviceId.equals(flow.deviceId().toString())) {
440 reason = "deviceId " + flow.deviceId();
441 return false;
442 }
443
444 // check treatment and instructions array
445 if (flow.treatment() != null) {
446 final JsonObject jsonTreatment = jsonFlow.get("treatment").asObject();
447 final JsonArray jsonInstructions = jsonTreatment.get("instructions").asArray();
Ray Milkey42507352015-03-20 15:16:10 -0700448 if (flow.treatment().immediate().size() != jsonInstructions.size()) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800449 reason = "instructions array size of " +
Ray Milkey42507352015-03-20 15:16:10 -0700450 Integer.toString(flow.treatment().immediate().size());
Ray Milkey4f5de002014-12-17 19:26:11 -0800451 return false;
452 }
Ray Milkey42507352015-03-20 15:16:10 -0700453 for (final Instruction instruction : flow.treatment().immediate()) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800454 boolean instructionFound = false;
Ray Milkey4f5de002014-12-17 19:26:11 -0800455 for (int instructionIndex = 0; instructionIndex < jsonInstructions.size(); instructionIndex++) {
Ray Milkeyc95bb9d2015-01-06 10:28:24 -0800456 final String jsonType =
457 jsonInstructions.get(instructionIndex)
458 .asObject().get("type").asString();
459 final String instructionType = instruction.type().name();
460 if (jsonType.equals(instructionType)) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800461 instructionFound = true;
462 }
463 }
464 if (!instructionFound) {
Ray Milkeyc95bb9d2015-01-06 10:28:24 -0800465 reason = "instruction " + instruction.toString();
Ray Milkey4f5de002014-12-17 19:26:11 -0800466 return false;
467 }
468 }
469 }
470
471 // check selector and criteria array
472 if (flow.selector() != null) {
473 final JsonObject jsonTreatment = jsonFlow.get("selector").asObject();
474 final JsonArray jsonCriteria = jsonTreatment.get("criteria").asArray();
475 if (flow.selector().criteria().size() != jsonCriteria.size()) {
476 reason = "criteria array size of " +
477 Integer.toString(flow.selector().criteria().size());
478 return false;
479 }
480 for (final Criterion criterion : flow.selector().criteria()) {
481 boolean criterionFound = false;
Ray Milkeyc95bb9d2015-01-06 10:28:24 -0800482
Ray Milkey4f5de002014-12-17 19:26:11 -0800483 for (int criterionIndex = 0; criterionIndex < jsonCriteria.size(); criterionIndex++) {
Ray Milkeyc95bb9d2015-01-06 10:28:24 -0800484 final String jsonType =
485 jsonCriteria.get(criterionIndex)
486 .asObject().get("type").asString();
487 final String criterionType = criterion.type().name();
488 if (jsonType.equals(criterionType)) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800489 criterionFound = true;
490 }
491 }
492 if (!criterionFound) {
Ray Milkeyc95bb9d2015-01-06 10:28:24 -0800493 reason = "criterion " + criterion.toString();
Ray Milkey4f5de002014-12-17 19:26:11 -0800494 return false;
495 }
496 }
497 }
498
499 return true;
500 }
501
502 @Override
503 public void describeTo(Description description) {
504 description.appendText(reason);
505 }
506 }
507
508 /**
509 * Factory to allocate a flow matcher.
510 *
511 * @param flow flow object we are looking for
512 * @return matcher
513 */
Jian Li2907ad22016-05-12 23:08:54 -0700514 private static FlowEntryJsonMatcher matchesFlow(FlowEntry flow, String expectedAppName) {
515 return new FlowEntryJsonMatcher(flow, expectedAppName);
Ray Milkey4f5de002014-12-17 19:26:11 -0800516 }
517
518 /**
519 * Hamcrest matcher to check that a flow is represented properly in a JSON
520 * array of flows.
521 */
Jian Li2907ad22016-05-12 23:08:54 -0700522 public static class FlowEntryJsonArrayMatcher extends TypeSafeMatcher<JsonArray> {
Ray Milkey4f5de002014-12-17 19:26:11 -0800523 private final FlowEntry flow;
524 private String reason = "";
525
Jian Li2907ad22016-05-12 23:08:54 -0700526 public FlowEntryJsonArrayMatcher(FlowEntry flowValue) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800527 flow = flowValue;
528 }
529
530 @Override
531 public boolean matchesSafely(JsonArray json) {
532 boolean flowFound = false;
533
534 for (int jsonFlowIndex = 0; jsonFlowIndex < json.size();
535 jsonFlowIndex++) {
536
537 final JsonObject jsonFlow = json.get(jsonFlowIndex).asObject();
538
539 final String flowId = Long.toString(flow.id().value());
540 final String jsonFlowId = jsonFlow.get("id").asString();
541 if (jsonFlowId.equals(flowId)) {
542 flowFound = true;
543
544 // We found the correct flow, check attribute values
Ray Milkeybe539db2015-09-04 11:00:43 -0700545 assertThat(jsonFlow, matchesFlow(flow, APP_ID.name()));
Ray Milkey4f5de002014-12-17 19:26:11 -0800546 }
547 }
548 if (!flowFound) {
549 reason = "Flow with id " + flow.id().toString() + " not found";
550 return false;
551 } else {
552 return true;
553 }
554 }
555
556 @Override
557 public void describeTo(Description description) {
558 description.appendText(reason);
559 }
560 }
561
562 /**
563 * Factory to allocate a flow array matcher.
564 *
565 * @param flow flow object we are looking for
566 * @return matcher
567 */
Jian Li2907ad22016-05-12 23:08:54 -0700568 private static FlowEntryJsonArrayMatcher hasFlow(FlowEntry flow) {
569 return new FlowEntryJsonArrayMatcher(flow);
570 }
571
572 /**
573 * Hamcrest matcher to check that a flow representation in JSON matches
574 * the actual flow rule.
575 */
576 public static class FlowRuleJsonMatcher extends TypeSafeMatcher<JsonObject> {
577 private final FlowRule flow;
578 private final String expectedAppId;
579 private String reason = "";
580
581 public FlowRuleJsonMatcher(FlowRule flowValue, String expectedAppIdValue) {
582 flow = flowValue;
583 expectedAppId = expectedAppIdValue;
584 }
585
586 @Override
587 public boolean matchesSafely(JsonObject jsonFlow) {
588 // check id
589 final String jsonId = jsonFlow.get("id").asString();
590 final String flowId = Long.toString(flow.id().value());
591 if (!jsonId.equals(flowId)) {
592 reason = "id " + flow.id().toString();
593 return false;
594 }
595
596 // check application id
597 final String jsonAppId = jsonFlow.get("appId").asString();
598 if (!jsonAppId.equals(expectedAppId)) {
599 reason = "appId " + Short.toString(flow.appId());
600 return false;
601 }
602
603 // check device id
604 final String jsonDeviceId = jsonFlow.get("deviceId").asString();
605 if (!jsonDeviceId.equals(flow.deviceId().toString())) {
606 reason = "deviceId " + flow.deviceId();
607 return false;
608 }
609
610 // check treatment and instructions array
611 if (flow.treatment() != null) {
612 final JsonObject jsonTreatment = jsonFlow.get("treatment").asObject();
613 final JsonArray jsonInstructions = jsonTreatment.get("instructions").asArray();
614 if (flow.treatment().immediate().size() != jsonInstructions.size()) {
615 reason = "instructions array size of " +
616 Integer.toString(flow.treatment().immediate().size());
617 return false;
618 }
619 for (final Instruction instruction : flow.treatment().immediate()) {
620 boolean instructionFound = false;
621 for (int instructionIndex = 0; instructionIndex < jsonInstructions.size(); instructionIndex++) {
622 final String jsonType =
623 jsonInstructions.get(instructionIndex)
624 .asObject().get("type").asString();
625 final String instructionType = instruction.type().name();
626 if (jsonType.equals(instructionType)) {
627 instructionFound = true;
628 }
629 }
630 if (!instructionFound) {
631 reason = "instruction " + instruction.toString();
632 return false;
633 }
634 }
635 }
636
637 // check selector and criteria array
638 if (flow.selector() != null) {
639 final JsonObject jsonTreatment = jsonFlow.get("selector").asObject();
640 final JsonArray jsonCriteria = jsonTreatment.get("criteria").asArray();
641 if (flow.selector().criteria().size() != jsonCriteria.size()) {
642 reason = "criteria array size of " +
643 Integer.toString(flow.selector().criteria().size());
644 return false;
645 }
646 for (final Criterion criterion : flow.selector().criteria()) {
647 boolean criterionFound = false;
648
649 for (int criterionIndex = 0; criterionIndex < jsonCriteria.size(); criterionIndex++) {
650 final String jsonType =
651 jsonCriteria.get(criterionIndex)
652 .asObject().get("type").asString();
653 final String criterionType = criterion.type().name();
654 if (jsonType.equals(criterionType)) {
655 criterionFound = true;
656 }
657 }
658 if (!criterionFound) {
659 reason = "criterion " + criterion.toString();
660 return false;
661 }
662 }
663 }
664
665 return true;
666 }
667
668 @Override
669 public void describeTo(Description description) {
670 description.appendText(reason);
671 }
672 }
673
674 /**
675 * Factory to allocate a flow matcher.
676 *
677 * @param flow flow rule object we are looking for
678 * @return matcher
679 */
680 private static FlowRuleJsonMatcher matchesFlowRule(FlowRule flow, String expectedAppName) {
681 return new FlowRuleJsonMatcher(flow, expectedAppName);
682 }
683
684 /**
685 * Hamcrest matcher to check that a flow is represented properly in a JSON
686 * array of flow rules.
687 */
688 public static class FlowRuleJsonArrayMatcher extends TypeSafeMatcher<JsonArray> {
689 private final FlowRule flow;
690 private String reason = "";
691
692 public FlowRuleJsonArrayMatcher(FlowRule flowValue) {
693 flow = flowValue;
694 }
695
696 @Override
697 public boolean matchesSafely(JsonArray json) {
698 boolean flowFound = false;
699
700 for (int jsonFlowIndex = 0; jsonFlowIndex < json.size();
701 jsonFlowIndex++) {
702
703 final JsonObject jsonFlow = json.get(jsonFlowIndex).asObject();
704
705 final String flowId = Long.toString(flow.id().value());
706 final String jsonFlowId = jsonFlow.get("id").asString();
707 if (jsonFlowId.equals(flowId)) {
708 flowFound = true;
709
710 // We found the correct flow, check attribute values
711 assertThat(jsonFlow, matchesFlowRule(flow, APP_ID.name()));
712 }
713 }
714 if (!flowFound) {
715 reason = "Flow with id " + flow.id().toString() + " not found";
716 return false;
717 } else {
718 return true;
719 }
720 }
721
722 @Override
723 public void describeTo(Description description) {
724 description.appendText(reason);
725 }
726 }
727
728 /**
729 * Factory to allocate a flow array matcher.
730 *
731 * @param flow flow rule object we are looking for
732 * @return matcher
733 */
734 private static FlowRuleJsonArrayMatcher hasFlowRule(FlowRule flow) {
735 return new FlowRuleJsonArrayMatcher(flow);
Ray Milkey4f5de002014-12-17 19:26:11 -0800736 }
737
738 /**
739 * Tests the result of the rest api GET when there are no flows.
740 */
741 @Test
742 public void testFlowsEmptyArray() {
743 expect(mockFlowService.getFlowEntries(deviceId1))
744 .andReturn(null).anyTimes();
745 expect(mockFlowService.getFlowEntries(deviceId2))
746 .andReturn(null).anyTimes();
747 replay(mockFlowService);
748 replay(mockDeviceService);
Jian Li9d616492016-03-09 10:52:49 -0800749 final WebTarget wt = target();
750 final String response = wt.path("flows").request().get(String.class);
Ray Milkey4f5de002014-12-17 19:26:11 -0800751 assertThat(response, is("{\"flows\":[]}"));
752 }
753
754 /**
755 * Tests the result of the rest api GET when there are active flows.
756 */
757 @Test
758 public void testFlowsPopulatedArray() {
759 setupMockFlows();
760 replay(mockFlowService);
761 replay(mockDeviceService);
Jian Li9d616492016-03-09 10:52:49 -0800762 final WebTarget wt = target();
763 final String response = wt.path("flows").request().get(String.class);
Jian Li80cfe452016-01-14 16:04:58 -0800764 final JsonObject result = Json.parse(response).asObject();
Ray Milkey4f5de002014-12-17 19:26:11 -0800765 assertThat(result, notNullValue());
766
767 assertThat(result.names(), hasSize(1));
768 assertThat(result.names().get(0), is("flows"));
769 final JsonArray jsonFlows = result.get("flows").asArray();
770 assertThat(jsonFlows, notNullValue());
771 assertThat(jsonFlows, hasFlow(flow1));
772 assertThat(jsonFlows, hasFlow(flow2));
773 assertThat(jsonFlows, hasFlow(flow3));
774 assertThat(jsonFlows, hasFlow(flow4));
775 }
776
777 /**
778 * Tests the result of a rest api GET for a device.
779 */
780 @Test
781 public void testFlowsSingleDevice() {
782 setupMockFlows();
783 final Set<FlowEntry> flows = new HashSet<>();
784 flows.add(flow5);
785 flows.add(flow6);
786 expect(mockFlowService.getFlowEntries(anyObject()))
787 .andReturn(flows).anyTimes();
788 replay(mockFlowService);
789 replay(mockDeviceService);
Jian Li9d616492016-03-09 10:52:49 -0800790 final WebTarget wt = target();
791 final String response = wt.path("flows/" + deviceId3).request().get(String.class);
Jian Li80cfe452016-01-14 16:04:58 -0800792 final JsonObject result = Json.parse(response).asObject();
Ray Milkey4f5de002014-12-17 19:26:11 -0800793 assertThat(result, notNullValue());
794
795 assertThat(result.names(), hasSize(1));
796 assertThat(result.names().get(0), is("flows"));
797 final JsonArray jsonFlows = result.get("flows").asArray();
798 assertThat(jsonFlows, notNullValue());
799 assertThat(jsonFlows, hasFlow(flow5));
800 assertThat(jsonFlows, hasFlow(flow6));
801 }
802
803 /**
804 * Tests the result of a rest api GET for a device.
805 */
806 @Test
807 public void testFlowsSingleDeviceWithFlowId() {
808 setupMockFlows();
809 final Set<FlowEntry> flows = new HashSet<>();
810 flows.add(flow5);
811 flows.add(flow6);
812 expect(mockFlowService.getFlowEntries(anyObject()))
813 .andReturn(flows).anyTimes();
814 replay(mockFlowService);
815 replay(mockDeviceService);
Jian Li9d616492016-03-09 10:52:49 -0800816 final WebTarget wt = target();
817 final String response = wt.path("flows/" + deviceId3 + "/"
818 + Long.toString(flow5.id().value())).request().get(String.class);
Jian Li80cfe452016-01-14 16:04:58 -0800819 final JsonObject result = Json.parse(response).asObject();
Ray Milkey4f5de002014-12-17 19:26:11 -0800820 assertThat(result, notNullValue());
821
822 assertThat(result.names(), hasSize(1));
823 assertThat(result.names().get(0), is("flows"));
824 final JsonArray jsonFlows = result.get("flows").asArray();
825 assertThat(jsonFlows, notNullValue());
826 assertThat(jsonFlows, hasFlow(flow5));
827 assertThat(jsonFlows, not(hasFlow(flow6)));
828 }
829
830 /**
831 * Tests that a fetch of a non-existent device object throws an exception.
832 */
833 @Test
834 public void testBadGet() {
Jian Li9d616492016-03-09 10:52:49 -0800835 expect(mockFlowService.getFlowEntries(anyObject()))
Ray Milkey4f5de002014-12-17 19:26:11 -0800836 .andReturn(null).anyTimes();
837 replay(mockFlowService);
838 replay(mockDeviceService);
839
Jian Li9d616492016-03-09 10:52:49 -0800840 WebTarget wt = target();
Ray Milkey4f5de002014-12-17 19:26:11 -0800841 try {
Jian Li9d616492016-03-09 10:52:49 -0800842 wt.path("flows/0").request().get(String.class);
Ray Milkey4f5de002014-12-17 19:26:11 -0800843 fail("Fetch of non-existent device did not throw an exception");
Jian Li9d616492016-03-09 10:52:49 -0800844 } catch (NotFoundException ex) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800845 assertThat(ex.getMessage(),
Jian Li9d616492016-03-09 10:52:49 -0800846 containsString("HTTP 404 Not Found"));
Ray Milkey4f5de002014-12-17 19:26:11 -0800847 }
848 }
Ray Milkeyd43fe452015-05-29 09:35:12 -0700849
850 /**
851 * Tests creating a flow with POST.
852 */
853 @Test
Jian Li2907ad22016-05-12 23:08:54 -0700854 public void testPostWithoutAppId() {
Ray Milkeyd43fe452015-05-29 09:35:12 -0700855 mockFlowService.applyFlowRules(anyObject());
856 expectLastCall();
857 replay(mockFlowService);
858
Jian Li9d616492016-03-09 10:52:49 -0800859 WebTarget wt = target();
Phaneendra Manda2be2f882015-11-11 15:23:40 +0530860 InputStream jsonStream = FlowsResourceTest.class
Ray Milkeyb82c42b2015-06-30 09:42:20 -0700861 .getResourceAsStream("post-flow.json");
Ray Milkeyd43fe452015-05-29 09:35:12 -0700862
Jian Li9d616492016-03-09 10:52:49 -0800863 Response response = wt.path("flows/of:0000000000000001")
864 .request(MediaType.APPLICATION_JSON_TYPE)
865 .post(Entity.json(jsonStream));
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700866 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
867 String location = response.getLocation().getPath();
868 assertThat(location, Matchers.startsWith("/flows/of:0000000000000001/"));
869 }
870
871 /**
Jian Li2907ad22016-05-12 23:08:54 -0700872 * Tests creating a flow with POST while specifying application identifier.
873 */
874 @Test
875 public void testPostWithAppId() {
876 mockFlowService.applyFlowRules(anyObject());
877 expectLastCall();
878 replay(mockFlowService);
879
880 WebTarget wt = target();
881 InputStream jsonStream = FlowsResourceTest.class
882 .getResourceAsStream("post-flow.json");
883
884 Response response = wt.path("flows/of:0000000000000001")
885 .queryParam("appId", "org.onosproject.rest")
886 .request(MediaType.APPLICATION_JSON_TYPE)
887 .post(Entity.json(jsonStream));
888 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
889 String location = response.getLocation().getPath();
890 assertThat(location, Matchers.startsWith("/flows/of:0000000000000001/"));
891 }
892
893 /**
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700894 * Tests deleting a flow.
895 */
896 @Test
897 public void testDelete() {
898 setupMockFlows();
899 mockFlowService.removeFlowRules(anyObject());
900 expectLastCall();
901 replay(mockFlowService);
902
Jian Li9d616492016-03-09 10:52:49 -0800903 WebTarget wt = target();
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700904
905 String location = "/flows/1/155";
906
Jian Li9d616492016-03-09 10:52:49 -0800907 Response deleteResponse = wt.path(location)
908 .request(MediaType.APPLICATION_JSON_TYPE)
909 .delete();
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700910 assertThat(deleteResponse.getStatus(),
911 is(HttpURLConnection.HTTP_NO_CONTENT));
Ray Milkeyd43fe452015-05-29 09:35:12 -0700912 }
Jian Li2907ad22016-05-12 23:08:54 -0700913
914 /**
915 * Tests the result of a rest api GET for an application.
916 */
917 @Test
918 public void testGetFlowByAppId() {
919 setupMockFlowRules();
920
921 expect(mockApplicationService.getId(anyObject())).andReturn(APP_ID).anyTimes();
922 replay(mockApplicationService);
923
924 expect(mockFlowService.getFlowRulesById(APP_ID)).andReturn(flowRules).anyTimes();
925 replay(mockFlowService);
926
927 final WebTarget wt = target();
928 final String response = wt.path("flows/application/1").request().get(String.class);
929 final JsonObject result = Json.parse(response).asObject();
930 assertThat(result, notNullValue());
931
932 assertThat(result.names(), hasSize(1));
933 assertThat(result.names().get(0), is("flows"));
934 final JsonArray jsonFlows = result.get("flows").asArray();
935 assertThat(jsonFlows, notNullValue());
936 assertThat(jsonFlows, hasFlowRule(flowRule1));
937 assertThat(jsonFlows, hasFlowRule(flowRule2));
938 assertThat(jsonFlows, hasFlowRule(flowRule3));
939 assertThat(jsonFlows, hasFlowRule(flowRule4));
940 }
941
942 /**
943 * Tests the result of a rest api DELETE for an application.
944 */
945 @Test
946 public void testRemoveFlowByAppId() {
947 expect(mockApplicationService.getId(anyObject())).andReturn(APP_ID).anyTimes();
948 replay(mockApplicationService);
949
950 mockFlowService.removeFlowRulesById(APP_ID);
951 expectLastCall();
952 replay(mockFlowService);
953
954 WebTarget wt = target();
955
956 String location = "/flows/application/1";
957
958 Response deleteResponse = wt.path(location)
959 .request()
960 .delete();
961 assertThat(deleteResponse.getStatus(),
962 is(HttpURLConnection.HTTP_NO_CONTENT));
963 }
Ray Milkey4f5de002014-12-17 19:26:11 -0800964}