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/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();
+    }
 }