REST API to create flows
Change-Id: I5d001782249c0eab249d7aa857ae465da95b5955
diff --git a/web/api/src/main/java/org/onosproject/rest/exceptions/IllegalArgumentExceptionMapper.java b/web/api/src/main/java/org/onosproject/rest/exceptions/IllegalArgumentExceptionMapper.java
new file mode 100644
index 0000000..6a381f7
--- /dev/null
+++ b/web/api/src/main/java/org/onosproject/rest/exceptions/IllegalArgumentExceptionMapper.java
@@ -0,0 +1,29 @@
+/*
+ * 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.rest.exceptions;
+
+import javax.ws.rs.core.Response;
+
+/**
+ * Mapper for illegal argument exceptions to the BAD_REQUEST response code.
+ */
+public class IllegalArgumentExceptionMapper extends AbstractMapper<IllegalArgumentException> {
+ @Override
+ protected Response.Status responseStatus() {
+ return Response.Status.BAD_REQUEST;
+ }
+}
+
diff --git a/web/api/src/main/java/org/onosproject/rest/resources/FlowsWebResource.java b/web/api/src/main/java/org/onosproject/rest/resources/FlowsWebResource.java
index 49840c4..9c39268 100644
--- a/web/api/src/main/java/org/onosproject/rest/resources/FlowsWebResource.java
+++ b/web/api/src/main/java/org/onosproject/rest/resources/FlowsWebResource.java
@@ -15,23 +15,30 @@
*/
package org.onosproject.rest.resources;
-import com.fasterxml.jackson.databind.node.ArrayNode;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-import org.onlab.util.ItemNotFoundException;
-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 org.onosproject.rest.AbstractWebResource;
+import java.io.IOException;
+import java.io.InputStream;
+import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
+import javax.ws.rs.POST;
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.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.rest.AbstractWebResource;
+
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
/**
* REST resource for interacting with the inventory of flows.
*/
@@ -113,4 +120,27 @@
}
return ok(root).build();
}
+
+ /**
+ * Creates a flow rule from a POST of a JSON string and attempts to apply it.
+ *
+ * @param stream input JSON
+ * @return status of the request - ACCEPTED if the JSON is correct,
+ * BAD_REQUEST if the JSON is invalid
+ */
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response createFlow(InputStream stream) {
+ try {
+ FlowRuleService service = get(FlowRuleService.class);
+ ObjectNode root = (ObjectNode) mapper().readTree(stream);
+ FlowRule rule = codec(FlowRule.class).decode(root, this);
+ service.applyFlowRules(rule);
+ } catch (IOException ex) {
+ return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
+ }
+ return Response.status(Response.Status.ACCEPTED).build();
+ }
+
}
diff --git a/web/api/src/main/webapp/WEB-INF/web.xml b/web/api/src/main/webapp/WEB-INF/web.xml
index a3e2060..d8759fe 100644
--- a/web/api/src/main/webapp/WEB-INF/web.xml
+++ b/web/api/src/main/webapp/WEB-INF/web.xml
@@ -61,6 +61,7 @@
org.onosproject.rest.exceptions.ServerErrorMapper,
org.onosproject.rest.exceptions.BadRequestMapper,
org.onosproject.rest.exceptions.WebApplicationExceptionMapper,
+ org.onosproject.rest.exceptions.IllegalArgumentExceptionMapper,
org.onosproject.rest.resources.JsonBodyWriter,
org.onosproject.rest.resources.ApplicationsWebResource,
diff --git a/web/api/src/test/java/org/onosproject/rest/FlowsResourceTest.java b/web/api/src/test/java/org/onosproject/rest/FlowsResourceTest.java
index 0f0dae8..b18340a 100644
--- a/web/api/src/test/java/org/onosproject/rest/FlowsResourceTest.java
+++ b/web/api/src/test/java/org/onosproject/rest/FlowsResourceTest.java
@@ -15,10 +15,13 @@
*/
package org.onosproject.rest;
+import java.net.HttpURLConnection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
+import javax.ws.rs.core.MediaType;
+
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
import org.junit.After;
@@ -30,11 +33,13 @@
import org.onlab.rest.BaseResource;
import org.onosproject.codec.CodecService;
import org.onosproject.codec.impl.CodecManager;
+import org.onosproject.core.CoreService;
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.NetTestTools;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
@@ -51,12 +56,15 @@
import com.eclipsesource.json.JsonArray;
import com.eclipsesource.json.JsonObject;
import com.google.common.collect.ImmutableSet;
+import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.UniformInterfaceException;
import com.sun.jersey.api.client.WebResource;
import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.anyShort;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import static org.hamcrest.Matchers.containsString;
@@ -72,6 +80,8 @@
*/
public class FlowsResourceTest extends ResourceTest {
final FlowRuleService mockFlowService = createMock(FlowRuleService.class);
+ CoreService mockCoreService = createMock(CoreService.class);
+
final HashMap<DeviceId, Set<FlowEntry>> rules = new HashMap<>();
final DeviceService mockDeviceService = createMock(DeviceService.class);
@@ -245,6 +255,11 @@
expect(mockDeviceService.getDevices())
.andReturn(ImmutableSet.of(device1, device2));
+ // Mock Core Service
+ expect(mockCoreService.getAppId(anyShort()))
+ .andReturn(NetTestTools.APP_ID).anyTimes();
+ replay(mockCoreService);
+
// Register the services needed for the test
final CodecManager codecService = new CodecManager();
codecService.activate();
@@ -252,7 +267,8 @@
new TestServiceDirectory()
.add(FlowRuleService.class, mockFlowService)
.add(DeviceService.class, mockDeviceService)
- .add(CodecService.class, codecService);
+ .add(CodecService.class, codecService)
+ .add(CoreService.class, mockCoreService);
BaseResource.setServiceDirectory(testDirectory);
}
@@ -263,6 +279,7 @@
@After
public void tearDownTest() {
verify(mockFlowService);
+ verify(mockCoreService);
}
/**
@@ -542,4 +559,27 @@
containsString("returned a response status of"));
}
}
+
+ /**
+ * Tests creating a flow with POST.
+ */
+ @Test
+ public void testPost() {
+ String json = "{\"appId\":2,\"priority\":1,\"isPermanent\":true,"
+ + "\"deviceId\":\"of:0000000000000001\","
+ + "\"treatment\":{\"instructions\":[ {\"type\":\"OUTPUT\",\"port\":2}]},"
+ + "\"selector\":{\"criteria\":[ {\"type\":\"ETH_TYPE\",\"ethType\":2054}]}}";
+
+ mockFlowService.applyFlowRules(anyObject());
+ expectLastCall();
+ replay(mockFlowService);
+
+ WebResource rs = resource();
+
+
+ ClientResponse response = rs.path("flows/")
+ .type(MediaType.APPLICATION_JSON_TYPE)
+ .post(ClientResponse.class, json);
+ assertThat(response.getStatus(), is(HttpURLConnection.HTTP_ACCEPTED));
+ }
}