Various pseudowire fixes and improvements.

- Co-ordination when creating - removing pseudowires from
  different instances with a use of a DistributedLock.
- Fixed REST API To return json with specific error for
  the single pw instantiation.
- Fixed REST API to return specific error also for pseudowires
  that could not be decoded.
- Minor bug fix to return appropriate error when instantiating
  a pw from the command line that could not be decoded.
- Fixed bug when creating spine-leaf-leaf pseudowire where we observed flows in pending state.
- Improved logging.

Change-Id: I60dd0ebf8af63ca74d18cfe4801d01846641fb7b
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 00d20a7..8abfa71 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
@@ -15,6 +15,7 @@
  */
 package org.onosproject.segmentrouting.web;
 
+import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import org.apache.commons.lang3.tuple.Pair;
@@ -27,9 +28,11 @@
 import org.onosproject.segmentrouting.pwaas.DefaultL2TunnelDescription;
 import org.onosproject.segmentrouting.pwaas.DefaultL2TunnelPolicy;
 import org.onosproject.segmentrouting.pwaas.L2Mode;
+import org.onosproject.segmentrouting.pwaas.L2TunnelDescription;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.ArrayList;
 import java.util.List;
 
 import static org.onosproject.segmentrouting.pwaas.PwaasUtil.*;
@@ -69,13 +72,11 @@
 
         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(SERVICE_DELIM_TAG, pseudowire.l2Tunnel().sdTag().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;
@@ -101,25 +102,77 @@
     }
 
     /**
-     * Returns a JSON containing the failed pseudowires and the reason that its one failed.
+     * Encoded in an Object Node the undecoed pseudowire and the specificError it failed.
+     *
+     * @param failedPW The failed pseudowire in json format
+     * @param specificError The specificError it failed
+     * @param context Our context
+     * @return A node containing the information we provided
+     */
+    public ObjectNode encodeError(JsonNode failedPW, String specificError,
+                                  CodecContext context) {
+        ObjectNode result = context.mapper().createObjectNode();
+
+        result.set(FAILED_PW, failedPW);
+        result.put(REASON, specificError);
+
+        return result;
+    }
+
+    /**
+     * Returns a JSON containing the failed pseudowires and the reason that they failed.
      *
      * @param failedPws Pairs of pws and reasons.
+     * @param undecodedPws Pairs of pws that we could not decode with reason being illegal arguments.
      * @param context The context
      * @return ObjectNode representing the json to return
      */
     public ObjectNode encodeFailedPseudowires(
             List<Pair<DefaultL2TunnelDescription, String>> failedPws,
+            List<Pair<JsonNode, String>> undecodedPws,
             CodecContext context) {
 
         ArrayNode failedNodes = context.mapper().createArrayNode();
         failedPws.stream()
                 .forEach(failed -> failedNodes.add(encodeError(failed.getKey(), failed.getValue(), context)));
+        undecodedPws.stream()
+                .forEach(failed -> failedNodes.add(encodeError(failed.getKey(), failed.getValue(), context)));
         final ObjectNode toReturn = context.mapper().createObjectNode();
         toReturn.set(FAILED_PWS, failedNodes);
         return toReturn;
     }
 
     /**
+     *
+     * @param json The json containing the pseudowires.
+     * @param context The context
+     * @return A pair of lists.
+     *         First list contains pseudowires that we were not able to decode
+     *         along with the reason we could not decode them.
+     *         Second list contains successfully decoded pseudowires which we are
+     *         going to instantiate.
+     */
+    public Pair<List<Pair<JsonNode, String>>, List<L2TunnelDescription>> decodePws(ArrayNode json,
+                                                                                   CodecContext context) {
+
+        List<L2TunnelDescription> decodedPws = new ArrayList<>();
+        List<Pair<JsonNode, String>> notDecodedPws = new ArrayList<>();
+        for (JsonNode node : json) {
+            DefaultL2TunnelDescription l2Description;
+            try {
+                l2Description = decode((ObjectNode) node, context);
+                decodedPws.add(l2Description);
+            } catch (IllegalArgumentException e) {
+                // the reason why we could not decode this pseudowire is encoded in the
+                // exception, we need to store it now
+                notDecodedPws.add(Pair.of(node, e.getMessage()));
+            }
+        }
+
+        return Pair.of(notDecodedPws, decodedPws);
+    }
+
+    /**
      * Decodes a json containg a single field with the pseudowire id.
      *
      * @param json Json to decode.
@@ -127,8 +180,10 @@
      */
     public static Integer decodeId(ObjectNode json) {
 
-        Integer id = parsePwId(json.path(PW_ID).asText());
-        if (id == null) {
+        Integer id;
+        try {
+            id = parsePwId(json.path(PW_ID).asText());
+        } catch (IllegalArgumentException e) {
             log.error("Pseudowire id is not an integer!");
             return null;
         }
@@ -139,66 +194,25 @@
     @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;
-        }
+        cP1 = ConnectPoint.deviceConnectPoint(json.path(CP1).asText());
+        cP2 = ConnectPoint.deviceConnectPoint(json.path(CP2).asText());
 
-        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;
-        }
+        VlanId cP1InnerVlan, cP1OuterVlan, cP2InnerVlan, cP2OuterVlan, sdTag;
+        cP1InnerVlan = parseVlan(json.path(CP1_INNER_TAG).asText());
+        cP1OuterVlan = parseVlan(json.path(CP1_OUTER_TAG).asText());
+        cP2InnerVlan = parseVlan(json.path(CP2_INNER_TAG).asText());
+        cP2OuterVlan = parseVlan(json.path(CP2_OUTER_TAG).asText());
+        sdTag = parseVlan(json.path(SERVICE_DELIM_TAG).asText());
 
         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,
+        DefaultL2Tunnel l2Tunnel = new DefaultL2Tunnel(mode, sdTag, id, pwLabel);
+        DefaultL2TunnelPolicy l2Policy = new DefaultL2TunnelPolicy(id, cP1, cP1InnerVlan, cP1OuterVlan,
                                              cP2, cP2InnerVlan, cP2OuterVlan);
-
         return new DefaultL2TunnelDescription(l2Tunnel, l2Policy);
 
     }