blob: 74defed198853df108b3bf98639b678b1592a533 [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 Milkeyd43fe452015-05-29 09:35:12 -070036import org.onosproject.core.CoreService;
Ray Milkey4f5de002014-12-17 19:26:11 -080037import org.onosproject.core.DefaultGroupId;
38import org.onosproject.core.GroupId;
39import org.onosproject.net.DefaultDevice;
40import org.onosproject.net.Device;
41import org.onosproject.net.DeviceId;
Ray Milkeyd43fe452015-05-29 09:35:12 -070042import org.onosproject.net.NetTestTools;
Ray Milkey4f5de002014-12-17 19:26:11 -080043import org.onosproject.net.device.DeviceService;
44import org.onosproject.net.flow.DefaultTrafficSelector;
45import org.onosproject.net.flow.DefaultTrafficTreatment;
46import org.onosproject.net.flow.FlowEntry;
47import org.onosproject.net.flow.FlowId;
Jonathan Hartf44e42c2015-08-04 09:58:46 -070048import org.onosproject.net.flow.FlowRule;
jcc3d4e14a2015-04-21 11:32:05 +080049import org.onosproject.net.flow.FlowRuleExtPayLoad;
Ray Milkey4f5de002014-12-17 19:26:11 -080050import org.onosproject.net.flow.FlowRuleService;
51import org.onosproject.net.flow.TrafficSelector;
52import org.onosproject.net.flow.TrafficTreatment;
53import org.onosproject.net.flow.criteria.Criterion;
54import org.onosproject.net.flow.instructions.Instruction;
jcc3d4e14a2015-04-21 11:32:05 +080055
Jian Li9d616492016-03-09 10:52:49 -080056import javax.ws.rs.NotFoundException;
57import javax.ws.rs.client.Entity;
58import javax.ws.rs.client.WebTarget;
Jonathan Hartf44e42c2015-08-04 09:58:46 -070059import javax.ws.rs.core.MediaType;
Jian Li9d616492016-03-09 10:52:49 -080060import javax.ws.rs.core.Response;
Jonathan Hartf44e42c2015-08-04 09:58:46 -070061import java.io.InputStream;
62import java.net.HttpURLConnection;
63import java.util.HashMap;
64import java.util.HashSet;
65import java.util.Set;
Thiago Santos877914d2016-07-20 18:29:29 -030066import java.util.concurrent.TimeUnit;
Ray Milkey4f5de002014-12-17 19:26:11 -080067
Thiago Santos877914d2016-07-20 18:29:29 -030068import static java.util.concurrent.TimeUnit.SECONDS;
Ray Milkey4f5de002014-12-17 19:26:11 -080069import static org.easymock.EasyMock.anyObject;
Ray Milkeyd43fe452015-05-29 09:35:12 -070070import static org.easymock.EasyMock.anyShort;
Jian Li2907ad22016-05-12 23:08:54 -070071import static org.easymock.EasyMock.anyString;
Ray Milkey4f5de002014-12-17 19:26:11 -080072import static org.easymock.EasyMock.createMock;
73import static org.easymock.EasyMock.expect;
Ray Milkeyd43fe452015-05-29 09:35:12 -070074import static org.easymock.EasyMock.expectLastCall;
Ray Milkey4f5de002014-12-17 19:26:11 -080075import static org.easymock.EasyMock.replay;
76import static org.easymock.EasyMock.verify;
77import static org.hamcrest.Matchers.containsString;
78import static org.hamcrest.Matchers.hasSize;
79import static org.hamcrest.Matchers.is;
80import static org.hamcrest.Matchers.not;
81import static org.hamcrest.Matchers.notNullValue;
82import static org.junit.Assert.assertThat;
83import static org.junit.Assert.fail;
Ray Milkeyeb5c7172015-06-23 14:59:27 -070084import static org.onosproject.net.NetTestTools.APP_ID;
Ray Milkey4f5de002014-12-17 19:26:11 -080085
86/**
87 * Unit tests for Flows REST APIs.
88 */
Ray Milkey9c3d3362015-01-28 10:39:56 -080089public class FlowsResourceTest extends ResourceTest {
Ray Milkey4f5de002014-12-17 19:26:11 -080090 final FlowRuleService mockFlowService = createMock(FlowRuleService.class);
Ray Milkeyd43fe452015-05-29 09:35:12 -070091 CoreService mockCoreService = createMock(CoreService.class);
92
Ray Milkey4f5de002014-12-17 19:26:11 -080093 final HashMap<DeviceId, Set<FlowEntry>> rules = new HashMap<>();
94
95 final DeviceService mockDeviceService = createMock(DeviceService.class);
96
97 final DeviceId deviceId1 = DeviceId.deviceId("1");
98 final DeviceId deviceId2 = DeviceId.deviceId("2");
99 final DeviceId deviceId3 = DeviceId.deviceId("3");
100 final Device device1 = new DefaultDevice(null, deviceId1, Device.Type.OTHER,
101 "", "", "", "", null);
102 final Device device2 = new DefaultDevice(null, deviceId2, Device.Type.OTHER,
103 "", "", "", "", null);
104
Jian Li2907ad22016-05-12 23:08:54 -0700105 final ApplicationService mockApplicationService = createMock(ApplicationService.class);
106
Ray Milkey4f5de002014-12-17 19:26:11 -0800107 final MockFlowEntry flow1 = new MockFlowEntry(deviceId1, 1);
108 final MockFlowEntry flow2 = new MockFlowEntry(deviceId1, 2);
109
110 final MockFlowEntry flow3 = new MockFlowEntry(deviceId2, 3);
111 final MockFlowEntry flow4 = new MockFlowEntry(deviceId2, 4);
112
113 final MockFlowEntry flow5 = new MockFlowEntry(deviceId2, 5);
114 final MockFlowEntry flow6 = new MockFlowEntry(deviceId2, 6);
115
Jian Li2907ad22016-05-12 23:08:54 -0700116 final MockFlowRule flowRule1 = new MockFlowRule(deviceId1, 1);
117 final MockFlowRule flowRule2 = new MockFlowRule(deviceId1, 2);
118
119 final MockFlowRule flowRule3 = new MockFlowRule(deviceId2, 3);
120 final MockFlowRule flowRule4 = new MockFlowRule(deviceId2, 4);
121
122 final Set<FlowRule> flowRules = Sets.newHashSet();
123
Ray Milkey4f5de002014-12-17 19:26:11 -0800124 /**
125 * Mock class for a flow entry.
126 */
127 private static class MockFlowEntry implements FlowEntry {
128 final DeviceId deviceId;
129 final long baseValue;
130 TrafficTreatment treatment;
131 TrafficSelector selector;
132
133 public MockFlowEntry(DeviceId deviceId, long id) {
134 this.deviceId = deviceId;
135 this.baseValue = id * 100;
136 }
137
138 @Override
139 public FlowEntryState state() {
140 return FlowEntryState.ADDED;
141 }
142
143 @Override
144 public long life() {
Thiago Santos877914d2016-07-20 18:29:29 -0300145 return life(SECONDS);
146 }
147
148 @Override
149 public long life(TimeUnit timeUnit) {
150 return SECONDS.convert(baseValue + 11, timeUnit);
Ray Milkey4f5de002014-12-17 19:26:11 -0800151 }
152
153 @Override
154 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
169 public int errType() {
170 return 0;
171 }
172
173 @Override
174 public int errCode() {
175 return 0;
176 }
177
178 @Override
179 public FlowId id() {
180 final long id = baseValue + 55;
181 return FlowId.valueOf(id);
182 }
183
184 @Override
alshabib08d98982015-04-21 16:25:50 -0700185 public GroupId groupId() {
186 return new DefaultGroupId(3);
Ray Milkey4f5de002014-12-17 19:26:11 -0800187 }
188
189 @Override
alshabib08d98982015-04-21 16:25:50 -0700190 public short appId() {
191 return 2;
Ray Milkey4f5de002014-12-17 19:26:11 -0800192 }
193
194 @Override
195 public int priority() {
196 return (int) (baseValue + 66);
197 }
198
199 @Override
200 public DeviceId deviceId() {
201 return deviceId;
202 }
203
204 @Override
205 public TrafficSelector selector() {
206 return selector;
207 }
208
209 @Override
210 public TrafficTreatment treatment() {
211 return treatment;
212 }
213
214 @Override
215 public int timeout() {
216 return (int) (baseValue + 77);
217 }
218
219 @Override
Murat Parlakisikc6759e82016-06-29 03:22:22 -0700220 public int hardTimeout() {
221 return 0;
222 }
223
224 @Override
225 public FlowRemoveReason reason() {
226 return FlowRemoveReason.NO_REASON;
227 }
228
229 @Override
Ray Milkey4f5de002014-12-17 19:26:11 -0800230 public boolean isPermanent() {
231 return false;
232 }
sangho11c30ac2015-01-22 14:30:55 -0800233
Yuta HIGUCHI32a53c52015-02-08 01:25:40 -0800234 @Override
alshabibdb774072015-04-20 13:13:51 -0700235 public int tableId() {
236 return 0;
237 }
jcc3d4e14a2015-04-21 11:32:05 +0800238
239 @Override
Jonathan Hartf44e42c2015-08-04 09:58:46 -0700240 public boolean exactMatch(FlowRule rule) {
241 return false;
242 }
243
244 @Override
jcc3d4e14a2015-04-21 11:32:05 +0800245 public FlowRuleExtPayLoad payLoad() {
246 return null;
247 }
Ray Milkey4f5de002014-12-17 19:26:11 -0800248 }
249
Ray Milkey4f5de002014-12-17 19:26:11 -0800250 /**
Jian Li2907ad22016-05-12 23:08:54 -0700251 * Mock class for a flow rule.
252 */
253 private static class MockFlowRule implements FlowRule {
254
255 final DeviceId deviceId;
256 final long baseValue;
257 TrafficTreatment treatment;
258 TrafficSelector selector;
259
260 public MockFlowRule(DeviceId deviceId, long id) {
261 this.deviceId = deviceId;
262 this.baseValue = id * 100;
263 }
264
265 @Override
266 public FlowId id() {
267 final long id = baseValue + 55;
268 return FlowId.valueOf(id);
269 }
270
271 @Override
272 public short appId() {
273 return 4;
274 }
275
276 @Override
277 public GroupId groupId() {
278 return new DefaultGroupId(3);
279 }
280
281 @Override
282 public int priority() {
283 return 0;
284 }
285
286 @Override
287 public DeviceId deviceId() {
288 return deviceId;
289 }
290
291 @Override
292 public TrafficSelector selector() {
293 return selector;
294 }
295
296 @Override
297 public TrafficTreatment treatment() {
298 return treatment;
299 }
300
301 @Override
302 public int timeout() {
303 return (int) (baseValue + 77);
304 }
305
306 @Override
Murat Parlakisikc6759e82016-06-29 03:22:22 -0700307 public int hardTimeout() {
308 return 0;
309 }
310
311 @Override
312 public FlowRemoveReason reason() {
313 return FlowRemoveReason.NO_REASON;
314 }
315
316 @Override
Jian Li2907ad22016-05-12 23:08:54 -0700317 public boolean isPermanent() {
318 return false;
319 }
320
321 @Override
322 public int tableId() {
323 return 0;
324 }
325
326 @Override
327 public boolean exactMatch(FlowRule rule) {
328 return false;
329 }
330
331 @Override
332 public FlowRuleExtPayLoad payLoad() {
333 return null;
334 }
335 }
336
337 /**
Ray Milkey4f5de002014-12-17 19:26:11 -0800338 * Populates some flows used as testing data.
339 */
340 private void setupMockFlows() {
341 flow2.treatment = DefaultTrafficTreatment.builder()
Ray Milkey4f5de002014-12-17 19:26:11 -0800342 .setEthDst(MacAddress.BROADCAST)
343 .build();
344 flow2.selector = DefaultTrafficSelector.builder()
345 .matchEthType((short) 3)
346 .matchIPProtocol((byte) 9)
347 .build();
348 flow4.treatment = DefaultTrafficTreatment.builder()
Ray Milkey4f5de002014-12-17 19:26:11 -0800349 .build();
350 final Set<FlowEntry> flows1 = new HashSet<>();
351 flows1.add(flow1);
352 flows1.add(flow2);
353
354 final Set<FlowEntry> flows2 = new HashSet<>();
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700355 flows2.add(flow3);
356 flows2.add(flow4);
Ray Milkey4f5de002014-12-17 19:26:11 -0800357
358 rules.put(deviceId1, flows1);
359 rules.put(deviceId2, flows2);
360
361 expect(mockFlowService.getFlowEntries(deviceId1))
362 .andReturn(rules.get(deviceId1)).anyTimes();
363 expect(mockFlowService.getFlowEntries(deviceId2))
364 .andReturn(rules.get(deviceId2)).anyTimes();
365 }
366
367 /**
Jian Li2907ad22016-05-12 23:08:54 -0700368 * Populates some flow rules used as testing data.
369 */
370 private void setupMockFlowRules() {
371 flowRule2.treatment = DefaultTrafficTreatment.builder()
372 .setEthDst(MacAddress.BROADCAST)
373 .build();
374 flowRule2.selector = DefaultTrafficSelector.builder()
375 .matchEthType((short) 3)
376 .matchIPProtocol((byte) 9)
377 .build();
378 flowRule4.treatment = DefaultTrafficTreatment.builder()
379 .build();
380
381 flowRules.add(flowRule1);
382 flowRules.add(flowRule2);
383 flowRules.add(flowRule3);
384 flowRules.add(flowRule4);
385 }
386
387 /**
Ray Milkey4f5de002014-12-17 19:26:11 -0800388 * Sets up the global values for all the tests.
389 */
390 @Before
Ray Milkeyed0b1662015-02-05 09:34:29 -0800391 public void setUpTest() {
Ray Milkey4f5de002014-12-17 19:26:11 -0800392 // Mock device service
393 expect(mockDeviceService.getDevice(deviceId1))
394 .andReturn(device1);
395 expect(mockDeviceService.getDevice(deviceId2))
396 .andReturn(device2);
397 expect(mockDeviceService.getDevices())
398 .andReturn(ImmutableSet.of(device1, device2));
399
Ray Milkeyd43fe452015-05-29 09:35:12 -0700400 // Mock Core Service
401 expect(mockCoreService.getAppId(anyShort()))
402 .andReturn(NetTestTools.APP_ID).anyTimes();
Jian Li2907ad22016-05-12 23:08:54 -0700403 expect(mockCoreService.getAppId(anyString()))
404 .andReturn(NetTestTools.APP_ID).anyTimes();
Jayasree Ghoshdba61112016-10-28 23:14:27 +0530405 expect(mockCoreService.registerApplication(anyString()))
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700406 .andReturn(APP_ID).anyTimes();
Ray Milkeyd43fe452015-05-29 09:35:12 -0700407 replay(mockCoreService);
408
Ray Milkey4f5de002014-12-17 19:26:11 -0800409 // Register the services needed for the test
410 final CodecManager codecService = new CodecManager();
411 codecService.activate();
412 ServiceDirectory testDirectory =
413 new TestServiceDirectory()
414 .add(FlowRuleService.class, mockFlowService)
415 .add(DeviceService.class, mockDeviceService)
Ray Milkeyd43fe452015-05-29 09:35:12 -0700416 .add(CodecService.class, codecService)
Jian Li2907ad22016-05-12 23:08:54 -0700417 .add(CoreService.class, mockCoreService)
418 .add(ApplicationService.class, mockApplicationService);
Ray Milkey4f5de002014-12-17 19:26:11 -0800419
420 BaseResource.setServiceDirectory(testDirectory);
421 }
422
423 /**
424 * Cleans up and verifies the mocks.
Ray Milkey4f5de002014-12-17 19:26:11 -0800425 */
426 @After
Ray Milkeyed0b1662015-02-05 09:34:29 -0800427 public void tearDownTest() {
Ray Milkey4f5de002014-12-17 19:26:11 -0800428 verify(mockFlowService);
Ray Milkeyd43fe452015-05-29 09:35:12 -0700429 verify(mockCoreService);
Ray Milkey4f5de002014-12-17 19:26:11 -0800430 }
431
432 /**
433 * Hamcrest matcher to check that a flow representation in JSON matches
434 * the actual flow entry.
435 */
Jian Li2907ad22016-05-12 23:08:54 -0700436 public static class FlowEntryJsonMatcher extends TypeSafeMatcher<JsonObject> {
Ray Milkey4f5de002014-12-17 19:26:11 -0800437 private final FlowEntry flow;
Ray Milkeybe539db2015-09-04 11:00:43 -0700438 private final String expectedAppId;
Ray Milkey4f5de002014-12-17 19:26:11 -0800439 private String reason = "";
440
Jian Li2907ad22016-05-12 23:08:54 -0700441 public FlowEntryJsonMatcher(FlowEntry flowValue, String expectedAppIdValue) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800442 flow = flowValue;
Ray Milkeybe539db2015-09-04 11:00:43 -0700443 expectedAppId = expectedAppIdValue;
Ray Milkey4f5de002014-12-17 19:26:11 -0800444 }
445
446 @Override
447 public boolean matchesSafely(JsonObject jsonFlow) {
448 // check id
449 final String jsonId = jsonFlow.get("id").asString();
450 final String flowId = Long.toString(flow.id().value());
451 if (!jsonId.equals(flowId)) {
452 reason = "id " + flow.id().toString();
453 return false;
454 }
455
456 // check application id
Ray Milkeybe539db2015-09-04 11:00:43 -0700457 final String jsonAppId = jsonFlow.get("appId").asString();
458 if (!jsonAppId.equals(expectedAppId)) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800459 reason = "appId " + Short.toString(flow.appId());
460 return false;
461 }
462
463 // check device id
464 final String jsonDeviceId = jsonFlow.get("deviceId").asString();
465 if (!jsonDeviceId.equals(flow.deviceId().toString())) {
466 reason = "deviceId " + flow.deviceId();
467 return false;
468 }
469
470 // check treatment and instructions array
471 if (flow.treatment() != null) {
472 final JsonObject jsonTreatment = jsonFlow.get("treatment").asObject();
473 final JsonArray jsonInstructions = jsonTreatment.get("instructions").asArray();
Ray Milkey42507352015-03-20 15:16:10 -0700474 if (flow.treatment().immediate().size() != jsonInstructions.size()) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800475 reason = "instructions array size of " +
Ray Milkey42507352015-03-20 15:16:10 -0700476 Integer.toString(flow.treatment().immediate().size());
Ray Milkey4f5de002014-12-17 19:26:11 -0800477 return false;
478 }
Ray Milkey42507352015-03-20 15:16:10 -0700479 for (final Instruction instruction : flow.treatment().immediate()) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800480 boolean instructionFound = false;
Ray Milkey4f5de002014-12-17 19:26:11 -0800481 for (int instructionIndex = 0; instructionIndex < jsonInstructions.size(); instructionIndex++) {
Ray Milkeyc95bb9d2015-01-06 10:28:24 -0800482 final String jsonType =
483 jsonInstructions.get(instructionIndex)
484 .asObject().get("type").asString();
485 final String instructionType = instruction.type().name();
486 if (jsonType.equals(instructionType)) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800487 instructionFound = true;
488 }
489 }
490 if (!instructionFound) {
Ray Milkeyc95bb9d2015-01-06 10:28:24 -0800491 reason = "instruction " + instruction.toString();
Ray Milkey4f5de002014-12-17 19:26:11 -0800492 return false;
493 }
494 }
495 }
496
497 // check selector and criteria array
498 if (flow.selector() != null) {
499 final JsonObject jsonTreatment = jsonFlow.get("selector").asObject();
500 final JsonArray jsonCriteria = jsonTreatment.get("criteria").asArray();
501 if (flow.selector().criteria().size() != jsonCriteria.size()) {
502 reason = "criteria array size of " +
503 Integer.toString(flow.selector().criteria().size());
504 return false;
505 }
506 for (final Criterion criterion : flow.selector().criteria()) {
507 boolean criterionFound = false;
Ray Milkeyc95bb9d2015-01-06 10:28:24 -0800508
Ray Milkey4f5de002014-12-17 19:26:11 -0800509 for (int criterionIndex = 0; criterionIndex < jsonCriteria.size(); criterionIndex++) {
Ray Milkeyc95bb9d2015-01-06 10:28:24 -0800510 final String jsonType =
511 jsonCriteria.get(criterionIndex)
512 .asObject().get("type").asString();
513 final String criterionType = criterion.type().name();
514 if (jsonType.equals(criterionType)) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800515 criterionFound = true;
516 }
517 }
518 if (!criterionFound) {
Ray Milkeyc95bb9d2015-01-06 10:28:24 -0800519 reason = "criterion " + criterion.toString();
Ray Milkey4f5de002014-12-17 19:26:11 -0800520 return false;
521 }
522 }
523 }
524
525 return true;
526 }
527
528 @Override
529 public void describeTo(Description description) {
530 description.appendText(reason);
531 }
532 }
533
534 /**
535 * Factory to allocate a flow matcher.
536 *
537 * @param flow flow object we are looking for
538 * @return matcher
539 */
Jian Li2907ad22016-05-12 23:08:54 -0700540 private static FlowEntryJsonMatcher matchesFlow(FlowEntry flow, String expectedAppName) {
541 return new FlowEntryJsonMatcher(flow, expectedAppName);
Ray Milkey4f5de002014-12-17 19:26:11 -0800542 }
543
544 /**
545 * Hamcrest matcher to check that a flow is represented properly in a JSON
546 * array of flows.
547 */
Jian Li2907ad22016-05-12 23:08:54 -0700548 public static class FlowEntryJsonArrayMatcher extends TypeSafeMatcher<JsonArray> {
Ray Milkey4f5de002014-12-17 19:26:11 -0800549 private final FlowEntry flow;
550 private String reason = "";
551
Jian Li2907ad22016-05-12 23:08:54 -0700552 public FlowEntryJsonArrayMatcher(FlowEntry flowValue) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800553 flow = flowValue;
554 }
555
556 @Override
557 public boolean matchesSafely(JsonArray json) {
558 boolean flowFound = false;
559
560 for (int jsonFlowIndex = 0; jsonFlowIndex < json.size();
561 jsonFlowIndex++) {
562
563 final JsonObject jsonFlow = json.get(jsonFlowIndex).asObject();
564
565 final String flowId = Long.toString(flow.id().value());
566 final String jsonFlowId = jsonFlow.get("id").asString();
567 if (jsonFlowId.equals(flowId)) {
568 flowFound = true;
569
570 // We found the correct flow, check attribute values
Ray Milkeybe539db2015-09-04 11:00:43 -0700571 assertThat(jsonFlow, matchesFlow(flow, APP_ID.name()));
Ray Milkey4f5de002014-12-17 19:26:11 -0800572 }
573 }
574 if (!flowFound) {
575 reason = "Flow with id " + flow.id().toString() + " not found";
576 return false;
577 } else {
578 return true;
579 }
580 }
581
582 @Override
583 public void describeTo(Description description) {
584 description.appendText(reason);
585 }
586 }
587
588 /**
589 * Factory to allocate a flow array matcher.
590 *
591 * @param flow flow object we are looking for
592 * @return matcher
593 */
Jian Li2907ad22016-05-12 23:08:54 -0700594 private static FlowEntryJsonArrayMatcher hasFlow(FlowEntry flow) {
595 return new FlowEntryJsonArrayMatcher(flow);
596 }
597
598 /**
599 * Hamcrest matcher to check that a flow representation in JSON matches
600 * the actual flow rule.
601 */
602 public static class FlowRuleJsonMatcher extends TypeSafeMatcher<JsonObject> {
603 private final FlowRule flow;
604 private final String expectedAppId;
605 private String reason = "";
606
607 public FlowRuleJsonMatcher(FlowRule flowValue, String expectedAppIdValue) {
608 flow = flowValue;
609 expectedAppId = expectedAppIdValue;
610 }
611
612 @Override
613 public boolean matchesSafely(JsonObject jsonFlow) {
614 // check id
615 final String jsonId = jsonFlow.get("id").asString();
616 final String flowId = Long.toString(flow.id().value());
617 if (!jsonId.equals(flowId)) {
618 reason = "id " + flow.id().toString();
619 return false;
620 }
621
622 // check application id
623 final String jsonAppId = jsonFlow.get("appId").asString();
624 if (!jsonAppId.equals(expectedAppId)) {
625 reason = "appId " + Short.toString(flow.appId());
626 return false;
627 }
628
629 // check device id
630 final String jsonDeviceId = jsonFlow.get("deviceId").asString();
631 if (!jsonDeviceId.equals(flow.deviceId().toString())) {
632 reason = "deviceId " + flow.deviceId();
633 return false;
634 }
635
636 // check treatment and instructions array
637 if (flow.treatment() != null) {
638 final JsonObject jsonTreatment = jsonFlow.get("treatment").asObject();
639 final JsonArray jsonInstructions = jsonTreatment.get("instructions").asArray();
640 if (flow.treatment().immediate().size() != jsonInstructions.size()) {
641 reason = "instructions array size of " +
642 Integer.toString(flow.treatment().immediate().size());
643 return false;
644 }
645 for (final Instruction instruction : flow.treatment().immediate()) {
646 boolean instructionFound = false;
647 for (int instructionIndex = 0; instructionIndex < jsonInstructions.size(); instructionIndex++) {
648 final String jsonType =
649 jsonInstructions.get(instructionIndex)
650 .asObject().get("type").asString();
651 final String instructionType = instruction.type().name();
652 if (jsonType.equals(instructionType)) {
653 instructionFound = true;
654 }
655 }
656 if (!instructionFound) {
657 reason = "instruction " + instruction.toString();
658 return false;
659 }
660 }
661 }
662
663 // check selector and criteria array
664 if (flow.selector() != null) {
665 final JsonObject jsonTreatment = jsonFlow.get("selector").asObject();
666 final JsonArray jsonCriteria = jsonTreatment.get("criteria").asArray();
667 if (flow.selector().criteria().size() != jsonCriteria.size()) {
668 reason = "criteria array size of " +
669 Integer.toString(flow.selector().criteria().size());
670 return false;
671 }
672 for (final Criterion criterion : flow.selector().criteria()) {
673 boolean criterionFound = false;
674
675 for (int criterionIndex = 0; criterionIndex < jsonCriteria.size(); criterionIndex++) {
676 final String jsonType =
677 jsonCriteria.get(criterionIndex)
678 .asObject().get("type").asString();
679 final String criterionType = criterion.type().name();
680 if (jsonType.equals(criterionType)) {
681 criterionFound = true;
682 }
683 }
684 if (!criterionFound) {
685 reason = "criterion " + criterion.toString();
686 return false;
687 }
688 }
689 }
690
691 return true;
692 }
693
694 @Override
695 public void describeTo(Description description) {
696 description.appendText(reason);
697 }
698 }
699
700 /**
701 * Factory to allocate a flow matcher.
702 *
703 * @param flow flow rule object we are looking for
704 * @return matcher
705 */
706 private static FlowRuleJsonMatcher matchesFlowRule(FlowRule flow, String expectedAppName) {
707 return new FlowRuleJsonMatcher(flow, expectedAppName);
708 }
709
710 /**
711 * Hamcrest matcher to check that a flow is represented properly in a JSON
712 * array of flow rules.
713 */
714 public static class FlowRuleJsonArrayMatcher extends TypeSafeMatcher<JsonArray> {
715 private final FlowRule flow;
716 private String reason = "";
717
718 public FlowRuleJsonArrayMatcher(FlowRule flowValue) {
719 flow = flowValue;
720 }
721
722 @Override
723 public boolean matchesSafely(JsonArray json) {
724 boolean flowFound = false;
725
726 for (int jsonFlowIndex = 0; jsonFlowIndex < json.size();
727 jsonFlowIndex++) {
728
729 final JsonObject jsonFlow = json.get(jsonFlowIndex).asObject();
730
731 final String flowId = Long.toString(flow.id().value());
732 final String jsonFlowId = jsonFlow.get("id").asString();
733 if (jsonFlowId.equals(flowId)) {
734 flowFound = true;
735
736 // We found the correct flow, check attribute values
737 assertThat(jsonFlow, matchesFlowRule(flow, APP_ID.name()));
738 }
739 }
740 if (!flowFound) {
741 reason = "Flow with id " + flow.id().toString() + " not found";
742 return false;
743 } else {
744 return true;
745 }
746 }
747
748 @Override
749 public void describeTo(Description description) {
750 description.appendText(reason);
751 }
752 }
753
754 /**
755 * Factory to allocate a flow array matcher.
756 *
757 * @param flow flow rule object we are looking for
758 * @return matcher
759 */
760 private static FlowRuleJsonArrayMatcher hasFlowRule(FlowRule flow) {
761 return new FlowRuleJsonArrayMatcher(flow);
Ray Milkey4f5de002014-12-17 19:26:11 -0800762 }
763
764 /**
765 * Tests the result of the rest api GET when there are no flows.
766 */
767 @Test
768 public void testFlowsEmptyArray() {
769 expect(mockFlowService.getFlowEntries(deviceId1))
770 .andReturn(null).anyTimes();
771 expect(mockFlowService.getFlowEntries(deviceId2))
772 .andReturn(null).anyTimes();
773 replay(mockFlowService);
774 replay(mockDeviceService);
Jian Li9d616492016-03-09 10:52:49 -0800775 final WebTarget wt = target();
776 final String response = wt.path("flows").request().get(String.class);
Ray Milkey4f5de002014-12-17 19:26:11 -0800777 assertThat(response, is("{\"flows\":[]}"));
778 }
779
780 /**
781 * Tests the result of the rest api GET when there are active flows.
782 */
783 @Test
784 public void testFlowsPopulatedArray() {
785 setupMockFlows();
786 replay(mockFlowService);
787 replay(mockDeviceService);
Jian Li9d616492016-03-09 10:52:49 -0800788 final WebTarget wt = target();
789 final String response = wt.path("flows").request().get(String.class);
Jian Li80cfe452016-01-14 16:04:58 -0800790 final JsonObject result = Json.parse(response).asObject();
Ray Milkey4f5de002014-12-17 19:26:11 -0800791 assertThat(result, notNullValue());
792
793 assertThat(result.names(), hasSize(1));
794 assertThat(result.names().get(0), is("flows"));
795 final JsonArray jsonFlows = result.get("flows").asArray();
796 assertThat(jsonFlows, notNullValue());
797 assertThat(jsonFlows, hasFlow(flow1));
798 assertThat(jsonFlows, hasFlow(flow2));
799 assertThat(jsonFlows, hasFlow(flow3));
800 assertThat(jsonFlows, hasFlow(flow4));
801 }
802
803 /**
804 * Tests the result of a rest api GET for a device.
805 */
806 @Test
807 public void testFlowsSingleDevice() {
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).request().get(String.class);
Jian Li80cfe452016-01-14 16:04:58 -0800818 final JsonObject result = Json.parse(response).asObject();
Ray Milkey4f5de002014-12-17 19:26:11 -0800819 assertThat(result, notNullValue());
820
821 assertThat(result.names(), hasSize(1));
822 assertThat(result.names().get(0), is("flows"));
823 final JsonArray jsonFlows = result.get("flows").asArray();
824 assertThat(jsonFlows, notNullValue());
825 assertThat(jsonFlows, hasFlow(flow5));
826 assertThat(jsonFlows, hasFlow(flow6));
827 }
828
829 /**
830 * Tests the result of a rest api GET for a device.
831 */
832 @Test
833 public void testFlowsSingleDeviceWithFlowId() {
834 setupMockFlows();
835 final Set<FlowEntry> flows = new HashSet<>();
836 flows.add(flow5);
837 flows.add(flow6);
838 expect(mockFlowService.getFlowEntries(anyObject()))
839 .andReturn(flows).anyTimes();
840 replay(mockFlowService);
841 replay(mockDeviceService);
Jian Li9d616492016-03-09 10:52:49 -0800842 final WebTarget wt = target();
843 final String response = wt.path("flows/" + deviceId3 + "/"
844 + Long.toString(flow5.id().value())).request().get(String.class);
Jian Li80cfe452016-01-14 16:04:58 -0800845 final JsonObject result = Json.parse(response).asObject();
Ray Milkey4f5de002014-12-17 19:26:11 -0800846 assertThat(result, notNullValue());
847
848 assertThat(result.names(), hasSize(1));
849 assertThat(result.names().get(0), is("flows"));
850 final JsonArray jsonFlows = result.get("flows").asArray();
851 assertThat(jsonFlows, notNullValue());
852 assertThat(jsonFlows, hasFlow(flow5));
853 assertThat(jsonFlows, not(hasFlow(flow6)));
854 }
855
856 /**
857 * Tests that a fetch of a non-existent device object throws an exception.
858 */
859 @Test
860 public void testBadGet() {
Jian Li9d616492016-03-09 10:52:49 -0800861 expect(mockFlowService.getFlowEntries(anyObject()))
Ray Milkey4f5de002014-12-17 19:26:11 -0800862 .andReturn(null).anyTimes();
863 replay(mockFlowService);
864 replay(mockDeviceService);
865
Jian Li9d616492016-03-09 10:52:49 -0800866 WebTarget wt = target();
Ray Milkey4f5de002014-12-17 19:26:11 -0800867 try {
Jian Li9d616492016-03-09 10:52:49 -0800868 wt.path("flows/0").request().get(String.class);
Ray Milkey4f5de002014-12-17 19:26:11 -0800869 fail("Fetch of non-existent device did not throw an exception");
Jian Li9d616492016-03-09 10:52:49 -0800870 } catch (NotFoundException ex) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800871 assertThat(ex.getMessage(),
Jian Li9d616492016-03-09 10:52:49 -0800872 containsString("HTTP 404 Not Found"));
Ray Milkey4f5de002014-12-17 19:26:11 -0800873 }
874 }
Ray Milkeyd43fe452015-05-29 09:35:12 -0700875
876 /**
877 * Tests creating a flow with POST.
878 */
879 @Test
Jian Li2907ad22016-05-12 23:08:54 -0700880 public void testPostWithoutAppId() {
Ray Milkeyd43fe452015-05-29 09:35:12 -0700881 replay(mockFlowService);
882
Jian Li9d616492016-03-09 10:52:49 -0800883 WebTarget wt = target();
Phaneendra Manda2be2f882015-11-11 15:23:40 +0530884 InputStream jsonStream = FlowsResourceTest.class
Ray Milkeyb82c42b2015-06-30 09:42:20 -0700885 .getResourceAsStream("post-flow.json");
Ray Milkeyd43fe452015-05-29 09:35:12 -0700886
Jian Li9d616492016-03-09 10:52:49 -0800887 Response response = wt.path("flows/of:0000000000000001")
888 .request(MediaType.APPLICATION_JSON_TYPE)
889 .post(Entity.json(jsonStream));
Jayasree Ghoshdba61112016-10-28 23:14:27 +0530890 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_NOT_FOUND));
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700891 }
892
893 /**
Jian Li2907ad22016-05-12 23:08:54 -0700894 * Tests creating a flow with POST while specifying application identifier.
895 */
896 @Test
897 public void testPostWithAppId() {
898 mockFlowService.applyFlowRules(anyObject());
899 expectLastCall();
900 replay(mockFlowService);
901
Jayasree Ghoshdba61112016-10-28 23:14:27 +0530902 expect(mockApplicationService.getId(anyObject())).andReturn(APP_ID).anyTimes();
903 replay(mockApplicationService);
904
Jian Li2907ad22016-05-12 23:08:54 -0700905 WebTarget wt = target();
906 InputStream jsonStream = FlowsResourceTest.class
907 .getResourceAsStream("post-flow.json");
908
909 Response response = wt.path("flows/of:0000000000000001")
910 .queryParam("appId", "org.onosproject.rest")
911 .request(MediaType.APPLICATION_JSON_TYPE)
912 .post(Entity.json(jsonStream));
913 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
914 String location = response.getLocation().getPath();
915 assertThat(location, Matchers.startsWith("/flows/of:0000000000000001/"));
916 }
917
918 /**
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700919 * Tests deleting a flow.
920 */
921 @Test
922 public void testDelete() {
923 setupMockFlows();
924 mockFlowService.removeFlowRules(anyObject());
925 expectLastCall();
926 replay(mockFlowService);
927
Jian Li9d616492016-03-09 10:52:49 -0800928 WebTarget wt = target();
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700929
930 String location = "/flows/1/155";
931
Jian Li9d616492016-03-09 10:52:49 -0800932 Response deleteResponse = wt.path(location)
933 .request(MediaType.APPLICATION_JSON_TYPE)
934 .delete();
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700935 assertThat(deleteResponse.getStatus(),
936 is(HttpURLConnection.HTTP_NO_CONTENT));
Ray Milkeyd43fe452015-05-29 09:35:12 -0700937 }
Jian Li2907ad22016-05-12 23:08:54 -0700938
939 /**
940 * Tests the result of a rest api GET for an application.
941 */
942 @Test
943 public void testGetFlowByAppId() {
944 setupMockFlowRules();
945
946 expect(mockApplicationService.getId(anyObject())).andReturn(APP_ID).anyTimes();
947 replay(mockApplicationService);
948
949 expect(mockFlowService.getFlowRulesById(APP_ID)).andReturn(flowRules).anyTimes();
950 replay(mockFlowService);
951
952 final WebTarget wt = target();
953 final String response = wt.path("flows/application/1").request().get(String.class);
954 final JsonObject result = Json.parse(response).asObject();
955 assertThat(result, notNullValue());
956
957 assertThat(result.names(), hasSize(1));
958 assertThat(result.names().get(0), is("flows"));
959 final JsonArray jsonFlows = result.get("flows").asArray();
960 assertThat(jsonFlows, notNullValue());
961 assertThat(jsonFlows, hasFlowRule(flowRule1));
962 assertThat(jsonFlows, hasFlowRule(flowRule2));
963 assertThat(jsonFlows, hasFlowRule(flowRule3));
964 assertThat(jsonFlows, hasFlowRule(flowRule4));
965 }
966
967 /**
968 * Tests the result of a rest api DELETE for an application.
969 */
970 @Test
971 public void testRemoveFlowByAppId() {
972 expect(mockApplicationService.getId(anyObject())).andReturn(APP_ID).anyTimes();
973 replay(mockApplicationService);
974
975 mockFlowService.removeFlowRulesById(APP_ID);
976 expectLastCall();
977 replay(mockFlowService);
978
979 WebTarget wt = target();
980
981 String location = "/flows/application/1";
982
983 Response deleteResponse = wt.path(location)
984 .request()
985 .delete();
986 assertThat(deleteResponse.getStatus(),
987 is(HttpURLConnection.HTTP_NO_CONTENT));
988 }
Ray Milkey4f5de002014-12-17 19:26:11 -0800989}