Implemented the following PW features and fixes:

   - PW support for H-AGG topologies. Support for leaf-spine-spine
     leaf-spine-spine-leaf pseudowires.
   - Renamed pw commands with sr-pw-* pattern and also removed redundant output
    from them.
   - Enabled bulk addition / removal of pws from the rest api.
   - Modified diagnostics tool with the updated command name.

Change-Id: I708db9281d0082b160cbd713910b420ef7df9da3
diff --git a/apps/segmentrouting/web/src/main/java/org/onosproject/segmentrouting/web/PseudowireCodec.java b/apps/segmentrouting/web/src/main/java/org/onosproject/segmentrouting/web/PseudowireCodec.java
index f2fe786..fba6ff9 100644
--- a/apps/segmentrouting/web/src/main/java/org/onosproject/segmentrouting/web/PseudowireCodec.java
+++ b/apps/segmentrouting/web/src/main/java/org/onosproject/segmentrouting/web/PseudowireCodec.java
@@ -78,7 +78,7 @@
      * @param json Json to decode.
      * @return The pseudowire id.
      */
-    public Integer decodeId(ObjectNode json) {
+    public static Integer decodeId(ObjectNode json) {
 
         Integer id = parsePwId(json.path(PW_ID).asText());
         if (id == null) {
diff --git a/apps/segmentrouting/web/src/main/java/org/onosproject/segmentrouting/web/PseudowireWebResource.java b/apps/segmentrouting/web/src/main/java/org/onosproject/segmentrouting/web/PseudowireWebResource.java
index ba151a1..1a71346 100644
--- a/apps/segmentrouting/web/src/main/java/org/onosproject/segmentrouting/web/PseudowireWebResource.java
+++ b/apps/segmentrouting/web/src/main/java/org/onosproject/segmentrouting/web/PseudowireWebResource.java
@@ -15,8 +15,11 @@
  */
 package org.onosproject.segmentrouting.web;
 
+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;
+import org.onlab.util.ItemNotFoundException;
 import org.onosproject.rest.AbstractWebResource;
 import org.onosproject.segmentrouting.SegmentRoutingService;
 import org.onosproject.segmentrouting.pwaas.DefaultL2TunnelDescription;
@@ -36,9 +39,12 @@
 import javax.ws.rs.core.Response;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.stream.Collectors;
 
+import static org.onlab.util.Tools.nullIsIllegal;
+
 /**
  * Query, create and remove pseudowires.
  */
@@ -46,6 +52,8 @@
 public class PseudowireWebResource extends AbstractWebResource {
 
     private static final PseudowireCodec PSEUDOWIRE_CODEC = new PseudowireCodec();
+    public static final String PWS = "pseudowires";
+    private static final String PWS_KEY_ERROR = "Pseudowires key must be present.";
 
     private static Logger log = LoggerFactory
             .getLogger(PseudowireWebResource.class);
@@ -107,7 +115,7 @@
             return Response.serverError().status(Response.Status.BAD_REQUEST).build();
         }
 
-        log.info("Creating pseudowire {} from rest api!", pseudowire.l2Tunnel().tunnelId());
+        log.debug("Creating pseudowire {} from rest api!", pseudowire.l2Tunnel().tunnelId());
 
         L2TunnelHandler.Result res = srService.addPseudowire(pseudowire);
         switch (res) {
@@ -118,7 +126,50 @@
                 return Response.serverError().status(Response.Status.INTERNAL_SERVER_ERROR).build();
 
             case SUCCESS:
-                log.info("Pseudowire {} succesfully deployed!", pseudowire.l2Tunnel().tunnelId());
+                log.debug("Pseudowire {} succesfully deployed!", pseudowire.l2Tunnel().tunnelId());
+                return Response.ok().build();
+            default:
+                return Response.ok().build();
+        }
+    }
+
+    /**
+     * Create a bulk of pseudowires.
+     *
+     * @param input JSON stream for pseudowires to create
+     * @return Response with appropriate status
+     * @throws IOException Throws IO exception.
+     * @onos.rsModel PseudowireCreateBulk
+     */
+    @POST
+    @Path("/bulk")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Response createPseudowiresBulk(InputStream input) throws IOException {
+
+        ObjectMapper mapper = new ObjectMapper();
+        ObjectNode pseudowireJson = (ObjectNode) mapper.readTree(input);
+        SegmentRoutingService srService = get(SegmentRoutingService.class);
+        List<DefaultL2TunnelDescription> pseudowires;
+
+        try {
+            ArrayNode pseudowiresArray = nullIsIllegal((ArrayNode) pseudowireJson.get(PWS), PWS_KEY_ERROR);
+            pseudowires = PSEUDOWIRE_CODEC.decode(pseudowiresArray, this);
+        } catch (ItemNotFoundException e) {
+            return Response.serverError().status(Response.Status.BAD_REQUEST).build();
+        }
+
+        log.debug("Creating pseudowires {} from rest api!", pseudowires);
+
+        L2TunnelHandler.Result res = srService.addPseudowiresBulk(pseudowires);
+        switch (res) {
+            case ADDITION_ERROR:
+                log.error("Bulk of pseudowires {} could not be added, error in configuration," +
+                                  " please check logs for more details!",
+                          pseudowires);
+                return Response.serverError().status(Response.Status.INTERNAL_SERVER_ERROR).build();
+
+            case SUCCESS:
+                log.debug("Bulk of pseudowires {} succesfully deployed!", pseudowires);
                 return Response.ok().build();
             default:
                 return Response.ok().build();
@@ -146,7 +197,7 @@
             return Response.serverError().status(Response.Status.BAD_REQUEST).build();
         }
 
-        log.info("Deleting pseudowire {} from rest api!", pseudowireId);
+        log.debug("Deleting pseudowire {} from rest api!", pseudowireId);
 
         L2TunnelHandler.Result res = srService.removePseudowire(pseudowireId);
         switch (res) {
@@ -157,10 +208,64 @@
 
                 return Response.noContent().build();
             case SUCCESS:
-                log.info("Pseudowire {} was removed succesfully!", pseudowireId);
+                log.debug("Pseudowire {} was removed succesfully!", pseudowireId);
                 return Response.noContent().build();
             default:
                 return Response.noContent().build();
         }
     }
+
+    /**
+     * Delete a bulk of pseudowires.
+     *
+     * @param input JSON stream for pseudowires to delete
+     * @return Response with appropriate status
+     * @throws IOException Throws IO exception.
+     * @onos.rsModel PseudowireDeleteBulk
+     */
+    @DELETE
+    @Path("/bulk")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Response removePseudowiresBulk(InputStream input) throws IOException {
+
+        ObjectMapper mapper = new ObjectMapper();
+        ObjectNode pseudowireJson = (ObjectNode) mapper.readTree(input);
+        SegmentRoutingService srService = get(SegmentRoutingService.class);
+
+        List<Integer> ids = new ArrayList<>();
+
+        // try to parse all ids, if key is not present, or if an id is not an int
+        // throw an exception and stop process
+        try {
+            for (JsonNode node : pseudowireJson.withArray(PWS)) {
+                Integer idToDelete = PseudowireCodec.decodeId((ObjectNode) node);
+                if (idToDelete == null) {
+                    log.error("Error when parsing pseudowire for deletion in REST API.");
+                    throw new IllegalArgumentException("Id of pseudowire should be an integer!");
+                }
+                ids.add(idToDelete);
+            }
+        } catch (IllegalArgumentException e) {
+            log.error("Pseudowire ID should be an integer.");
+            return Response.serverError().status(Response.Status.BAD_REQUEST).build();
+        } catch (UnsupportedOperationException e) {
+            log.error("Pseudowires for deletion should be an array of pseudowire ids.");
+            return Response.serverError().status(Response.Status.BAD_REQUEST).build();
+        }
+
+        for (Integer pseudowireId : ids) {
+            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);
+                case SUCCESS:
+                    log.debug("Pseudowire {} was removed succesfully!", pseudowireId);
+                default:
+            }
+        }
+
+        return Response.noContent().build();
+    }
 }
