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/app/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
index 240d6e9..ff0f2a6 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
@@ -876,6 +876,39 @@
             }
         }
 
+        if (!targetIsEdge && !dest1IsEdge) {
+            // MPLS rules for inter-connected spines
+            // can be merged with above if, left it here for clarity
+            log.debug(". populateEcmpRoutingRulePartial in device{} towards {} for "
+                              + "all MPLS rules", targetSw, destSw1);
+
+            result = rulePopulator.populateMplsRule(targetSw, destSw1,
+                                                        nextHops.get(destSw1),
+                                                        dest1RouterIpv4);
+            if (!result) {
+                return false;
+            }
+
+            if (dest1RouterIpv6 != null) {
+                int v4sid = 0, v6sid = 0;
+                try {
+                    v4sid = config.getIPv4SegmentId(destSw1);
+                    v6sid = config.getIPv6SegmentId(destSw1);
+                } catch (DeviceConfigNotFoundException e) {
+                    log.warn(e.getMessage());
+                }
+                if (v4sid != v6sid) {
+                    result = rulePopulator.populateMplsRule(targetSw, destSw1,
+                                                            nextHops.get(destSw1),
+                                                            dest1RouterIpv6);
+                    if (!result) {
+                        return false;
+                    }
+                }
+           }
+        }
+
+
         // To save on ECMP groups
         // avoid MPLS rules in non-edge-devices to non-edge-devices
         // avoid MPLS transit rules in edge-devices
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
index 65ffb53..1f035d7 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
@@ -556,6 +556,11 @@
     }
 
     @Override
+    public Set<L2TunnelDescription> getL2TunnelDescriptions() {
+        return l2TunnelHandler.getL2Descriptions();
+    }
+
+    @Override
     public List<L2Tunnel> getL2Tunnels() {
         return l2TunnelHandler.getL2Tunnels();
     }
@@ -566,43 +571,41 @@
     }
 
     @Override
+    public L2TunnelHandler.Result addPseudowiresBulk(List<DefaultL2TunnelDescription> bulkPseudowires) {
+
+        Set<L2TunnelDescription> pseudowires = l2TunnelHandler.getL2Descriptions();
+
+        pseudowires.addAll(bulkPseudowires);
+        Set<L2TunnelDescription> newPseudowires = new HashSet(bulkPseudowires);
+
+        // check global validity for all the new pseudowires, if it fails
+        // do not add any of them
+        boolean res = configurationValidity(pseudowires);
+        if (res) {
+            l2TunnelHandler.deploy(newPseudowires);
+            return L2TunnelHandler.Result.SUCCESS;
+        } else {
+            log.error("Bulk pseudowires {} can not be added, error in global configuration!",
+                     newPseudowires);
+            return L2TunnelHandler.Result.ADDITION_ERROR;
+        }
+    }
+
+    @Override
     public L2TunnelHandler.Result addPseudowire(L2TunnelDescription l2TunnelDescription) {
 
-        List<L2Tunnel> tunnels = getL2Tunnels();
-        List<L2TunnelPolicy> policies = getL2Policies();
-
-        // combine polices and tunnels to pseudowires
-        List<L2TunnelDescription> pseudowires = tunnels.stream()
-                .map(l2Tunnel -> {
-                    L2TunnelPolicy policy = null;
-                    for (L2TunnelPolicy l2Policy : policies) {
-                        if (l2Policy.tunnelId() == l2Tunnel.tunnelId()) {
-                            policy = l2Policy;
-                            break;
-                        }
-                    }
-
-                    return new DefaultL2TunnelDescription(l2Tunnel, policy);
-                })
-                .collect(Collectors.toList());
-
-
-        // creating a new list with the new pseudowire
-        Set<L2TunnelDescription> newPseudowires = new HashSet<>(pseudowires);
+        Set<L2TunnelDescription> newPseudowires = l2TunnelHandler.getL2Descriptions();
 
         // corner case where we try to add the exact same pseudowire
         if (newPseudowires.contains(l2TunnelDescription)) {
             log.info("Pseudowire with {} already exists!", l2TunnelDescription);
             return L2TunnelHandler.Result.SUCCESS;
         }
-
         // add the new pseudowire to the Set
         newPseudowires.add(l2TunnelDescription);
-
         // validate the new set of pseudowires
         boolean res = configurationValidity(newPseudowires);
         if (res) {
-
             // deploy a set with ONLY the new pseudowire
             newPseudowires = new HashSet<>();
             newPseudowires.add(l2TunnelDescription);
@@ -612,7 +615,6 @@
                      l2TunnelDescription.l2Tunnel().tunnelId());
             return L2TunnelHandler.Result.SUCCESS;
         } else {
-
             log.error("Pseudowire with {} can not be added!", l2TunnelDescription.l2Tunnel().tunnelId());
             return L2TunnelHandler.Result.ADDITION_ERROR;
         }
