Ray Milkey

Implement REST APIs for flows:

- get all flows
- get all flows for a device
- get all flows for a device with a given ID

Change-Id: Ifb1541e4ae4a7e49f1347b34bef2fe788902368d
......@@ -41,6 +41,7 @@
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
......
......@@ -29,6 +29,9 @@ import org.onosproject.net.Host;
import org.onosproject.net.HostLocation;
import org.onosproject.net.Link;
import org.onosproject.net.Port;
import org.onosproject.net.flow.FlowEntry;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.intent.Intent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -59,6 +62,9 @@ public class CodecManager implements CodecService {
registerCodec(Host.class, new HostCodec());
registerCodec(HostLocation.class, new HostLocationCodec());
registerCodec(Intent.class, new IntentCodec());
registerCodec(FlowEntry.class, new FlowEntryCodec());
registerCodec(TrafficTreatment.class, new TrafficTreatmentCodec());
registerCodec(TrafficSelector.class, new TrafficSelectorCodec());
log.info("Started");
}
......
/*
* Copyright 2014 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.codec.impl;
import org.onosproject.codec.CodecContext;
import org.onosproject.codec.JsonCodec;
import org.onosproject.net.flow.FlowEntry;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import com.fasterxml.jackson.databind.node.ObjectNode;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Flow entry JSON codec.
*/
public class FlowEntryCodec extends JsonCodec<FlowEntry> {
@Override
public ObjectNode encode(FlowEntry flowEntry, CodecContext context) {
checkNotNull(flowEntry, "Flow entry cannot be null");
final ObjectNode result = context.mapper().createObjectNode()
.put("id", Long.toString(flowEntry.id().value()))
.put("appId", flowEntry.appId())
.put("groupId", flowEntry.groupId().id())
.put("priority", flowEntry.priority())
.put("timeout", flowEntry.timeout())
.put("isPermanent", flowEntry.isPermanent())
.put("deviceId", flowEntry.deviceId().toString())
.put("state", flowEntry.state().toString())
.put("life", flowEntry.life())
.put("packets", flowEntry.packets())
.put("bytes", flowEntry.bytes())
.put("lastSeen", flowEntry.lastSeen());
if (flowEntry.treatment() != null) {
final JsonCodec<TrafficTreatment> treatmentCodec =
new TrafficTreatmentCodec();
result.set("treatment", treatmentCodec.encode(flowEntry.treatment(), context));
}
if (flowEntry.selector() != null) {
final JsonCodec<TrafficSelector> selectorCodec =
new TrafficSelectorCodec();
result.set("selector", selectorCodec.encode(flowEntry.selector(), context));
}
return result;
}
}
/*
* Copyright 2014 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.codec.impl;
import org.onosproject.codec.CodecContext;
import org.onosproject.codec.JsonCodec;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.criteria.Criterion;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Traffic selector codec.
*/
public class TrafficSelectorCodec extends JsonCodec<TrafficSelector> {
@Override
public ObjectNode encode(TrafficSelector selector, CodecContext context) {
checkNotNull(selector, "Traffic selector cannot be null");
final ObjectNode result = context.mapper().createObjectNode();
final ArrayNode jsonCriteria = result.putArray("criteria");
if (selector.criteria() != null) {
for (final Criterion criterion :selector.criteria()) {
// TODO: would be better to have a codec that understands criteria
jsonCriteria.add(criterion.toString());
}
}
return result;
}
}
/*
* Copyright 2014 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.codec.impl;
import org.onosproject.codec.CodecContext;
import org.onosproject.codec.JsonCodec;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.flow.instructions.Instruction;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Traffic treatment codec.
*/
public class TrafficTreatmentCodec extends JsonCodec<TrafficTreatment> {
@Override
public ObjectNode encode(TrafficTreatment treatment, CodecContext context) {
checkNotNull(treatment, "Traffic treatment cannot be null");
final ObjectNode result = context.mapper().createObjectNode();
final ArrayNode jsonInstructions = result.putArray("instructions");
if (treatment.instructions() != null) {
for (final Instruction instruction : treatment.instructions()) {
// TODO: would be better to have a codec that understands instructions
jsonInstructions.add(instruction.toString());
}
}
return result;
}
}
/*
* Copyright 2014 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.rest;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.onlab.util.ItemNotFoundException;
import org.onosproject.codec.impl.FlowEntryCodec;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.flow.FlowEntry;
import org.onosproject.net.flow.FlowRuleService;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
* REST resource for interacting with the inventory of flows.
*/
@Path("flows")
public class FlowsWebResource extends AbstractWebResource {
public static final String DEVICE_NOT_FOUND = "Device is not found";
final FlowRuleService service = get(FlowRuleService.class);
final ObjectNode root = mapper().createObjectNode();
final ArrayNode flowsNode = root.putArray("flows");
final FlowEntryCodec flowEntryCodec = new FlowEntryCodec();
/**
* Gets an array containing all the intents in the system.
*
* @return array of all the intents in the system
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getFlows() {
final Iterable<Device> devices = get(DeviceService.class).getDevices();
for (final Device device : devices) {
final Iterable<FlowEntry> deviceEntries = service.getFlowEntries(device.id());
if (deviceEntries != null) {
for (final FlowEntry entry : deviceEntries) {
flowsNode.add(flowEntryCodec.encode(entry, this));
}
}
}
return ok(root.toString()).build();
}
/**
* Gets the flows for a device, where the device is specified by Id.
*
* @param deviceId Id of device to look up
* @return flow data as an array
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("{deviceId}")
public Response getFlowByDeviceId(@PathParam("deviceId") String deviceId) {
final Iterable<FlowEntry> deviceEntries =
service.getFlowEntries(DeviceId.deviceId(deviceId));
if (!deviceEntries.iterator().hasNext()) {
throw new ItemNotFoundException(DEVICE_NOT_FOUND);
}
for (final FlowEntry entry : deviceEntries) {
flowsNode.add(flowEntryCodec.encode(entry, this));
}
return ok(root.toString()).build();
}
/**
* Gets the flows for a device, where the device is specified by Id.
*
* @param deviceId Id of device to look up
* @param flowId Id of flow to look up
* @return flow data as an array
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("{deviceId}/{flowId}")
public Response getFlowByDeviceIdAndFlowId(@PathParam("deviceId") String deviceId,
@PathParam("flowId") long flowId) {
final Iterable<FlowEntry> deviceEntries =
service.getFlowEntries(DeviceId.deviceId(deviceId));
if (!deviceEntries.iterator().hasNext()) {
throw new ItemNotFoundException(DEVICE_NOT_FOUND);
}
for (final FlowEntry entry : deviceEntries) {
if (entry.id().value() == flowId) {
flowsNode.add(flowEntryCodec.encode(entry, this));
}
}
return ok(root.toString()).build();
}
}
......@@ -58,7 +58,7 @@ public class IntentsWebResource extends AbstractWebResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("{id}")
public Response getHostById(@PathParam("id") long id) {
public Response getIntentById(@PathParam("id") long id) {
final Intent intent = nullIsNotFound(get(IntentService.class)
.getIntent(IntentId.valueOf(id)),
INTENT_NOT_FOUND);
......
......@@ -39,6 +39,7 @@
org.onosproject.rest.LinksWebResource,
org.onosproject.rest.HostsWebResource,
org.onosproject.rest.IntentsWebResource,
org.onosproject.rest.FlowsWebResource,
org.onosproject.rest.ConfigResource
</param-value>
</init-param>
......
/*
* Copyright 2014 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.rest;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.onlab.osgi.ServiceDirectory;
import org.onlab.osgi.TestServiceDirectory;
import org.onlab.packet.MacAddress;
import org.onlab.rest.BaseResource;
import org.onosproject.codec.CodecService;
import org.onosproject.codec.impl.CodecManager;
import org.onosproject.core.DefaultGroupId;
import org.onosproject.core.GroupId;
import org.onosproject.net.DefaultDevice;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.FlowEntry;
import org.onosproject.net.flow.FlowId;
import org.onosproject.net.flow.FlowRuleService;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.flow.criteria.Criterion;
import org.onosproject.net.flow.instructions.Instruction;
import org.onosproject.net.flow.instructions.L0ModificationInstruction;
import com.eclipsesource.json.JsonArray;
import com.eclipsesource.json.JsonObject;
import com.eclipsesource.json.JsonValue;
import com.google.common.collect.ImmutableSet;
import com.sun.jersey.api.client.UniformInterfaceException;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.test.framework.JerseyTest;
import static org.easymock.EasyMock.anyObject;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
/**
* Unit tests for Flows REST APIs.
*/
public class FlowsResourceTest extends JerseyTest {
final FlowRuleService mockFlowService = createMock(FlowRuleService.class);
final HashMap<DeviceId, Set<FlowEntry>> rules = new HashMap<>();
final DeviceService mockDeviceService = createMock(DeviceService.class);
final DeviceId deviceId1 = DeviceId.deviceId("1");
final DeviceId deviceId2 = DeviceId.deviceId("2");
final DeviceId deviceId3 = DeviceId.deviceId("3");
final Device device1 = new DefaultDevice(null, deviceId1, Device.Type.OTHER,
"", "", "", "", null);
final Device device2 = new DefaultDevice(null, deviceId2, Device.Type.OTHER,
"", "", "", "", null);
final MockFlowEntry flow1 = new MockFlowEntry(deviceId1, 1);
final MockFlowEntry flow2 = new MockFlowEntry(deviceId1, 2);
final MockFlowEntry flow3 = new MockFlowEntry(deviceId2, 3);
final MockFlowEntry flow4 = new MockFlowEntry(deviceId2, 4);
final MockFlowEntry flow5 = new MockFlowEntry(deviceId2, 5);
final MockFlowEntry flow6 = new MockFlowEntry(deviceId2, 6);
/**
* Mock class for a flow entry.
*/
private static class MockFlowEntry implements FlowEntry {
final DeviceId deviceId;
final long baseValue;
TrafficTreatment treatment;
TrafficSelector selector;
public MockFlowEntry(DeviceId deviceId, long id) {
this.deviceId = deviceId;
this.baseValue = id * 100;
}
@Override
public FlowEntryState state() {
return FlowEntryState.ADDED;
}
@Override
public long life() {
return baseValue + 11;
}
@Override
public long packets() {
return baseValue + 22;
}
@Override
public long bytes() {
return baseValue + 33;
}
@Override
public long lastSeen() {
return baseValue + 44;
}
@Override
public int errType() {
return 0;
}
@Override
public int errCode() {
return 0;
}
@Override
public FlowId id() {
final long id = baseValue + 55;
return FlowId.valueOf(id);
}
@Override
public short appId() {
return 2;
}
@Override
public GroupId groupId() {
return new DefaultGroupId(3);
}
@Override
public int priority() {
return (int) (baseValue + 66);
}
@Override
public DeviceId deviceId() {
return deviceId;
}
@Override
public TrafficSelector selector() {
return selector;
}
@Override
public TrafficTreatment treatment() {
return treatment;
}
@Override
public int timeout() {
return (int) (baseValue + 77);
}
@Override
public boolean isPermanent() {
return false;
}
}
public FlowsResourceTest() {
super("org.onosproject.rest");
}
/**
* Populates some flows used as testing data.
*/
private void setupMockFlows() {
flow2.treatment = DefaultTrafficTreatment.builder()
.add(new L0ModificationInstruction.ModLambdaInstruction(
L0ModificationInstruction.L0SubType.LAMBDA, (short) 4))
.add(new L0ModificationInstruction.ModLambdaInstruction(
L0ModificationInstruction.L0SubType.LAMBDA, (short) 5))
.setEthDst(MacAddress.BROADCAST)
.build();
flow2.selector = DefaultTrafficSelector.builder()
.matchEthType((short) 3)
.matchIPProtocol((byte) 9)
.build();
flow4.treatment = DefaultTrafficTreatment.builder()
.add(new L0ModificationInstruction.ModLambdaInstruction(
L0ModificationInstruction.L0SubType.LAMBDA, (short) 6))
.build();
final Set<FlowEntry> flows1 = new HashSet<>();
flows1.add(flow1);
flows1.add(flow2);
final Set<FlowEntry> flows2 = new HashSet<>();
flows1.add(flow3);
flows1.add(flow4);
rules.put(deviceId1, flows1);
rules.put(deviceId2, flows2);
expect(mockFlowService.getFlowEntries(deviceId1))
.andReturn(rules.get(deviceId1)).anyTimes();
expect(mockFlowService.getFlowEntries(deviceId2))
.andReturn(rules.get(deviceId2)).anyTimes();
}
/**
* Sets up the global values for all the tests.
*/
@Before
public void setUp() {
// Mock device service
expect(mockDeviceService.getDevice(deviceId1))
.andReturn(device1);
expect(mockDeviceService.getDevice(deviceId2))
.andReturn(device2);
expect(mockDeviceService.getDevices())
.andReturn(ImmutableSet.of(device1, device2));
// Register the services needed for the test
final CodecManager codecService = new CodecManager();
codecService.activate();
ServiceDirectory testDirectory =
new TestServiceDirectory()
.add(FlowRuleService.class, mockFlowService)
.add(DeviceService.class, mockDeviceService)
.add(CodecService.class, codecService);
BaseResource.setServiceDirectory(testDirectory);
}
/**
* Cleans up and verifies the mocks.
*
* @throws Exception if the super teardown fails.
*/
@After
public void tearDown() throws Exception {
super.tearDown();
verify(mockFlowService);
}
/**
* Hamcrest matcher to check that a flow representation in JSON matches
* the actual flow entry.
*/
public static class FlowJsonMatcher extends TypeSafeMatcher<JsonObject> {
private final FlowEntry flow;
private String reason = "";
public FlowJsonMatcher(FlowEntry flowValue) {
flow = flowValue;
}
@Override
public boolean matchesSafely(JsonObject jsonFlow) {
// check id
final String jsonId = jsonFlow.get("id").asString();
final String flowId = Long.toString(flow.id().value());
if (!jsonId.equals(flowId)) {
reason = "id " + flow.id().toString();
return false;
}
// check application id
final int jsonAppId = jsonFlow.get("appId").asInt();
if (jsonAppId != flow.appId()) {
reason = "appId " + Short.toString(flow.appId());
return false;
}
// check device id
final String jsonDeviceId = jsonFlow.get("deviceId").asString();
if (!jsonDeviceId.equals(flow.deviceId().toString())) {
reason = "deviceId " + flow.deviceId();
return false;
}
// check treatment and instructions array
if (flow.treatment() != null) {
final JsonObject jsonTreatment = jsonFlow.get("treatment").asObject();
final JsonArray jsonInstructions = jsonTreatment.get("instructions").asArray();
if (flow.treatment().instructions().size() != jsonInstructions.size()) {
reason = "instructions array size of " +
Integer.toString(flow.treatment().instructions().size());
return false;
}
for (final Instruction instruction : flow.treatment().instructions()) {
boolean instructionFound = false;
final String instructionString = instruction.toString();
for (int instructionIndex = 0; instructionIndex < jsonInstructions.size(); instructionIndex++) {
final JsonValue value = jsonInstructions.get(instructionIndex);
if (value.asString().equals(instructionString)) {
instructionFound = true;
}
}
if (!instructionFound) {
reason = "instruction " + instructionString;
return false;
}
}
}
// check selector and criteria array
if (flow.selector() != null) {
final JsonObject jsonTreatment = jsonFlow.get("selector").asObject();
final JsonArray jsonCriteria = jsonTreatment.get("criteria").asArray();
if (flow.selector().criteria().size() != jsonCriteria.size()) {
reason = "criteria array size of " +
Integer.toString(flow.selector().criteria().size());
return false;
}
for (final Criterion criterion : flow.selector().criteria()) {
boolean criterionFound = false;
final String criterionString = criterion.toString();
for (int criterionIndex = 0; criterionIndex < jsonCriteria.size(); criterionIndex++) {
final JsonValue value = jsonCriteria.get(criterionIndex);
if (value.asString().equals(criterionString)) {
criterionFound = true;
}
}
if (!criterionFound) {
reason = "criterion " + criterionString;
return false;
}
}
}
return true;
}
@Override
public void describeTo(Description description) {
description.appendText(reason);
}
}
/**
* Factory to allocate a flow matcher.
*
* @param flow flow object we are looking for
* @return matcher
*/
private static FlowJsonMatcher matchesFlow(FlowEntry flow) {
return new FlowJsonMatcher(flow);
}
/**
* Hamcrest matcher to check that a flow is represented properly in a JSON
* array of flows.
*/
public static class FlowJsonArrayMatcher extends TypeSafeMatcher<JsonArray> {
private final FlowEntry flow;
private String reason = "";
public FlowJsonArrayMatcher(FlowEntry flowValue) {
flow = flowValue;
}
@Override
public boolean matchesSafely(JsonArray json) {
boolean flowFound = false;
for (int jsonFlowIndex = 0; jsonFlowIndex < json.size();
jsonFlowIndex++) {
final JsonObject jsonFlow = json.get(jsonFlowIndex).asObject();
final String flowId = Long.toString(flow.id().value());
final String jsonFlowId = jsonFlow.get("id").asString();
if (jsonFlowId.equals(flowId)) {
flowFound = true;
// We found the correct flow, check attribute values
assertThat(jsonFlow, matchesFlow(flow));
}
}
if (!flowFound) {
reason = "Flow with id " + flow.id().toString() + " not found";
return false;
} else {
return true;
}
}
@Override
public void describeTo(Description description) {
description.appendText(reason);
}
}
/**
* Factory to allocate a flow array matcher.
*
* @param flow flow object we are looking for
* @return matcher
*/
private static FlowJsonArrayMatcher hasFlow(FlowEntry flow) {
return new FlowJsonArrayMatcher(flow);
}
/**
* Tests the result of the rest api GET when there are no flows.
*/
@Test
public void testFlowsEmptyArray() {
expect(mockFlowService.getFlowEntries(deviceId1))
.andReturn(null).anyTimes();
expect(mockFlowService.getFlowEntries(deviceId2))
.andReturn(null).anyTimes();
replay(mockFlowService);
replay(mockDeviceService);
final WebResource rs = resource();
final String response = rs.path("flows").get(String.class);
assertThat(response, is("{\"flows\":[]}"));
}
/**
* Tests the result of the rest api GET when there are active flows.
*/
@Test
public void testFlowsPopulatedArray() {
setupMockFlows();
replay(mockFlowService);
replay(mockDeviceService);
final WebResource rs = resource();
final String response = rs.path("flows").get(String.class);
final JsonObject result = JsonObject.readFrom(response);
assertThat(result, notNullValue());
assertThat(result.names(), hasSize(1));
assertThat(result.names().get(0), is("flows"));
final JsonArray jsonFlows = result.get("flows").asArray();
assertThat(jsonFlows, notNullValue());
assertThat(jsonFlows, hasFlow(flow1));
assertThat(jsonFlows, hasFlow(flow2));
assertThat(jsonFlows, hasFlow(flow3));
assertThat(jsonFlows, hasFlow(flow4));
}
/**
* Tests the result of a rest api GET for a device.
*/
@Test
public void testFlowsSingleDevice() {
setupMockFlows();
final Set<FlowEntry> flows = new HashSet<>();
flows.add(flow5);
flows.add(flow6);
expect(mockFlowService.getFlowEntries(anyObject()))
.andReturn(flows).anyTimes();
replay(mockFlowService);
replay(mockDeviceService);
final WebResource rs = resource();
final String response = rs.path("flows/" + deviceId3).get(String.class);
final JsonObject result = JsonObject.readFrom(response);
assertThat(result, notNullValue());
assertThat(result.names(), hasSize(1));
assertThat(result.names().get(0), is("flows"));
final JsonArray jsonFlows = result.get("flows").asArray();
assertThat(jsonFlows, notNullValue());
assertThat(jsonFlows, hasFlow(flow5));
assertThat(jsonFlows, hasFlow(flow6));
}
/**
* Tests the result of a rest api GET for a device.
*/
@Test
public void testFlowsSingleDeviceWithFlowId() {
setupMockFlows();
final Set<FlowEntry> flows = new HashSet<>();
flows.add(flow5);
flows.add(flow6);
expect(mockFlowService.getFlowEntries(anyObject()))
.andReturn(flows).anyTimes();
replay(mockFlowService);
replay(mockDeviceService);
final WebResource rs = resource();
final String response = rs.path("flows/" + deviceId3 + "/"
+ Long.toString(flow5.id().value())).get(String.class);
final JsonObject result = JsonObject.readFrom(response);
assertThat(result, notNullValue());
assertThat(result.names(), hasSize(1));
assertThat(result.names().get(0), is("flows"));
final JsonArray jsonFlows = result.get("flows").asArray();
assertThat(jsonFlows, notNullValue());
assertThat(jsonFlows, hasFlow(flow5));
assertThat(jsonFlows, not(hasFlow(flow6)));
}
/**
* Tests that a fetch of a non-existent device object throws an exception.
*/
@Test
public void testBadGet() {
expect(mockFlowService.getFlowEntries(deviceId1))
.andReturn(null).anyTimes();
expect(mockFlowService.getFlowEntries(deviceId2))
.andReturn(null).anyTimes();
replay(mockFlowService);
replay(mockDeviceService);
WebResource rs = resource();
try {
rs.path("flows/0").get(String.class);
fail("Fetch of non-existent device did not throw an exception");
} catch (UniformInterfaceException ex) {
assertThat(ex.getMessage(),
containsString("returned a response status of"));
}
}
}
......@@ -256,6 +256,7 @@ public class IntentsResourceTest extends JerseyTest {
private static IntentJsonArrayMatcher hasIntent(Intent intent) {
return new IntentJsonArrayMatcher(intent);
}
@Before
public void setUp() {
expect(mockIntentService.getIntents()).andReturn(intents).anyTimes();
......
......@@ -63,10 +63,7 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
......@@ -131,6 +128,7 @@
com.fasterxml.jackson.databind,
com.fasterxml.jackson.databind.node,
com.google.common.base.*,
com.google.common.collect.*,
org.eclipse.jetty.websocket.*,
org.onlab.util.*,
org.onlab.osgi.*,
......