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);
 
     }
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 854697b..4867c90 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
@@ -24,6 +24,7 @@
 import org.onosproject.rest.AbstractWebResource;
 import org.onosproject.segmentrouting.SegmentRoutingService;
 import org.onosproject.segmentrouting.pwaas.DefaultL2TunnelDescription;
+import org.onosproject.segmentrouting.pwaas.L2TunnelDescription;
 import org.onosproject.segmentrouting.pwaas.L2TunnelPolicy;
 import org.onosproject.segmentrouting.pwaas.L2Tunnel;
 import org.onosproject.segmentrouting.pwaas.L2TunnelHandler;
@@ -111,28 +112,36 @@
         ObjectMapper mapper = new ObjectMapper();
         ObjectNode pseudowireJson = readTreeFromStream(mapper, input);
         SegmentRoutingService srService = get(SegmentRoutingService.class);
+        List<Pair<DefaultL2TunnelDescription, String>> failed = new ArrayList<>();
+        List<Pair<JsonNode, String>> undecoded = new ArrayList<>();
 
-        DefaultL2TunnelDescription pseudowire = PSEUDOWIRE_CODEC.decode(pseudowireJson, this);
-        if (pseudowire == null) {
-            return Response.serverError().status(Response.Status.BAD_REQUEST).build();
+        DefaultL2TunnelDescription pseudowire;
+        try {
+            pseudowire = PSEUDOWIRE_CODEC.decode(pseudowireJson, this);
+
+            // pseudowire decoded, try to instantiate it, if we fail add it to failed list
+            long tunId = pseudowire.l2Tunnel().tunnelId();
+            log.debug("Creating pseudowire {} from rest api!", tunId);
+
+            L2TunnelHandler.Result res = srService.addPseudowire(pseudowire);
+            if (res != L2TunnelHandler.Result.SUCCESS) {
+                log.error("Could not create pseudowire {} : {}", pseudowire.l2Tunnel().tunnelId(),
+                          res.getSpecificError());
+                failed.add(Pair.of(pseudowire, res.getSpecificError()));
+            }
+        } catch (IllegalArgumentException e) {
+            log.debug("Pseudowire could not be decoded : {}", e.getMessage());
+            undecoded.add(Pair.of(pseudowireJson, e.getMessage()));
         }
 
-        long tunId = pseudowire.l2Tunnel().tunnelId();
-        log.debug("Creating pseudowire {} from rest api!", tunId);
-
-        L2TunnelHandler.Result res = srService.addPseudowire(pseudowire);
-        switch (res) {
-            case WRONG_PARAMETERS:
-            case CONFIGURATION_ERROR:
-            case PATH_NOT_FOUND:
-            case INTERNAL_ERROR:
-                log.error("Pseudowire {} could not be added : {}", tunId, res.getSpecificError());
-                return Response.serverError().status(Response.Status.INTERNAL_SERVER_ERROR).build();
-            case SUCCESS:
-                log.info("Pseudowire {} succesfully deployed!", pseudowire.l2Tunnel().tunnelId());
-                return Response.ok().build();
-            default:
-                return Response.ok().build();
+        if ((failed.size() == 0) && (undecoded.size() == 0)) {
+            // pseudowire instantiated correctly
+            return Response.ok().build();
+        } else {
+            // failed to decode or instantiate pseudowire, return the reason
+            PseudowireCodec pwCodec = new PseudowireCodec();
+            ObjectNode result = pwCodec.encodeFailedPseudowires(failed, undecoded, this);
+            return Response.serverError().entity(result).build();
         }
     }
 
@@ -152,33 +161,36 @@
         ObjectMapper mapper = new ObjectMapper();
         ObjectNode pseudowireJson = readTreeFromStream(mapper, input);
         SegmentRoutingService srService = get(SegmentRoutingService.class);
-        List<DefaultL2TunnelDescription> pseudowires;
+        Pair<List<Pair<JsonNode, String>>, List<L2TunnelDescription>> pseudowires;
 
         try {
             ArrayNode pseudowiresArray = nullIsIllegal((ArrayNode) pseudowireJson.get(PWS), PWS_KEY_ERROR);
-            pseudowires = PSEUDOWIRE_CODEC.decode(pseudowiresArray, this);
+            // get two lists, first one contains pseudowires that we were unable to decode
+            // that have faulty arguments, second one contains pseudowires that we decoded
+            // succesfully
+            pseudowires = PSEUDOWIRE_CODEC.decodePws(pseudowiresArray, this);
         } catch (ItemNotFoundException e) {
             return Response.serverError().status(Response.Status.BAD_REQUEST).build();
         }
 
         log.debug("Creating pseudowires {} from rest api!", pseudowires);
         List<Pair<DefaultL2TunnelDescription, String>> failed = new ArrayList<>();
-
-        for (DefaultL2TunnelDescription pw : pseudowires) {
+        for (L2TunnelDescription pw : pseudowires.getRight()) {
             L2TunnelHandler.Result res = srService.addPseudowire(pw);
-            if (!(res == L2TunnelHandler.Result.SUCCESS)) {
-                log.trace("Could not create pseudowire {} : {}", pw.l2Tunnel().tunnelId(), res.getSpecificError());
-                failed.add(Pair.of(pw, res.getSpecificError()));
+            if (res != L2TunnelHandler.Result.SUCCESS) {
+                log.error("Could not create pseudowire {} : {}", pw.l2Tunnel().tunnelId(), res.getSpecificError());
+                failed.add(Pair.of((DefaultL2TunnelDescription) pw, res.getSpecificError()));
             }
         }
+        List<Pair<JsonNode, String>> undecodedPws = pseudowires.getLeft();
 
-        if (failed.size() == 0) {
-            // all pseudowires were instantiated
+        if ((failed.size() == 0) && (undecodedPws.size() == 0)) {
+            // all pseudowires were decoded and instantiated succesfully
             return Response.ok().build();
         } else {
             PseudowireCodec pwCodec = new PseudowireCodec();
             // some failed, need to report them to user
-            ObjectNode result = pwCodec.encodeFailedPseudowires(failed, this);
+            ObjectNode result = pwCodec.encodeFailedPseudowires(failed, undecodedPws, this);
             return Response.serverError().entity(result).build();
         }
     }