diff --git a/apps/segmentrouting/web/src/main/resources/definitions/PseudowireCreateBulk.json b/apps/segmentrouting/web/src/main/resources/definitions/PseudowireCreateBulk.json
new file mode 100644
index 0000000..44db619
--- /dev/null
+++ b/apps/segmentrouting/web/src/main/resources/definitions/PseudowireCreateBulk.json
@@ -0,0 +1,66 @@
+{
+  "type": "object",
+  "title": "pseudowire-creation-bulk",
+  "required": [
+    "pseudowires"
+  ],
+  "properties": {
+    "pseudowires": {
+      "type": "array",
+      "items": {
+        "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/apps/segmentrouting/web/src/main/resources/definitions/PseudowireDeleteBulk.json b/apps/segmentrouting/web/src/main/resources/definitions/PseudowireDeleteBulk.json
new file mode 100644
index 0000000..2edc7e6
--- /dev/null
+++ b/apps/segmentrouting/web/src/main/resources/definitions/PseudowireDeleteBulk.json
@@ -0,0 +1,26 @@
+{
+  "type": "object",
+  "title": "pseudowire-deletion-bulk",
+  "required": [
+    "pseudowires"
+  ],
+  "properties": {
+    "pseudowires": {
+      "type": "array",
+      "items": {
+        "type": "object",
+        "title": "pseudowire-1",
+        "required": [
+          "pwId"
+        ],
+        "properties": {
+          "pwId": {
+            "type": "string",
+            "example": "42",
+            "description": "Id of pseudowire to Delete."
+          }
+        }
+      }
+    }
+  }
+}
\ No newline at end of file