REST API for pseudowire addition / deletion.
Refactored pseudowire code to use REST in order
to add or delete pseudowires individually. Previous implementation
used the network configuration, which is now completely
removed from the code. Further, I re-organized the code
and create a utility class that holds all the necessary
functionality for verifying pseudowires.
Further, I removed all mastership checks in the pw code
since now a specific pseudowire creation is sent to a single
instance, which will handle the call.
Change-Id: I1eb5e7cc7730ad792ea84dd389475768153e2b68
diff --git a/web/src/main/java/org/onosproject/segmentrouting/package-info.java b/web/src/main/java/org/onosproject/segmentrouting/package-info.java
index ff9555e..1d50ba9 100644
--- a/web/src/main/java/org/onosproject/segmentrouting/package-info.java
+++ b/web/src/main/java/org/onosproject/segmentrouting/package-info.java
@@ -15,6 +15,6 @@
*/
/**
- * Segment routing application components.
+ * Segment routing REST implementation.
*/
package org.onosproject.segmentrouting.web;
\ No newline at end of file
diff --git a/web/src/main/java/org/onosproject/segmentrouting/web/PolicyWebResource.java b/web/src/main/java/org/onosproject/segmentrouting/web/PolicyWebResource.java
index abfba3c..cebcf84 100644
--- a/web/src/main/java/org/onosproject/segmentrouting/web/PolicyWebResource.java
+++ b/web/src/main/java/org/onosproject/segmentrouting/web/PolicyWebResource.java
@@ -26,7 +26,6 @@
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
-import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
@@ -37,7 +36,7 @@
/**
* Query, create and remove segment routing plicies.
*/
-@Path("policy")
+// @Path("policy")
public class PolicyWebResource extends AbstractWebResource {
private static final PolicyCodec POLICY_CODEC = new PolicyCodec();
diff --git a/web/src/main/java/org/onosproject/segmentrouting/web/PseudowireCodec.java b/web/src/main/java/org/onosproject/segmentrouting/web/PseudowireCodec.java
new file mode 100644
index 0000000..f2fe786
--- /dev/null
+++ b/web/src/main/java/org/onosproject/segmentrouting/web/PseudowireCodec.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2015-present Open Networking Foundation
+ *
+ * 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.segmentrouting.web;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onlab.packet.MplsLabel;
+import org.onlab.packet.VlanId;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.segmentrouting.pwaas.DefaultL2Tunnel;
+import org.onosproject.segmentrouting.pwaas.DefaultL2TunnelDescription;
+import org.onosproject.segmentrouting.pwaas.DefaultL2TunnelPolicy;
+import org.onosproject.segmentrouting.pwaas.L2Mode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.onosproject.segmentrouting.pwaas.PwaasUtil.*;
+
+/**
+ * Codec of PseudowireCodec class.
+ */
+public final class PseudowireCodec extends JsonCodec<DefaultL2TunnelDescription> {
+
+ // JSON field names
+ private static final String PW_ID = "pwId";
+ private static final String CP1 = "cP1";
+ private static final String CP2 = "cP2";
+ private static final String CP1_INNER_TAG = "cP1InnerTag";
+ private static final String CP1_OUTER_TAG = "cP1OuterTag";
+ private static final String CP2_INNER_TAG = "cP2InnerTag";
+ private static final String CP2_OUTER_TAG = "cP2OouterTag";
+ private static final String MODE = "mode";
+ private static final String SERVICE_DELIM_TAG = "serviceTag";
+ private static final String PW_LABEL = "pwLabel";
+
+ private static Logger log = LoggerFactory
+ .getLogger(PseudowireCodec.class);
+
+ @Override
+ public ObjectNode encode(DefaultL2TunnelDescription pseudowire, CodecContext context) {
+ final ObjectNode result = context.mapper().createObjectNode()
+ .put(PW_ID, pseudowire.l2Tunnel().tunnelId());
+
+ result.put(CP1, pseudowire.l2TunnelPolicy().cP1().toString());
+ result.put(CP2, pseudowire.l2TunnelPolicy().cP2().toString());
+
+ result.put(CP1_INNER_TAG, pseudowire.l2TunnelPolicy().cP1InnerTag().toString());
+ result.put(CP1_OUTER_TAG, pseudowire.l2TunnelPolicy().cP1OuterTag().toString());
+
+
+ result.put(CP2_INNER_TAG, pseudowire.l2TunnelPolicy().cP2InnerTag().toString());
+ result.put(CP2_OUTER_TAG, pseudowire.l2TunnelPolicy().cP2OuterTag().toString());
+
+ result.put(MODE, pseudowire.l2Tunnel().pwMode() == L2Mode.RAW ? "RAW" : "TAGGED");
+ result.put(SERVICE_DELIM_TAG, pseudowire.l2Tunnel().sdTag().toString());
+ result.put(PW_LABEL, pseudowire.l2Tunnel().pwLabel().toString());
+
+ return result;
+ }
+
+ /**
+ * Decodes a json containg a single field with the pseudowire id.
+ *
+ * @param json Json to decode.
+ * @return The pseudowire id.
+ */
+ public Integer decodeId(ObjectNode json) {
+
+ Integer id = parsePwId(json.path(PW_ID).asText());
+ if (id == null) {
+ log.error("Pseudowire id is not an integer!");
+ return null;
+ }
+
+ return id;
+ }
+
+ @Override
+ public DefaultL2TunnelDescription decode(ObjectNode json, CodecContext context) {
+
+ String tempString;
+
+ Integer id = parsePwId(json.path(PW_ID).asText());
+ if (id == null) {
+ log.error("Pseudowire id is not an integer");
+ return null;
+ }
+
+ ConnectPoint cP1, cP2;
+ try {
+ tempString = json.path(CP1).asText();
+ cP1 = ConnectPoint.deviceConnectPoint(tempString);
+ } catch (Exception e) {
+ log.error("cP1 is not a valid connect point!");
+ return null;
+ }
+
+ try {
+ tempString = json.path(CP2).asText();
+ cP2 = ConnectPoint.deviceConnectPoint(tempString);
+ } catch (Exception e) {
+ log.error("cP2 is not a valid connect point!");
+ return null;
+ }
+
+ VlanId cP1InnerVlan = parseVlan(json.path(CP1_INNER_TAG).asText());
+ VlanId cP1OuterVlan = parseVlan(json.path(CP1_OUTER_TAG).asText());
+ VlanId cP2InnerVlan = parseVlan(json.path(CP2_INNER_TAG).asText());
+ VlanId cP2OuterVlan = parseVlan(json.path(CP2_OUTER_TAG).asText());
+ if ((cP1InnerVlan == null) || (cP1OuterVlan == null) ||
+ (cP2InnerVlan == null) || (cP2OuterVlan == null)) {
+ log.error("One or more vlan for cp1 or cp2 is malformed, it shouldbe an integer / Any / None / *");
+ return null;
+ }
+
+ L2Mode mode = parseMode(json.path(MODE).asText());
+ if (mode == null) {
+ log.error("Mode should be RAW or TAGGED!");
+ return null;
+ }
+
+ VlanId sdTag = parseVlan(json.path(SERVICE_DELIM_TAG).asText());
+ if (sdTag == null) {
+ log.error("SD tag is malformed, it should be an integer / Any / None / *");
+ return null;
+ }
+
+ MplsLabel pwLabel = parsePWLabel(json.path(PW_LABEL).asText());
+ if (pwLabel == null) {
+ log.error("PW label is malformed, should be an integer!");
+ return null;
+ }
+
+ DefaultL2Tunnel l2Tunnel;
+ DefaultL2TunnelPolicy l2Policy;
+
+ l2Tunnel = new DefaultL2Tunnel(mode, sdTag, id, pwLabel);
+ l2Policy = new DefaultL2TunnelPolicy(id, cP1, cP1InnerVlan, cP1OuterVlan,
+ cP2, cP2InnerVlan, cP2OuterVlan);
+
+ return new DefaultL2TunnelDescription(l2Tunnel, l2Policy);
+
+ }
+}
diff --git a/web/src/main/java/org/onosproject/segmentrouting/web/PseudowireWebResource.java b/web/src/main/java/org/onosproject/segmentrouting/web/PseudowireWebResource.java
new file mode 100644
index 0000000..ba151a1
--- /dev/null
+++ b/web/src/main/java/org/onosproject/segmentrouting/web/PseudowireWebResource.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2015-present Open Networking Foundation
+ *
+ * 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.segmentrouting.web;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.rest.AbstractWebResource;
+import org.onosproject.segmentrouting.SegmentRoutingService;
+import org.onosproject.segmentrouting.pwaas.DefaultL2TunnelDescription;
+import org.onosproject.segmentrouting.pwaas.L2TunnelPolicy;
+import org.onosproject.segmentrouting.pwaas.L2Tunnel;
+import org.onosproject.segmentrouting.pwaas.L2TunnelHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Query, create and remove pseudowires.
+ */
+@Path("pseudowire")
+public class PseudowireWebResource extends AbstractWebResource {
+
+ private static final PseudowireCodec PSEUDOWIRE_CODEC = new PseudowireCodec();
+
+ private static Logger log = LoggerFactory
+ .getLogger(PseudowireWebResource.class);
+
+ /**
+ * Get all pseudowires.
+ * Returns an array of pseudowires.
+ *
+ * @return status of OK
+ */
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getPseudowire() {
+ SegmentRoutingService srService = get(SegmentRoutingService.class);
+
+ log.debug("Fetching pseudowires form rest api!");
+
+ List<L2TunnelPolicy> policies = srService.getL2Policies();
+ List<L2Tunnel> tunnels = srService.getL2Tunnels();
+ List<DefaultL2TunnelDescription> pseudowires = tunnels.stream()
+ .map(l2Tunnel -> {
+ L2TunnelPolicy policy = null;
+ for (L2TunnelPolicy l2Policy : policies) {
+ if (l2Policy.tunnelId() == l2Tunnel.tunnelId()) {
+ policy = l2Policy;
+ break;
+ }
+ }
+
+ // return a copy
+ return new DefaultL2TunnelDescription(l2Tunnel, policy);
+ })
+ .collect(Collectors.toList());
+
+ ObjectNode result = new ObjectMapper().createObjectNode();
+ result.set("pseudowires", new PseudowireCodec().encode(pseudowires, this));
+
+ return ok(result.toString()).build();
+ }
+
+ /**
+ * Create a new pseudowire.
+ *
+ * @param input JSON stream for pseudowire to create
+ * @return Response with appropriate status
+ * @throws IOException Throws IO exception.
+ * @onos.rsModel PseudowireCreate
+ */
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response createPseudowire(InputStream input) throws IOException {
+
+ ObjectMapper mapper = new ObjectMapper();
+ ObjectNode pseudowireJson = (ObjectNode) mapper.readTree(input);
+ SegmentRoutingService srService = get(SegmentRoutingService.class);
+
+ DefaultL2TunnelDescription pseudowire = PSEUDOWIRE_CODEC.decode(pseudowireJson, this);
+ if (pseudowire == null) {
+ return Response.serverError().status(Response.Status.BAD_REQUEST).build();
+ }
+
+ log.info("Creating pseudowire {} from rest api!", pseudowire.l2Tunnel().tunnelId());
+
+ L2TunnelHandler.Result res = srService.addPseudowire(pseudowire);
+ switch (res) {
+ case ADDITION_ERROR:
+ log.error("Pseudowire {} could not be added, error in configuration," +
+ " please check logs for more details!",
+ pseudowire.l2Tunnel().tunnelId());
+ return Response.serverError().status(Response.Status.INTERNAL_SERVER_ERROR).build();
+
+ case SUCCESS:
+ log.info("Pseudowire {} succesfully deployed!", pseudowire.l2Tunnel().tunnelId());
+ return Response.ok().build();
+ default:
+ return Response.ok().build();
+ }
+ }
+
+ /**
+ * Delete a pseudowire.
+ *
+ * @param input JSON stream for pseudowire to delete
+ * @return Response with appropriate status
+ * @throws IOException Throws IO exception.
+ * @onos.rsModel PseudowireDelete
+ */
+ @DELETE
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response removePseudowire(InputStream input) throws IOException {
+
+ ObjectMapper mapper = new ObjectMapper();
+ ObjectNode pseudowireJson = (ObjectNode) mapper.readTree(input);
+ SegmentRoutingService srService = get(SegmentRoutingService.class);
+
+ Integer pseudowireId = PSEUDOWIRE_CODEC.decodeId(pseudowireJson);
+ if (pseudowireId == null) {
+ return Response.serverError().status(Response.Status.BAD_REQUEST).build();
+ }
+
+ log.info("Deleting pseudowire {} from rest api!", pseudowireId);
+
+ L2TunnelHandler.Result res = srService.removePseudowire(pseudowireId);
+ switch (res) {
+ case REMOVAL_ERROR:
+ log.error("Pseudowire {} could not be removed, error in configuration," +
+ " please check logs for more details!",
+ pseudowireId);
+
+ return Response.noContent().build();
+ case SUCCESS:
+ log.info("Pseudowire {} was removed succesfully!", pseudowireId);
+ return Response.noContent().build();
+ default:
+ return Response.noContent().build();
+ }
+ }
+}
diff --git a/web/src/main/java/org/onosproject/segmentrouting/web/SegmentRoutingWebApplication.java b/web/src/main/java/org/onosproject/segmentrouting/web/SegmentRoutingWebApplication.java
new file mode 100644
index 0000000..4aa1a71
--- /dev/null
+++ b/web/src/main/java/org/onosproject/segmentrouting/web/SegmentRoutingWebApplication.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2015-present Open Networking Foundation
+ *
+ * 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.segmentrouting.web;
+
+import org.onlab.rest.AbstractWebApplication;
+
+import java.util.Set;
+
+/**
+ * Segment Routing Web application.
+ */
+public class SegmentRoutingWebApplication extends AbstractWebApplication {
+ @Override
+ public Set<Class<?>> getClasses() {
+ return getClasses(PseudowireWebResource.class);
+ }
+}
diff --git a/web/src/main/java/org/onosproject/segmentrouting/web/TunnelWebResource.java b/web/src/main/java/org/onosproject/segmentrouting/web/TunnelWebResource.java
index 7a368f5..476bcd8 100644
--- a/web/src/main/java/org/onosproject/segmentrouting/web/TunnelWebResource.java
+++ b/web/src/main/java/org/onosproject/segmentrouting/web/TunnelWebResource.java
@@ -26,7 +26,6 @@
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
-import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
@@ -37,7 +36,7 @@
/**
* Query, create and remove segment routing tunnels.
*/
-@Path("tunnel")
+// @Path("tunnel")
public class TunnelWebResource extends AbstractWebResource {
private static final TunnelCodec TUNNEL_CODEC = new TunnelCodec();
diff --git a/web/src/main/java/org/onosproject/segmentrouting/web/package-info.java b/web/src/main/java/org/onosproject/segmentrouting/web/package-info.java
index 101d40e..ff9555e 100644
--- a/web/src/main/java/org/onosproject/segmentrouting/web/package-info.java
+++ b/web/src/main/java/org/onosproject/segmentrouting/web/package-info.java
@@ -15,6 +15,6 @@
*/
/**
- * Set of resources implementing the segment routing application REST API.
+ * Segment routing application components.
*/
package org.onosproject.segmentrouting.web;
\ No newline at end of file
diff --git a/web/src/main/resources/definitions/PseudowireCreate.json b/web/src/main/resources/definitions/PseudowireCreate.json
new file mode 100644
index 0000000..feddd18
--- /dev/null
+++ b/web/src/main/resources/definitions/PseudowireCreate.json
@@ -0,0 +1,68 @@
+{
+ "type": "object",
+ "title": "pseudowire-creation",
+ "required": [
+ "pwId",
+ "cP1",
+ "cP2",
+ "cP1InnerTag",
+ "cP1OuterTag",
+ "cP2InnerTag",
+ "cP2OuterTag",
+ "mode",
+ "sdTag",
+ "pwLabel"
+ ],
+ "properties": {
+ "pwId": {
+ "type": "string",
+ "example": "42",
+ "description": "Id of pseudowire to create."
+ },
+ "cP1": {
+ "type": "string",
+ "example": "of:0000000000000227/25",
+ "description": "Pseudowire connection point 1."
+ },
+ "cP2": {
+ "type": "string",
+ "example": "of:0000000000000226/25",
+ "description": "Pseudowire connection point 2."
+ },
+ "cP1InnerTag": {
+ "type": "string",
+ "example": "101",
+ "description": "Inner vlan for pseudowire connection point 1."
+ },
+ "cP1OuterTag": {
+ "type": "string",
+ "example": "",
+ "description": "Outer vlan for pseudowire connection point 1."
+ },
+ "cP2InnerTag": {
+ "type": "string",
+ "example": "101",
+ "description": "Inner vlan for pseudowire connection point 2."
+ },
+ "cP2OuterTag": {
+ "type": "string",
+ "example": "",
+ "description": "Outer vlan for pseudowire connection point 2."
+ },
+ "mode": {
+ "type": "string",
+ "example": "RAW",
+ "description": "Working mode of pseudowire."
+ },
+ "sDTag": {
+ "type": "string",
+ "example": "",
+ "description": "Service delimiting tag of the pseudowire"
+ },
+ "pwLabel": {
+ "type": "256",
+ "example": "",
+ "description": "Pseudowire label."
+ }
+ }
+}
\ No newline at end of file
diff --git a/web/src/main/resources/definitions/PseudowireDelete.json b/web/src/main/resources/definitions/PseudowireDelete.json
new file mode 100644
index 0000000..4dd5c04
--- /dev/null
+++ b/web/src/main/resources/definitions/PseudowireDelete.json
@@ -0,0 +1,14 @@
+{
+ "type": "object",
+ "title": "pseudowire-deletion",
+ "required": [
+ "pwId"
+ ],
+ "properties": {
+ "pwId": {
+ "type": "string",
+ "example": "42",
+ "description": "Id of pseudowire to delete."
+ }
+ }
+}
\ No newline at end of file
diff --git a/web/src/main/webapp/WEB-INF/web.xml b/web/src/main/webapp/WEB-INF/web.xml
index 5aac978..73154e3 100644
--- a/web/src/main/webapp/WEB-INF/web.xml
+++ b/web/src/main/webapp/WEB-INF/web.xml
@@ -14,21 +14,38 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
+<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="ONOS" version="2.5">
- <display-name>Segment Routing REST API v1.0</display-name>
+ <display-name>Segment Routing Server REST API v1.0</display-name>
+
+ <security-constraint>
+ <web-resource-collection>
+ <web-resource-name>Secured</web-resource-name>
+ <url-pattern>/*</url-pattern>
+ </web-resource-collection>
+ <auth-constraint>
+ <role-name>admin</role-name>
+ </auth-constraint>
+ </security-constraint>
+
+ <security-role>
+ <role-name>admin</role-name>
+ </security-role>
+
+ <login-config>
+ <auth-method>BASIC</auth-method>
+ <realm-name>karaf</realm-name>
+ </login-config>
<servlet>
<servlet-name>JAX-RS Service</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
- <param-name>jersey.config.server.provider.classnames</param-name>
- <param-value>
- org.onosproject.segmentrouting.web.TunnelWebResource,
- org.onosproject.segmentrouting.web.PolicyWebResource
- </param-value>
+ <param-name>javax.ws.rs.Application</param-name>
+ <param-value>org.onosproject.segmentrouting.web.SegmentRoutingWebApplication</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>