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/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));