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>