[ONOS-3163] Flow classifier web resource UT. and web resource fixes.
Change-Id: Ie23a450e2944c969753af5f5afb38780833b5a7e
diff --git a/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/FlowClassifierWebResource.java b/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/FlowClassifierWebResource.java
index 7a57c0a..ee0dec3 100644
--- a/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/FlowClassifierWebResource.java
+++ b/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/FlowClassifierWebResource.java
@@ -15,15 +15,12 @@
*/
package org.onosproject.vtnweb.resources;
-import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
-import static org.onlab.util.Tools.nullIsNotFound;
import static javax.ws.rs.core.Response.Status.NOT_FOUND;
+import static javax.ws.rs.core.Response.Status.OK;
+import static org.onlab.util.Tools.nullIsNotFound;
import java.io.IOException;
import java.io.InputStream;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.UUID;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
@@ -36,13 +33,17 @@
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
+import org.onosproject.rest.AbstractWebResource;
import org.onosproject.vtnrsc.FlowClassifier;
import org.onosproject.vtnrsc.FlowClassifierId;
-import org.onosproject.rest.AbstractWebResource;
import org.onosproject.vtnrsc.flowclassifier.FlowClassifierService;
import org.onosproject.vtnweb.web.FlowClassifierCodec;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
@@ -51,106 +52,84 @@
@Path("flow_classifiers")
public class FlowClassifierWebResource extends AbstractWebResource {
+ private final Logger log = LoggerFactory.getLogger(FlowClassifierWebResource.class);
+
final FlowClassifierService service = get(FlowClassifierService.class);
- final ObjectNode root = mapper().createObjectNode();
public static final String FLOW_CLASSIFIER_NOT_FOUND = "Flow classifier not found";
/**
- * Get all flow classifiers created. Returns list of all flow classifiers
- * created.
+ * Get all flow classifiers created.
*
- * @return 200 OK
+ * @return 200 OK, 404 if given flow classifier does not exist
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getFlowClassifiers() {
- Iterable<FlowClassifier> flowClassifiers = service.getFlowClassifiers();
+ final Iterable<FlowClassifier> flowClassifiers = service.getFlowClassifiers();
ObjectNode result = new ObjectMapper().createObjectNode();
- result.set("flow_classifiers", new FlowClassifierCodec().encode(flowClassifiers, this));
+ ArrayNode flowClassifierEntry = result.putArray("flow_classifiers");
+ if (flowClassifiers != null) {
+ for (final FlowClassifier flowClassifier : flowClassifiers) {
+ flowClassifierEntry.add(new FlowClassifierCodec().encode(flowClassifier, this));
+ }
+ }
return ok(result.toString()).build();
}
/**
- * Get details of a flow classifier. Returns details of a specified flow
- * classifier id.
+ * Get details of a flow classifier.
*
* @param id flow classifier id
- * @return 200 OK
+ * @return 200 OK , 404 if given identifier does not exist
*/
@GET
@Path("{flow_id}")
@Produces(MediaType.APPLICATION_JSON)
public Response getFlowClassifier(@PathParam("flow_id") String id) {
- if (!service.hasFlowClassifier(FlowClassifierId.of(UUID.fromString(id)))) {
+ if (!service.hasFlowClassifier(FlowClassifierId.of(id))) {
return Response.status(NOT_FOUND).entity(FLOW_CLASSIFIER_NOT_FOUND).build();
}
- FlowClassifier flowClassifier = nullIsNotFound(
- service.getFlowClassifier(FlowClassifierId.of(UUID.fromString(id))),
+ FlowClassifier flowClassifier = nullIsNotFound(service.getFlowClassifier(FlowClassifierId.of(id)),
FLOW_CLASSIFIER_NOT_FOUND);
ObjectNode result = new ObjectMapper().createObjectNode();
result.set("flow_classifier", new FlowClassifierCodec().encode(flowClassifier, this));
+
return ok(result.toString()).build();
}
/**
* Creates and stores a new flow classifier.
*
- * @param flowClassifierId flow classifier identifier
* @param stream flow classifier from JSON
* @return status of the request - CREATED if the JSON is correct,
- * BAD_REQUEST if the JSON is invalid
- */
- @POST
- @Path("{flow_id}")
- @Consumes(MediaType.APPLICATION_JSON)
- @Produces(MediaType.APPLICATION_JSON)
- public Response createFlowClassifier(@PathParam("flow_id") String flowClassifierId, InputStream stream) {
- URI location;
- try {
- ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
-
- FlowClassifier flowClassifier = codec(FlowClassifier.class).decode(jsonTree, this);
- service.createFlowClassifier(flowClassifier);
- location = new URI(flowClassifierId);
- } catch (IOException | URISyntaxException ex) {
- throw new IllegalArgumentException(ex);
- }
- return Response.created(location).build();
- }
-
- /**
- * Creates and stores a new flow classifier.
- *
- * @param stream flow classifier from JSON
- * @return status of the request - CREATED if the JSON is correct,
- * BAD_REQUEST if the JSON is invalid
+ * BAD_REQUEST if the JSON is invalid
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response createFlowClassifier(InputStream stream) {
- URI location;
try {
- ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
+ ObjectMapper mapper = new ObjectMapper();
+ ObjectNode jsonTree = (ObjectNode) mapper.readTree(stream);
+ JsonNode flow = jsonTree.get("flow_classifier");
- FlowClassifier flowClassifier = codec(FlowClassifier.class).decode(jsonTree, this);
- service.createFlowClassifier(flowClassifier);
- location = new URI(flowClassifier.flowClassifierId().toString());
- } catch (IOException | URISyntaxException ex) {
+ FlowClassifier flowClassifier = new FlowClassifierCodec().decode((ObjectNode) flow, this);
+ Boolean issuccess = nullIsNotFound(service.createFlowClassifier(flowClassifier), FLOW_CLASSIFIER_NOT_FOUND);
+ return Response.status(OK).entity(issuccess.toString()).build();
+ } catch (IOException ex) {
+ log.error("Exception while creating flow classifier {}.", ex.toString());
throw new IllegalArgumentException(ex);
}
- return Response.created(location).build();
}
/**
- * Update details of a flow classifier. Update details of a specified flow
- * classifier id.
+ * Update details of a flow classifier.
*
* @param id flow classifier id
* @param stream InputStream
- * @return 200 OK
+ * @return 200 OK, 404 if given identifier does not exist
*/
@PUT
@Path("{flow_id}")
@@ -158,35 +137,29 @@
@Consumes(MediaType.APPLICATION_JSON)
public Response updateFlowClassifier(@PathParam("flow_id") String id, final InputStream stream) {
try {
- ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
- FlowClassifier flowClassifier = codec(FlowClassifier.class).decode(jsonTree, this);
+
+ JsonNode jsonTree = mapper().readTree(stream);
+ JsonNode flow = jsonTree.get("flow_classifier");
+ FlowClassifier flowClassifier = new FlowClassifierCodec().decode((ObjectNode) flow, this);
Boolean result = nullIsNotFound(service.updateFlowClassifier(flowClassifier), FLOW_CLASSIFIER_NOT_FOUND);
- if (!result) {
- return Response.status(204).entity(FLOW_CLASSIFIER_NOT_FOUND).build();
- }
- return Response.status(203).entity(result.toString()).build();
- } catch (Exception e) {
- return Response.status(INTERNAL_SERVER_ERROR).entity(e.toString()).build();
+ return Response.status(OK).entity(result.toString()).build();
+ } catch (IOException e) {
+ log.error("Update flow classifier failed because of exception {}.", e.toString());
+ throw new IllegalArgumentException(e);
}
}
/**
- * Delete details of a flow classifier. Delete details of a specified flow
- * classifier id.
+ * Delete details of a flow classifier.
*
* @param id flow classifier id
- * @return 200 OK
- * @throws IOException when input doesn't match.
*/
@Path("{flow_id}")
@DELETE
- public Response deleteFlowClassifier(@PathParam("flow_id") String id) throws IOException {
- try {
- FlowClassifierId flowClassifierId = FlowClassifierId.of(UUID.fromString(id));
- service.removeFlowClassifier(flowClassifierId);
- return Response.status(201).entity("SUCCESS").build();
- } catch (Exception e) {
- return Response.status(INTERNAL_SERVER_ERROR).entity(e.toString()).build();
- }
+ public void deleteFlowClassifier(@PathParam("flow_id") String id) {
+ log.debug("Deletes flow classifier by identifier {}.", id);
+ FlowClassifierId flowClassifierId = FlowClassifierId.of(id);
+ Boolean issuccess = nullIsNotFound(service.removeFlowClassifier(flowClassifierId), FLOW_CLASSIFIER_NOT_FOUND);
+
}
}
diff --git a/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/FlowClassifierResourceTest.java b/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/FlowClassifierResourceTest.java
new file mode 100644
index 0000000..67f8f35
--- /dev/null
+++ b/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/FlowClassifierResourceTest.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright 2014-2015 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.vtnweb.resources;
+
+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.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+import javax.ws.rs.core.MediaType;
+
+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.IpPrefix;
+import org.onlab.rest.BaseResource;
+import org.onosproject.vtnrsc.FlowClassifier;
+import org.onosproject.vtnrsc.FlowClassifierId;
+import org.onosproject.vtnrsc.TenantId;
+import org.onosproject.vtnrsc.VirtualPortId;
+import org.onosproject.vtnrsc.flowclassifier.FlowClassifierService;
+
+import com.eclipsesource.json.JsonObject;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.UniformInterfaceException;
+import com.sun.jersey.api.client.WebResource;
+/**
+ * Unit tests for flow classifier REST APIs.
+ */
+public class FlowClassifierResourceTest extends VtnResourceTest {
+
+ final FlowClassifierService flowClassifierService = createMock(FlowClassifierService.class);
+
+ FlowClassifierId flowClassifierId1 = FlowClassifierId.of("4a334cd4-fe9c-4fae-af4b-321c5e2eb051");
+ TenantId tenantId1 = TenantId.tenantId("1814726e2d22407b8ca76db5e567dcf1");
+ VirtualPortId srcPortId1 = VirtualPortId.portId("dace4513-24fc-4fae-af4b-321c5e2eb3d1");
+ VirtualPortId dstPortId1 = VirtualPortId.portId("aef3478a-4a56-2a6e-cd3a-9dee4e2ec345");
+
+ final MockFlowClassifier flowClassifier1 = new MockFlowClassifier(flowClassifierId1, tenantId1, "flowClassifier1",
+ "Mock flow classifier", "IPv4", "IP", 1001, 1500,
+ 5001, 6000, IpPrefix.valueOf("1.1.1.1/16"),
+ IpPrefix.valueOf("22.12.34.45/16"),
+ srcPortId1, dstPortId1);
+
+ /**
+ * Mock class for a flow classifier.
+ */
+ private static class MockFlowClassifier implements FlowClassifier {
+
+ private final FlowClassifierId flowClassifierId;
+ private final TenantId tenantId;
+ private final String name;
+ private final String description;
+ private final String etherType;
+ private final String protocol;
+ private final int minSrcPortRange;
+ private final int maxSrcPortRange;
+ private final int minDstPortRange;
+ private final int maxDstPortRange;
+ private final IpPrefix srcIpPrefix;
+ private final IpPrefix dstIpPrefix;
+ private final VirtualPortId srcPort;
+ private final VirtualPortId dstPort;
+
+ public MockFlowClassifier(FlowClassifierId flowClassifierId, TenantId tenantId, String name,
+ String description, String etherType, String protocol, int minSrcPortRange,
+ int maxSrcPortRange, int minDstPortRange, int maxDstPortRange, IpPrefix srcIpPrefix,
+ IpPrefix dstIpPrefix, VirtualPortId srcPort, VirtualPortId dstPort) {
+ this.flowClassifierId = flowClassifierId;
+ this.tenantId = tenantId;
+ this.name = name;
+ this.description = description;
+ this.etherType = etherType;
+ this.protocol = protocol;
+ this.minSrcPortRange = minSrcPortRange;
+ this.maxSrcPortRange = maxSrcPortRange;
+ this.minDstPortRange = minDstPortRange;
+ this.maxDstPortRange = maxDstPortRange;
+ this.srcIpPrefix = srcIpPrefix;
+ this.dstIpPrefix = dstIpPrefix;
+ this.srcPort = srcPort;
+ this.dstPort = dstPort;
+ }
+
+
+ @Override
+ public FlowClassifierId flowClassifierId() {
+ return flowClassifierId;
+ }
+
+ @Override
+ public TenantId tenantId() {
+ return tenantId;
+ }
+
+ @Override
+ public String name() {
+ return name;
+ }
+
+ @Override
+ public String description() {
+ return description;
+ }
+
+ @Override
+ public String etherType() {
+ return etherType;
+ }
+
+ @Override
+ public String protocol() {
+ return protocol;
+ }
+
+ @Override
+ public int minSrcPortRange() {
+ return minSrcPortRange;
+ }
+
+ @Override
+ public int maxSrcPortRange() {
+ return maxSrcPortRange;
+ }
+
+ @Override
+ public int minDstPortRange() {
+ return minDstPortRange;
+ }
+
+ @Override
+ public int maxDstPortRange() {
+ return maxDstPortRange;
+ }
+
+ @Override
+ public IpPrefix srcIpPrefix() {
+ return srcIpPrefix;
+ }
+
+ @Override
+ public IpPrefix dstIpPrefix() {
+ return dstIpPrefix;
+ }
+
+ @Override
+ public VirtualPortId srcPort() {
+ return srcPort;
+ }
+
+ @Override
+ public VirtualPortId dstPort() {
+ return dstPort;
+ }
+
+ @Override
+ public boolean exactMatch(FlowClassifier flowClassifier) {
+ return this.equals(flowClassifier) &&
+ Objects.equals(this.flowClassifierId, flowClassifier.flowClassifierId()) &&
+ Objects.equals(this.tenantId, flowClassifier.tenantId());
+ }
+ }
+
+ /**
+ * Sets up the global values for all the tests.
+ */
+ @Before
+ public void setUpTest() {
+ ServiceDirectory testDirectory = new TestServiceDirectory().add(FlowClassifierService.class,
+ flowClassifierService);
+ BaseResource.setServiceDirectory(testDirectory);
+
+ }
+
+ /**
+ * Cleans up.
+ */
+ @After
+ public void tearDownTest() {
+ }
+
+ /**
+ * Tests the result of the rest api GET when there are no flow classifiers.
+ */
+ @Test
+ public void testFlowClassifiersEmpty() {
+
+ expect(flowClassifierService.getFlowClassifiers()).andReturn(null).anyTimes();
+ replay(flowClassifierService);
+ final WebResource rs = resource();
+ final String response = rs.path("flow_classifiers").get(String.class);
+ assertThat(response, is("{\"flow_classifiers\":[]}"));
+ }
+
+ /**
+ * Tests the result of a rest api GET for flow classifier id.
+ */
+ @Test
+ public void testGetFlowClassifierId() {
+
+ final Set<FlowClassifier> flowClassifiers = new HashSet<>();
+ flowClassifiers.add(flowClassifier1);
+
+ expect(flowClassifierService.hasFlowClassifier(anyObject())).andReturn(true).anyTimes();
+ expect(flowClassifierService.getFlowClassifier(anyObject())).andReturn(flowClassifier1).anyTimes();
+ replay(flowClassifierService);
+
+ final WebResource rs = resource();
+ final String response = rs.path("flow_classifiers/4a334cd4-fe9c-4fae-af4b-321c5e2eb051").get(String.class);
+ final JsonObject result = JsonObject.readFrom(response);
+ assertThat(result, notNullValue());
+ }
+
+ /**
+ * Tests that a fetch of a non-existent flow classifier object throws an exception.
+ */
+ @Test
+ public void testBadGet() {
+ expect(flowClassifierService.getFlowClassifier(anyObject()))
+ .andReturn(null).anyTimes();
+ replay(flowClassifierService);
+ WebResource rs = resource();
+ try {
+ rs.path("flow_classifiers/78dcd363-fc23-aeb6-f44b-56dc5aafb3ae").get(String.class);
+ fail("Fetch of non-existent flow classifier did not throw an exception");
+ } catch (UniformInterfaceException ex) {
+ assertThat(ex.getMessage(),
+ containsString("returned a response status of"));
+ }
+ }
+
+ /**
+ * Tests creating a flow classifier with POST.
+ */
+ @Test
+ public void testPost() {
+
+ expect(flowClassifierService.createFlowClassifier(anyObject()))
+ .andReturn(true).anyTimes();
+ replay(flowClassifierService);
+
+ WebResource rs = resource();
+ InputStream jsonStream = FlowClassifierResourceTest.class.getResourceAsStream("post-FlowClassifier.json");
+
+ ClientResponse response = rs.path("flow_classifiers")
+ .type(MediaType.APPLICATION_JSON_TYPE)
+ .post(ClientResponse.class, jsonStream);
+ assertThat(response.getStatus(), is(HttpURLConnection.HTTP_OK));
+ }
+
+ /**
+ * Tests deleting a flow classifier.
+ */
+ @Test
+ public void testDelete() {
+ expect(flowClassifierService.removeFlowClassifier(anyObject()))
+ .andReturn(true).anyTimes();
+ replay(flowClassifierService);
+
+ WebResource rs = resource();
+
+ String location = "flow_classifiers/4a334cd4-fe9c-4fae-af4b-321c5e2eb051";
+
+ ClientResponse deleteResponse = rs.path(location)
+ .type(MediaType.APPLICATION_JSON_TYPE)
+ .delete(ClientResponse.class);
+ assertThat(deleteResponse.getStatus(),
+ is(HttpURLConnection.HTTP_NO_CONTENT));
+ }
+}
diff --git a/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/VtnResourceTest.java b/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/VtnResourceTest.java
new file mode 100644
index 0000000..4b95844
--- /dev/null
+++ b/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/VtnResourceTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2015 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.vtnweb.resources;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+
+import com.sun.jersey.test.framework.AppDescriptor;
+import com.sun.jersey.test.framework.JerseyTest;
+import com.sun.jersey.test.framework.WebAppDescriptor;
+
+/**
+ * Base class for VTN REST API tests. Performs common configuration operations.
+ */
+public class VtnResourceTest extends JerseyTest {
+
+ /**
+ * Assigns an available port for the test.
+ *
+ * @param defaultPort If a port cannot be determined, this one is used.
+ * @return free port
+ */
+ @Override
+ public int getPort(int defaultPort) {
+ try {
+ ServerSocket socket = new ServerSocket(0);
+ socket.setReuseAddress(true);
+ int port = socket.getLocalPort();
+ socket.close();
+ return port;
+ } catch (IOException ioe) {
+ return defaultPort;
+ }
+ }
+
+ @Override
+ public AppDescriptor configure() {
+ return new WebAppDescriptor.Builder("org.onosproject.vtnweb.resources").build();
+ }
+
+}
diff --git a/apps/vtn/vtnweb/src/test/resources/org/onosproject/vtnweb/resources/post-FlowClassifier.json b/apps/vtn/vtnweb/src/test/resources/org/onosproject/vtnweb/resources/post-FlowClassifier.json
new file mode 100644
index 0000000..6e72e8f
--- /dev/null
+++ b/apps/vtn/vtnweb/src/test/resources/org/onosproject/vtnweb/resources/post-FlowClassifier.json
@@ -0,0 +1,14 @@
+{"flow_classifier": {
+ "id": "4a334cd4-fe9c-4fae-af4b-321c5e2eb051",
+ "name": "flow1",
+ "tenant_id": "1814726e2d22407b8ca76db5e567dcf1",
+ "description": "flow classifier",
+ "ethertype": "IPv4",
+ "protocol": "tcp",
+ "source_port_range_min": 22, "source_port_range_max": 4000,
+ "destination_port_range_min": 80, "destination_port_range_max": 80,
+ "source_ip_prefix": "1.1.1.1/16" , "destination_ip_prefix": "22.12.34.45/16",
+ "logical_destination_port": "dace4513-24fc-4fae-af4b-321c5e2eb3d1",
+ "logical_source_port": "aef3478a-4a56-2a6e-cd3a-9dee4e2ec345"
+ }
+}