blob: 2ecd373cee218efea1f08a2084c6717ceaaedee3 [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;
Murat Parlakisikc6759e82016-06-29 03:22:22 -070049import org.onosproject.net.flow.FlowRule.FlowRemoveReason;
Jonathan Hartf44e42c2015-08-04 09:58:46 -070050import org.onosproject.net.flow.FlowRule;
jcc3d4e14a2015-04-21 11:32:05 +080051import org.onosproject.net.flow.FlowRuleExtPayLoad;
Ray Milkey4f5de002014-12-17 19:26:11 -080052import org.onosproject.net.flow.FlowRuleService;
53import org.onosproject.net.flow.TrafficSelector;
54import org.onosproject.net.flow.TrafficTreatment;
55import org.onosproject.net.flow.criteria.Criterion;
56import org.onosproject.net.flow.instructions.Instruction;
jcc3d4e14a2015-04-21 11:32:05 +080057
Jian Li9d616492016-03-09 10:52:49 -080058import javax.ws.rs.NotFoundException;
59import javax.ws.rs.client.Entity;
60import javax.ws.rs.client.WebTarget;
Jonathan Hartf44e42c2015-08-04 09:58:46 -070061import javax.ws.rs.core.MediaType;
Jian Li9d616492016-03-09 10:52:49 -080062import javax.ws.rs.core.Response;
Jonathan Hartf44e42c2015-08-04 09:58:46 -070063import java.io.InputStream;
64import java.net.HttpURLConnection;
65import java.util.HashMap;
66import java.util.HashSet;
67import java.util.Set;
Thiago Santos877914d2016-07-20 18:29:29 -030068import java.util.concurrent.TimeUnit;
Ray Milkey4f5de002014-12-17 19:26:11 -080069
Thiago Santos877914d2016-07-20 18:29:29 -030070import static java.util.concurrent.TimeUnit.SECONDS;
Ray Milkey4f5de002014-12-17 19:26:11 -080071import static org.easymock.EasyMock.anyObject;
Ray Milkeyd43fe452015-05-29 09:35:12 -070072import static org.easymock.EasyMock.anyShort;
Jian Li2907ad22016-05-12 23:08:54 -070073import static org.easymock.EasyMock.anyString;
Ray Milkey4f5de002014-12-17 19:26:11 -080074import static org.easymock.EasyMock.createMock;
75import static org.easymock.EasyMock.expect;
Ray Milkeyd43fe452015-05-29 09:35:12 -070076import static org.easymock.EasyMock.expectLastCall;
Ray Milkey4f5de002014-12-17 19:26:11 -080077import static org.easymock.EasyMock.replay;
78import static org.easymock.EasyMock.verify;
79import static org.hamcrest.Matchers.containsString;
80import static org.hamcrest.Matchers.hasSize;
81import static org.hamcrest.Matchers.is;
82import static org.hamcrest.Matchers.not;
83import static org.hamcrest.Matchers.notNullValue;
84import static org.junit.Assert.assertThat;
85import static org.junit.Assert.fail;
Ray Milkeyeb5c7172015-06-23 14:59:27 -070086import static org.onosproject.net.NetTestTools.APP_ID;
Ray Milkey4f5de002014-12-17 19:26:11 -080087
88/**
89 * Unit tests for Flows REST APIs.
90 */
Ray Milkey9c3d3362015-01-28 10:39:56 -080091public class FlowsResourceTest extends ResourceTest {
Ray Milkey4f5de002014-12-17 19:26:11 -080092 final FlowRuleService mockFlowService = createMock(FlowRuleService.class);
Ray Milkeyd43fe452015-05-29 09:35:12 -070093 CoreService mockCoreService = createMock(CoreService.class);
94
Ray Milkey4f5de002014-12-17 19:26:11 -080095 final HashMap<DeviceId, Set<FlowEntry>> rules = new HashMap<>();
96
97 final DeviceService mockDeviceService = createMock(DeviceService.class);
98
99 final DeviceId deviceId1 = DeviceId.deviceId("1");
100 final DeviceId deviceId2 = DeviceId.deviceId("2");
101 final DeviceId deviceId3 = DeviceId.deviceId("3");
102 final Device device1 = new DefaultDevice(null, deviceId1, Device.Type.OTHER,
103 "", "", "", "", null);
104 final Device device2 = new DefaultDevice(null, deviceId2, Device.Type.OTHER,
105 "", "", "", "", null);
106
Jian Li2907ad22016-05-12 23:08:54 -0700107 final ApplicationService mockApplicationService = createMock(ApplicationService.class);
108
Ray Milkey4f5de002014-12-17 19:26:11 -0800109 final MockFlowEntry flow1 = new MockFlowEntry(deviceId1, 1);
110 final MockFlowEntry flow2 = new MockFlowEntry(deviceId1, 2);
111
112 final MockFlowEntry flow3 = new MockFlowEntry(deviceId2, 3);
113 final MockFlowEntry flow4 = new MockFlowEntry(deviceId2, 4);
114
115 final MockFlowEntry flow5 = new MockFlowEntry(deviceId2, 5);
116 final MockFlowEntry flow6 = new MockFlowEntry(deviceId2, 6);
117
Jian Li2907ad22016-05-12 23:08:54 -0700118 final MockFlowRule flowRule1 = new MockFlowRule(deviceId1, 1);
119 final MockFlowRule flowRule2 = new MockFlowRule(deviceId1, 2);
120
121 final MockFlowRule flowRule3 = new MockFlowRule(deviceId2, 3);
122 final MockFlowRule flowRule4 = new MockFlowRule(deviceId2, 4);
123
124 final Set<FlowRule> flowRules = Sets.newHashSet();
125
Ray Milkey4f5de002014-12-17 19:26:11 -0800126 /**
127 * Mock class for a flow entry.
128 */
129 private static class MockFlowEntry implements FlowEntry {
130 final DeviceId deviceId;
131 final long baseValue;
132 TrafficTreatment treatment;
133 TrafficSelector selector;
134
135 public MockFlowEntry(DeviceId deviceId, long id) {
136 this.deviceId = deviceId;
137 this.baseValue = id * 100;
138 }
139
140 @Override
141 public FlowEntryState state() {
142 return FlowEntryState.ADDED;
143 }
144
145 @Override
146 public long life() {
Thiago Santos877914d2016-07-20 18:29:29 -0300147 return life(SECONDS);
148 }
149
150 @Override
151 public long life(TimeUnit timeUnit) {
152 return SECONDS.convert(baseValue + 11, timeUnit);
Ray Milkey4f5de002014-12-17 19:26:11 -0800153 }
154
155 @Override
156 public long packets() {
157 return baseValue + 22;
158 }
159
160 @Override
161 public long bytes() {
162 return baseValue + 33;
163 }
164
165 @Override
166 public long lastSeen() {
167 return baseValue + 44;
168 }
169
170 @Override
171 public int errType() {
172 return 0;
173 }
174
175 @Override
176 public int errCode() {
177 return 0;
178 }
179
180 @Override
181 public FlowId id() {
182 final long id = baseValue + 55;
183 return FlowId.valueOf(id);
184 }
185
186 @Override
alshabib08d98982015-04-21 16:25:50 -0700187 public GroupId groupId() {
188 return new DefaultGroupId(3);
Ray Milkey4f5de002014-12-17 19:26:11 -0800189 }
190
191 @Override
alshabib08d98982015-04-21 16:25:50 -0700192 public short appId() {
193 return 2;
Ray Milkey4f5de002014-12-17 19:26:11 -0800194 }
195
196 @Override
197 public int priority() {
198 return (int) (baseValue + 66);
199 }
200
201 @Override
202 public DeviceId deviceId() {
203 return deviceId;
204 }
205
206 @Override
207 public TrafficSelector selector() {
208 return selector;
209 }
210
211 @Override
212 public TrafficTreatment treatment() {
213 return treatment;
214 }
215
216 @Override
217 public int timeout() {
218 return (int) (baseValue + 77);
219 }
220
221 @Override
Murat Parlakisikc6759e82016-06-29 03:22:22 -0700222 public int hardTimeout() {
223 return 0;
224 }
225
226 @Override
227 public FlowRemoveReason reason() {
228 return FlowRemoveReason.NO_REASON;
229 }
230
231 @Override
Ray Milkey4f5de002014-12-17 19:26:11 -0800232 public boolean isPermanent() {
233 return false;
234 }
sangho11c30ac2015-01-22 14:30:55 -0800235
Yuta HIGUCHI32a53c52015-02-08 01:25:40 -0800236 @Override
alshabibdb774072015-04-20 13:13:51 -0700237 public int tableId() {
238 return 0;
239 }
jcc3d4e14a2015-04-21 11:32:05 +0800240
241 @Override
Jonathan Hartf44e42c2015-08-04 09:58:46 -0700242 public boolean exactMatch(FlowRule rule) {
243 return false;
244 }
245
246 @Override
jcc3d4e14a2015-04-21 11:32:05 +0800247 public FlowRuleExtPayLoad payLoad() {
248 return null;
249 }
Ray Milkey4f5de002014-12-17 19:26:11 -0800250 }
251
Ray Milkey4f5de002014-12-17 19:26:11 -0800252 /**
Jian Li2907ad22016-05-12 23:08:54 -0700253 * Mock class for a flow rule.
254 */
255 private static class MockFlowRule implements FlowRule {
256
257 final DeviceId deviceId;
258 final long baseValue;
259 TrafficTreatment treatment;
260 TrafficSelector selector;
261
262 public MockFlowRule(DeviceId deviceId, long id) {
263 this.deviceId = deviceId;
264 this.baseValue = id * 100;
265 }
266
267 @Override
268 public FlowId id() {
269 final long id = baseValue + 55;
270 return FlowId.valueOf(id);
271 }
272
273 @Override
274 public short appId() {
275 return 4;
276 }
277
278 @Override
279 public GroupId groupId() {
280 return new DefaultGroupId(3);
281 }
282
283 @Override
284 public int priority() {
285 return 0;
286 }
287
288 @Override
289 public DeviceId deviceId() {
290 return deviceId;
291 }
292
293 @Override
294 public TrafficSelector selector() {
295 return selector;
296 }
297
298 @Override
299 public TrafficTreatment treatment() {
300 return treatment;
301 }
302
303 @Override
304 public int timeout() {
305 return (int) (baseValue + 77);
306 }
307
308 @Override
Murat Parlakisikc6759e82016-06-29 03:22:22 -0700309 public int hardTimeout() {
310 return 0;
311 }
312
313 @Override
314 public FlowRemoveReason reason() {
315 return FlowRemoveReason.NO_REASON;
316 }
317
318 @Override
Jian Li2907ad22016-05-12 23:08:54 -0700319 public boolean isPermanent() {
320 return false;
321 }
322
323 @Override
324 public int tableId() {
325 return 0;
326 }
327
328 @Override
329 public boolean exactMatch(FlowRule rule) {
330 return false;
331 }
332
333 @Override
334 public FlowRuleExtPayLoad payLoad() {
335 return null;
336 }
337 }
338
339 /**
Ray Milkey4f5de002014-12-17 19:26:11 -0800340 * Populates some flows used as testing data.
341 */
342 private void setupMockFlows() {
343 flow2.treatment = DefaultTrafficTreatment.builder()
Ray Milkey4f5de002014-12-17 19:26:11 -0800344 .setEthDst(MacAddress.BROADCAST)
345 .build();
346 flow2.selector = DefaultTrafficSelector.builder()
347 .matchEthType((short) 3)
348 .matchIPProtocol((byte) 9)
349 .build();
350 flow4.treatment = DefaultTrafficTreatment.builder()
Ray Milkey4f5de002014-12-17 19:26:11 -0800351 .build();
352 final Set<FlowEntry> flows1 = new HashSet<>();
353 flows1.add(flow1);
354 flows1.add(flow2);
355
356 final Set<FlowEntry> flows2 = new HashSet<>();
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700357 flows2.add(flow3);
358 flows2.add(flow4);
Ray Milkey4f5de002014-12-17 19:26:11 -0800359
360 rules.put(deviceId1, flows1);
361 rules.put(deviceId2, flows2);
362
363 expect(mockFlowService.getFlowEntries(deviceId1))
364 .andReturn(rules.get(deviceId1)).anyTimes();
365 expect(mockFlowService.getFlowEntries(deviceId2))
366 .andReturn(rules.get(deviceId2)).anyTimes();
367 }
368
369 /**
Jian Li2907ad22016-05-12 23:08:54 -0700370 * Populates some flow rules used as testing data.
371 */
372 private void setupMockFlowRules() {
373 flowRule2.treatment = DefaultTrafficTreatment.builder()
374 .setEthDst(MacAddress.BROADCAST)
375 .build();
376 flowRule2.selector = DefaultTrafficSelector.builder()
377 .matchEthType((short) 3)
378 .matchIPProtocol((byte) 9)
379 .build();
380 flowRule4.treatment = DefaultTrafficTreatment.builder()
381 .build();
382
383 flowRules.add(flowRule1);
384 flowRules.add(flowRule2);
385 flowRules.add(flowRule3);
386 flowRules.add(flowRule4);
387 }
388
389 /**
Ray Milkey4f5de002014-12-17 19:26:11 -0800390 * Sets up the global values for all the tests.
391 */
392 @Before
Ray Milkeyed0b1662015-02-05 09:34:29 -0800393 public void setUpTest() {
Ray Milkey4f5de002014-12-17 19:26:11 -0800394 // Mock device service
395 expect(mockDeviceService.getDevice(deviceId1))
396 .andReturn(device1);
397 expect(mockDeviceService.getDevice(deviceId2))
398 .andReturn(device2);
399 expect(mockDeviceService.getDevices())
400 .andReturn(ImmutableSet.of(device1, device2));
401
Ray Milkeyd43fe452015-05-29 09:35:12 -0700402 // Mock Core Service
403 expect(mockCoreService.getAppId(anyShort()))
404 .andReturn(NetTestTools.APP_ID).anyTimes();
Jian Li2907ad22016-05-12 23:08:54 -0700405 expect(mockCoreService.getAppId(anyString()))
406 .andReturn(NetTestTools.APP_ID).anyTimes();
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700407 expect(mockCoreService.registerApplication(FlowRuleCodec.REST_APP_ID))
408 .andReturn(APP_ID).anyTimes();
Ray Milkeyd43fe452015-05-29 09:35:12 -0700409 replay(mockCoreService);
410
Ray Milkey4f5de002014-12-17 19:26:11 -0800411 // Register the services needed for the test
412 final CodecManager codecService = new CodecManager();
413 codecService.activate();
414 ServiceDirectory testDirectory =
415 new TestServiceDirectory()
416 .add(FlowRuleService.class, mockFlowService)
417 .add(DeviceService.class, mockDeviceService)
Ray Milkeyd43fe452015-05-29 09:35:12 -0700418 .add(CodecService.class, codecService)
Jian Li2907ad22016-05-12 23:08:54 -0700419 .add(CoreService.class, mockCoreService)
420 .add(ApplicationService.class, mockApplicationService);
Ray Milkey4f5de002014-12-17 19:26:11 -0800421
422 BaseResource.setServiceDirectory(testDirectory);
423 }
424
425 /**
426 * Cleans up and verifies the mocks.
Ray Milkey4f5de002014-12-17 19:26:11 -0800427 */
428 @After
Ray Milkeyed0b1662015-02-05 09:34:29 -0800429 public void tearDownTest() {
Ray Milkey4f5de002014-12-17 19:26:11 -0800430 verify(mockFlowService);
Ray Milkeyd43fe452015-05-29 09:35:12 -0700431 verify(mockCoreService);
Ray Milkey4f5de002014-12-17 19:26:11 -0800432 }
433
434 /**
435 * Hamcrest matcher to check that a flow representation in JSON matches
436 * the actual flow entry.
437 */
Jian Li2907ad22016-05-12 23:08:54 -0700438 public static class FlowEntryJsonMatcher extends TypeSafeMatcher<JsonObject> {
Ray Milkey4f5de002014-12-17 19:26:11 -0800439 private final FlowEntry flow;
Ray Milkeybe539db2015-09-04 11:00:43 -0700440 private final String expectedAppId;
Ray Milkey4f5de002014-12-17 19:26:11 -0800441 private String reason = "";
442
Jian Li2907ad22016-05-12 23:08:54 -0700443 public FlowEntryJsonMatcher(FlowEntry flowValue, String expectedAppIdValue) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800444 flow = flowValue;
Ray Milkeybe539db2015-09-04 11:00:43 -0700445 expectedAppId = expectedAppIdValue;
Ray Milkey4f5de002014-12-17 19:26:11 -0800446 }
447
448 @Override
449 public boolean matchesSafely(JsonObject jsonFlow) {
450 // check id
451 final String jsonId = jsonFlow.get("id").asString();
452 final String flowId = Long.toString(flow.id().value());
453 if (!jsonId.equals(flowId)) {
454 reason = "id " + flow.id().toString();
455 return false;
456 }
457
458 // check application id
Ray Milkeybe539db2015-09-04 11:00:43 -0700459 final String jsonAppId = jsonFlow.get("appId").asString();
460 if (!jsonAppId.equals(expectedAppId)) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800461 reason = "appId " + Short.toString(flow.appId());
462 return false;
463 }
464
465 // check device id
466 final String jsonDeviceId = jsonFlow.get("deviceId").asString();
467 if (!jsonDeviceId.equals(flow.deviceId().toString())) {
468 reason = "deviceId " + flow.deviceId();
469 return false;
470 }
471
472 // check treatment and instructions array
473 if (flow.treatment() != null) {
474 final JsonObject jsonTreatment = jsonFlow.get("treatment").asObject();
475 final JsonArray jsonInstructions = jsonTreatment.get("instructions").asArray();
Ray Milkey42507352015-03-20 15:16:10 -0700476 if (flow.treatment().immediate().size() != jsonInstructions.size()) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800477 reason = "instructions array size of " +
Ray Milkey42507352015-03-20 15:16:10 -0700478 Integer.toString(flow.treatment().immediate().size());
Ray Milkey4f5de002014-12-17 19:26:11 -0800479 return false;
480 }
Ray Milkey42507352015-03-20 15:16:10 -0700481 for (final Instruction instruction : flow.treatment().immediate()) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800482 boolean instructionFound = false;
Ray Milkey4f5de002014-12-17 19:26:11 -0800483 for (int instructionIndex = 0; instructionIndex < jsonInstructions.size(); instructionIndex++) {
Ray Milkeyc95bb9d2015-01-06 10:28:24 -0800484 final String jsonType =
485 jsonInstructions.get(instructionIndex)
486 .asObject().get("type").asString();
487 final String instructionType = instruction.type().name();
488 if (jsonType.equals(instructionType)) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800489 instructionFound = true;
490 }
491 }
492 if (!instructionFound) {
Ray Milkeyc95bb9d2015-01-06 10:28:24 -0800493 reason = "instruction " + instruction.toString();
Ray Milkey4f5de002014-12-17 19:26:11 -0800494 return false;
495 }
496 }
497 }
498
499 // check selector and criteria array
500 if (flow.selector() != null) {
501 final JsonObject jsonTreatment = jsonFlow.get("selector").asObject();
502 final JsonArray jsonCriteria = jsonTreatment.get("criteria").asArray();
503 if (flow.selector().criteria().size() != jsonCriteria.size()) {
504 reason = "criteria array size of " +
505 Integer.toString(flow.selector().criteria().size());
506 return false;
507 }
508 for (final Criterion criterion : flow.selector().criteria()) {
509 boolean criterionFound = false;
Ray Milkeyc95bb9d2015-01-06 10:28:24 -0800510
Ray Milkey4f5de002014-12-17 19:26:11 -0800511 for (int criterionIndex = 0; criterionIndex < jsonCriteria.size(); criterionIndex++) {
Ray Milkeyc95bb9d2015-01-06 10:28:24 -0800512 final String jsonType =
513 jsonCriteria.get(criterionIndex)
514 .asObject().get("type").asString();
515 final String criterionType = criterion.type().name();
516 if (jsonType.equals(criterionType)) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800517 criterionFound = true;
518 }
519 }
520 if (!criterionFound) {
Ray Milkeyc95bb9d2015-01-06 10:28:24 -0800521 reason = "criterion " + criterion.toString();
Ray Milkey4f5de002014-12-17 19:26:11 -0800522 return false;
523 }
524 }
525 }
526
527 return true;
528 }
529
530 @Override
531 public void describeTo(Description description) {
532 description.appendText(reason);
533 }
534 }
535
536 /**
537 * Factory to allocate a flow matcher.
538 *
539 * @param flow flow object we are looking for
540 * @return matcher
541 */
Jian Li2907ad22016-05-12 23:08:54 -0700542 private static FlowEntryJsonMatcher matchesFlow(FlowEntry flow, String expectedAppName) {
543 return new FlowEntryJsonMatcher(flow, expectedAppName);
Ray Milkey4f5de002014-12-17 19:26:11 -0800544 }
545
546 /**
547 * Hamcrest matcher to check that a flow is represented properly in a JSON
548 * array of flows.
549 */
Jian Li2907ad22016-05-12 23:08:54 -0700550 public static class FlowEntryJsonArrayMatcher extends TypeSafeMatcher<JsonArray> {
Ray Milkey4f5de002014-12-17 19:26:11 -0800551 private final FlowEntry flow;
552 private String reason = "";
553
Jian Li2907ad22016-05-12 23:08:54 -0700554 public FlowEntryJsonArrayMatcher(FlowEntry flowValue) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800555 flow = flowValue;
556 }
557
558 @Override
559 public boolean matchesSafely(JsonArray json) {
560 boolean flowFound = false;
561
562 for (int jsonFlowIndex = 0; jsonFlowIndex < json.size();
563 jsonFlowIndex++) {
564
565 final JsonObject jsonFlow = json.get(jsonFlowIndex).asObject();
566
567 final String flowId = Long.toString(flow.id().value());
568 final String jsonFlowId = jsonFlow.get("id").asString();
569 if (jsonFlowId.equals(flowId)) {
570 flowFound = true;
571
572 // We found the correct flow, check attribute values
Ray Milkeybe539db2015-09-04 11:00:43 -0700573 assertThat(jsonFlow, matchesFlow(flow, APP_ID.name()));
Ray Milkey4f5de002014-12-17 19:26:11 -0800574 }
575 }
576 if (!flowFound) {
577 reason = "Flow with id " + flow.id().toString() + " not found";
578 return false;
579 } else {
580 return true;
581 }
582 }
583
584 @Override
585 public void describeTo(Description description) {
586 description.appendText(reason);
587 }
588 }
589
590 /**
591 * Factory to allocate a flow array matcher.
592 *
593 * @param flow flow object we are looking for
594 * @return matcher
595 */
Jian Li2907ad22016-05-12 23:08:54 -0700596 private static FlowEntryJsonArrayMatcher hasFlow(FlowEntry flow) {
597 return new FlowEntryJsonArrayMatcher(flow);
598 }
599
600 /**
601 * Hamcrest matcher to check that a flow representation in JSON matches
602 * the actual flow rule.
603 */
604 public static class FlowRuleJsonMatcher extends TypeSafeMatcher<JsonObject> {
605 private final FlowRule flow;
606 private final String expectedAppId;
607 private String reason = "";
608
609 public FlowRuleJsonMatcher(FlowRule flowValue, String expectedAppIdValue) {
610 flow = flowValue;
611 expectedAppId = expectedAppIdValue;
612 }
613
614 @Override
615 public boolean matchesSafely(JsonObject jsonFlow) {
616 // check id
617 final String jsonId = jsonFlow.get("id").asString();
618 final String flowId = Long.toString(flow.id().value());
619 if (!jsonId.equals(flowId)) {
620 reason = "id " + flow.id().toString();
621 return false;
622 }
623
624 // check application id
625 final String jsonAppId = jsonFlow.get("appId").asString();
626 if (!jsonAppId.equals(expectedAppId)) {
627 reason = "appId " + Short.toString(flow.appId());
628 return false;
629 }
630
631 // check device id
632 final String jsonDeviceId = jsonFlow.get("deviceId").asString();
633 if (!jsonDeviceId.equals(flow.deviceId().toString())) {
634 reason = "deviceId " + flow.deviceId();
635 return false;
636 }
637
638 // check treatment and instructions array
639 if (flow.treatment() != null) {
640 final JsonObject jsonTreatment = jsonFlow.get("treatment").asObject();
641 final JsonArray jsonInstructions = jsonTreatment.get("instructions").asArray();
642 if (flow.treatment().immediate().size() != jsonInstructions.size()) {
643 reason = "instructions array size of " +
644 Integer.toString(flow.treatment().immediate().size());
645 return false;
646 }
647 for (final Instruction instruction : flow.treatment().immediate()) {
648 boolean instructionFound = false;
649 for (int instructionIndex = 0; instructionIndex < jsonInstructions.size(); instructionIndex++) {
650 final String jsonType =
651 jsonInstructions.get(instructionIndex)
652 .asObject().get("type").asString();
653 final String instructionType = instruction.type().name();
654 if (jsonType.equals(instructionType)) {
655 instructionFound = true;
656 }
657 }
658 if (!instructionFound) {
659 reason = "instruction " + instruction.toString();
660 return false;
661 }
662 }
663 }
664
665 // check selector and criteria array
666 if (flow.selector() != null) {
667 final JsonObject jsonTreatment = jsonFlow.get("selector").asObject();
668 final JsonArray jsonCriteria = jsonTreatment.get("criteria").asArray();
669 if (flow.selector().criteria().size() != jsonCriteria.size()) {
670 reason = "criteria array size of " +
671 Integer.toString(flow.selector().criteria().size());
672 return false;
673 }
674 for (final Criterion criterion : flow.selector().criteria()) {
675 boolean criterionFound = false;
676
677 for (int criterionIndex = 0; criterionIndex < jsonCriteria.size(); criterionIndex++) {
678 final String jsonType =
679 jsonCriteria.get(criterionIndex)
680 .asObject().get("type").asString();
681 final String criterionType = criterion.type().name();
682 if (jsonType.equals(criterionType)) {
683 criterionFound = true;
684 }
685 }
686 if (!criterionFound) {
687 reason = "criterion " + criterion.toString();
688 return false;
689 }
690 }
691 }
692
693 return true;
694 }
695
696 @Override
697 public void describeTo(Description description) {
698 description.appendText(reason);
699 }
700 }
701
702 /**
703 * Factory to allocate a flow matcher.
704 *
705 * @param flow flow rule object we are looking for
706 * @return matcher
707 */
708 private static FlowRuleJsonMatcher matchesFlowRule(FlowRule flow, String expectedAppName) {
709 return new FlowRuleJsonMatcher(flow, expectedAppName);
710 }
711
712 /**
713 * Hamcrest matcher to check that a flow is represented properly in a JSON
714 * array of flow rules.
715 */
716 public static class FlowRuleJsonArrayMatcher extends TypeSafeMatcher<JsonArray> {
717 private final FlowRule flow;
718 private String reason = "";
719
720 public FlowRuleJsonArrayMatcher(FlowRule flowValue) {
721 flow = flowValue;
722 }
723
724 @Override
725 public boolean matchesSafely(JsonArray json) {
726 boolean flowFound = false;
727
728 for (int jsonFlowIndex = 0; jsonFlowIndex < json.size();
729 jsonFlowIndex++) {
730
731 final JsonObject jsonFlow = json.get(jsonFlowIndex).asObject();
732
733 final String flowId = Long.toString(flow.id().value());
734 final String jsonFlowId = jsonFlow.get("id").asString();
735 if (jsonFlowId.equals(flowId)) {
736 flowFound = true;
737
738 // We found the correct flow, check attribute values
739 assertThat(jsonFlow, matchesFlowRule(flow, APP_ID.name()));
740 }
741 }
742 if (!flowFound) {
743 reason = "Flow with id " + flow.id().toString() + " not found";
744 return false;
745 } else {
746 return true;
747 }
748 }
749
750 @Override
751 public void describeTo(Description description) {
752 description.appendText(reason);
753 }
754 }
755
756 /**
757 * Factory to allocate a flow array matcher.
758 *
759 * @param flow flow rule object we are looking for
760 * @return matcher
761 */
762 private static FlowRuleJsonArrayMatcher hasFlowRule(FlowRule flow) {
763 return new FlowRuleJsonArrayMatcher(flow);
Ray Milkey4f5de002014-12-17 19:26:11 -0800764 }
765
766 /**
767 * Tests the result of the rest api GET when there are no flows.
768 */
769 @Test
770 public void testFlowsEmptyArray() {
771 expect(mockFlowService.getFlowEntries(deviceId1))
772 .andReturn(null).anyTimes();
773 expect(mockFlowService.getFlowEntries(deviceId2))
774 .andReturn(null).anyTimes();
775 replay(mockFlowService);
776 replay(mockDeviceService);
Jian Li9d616492016-03-09 10:52:49 -0800777 final WebTarget wt = target();
778 final String response = wt.path("flows").request().get(String.class);
Ray Milkey4f5de002014-12-17 19:26:11 -0800779 assertThat(response, is("{\"flows\":[]}"));
780 }
781
782 /**
783 * Tests the result of the rest api GET when there are active flows.
784 */
785 @Test
786 public void testFlowsPopulatedArray() {
787 setupMockFlows();
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").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(flow1));
800 assertThat(jsonFlows, hasFlow(flow2));
801 assertThat(jsonFlows, hasFlow(flow3));
802 assertThat(jsonFlows, hasFlow(flow4));
803 }
804
805 /**
806 * Tests the result of a rest api GET for a device.
807 */
808 @Test
809 public void testFlowsSingleDevice() {
810 setupMockFlows();
811 final Set<FlowEntry> flows = new HashSet<>();
812 flows.add(flow5);
813 flows.add(flow6);
814 expect(mockFlowService.getFlowEntries(anyObject()))
815 .andReturn(flows).anyTimes();
816 replay(mockFlowService);
817 replay(mockDeviceService);
Jian Li9d616492016-03-09 10:52:49 -0800818 final WebTarget wt = target();
819 final String response = wt.path("flows/" + deviceId3).request().get(String.class);
Jian Li80cfe452016-01-14 16:04:58 -0800820 final JsonObject result = Json.parse(response).asObject();
Ray Milkey4f5de002014-12-17 19:26:11 -0800821 assertThat(result, notNullValue());
822
823 assertThat(result.names(), hasSize(1));
824 assertThat(result.names().get(0), is("flows"));
825 final JsonArray jsonFlows = result.get("flows").asArray();
826 assertThat(jsonFlows, notNullValue());
827 assertThat(jsonFlows, hasFlow(flow5));
828 assertThat(jsonFlows, hasFlow(flow6));
829 }
830
831 /**
832 * Tests the result of a rest api GET for a device.
833 */
834 @Test
835 public void testFlowsSingleDeviceWithFlowId() {
836 setupMockFlows();
837 final Set<FlowEntry> flows = new HashSet<>();
838 flows.add(flow5);
839 flows.add(flow6);
840 expect(mockFlowService.getFlowEntries(anyObject()))
841 .andReturn(flows).anyTimes();
842 replay(mockFlowService);
843 replay(mockDeviceService);
Jian Li9d616492016-03-09 10:52:49 -0800844 final WebTarget wt = target();
845 final String response = wt.path("flows/" + deviceId3 + "/"
846 + Long.toString(flow5.id().value())).request().get(String.class);
Jian Li80cfe452016-01-14 16:04:58 -0800847 final JsonObject result = Json.parse(response).asObject();
Ray Milkey4f5de002014-12-17 19:26:11 -0800848 assertThat(result, notNullValue());
849
850 assertThat(result.names(), hasSize(1));
851 assertThat(result.names().get(0), is("flows"));
852 final JsonArray jsonFlows = result.get("flows").asArray();
853 assertThat(jsonFlows, notNullValue());
854 assertThat(jsonFlows, hasFlow(flow5));
855 assertThat(jsonFlows, not(hasFlow(flow6)));
856 }
857
858 /**
859 * Tests that a fetch of a non-existent device object throws an exception.
860 */
861 @Test
862 public void testBadGet() {
Jian Li9d616492016-03-09 10:52:49 -0800863 expect(mockFlowService.getFlowEntries(anyObject()))
Ray Milkey4f5de002014-12-17 19:26:11 -0800864 .andReturn(null).anyTimes();
865 replay(mockFlowService);
866 replay(mockDeviceService);
867
Jian Li9d616492016-03-09 10:52:49 -0800868 WebTarget wt = target();
Ray Milkey4f5de002014-12-17 19:26:11 -0800869 try {
Jian Li9d616492016-03-09 10:52:49 -0800870 wt.path("flows/0").request().get(String.class);
Ray Milkey4f5de002014-12-17 19:26:11 -0800871 fail("Fetch of non-existent device did not throw an exception");
Jian Li9d616492016-03-09 10:52:49 -0800872 } catch (NotFoundException ex) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800873 assertThat(ex.getMessage(),
Jian Li9d616492016-03-09 10:52:49 -0800874 containsString("HTTP 404 Not Found"));
Ray Milkey4f5de002014-12-17 19:26:11 -0800875 }
876 }
Ray Milkeyd43fe452015-05-29 09:35:12 -0700877
878 /**
879 * Tests creating a flow with POST.
880 */
881 @Test
Jian Li2907ad22016-05-12 23:08:54 -0700882 public void testPostWithoutAppId() {
Ray Milkeyd43fe452015-05-29 09:35:12 -0700883 mockFlowService.applyFlowRules(anyObject());
884 expectLastCall();
885 replay(mockFlowService);
886
Jian Li9d616492016-03-09 10:52:49 -0800887 WebTarget wt = target();
Phaneendra Manda2be2f882015-11-11 15:23:40 +0530888 InputStream jsonStream = FlowsResourceTest.class
Ray Milkeyb82c42b2015-06-30 09:42:20 -0700889 .getResourceAsStream("post-flow.json");
Ray Milkeyd43fe452015-05-29 09:35:12 -0700890
Jian Li9d616492016-03-09 10:52:49 -0800891 Response response = wt.path("flows/of:0000000000000001")
892 .request(MediaType.APPLICATION_JSON_TYPE)
893 .post(Entity.json(jsonStream));
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700894 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
895 String location = response.getLocation().getPath();
896 assertThat(location, Matchers.startsWith("/flows/of:0000000000000001/"));
897 }
898
899 /**
Jian Li2907ad22016-05-12 23:08:54 -0700900 * Tests creating a flow with POST while specifying application identifier.
901 */
902 @Test
903 public void testPostWithAppId() {
904 mockFlowService.applyFlowRules(anyObject());
905 expectLastCall();
906 replay(mockFlowService);
907
908 WebTarget wt = target();
909 InputStream jsonStream = FlowsResourceTest.class
910 .getResourceAsStream("post-flow.json");
911
912 Response response = wt.path("flows/of:0000000000000001")
913 .queryParam("appId", "org.onosproject.rest")
914 .request(MediaType.APPLICATION_JSON_TYPE)
915 .post(Entity.json(jsonStream));
916 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
917 String location = response.getLocation().getPath();
918 assertThat(location, Matchers.startsWith("/flows/of:0000000000000001/"));
919 }
920
921 /**
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700922 * Tests deleting a flow.
923 */
924 @Test
925 public void testDelete() {
926 setupMockFlows();
927 mockFlowService.removeFlowRules(anyObject());
928 expectLastCall();
929 replay(mockFlowService);
930
Jian Li9d616492016-03-09 10:52:49 -0800931 WebTarget wt = target();
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700932
933 String location = "/flows/1/155";
934
Jian Li9d616492016-03-09 10:52:49 -0800935 Response deleteResponse = wt.path(location)
936 .request(MediaType.APPLICATION_JSON_TYPE)
937 .delete();
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700938 assertThat(deleteResponse.getStatus(),
939 is(HttpURLConnection.HTTP_NO_CONTENT));
Ray Milkeyd43fe452015-05-29 09:35:12 -0700940 }
Jian Li2907ad22016-05-12 23:08:54 -0700941
942 /**
943 * Tests the result of a rest api GET for an application.
944 */
945 @Test
946 public void testGetFlowByAppId() {
947 setupMockFlowRules();
948
949 expect(mockApplicationService.getId(anyObject())).andReturn(APP_ID).anyTimes();
950 replay(mockApplicationService);
951
952 expect(mockFlowService.getFlowRulesById(APP_ID)).andReturn(flowRules).anyTimes();
953 replay(mockFlowService);
954
955 final WebTarget wt = target();
956 final String response = wt.path("flows/application/1").request().get(String.class);
957 final JsonObject result = Json.parse(response).asObject();
958 assertThat(result, notNullValue());
959
960 assertThat(result.names(), hasSize(1));
961 assertThat(result.names().get(0), is("flows"));
962 final JsonArray jsonFlows = result.get("flows").asArray();
963 assertThat(jsonFlows, notNullValue());
964 assertThat(jsonFlows, hasFlowRule(flowRule1));
965 assertThat(jsonFlows, hasFlowRule(flowRule2));
966 assertThat(jsonFlows, hasFlowRule(flowRule3));
967 assertThat(jsonFlows, hasFlowRule(flowRule4));
968 }
969
970 /**
971 * Tests the result of a rest api DELETE for an application.
972 */
973 @Test
974 public void testRemoveFlowByAppId() {
975 expect(mockApplicationService.getId(anyObject())).andReturn(APP_ID).anyTimes();
976 replay(mockApplicationService);
977
978 mockFlowService.removeFlowRulesById(APP_ID);
979 expectLastCall();
980 replay(mockFlowService);
981
982 WebTarget wt = target();
983
984 String location = "/flows/application/1";
985
986 Response deleteResponse = wt.path(location)
987 .request()
988 .delete();
989 assertThat(deleteResponse.getStatus(),
990 is(HttpURLConnection.HTTP_NO_CONTENT));
991 }
Ray Milkey4f5de002014-12-17 19:26:11 -0800992}