[AETHER-76] Impelentation of a new Trellis Troubleshoot Tool (T3) for offline mode

- For the performance improvement, T3 offline mode uses snapshots of the network states
called Network Information Base (NIB) instead of runtime interactions with ONOS core
during troubleshooting a Trellis system.
- Partially tested with some mininet topos for trellis
(https://github.com/opennetworkinglab/routing/tree/master/trellis).
- Usage instruction docs (https://docs.trellisfabric.org/troubleshooting.html).

Change-Id: Ice608f77aa96bfbcadfff34991c4a1b6d93125b6
(cherry picked from commit eaa6329aba67c2577fdca7d3ddf230611e82f9f7)
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/CriterionCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/CriterionCodec.java
index 24b749c..93cfe5d 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/CriterionCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/CriterionCodec.java
@@ -82,6 +82,7 @@
     static final String PI_MATCH_MASK = "mask";
     static final String PI_MATCH_HIGH_VALUE = "highValue";
     static final String PI_MATCH_LOW_VALUE = "lowValue";
+    static final String EXTENSION = "extension";
 
     @Override
     public ObjectNode encode(Criterion criterion, CodecContext context) {
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/DecodeCriterionCodecHelper.java b/core/common/src/main/java/org/onosproject/codec/impl/DecodeCriterionCodecHelper.java
index f690d5d..9516ed3 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/DecodeCriterionCodecHelper.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/DecodeCriterionCodecHelper.java
@@ -15,6 +15,7 @@
  */
 package org.onosproject.codec.impl;
 
+import com.esotericsoftware.kryo.io.Input;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import org.onlab.packet.Ip6Address;
@@ -33,20 +34,28 @@
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.flow.criteria.Criteria;
 import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.ExtensionCriterion;
 import org.onosproject.net.flow.criteria.PiCriterion;
 import org.onosproject.net.pi.model.PiMatchFieldId;
 import org.onosproject.net.pi.model.PiMatchType;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.slf4j.Logger;
 
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
 import java.util.HashMap;
 import java.util.Map;
 
 import static org.onlab.util.Tools.nullIsIllegal;
+import static org.slf4j.LoggerFactory.getLogger;
 
 /**
  * Decode portion of the criterion codec.
  */
 public final class DecodeCriterionCodecHelper {
 
+    private static final Logger log = getLogger(DecodeCriterionCodecHelper.class);
+
     private final ObjectNode json;
 
     protected static final String MISSING_MEMBER_MESSAGE =
@@ -114,6 +123,7 @@
         decoderMap.put(Criterion.Type.ODU_SIGID.name(), new OduSigIdDecoder());
         decoderMap.put(Criterion.Type.ODU_SIGTYPE.name(), new OduSigTypeDecoder());
         decoderMap.put(Criterion.Type.PROTOCOL_INDEPENDENT.name(), new PiDecoder());
+        decoderMap.put(Criterion.Type.EXTENSION.name(), new ExtensionDecoder());
     }
 
     private class EthTypeDecoder implements CriterionDecoder {
@@ -130,6 +140,7 @@
             return Criteria.matchEthType(ethType);
         }
     }
+
     private class EthDstDecoder implements CriterionDecoder {
         @Override
         public Criterion decodeCriterion(ObjectNode json) {
@@ -139,6 +150,7 @@
             return Criteria.matchEthDst(mac);
         }
     }
+
     private class EthDstMaskedDecoder implements CriterionDecoder {
         @Override
         public Criterion decodeCriterion(ObjectNode json) {
@@ -149,6 +161,7 @@
             return Criteria.matchEthDstMasked(mac, macMask);
         }
     }
+
     private class EthSrcDecoder implements CriterionDecoder {
         @Override
         public Criterion decodeCriterion(ObjectNode json) {
@@ -669,6 +682,24 @@
         }
     }
 
+    private class ExtensionDecoder implements CriterionDecoder {
+        @Override
+        public Criterion decodeCriterion(ObjectNode json) {
+            try {
+                byte[] buffer = nullIsIllegal(json.get(CriterionCodec.EXTENSION),
+                        CriterionCodec.EXTENSION + MISSING_MEMBER_MESSAGE).binaryValue();
+                Input input = new Input(new ByteArrayInputStream(buffer));
+                ExtensionCriterion extensionCriterion =
+                        KryoNamespaces.API.borrow().readObject(input, ExtensionCriterion.class);
+                input.close();
+                return extensionCriterion;
+            } catch (IOException e) {
+                log.warn("Cannot convert the {} field into byte array", CriterionCodec.EXTENSION);
+                return null;
+            }
+        }
+    }
+
     /**
      * Decodes the JSON into a criterion object.
      *
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/DecodeInstructionCodecHelper.java b/core/common/src/main/java/org/onosproject/codec/impl/DecodeInstructionCodecHelper.java
index 10f9e5e..7307939 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/DecodeInstructionCodecHelper.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/DecodeInstructionCodecHelper.java
@@ -137,7 +137,10 @@
             long tunnelId = nullIsIllegal(json.get(InstructionCodec.TUNNEL_ID),
                     InstructionCodec.TUNNEL_ID + InstructionCodec.MISSING_MEMBER_MESSAGE).asLong();
             return Instructions.modTunnelId(tunnelId);
+        } else if (subType.equals(L2ModificationInstruction.L2SubType.MPLS_BOS.name())) {
+            return Instructions.modMplsBos(json.get("bos").asBoolean());
         }
+
         throw new IllegalArgumentException("L2 Instruction subtype "
                 + subType + " is not supported");
     }
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/DeviceCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/DeviceCodec.java
index d34092a..c42dcfd 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/DeviceCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/DeviceCodec.java
@@ -20,6 +20,7 @@
 import org.onlab.packet.ChassisId;
 import org.onosproject.codec.CodecContext;
 import org.onosproject.net.Annotations;
+import org.onosproject.net.DefaultAnnotations;
 import org.onosproject.net.DefaultDevice;
 import org.onosproject.net.Device;
 import org.onosproject.net.Device.Type;
@@ -99,4 +100,26 @@
         return new DefaultDevice(pid, id, type, mfr, hw, sw, serial,
                                  chassisId, annotations);
     }
+
+    /**
+     * Extracts annotations of given Object.
+     *
+     * @param deviceNode annotated JSON object node representing a device
+     * @param context decode context
+     * @return extracted Annotations
+     */
+    @Override
+    protected Annotations extractAnnotations(ObjectNode deviceNode, CodecContext context) {
+        ObjectNode annotationsNode = get(deviceNode, "annotations");
+        if (annotationsNode != null) {
+            // add needed fields to the annotations of the Device object
+            if (deviceNode.get(AVAILABLE) != null) {
+                annotationsNode.put(AVAILABLE, deviceNode.get(AVAILABLE).asText());
+            }
+            return context.codec(Annotations.class).decode(annotationsNode, context);
+        } else {
+            return DefaultAnnotations.EMPTY;
+        }
+    }
+
 }
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/EncodeCriterionCodecHelper.java b/core/common/src/main/java/org/onosproject/codec/impl/EncodeCriterionCodecHelper.java
index 067a99a..c99e353 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/EncodeCriterionCodecHelper.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/EncodeCriterionCodecHelper.java
@@ -15,6 +15,7 @@
  */
 package org.onosproject.codec.impl;
 
+import com.esotericsoftware.kryo.io.Output;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import org.onlab.util.HexString;
@@ -57,7 +58,9 @@
 import org.onosproject.net.pi.runtime.PiLpmFieldMatch;
 import org.onosproject.net.pi.runtime.PiRangeFieldMatch;
 import org.onosproject.net.pi.runtime.PiTernaryFieldMatch;
+import org.onosproject.store.serializers.KryoNamespaces;
 
+import java.io.ByteArrayOutputStream;
 import java.util.EnumMap;
 
 import static com.google.common.base.Preconditions.checkNotNull;
@@ -133,6 +136,7 @@
         formatMap.put(Criterion.Type.ODU_SIGID, new FormatOduSignalId());
         formatMap.put(Criterion.Type.ODU_SIGTYPE, new FormatOduSignalType());
         formatMap.put(Criterion.Type.PROTOCOL_INDEPENDENT, new FormatProtocolIndependent());
+        formatMap.put(Criterion.Type.EXTENSION, new FormatExtension());
         // Currently unimplemented
         formatMap.put(Criterion.Type.ARP_OP, new FormatUnknown());
         formatMap.put(Criterion.Type.ARP_SPA, new FormatUnknown());
@@ -146,7 +150,6 @@
         formatMap.put(Criterion.Type.TCP_FLAGS, new FormatUnknown());
         formatMap.put(Criterion.Type.ACTSET_OUTPUT, new FormatUnknown());
         formatMap.put(Criterion.Type.PACKET_TYPE, new FormatUnknown());
-        formatMap.put(Criterion.Type.EXTENSION, new FormatUnknown());
         formatMap.put(Criterion.Type.ETH_SRC_MASKED, new FormatUnknown());
         formatMap.put(Criterion.Type.TCP_SRC_MASKED, new FormatUnknown());
         formatMap.put(Criterion.Type.TCP_DST_MASKED, new FormatUnknown());
@@ -579,6 +582,19 @@
         }
     }
 
+    private class FormatExtension implements CriterionTypeFormatter {
+        @Override
+        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
+            Output output = new Output(new ByteArrayOutputStream());
+            KryoNamespaces.API.borrow().writeObject(output, criterion);
+            root.put(CriterionCodec.EXTENSION, output.toBytes());
+            output.flush();
+            output.close();
+
+            return root;
+        }
+    }
+
     private class FormatDummyType implements CriterionTypeFormatter {
 
         @Override
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/EncodeInstructionCodecHelper.java b/core/common/src/main/java/org/onosproject/codec/impl/EncodeInstructionCodecHelper.java
index 0d67eff..996fa8c 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/EncodeInstructionCodecHelper.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/EncodeInstructionCodecHelper.java
@@ -315,6 +315,8 @@
         }
 
         if (device.is(ExtensionTreatmentCodec.class)) {
+            // for extension instructions, encoding device id is needed for the corresponding decoder
+            result.put("deviceId", deviceId.toString());
             ExtensionTreatmentCodec treatmentCodec = device.as(ExtensionTreatmentCodec.class);
             ObjectNode node = treatmentCodec.encode(extensionInstruction.extensionInstruction(), context);
             result.set(InstructionCodec.EXTENSION, node);