@@ -645,7 +647,6 @@
         } else {
 
             l2TunnelHandler.tearDown(new HashSet<>(pseudowires));
-
             log.info("Removal of pseudowire with {} started, check log for any errors in this process!",
                      pwId);
             return L2TunnelHandler.Result.SUCCESS;
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingService.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingService.java
index 759495b..dc3d7b1 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingService.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingService.java
@@ -23,6 +23,7 @@
 import org.onosproject.net.PortNumber;
 import org.onosproject.segmentrouting.grouphandler.NextNeighbors;
 import org.onosproject.segmentrouting.mcast.McastRole;
+import org.onosproject.segmentrouting.pwaas.DefaultL2TunnelDescription;
 import org.onosproject.segmentrouting.pwaas.L2Tunnel;
 import org.onosproject.segmentrouting.pwaas.L2TunnelHandler;
 import org.onosproject.segmentrouting.pwaas.L2TunnelPolicy;
@@ -89,6 +90,13 @@
     List<Policy> getPolicies();
 
     /**
+     * Returns the l2 tunnel descriptions.
+     *
+     * @return set of l2 tunnel descriptions.
+     */
+    Set<L2TunnelDescription> getL2TunnelDescriptions();
+
+    /**
      * Returns all l2 tunnels of pseudowires.
      *
      * @return list of l2 tunnels
@@ -103,7 +111,7 @@
     List<L2TunnelPolicy> getL2Policies();
 
     /**
-     * Removes pseudowire. Used ONLY by the REST api.
+     * Removes pseudowire.
      *
      * @param pwId The id of the pseudowire.
      * @return SUCCESS if operation successful or a descriptive error otherwise.
@@ -111,7 +119,7 @@
     L2TunnelHandler.Result removePseudowire(Integer pwId);
 
     /**
-     * Adds a Pseudowire to the configuration.
+     * Adds a Pseudowire to the system.
      *
      * @param tunnel The pseudowire tunnel.
      * @return SUCCESS if operation is successful or a descriptive error otherwise.
@@ -119,6 +127,14 @@
     L2TunnelHandler.Result addPseudowire(L2TunnelDescription tunnel);
 
     /**
+     * Adss a set of pseudowires.
+     * @param l2TunnelDescriptions The pseudowires to add.
+     * @return SUCCESS if ALL pseudowires can be instantiated and are deployed, or a
+     *         a descriptive error otherwise, without deploying any pseudowire.
+     */
+    L2TunnelHandler.Result addPseudowiresBulk(List<DefaultL2TunnelDescription> l2TunnelDescriptions);
+
+    /**
      * Creates a policy.
      *
      * @param policy policy reference to create
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/cli/PseudowireAddCommand.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/cli/PseudowireAddCommand.java
index ab80aff..b69763d 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/cli/PseudowireAddCommand.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/cli/PseudowireAddCommand.java
@@ -35,7 +35,7 @@
 /**
  * Command to add a pseuwodire.
  */
-@Command(scope = "onos", name = "pseudowire-add",
+@Command(scope = "onos", name = "sr-pw-add",
         description = "Add a pseudowire to the network configuration, if it already exists update it.")
 public class PseudowireAddCommand extends AbstractShellCommand {
 
@@ -121,10 +121,9 @@
 
         switch (res) {
             case ADDITION_ERROR:
-                print("Pseudowire could not be added, please check logs for more details!");
+                print("Pseudowire could not be added!");
                 break;
             case SUCCESS:
-                print("Pseudowire was added succesfully!");
                 break;
             default:
                 break;
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/cli/PseudowireListCommand.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/cli/PseudowireListCommand.java
index c48f633..a633a8a 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/cli/PseudowireListCommand.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/cli/PseudowireListCommand.java
@@ -30,7 +30,7 @@
 /**
  * Command to show the pseudowires.
  */
-@Command(scope = "onos", name = "pseudowires",
+@Command(scope = "onos", name = "sr-pw-list",
         description = "Lists all pseudowires")
 public class PseudowireListCommand extends AbstractShellCommand {
 
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/cli/PseudowireRemoveCommand.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/cli/PseudowireRemoveCommand.java
index 0f027b2..305f0be 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/cli/PseudowireRemoveCommand.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/cli/PseudowireRemoveCommand.java
@@ -29,7 +29,7 @@
 /**
  * Command to remove a pseudowire.
  */
-@Command(scope = "onos", name = "pseudowire-remove",
+@Command(scope = "onos", name = "sr-pw-remove",
         description = "Remove a pseudowire")
 public class PseudowireRemoveCommand extends AbstractShellCommand {
 
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2TunnelHandler.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2TunnelHandler.java
index 4b3272d..acaf9f6 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2TunnelHandler.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2TunnelHandler.java
@@ -175,6 +175,27 @@
         // use it in the future.
     }
 
+    @Override
+    public Set<L2TunnelDescription> getL2Descriptions() {
+        List<L2Tunnel> tunnels = getL2Tunnels();
+        List<L2TunnelPolicy> policies = getL2Policies();
+
+        // determine affected pseudowires and update them at once
+        return tunnels.stream()
+                .map(l2Tunnel -> {
+                    L2TunnelPolicy policy = null;
+                    for (L2TunnelPolicy l2Policy : policies) {
+                        if (l2Policy.tunnelId() == l2Tunnel.tunnelId()) {
+                            policy = l2Policy;
+                            break;
+                        }
+                    }
+
+                    return new DefaultL2TunnelDescription(l2Tunnel, policy);
+                })
+                .collect(Collectors.toSet());
+    }
+
     /**
      * Returns all L2 Policies.
      *
@@ -188,7 +209,6 @@
                 .stream()
                 .map(Versioned::value)
                 .collect(Collectors.toList()));
-
     }
 
     /**
@@ -204,7 +224,6 @@
                 .stream()
                 .map(Versioned::value)
                 .collect(Collectors.toList()));
-
     }
 
     @Override
@@ -298,6 +317,16 @@
     }
 
     /**
+     * Returns true if path size is valid according to the current logic.
+     *
+     * @param pathSize The size of the path
+     * @return True if path size is valid, false otherwise.
+     */
+    private boolean isValidPathSize(int pathSize) {
+        return ((pathSize >= 1) && (pathSize <= 4));
+    }
+
+    /**
      * Adds a single pseudowire.
      *
      * @param pw The pseudowire
@@ -327,31 +356,27 @@
 
         Link fwdNextHop;
         Link revNextHop;
-        if (!spinePw) {
-            if (path.size() != 2) {
-                log.info("Deploying process : Path between two leafs should have size of 2, for pseudowire {}",
-                         l2TunnelId);
-                return INTERNAL_ERROR;
-            }
-
-            fwdNextHop = path.get(0);
-            revNextHop = reverseLink(path.get(1));
-        } else {
-            if (path.size() != 1) {
-                log.info("Deploying process : Path between leaf spine should equal to 1, for pseudowire {}",
-                         l2TunnelId);
-                return INTERNAL_ERROR;
-            }
-
-            fwdNextHop = path.get(0);
-            revNextHop = reverseLink(path.get(0));
+        if (!isValidPathSize(path.size())) {
+            log.error("Deploying process : Path size for pseudowire should be of" +
+                              " one of the following sizes = [1, 2, 3, 4], for pseudowire {}",
+                      l2TunnelId);
+            return INTERNAL_ERROR;
         }
 
+        // spinePw signifies if we have a leaf-spine pw
+        // thus only one label should be pushed (that of pw)
+        // if size>1 we need to push intermediate labels also.
+        if (path.size() > 1) {
+            spinePw = false;
+        }
+
+        fwdNextHop = path.get(0);
+        revNextHop = reverseLink(path.get(path.size() - 1));
+
         pw.l2Tunnel().setPath(path);
         pw.l2Tunnel().setTransportVlan(determineTransportVlan(spinePw));
 
         // next hops for next objectives
-
         log.info("Deploying process : Establishing forward direction for pseudowire {}", l2TunnelId);
 
         VlanId egressVlan = determineEgressVlan(pw.l2TunnelPolicy().cP1OuterTag(),
@@ -618,24 +643,23 @@
         }
 
         Link fwdNextHop, revNextHop;
-        if (!finalNewPwSpine) {
-            if (path.size() != 2) {
-                log.error("Update process : Error, path between two leafs should have size of 2, for pseudowire {}",
-                         newPw.l2Tunnel().tunnelId());
-                return;
-            }
-            fwdNextHop = path.get(0);
-            revNextHop = reverseLink(path.get(1));
-        } else {
-            if (path.size() != 1) {
-                log.error("Update process : Error, path between leaf spine should equal to 1, for pseudowire {}",
-                         newPw.l2Tunnel().tunnelId());
-                return;
-            }
-            fwdNextHop = path.get(0);
-            revNextHop = reverseLink(path.get(0));
+        if (!isValidPathSize(path.size())) {
+            log.error("Deploying process : Path size for pseudowire should be of one of the following sizes" +
+                              " = [1, 2, 3, 4], for pseudowire {}",
+                      newPw.l2Tunnel().tunnelId());
+            return;
         }
 
+        // spinePw signifies if we have a leaf-spine pw
+        // thus only one label should be pushed (that of pw)
+        // if size>1 we need to push intermediate labels also.
+        if (path.size() > 1) {
+            newPwSpine = false;
+        }
+
+        fwdNextHop = path.get(0);
+        revNextHop = reverseLink(path.get(path.size() - 1));
+
         // set new path and transport vlan.
         newPw.l2Tunnel().setPath(path);
         newPw.l2Tunnel().setTransportVlan(determineTransportVlan(newPwSpine));
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/pwaas/L2TunnelHandler.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/pwaas/L2TunnelHandler.java
index 5fce137..ad4f50a 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/pwaas/L2TunnelHandler.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/pwaas/L2TunnelHandler.java
@@ -25,6 +25,13 @@
     void init();
 
     /**
+     * Combines policies and tunnels to create descriptions.
+     *
+     * @return Set of l2 tunnel descriptions.
+     */
+    Set<L2TunnelDescription> getL2Descriptions();
+
+    /**
      * Returns a copy of the l2 policies that exist in the store.
      *
      * @return The l2 policies
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
diff --git a/tools/package/runtime/bin/onos-diagnostics b/tools/package/runtime/bin/onos-diagnostics
index 4938a7c..76d42f8 100755
--- a/tools/package/runtime/bin/onos-diagnostics
+++ b/tools/package/runtime/bin/onos-diagnostics
@@ -97,7 +97,7 @@
     "sr-mcast-next"
     "sr-mcast-tree"
     "sr-next-hops"
-    "pseudowires"
+    "sr-pw-list"
     "dhcp-relay"
 )