[ONOS-4258] Initial implementation of supporting extension in REST

- Support Nicira extension

Change-Id: I62bf4417e43459727ce7d4b1ac929c6cf0b7826f
diff --git a/core/api/src/main/java/org/onosproject/codec/ExtensionSelectorCodec.java b/core/api/src/main/java/org/onosproject/codec/ExtensionSelectorCodec.java
new file mode 100644
index 0000000..fc51216
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/codec/ExtensionSelectorCodec.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.codec;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.net.driver.HandlerBehaviour;
+import org.onosproject.net.flow.criteria.ExtensionSelector;
+
+/**
+ * Interface for encode and decode extension selector.
+ */
+public interface ExtensionSelectorCodec extends HandlerBehaviour {
+
+    /**
+     * Encodes an extension selector to an JSON object.
+     *
+     * @param extensionSelector extension selector
+     * @param  context encoding context
+     * @return JSON object
+     */
+    default ObjectNode encode(ExtensionSelector extensionSelector, CodecContext context) {
+        return null;
+    }
+
+    /**
+     * Decodes an JSON object to an extension selector.
+     *
+     * @param objectNode JSON object
+     * @param  context decoding context
+     * @return extension selector
+     */
+    default ExtensionSelector decode(ObjectNode objectNode, CodecContext context) {
+        return null;
+    }
+}
diff --git a/core/api/src/main/java/org/onosproject/codec/ExtensionTreatmentCodec.java b/core/api/src/main/java/org/onosproject/codec/ExtensionTreatmentCodec.java
new file mode 100644
index 0000000..e33258b
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/codec/ExtensionTreatmentCodec.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.codec;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.net.driver.HandlerBehaviour;
+import org.onosproject.net.flow.instructions.ExtensionTreatment;
+
+/**
+ * Interface for encode and decode extension treatment.
+ */
+public interface ExtensionTreatmentCodec extends HandlerBehaviour {
+
+    /**
+     * Encodes an extension treatment to an JSON object.
+     *
+     * @param extensionTreatment extension treatment
+     * @param  context encoding context
+     * @return JSON object
+     */
+    default ObjectNode encode(ExtensionTreatment extensionTreatment, CodecContext context) {
+        return null;
+    }
+
+    /**
+     * Decodes an JSON object to an extension treatment.
+     *
+     * @param objectNode JSON object
+     * @param  context decoding context
+     * @return extension treatment
+     */
+    default ExtensionTreatment decode(ObjectNode objectNode, CodecContext context) {
+        return null;
+    }
+}
diff --git a/core/api/src/main/java/org/onosproject/net/flow/instructions/ExtensionTreatmentType.java b/core/api/src/main/java/org/onosproject/net/flow/instructions/ExtensionTreatmentType.java
index cf2400c..df9789e 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/instructions/ExtensionTreatmentType.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/instructions/ExtensionTreatmentType.java
@@ -79,6 +79,15 @@
         this.type = type;
     }
 
+    /**
+     * Returns extension treatment type.
+     *
+     * @return extension treatment type
+     */
+    public int type() {
+        return type;
+    }
+
     @Override
     public int hashCode() {
         return Objects.hash(type);
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 03dd496..b70a684 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
@@ -15,20 +15,28 @@
  */
 package org.onosproject.codec.impl;
 
+import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onlab.osgi.DefaultServiceDirectory;
+import org.onlab.osgi.ServiceDirectory;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.MplsLabel;
 import org.onlab.packet.TpPort;
 import org.onlab.packet.VlanId;
 import org.onlab.util.HexString;
+import org.onosproject.codec.ExtensionTreatmentCodec;
 import org.onosproject.core.DefaultGroupId;
 import org.onosproject.core.GroupId;
 import org.onosproject.net.ChannelSpacing;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
 import org.onosproject.net.GridType;
 import org.onosproject.net.OchSignal;
 import org.onosproject.net.OduSignalId;
 import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.instructions.ExtensionTreatment;
 import org.onosproject.net.flow.instructions.Instruction;
 import org.onosproject.net.flow.instructions.Instructions;
 import org.onosproject.net.flow.instructions.L0ModificationInstruction;
@@ -37,13 +45,16 @@
 import org.onosproject.net.flow.instructions.L3ModificationInstruction;
 import org.onosproject.net.flow.instructions.L4ModificationInstruction;
 import org.onosproject.net.meter.MeterId;
+import org.slf4j.Logger;
 
 import static org.onlab.util.Tools.nullIsIllegal;
+import static org.slf4j.LoggerFactory.getLogger;
 
 /**
  * Decoding portion of the instruction codec.
  */
 public final class DecodeInstructionCodecHelper {
+    protected static final Logger log = getLogger(DecodeInstructionCodecHelper.class);
     private final ObjectNode json;
 
     /**
@@ -227,6 +238,45 @@
     }
 
     /**
+     * Decodes a extension instruction.
+     *
+     * @return extension treatment
+     */
+    private Instruction decodeExtension() {
+        ObjectNode node = (ObjectNode) json.get(InstructionCodec.EXTENSION);
+        if (node != null) {
+            DeviceId deviceId = getDeviceId();
+
+            ServiceDirectory serviceDirectory = new DefaultServiceDirectory();
+            DeviceService deviceService = serviceDirectory.get(DeviceService.class);
+            Device device = deviceService.getDevice(deviceId);
+
+            if (device.is(ExtensionTreatmentCodec.class)) {
+                ExtensionTreatmentCodec treatmentCodec = device.as(ExtensionTreatmentCodec.class);
+                ExtensionTreatment treatment = treatmentCodec.decode(node, null);
+                return Instructions.extension(treatment, deviceId);
+            } else {
+                log.warn("There is no codec to decode extension for device {}", deviceId.toString());
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns device identifier.
+     *
+     * @return device identifier
+     * @throws IllegalArgumentException if the JSON is invalid
+     */
+    private DeviceId getDeviceId() {
+        JsonNode deviceIdNode = json.get(InstructionCodec.DEVICE_ID);
+        if (deviceIdNode != null) {
+            return DeviceId.deviceId(deviceIdNode.asText());
+        }
+        throw new IllegalArgumentException("Empty device identifier");
+    }
+
+    /**
      * Extracts port number of the given json node.
      *
      * @param jsonNode json node
@@ -290,6 +340,8 @@
             return decodeL3();
         } else if (type.equals(Instruction.Type.L4MODIFICATION.name())) {
             return decodeL4();
+        } else if (type.equals(Instruction.Type.EXTENSION.name())) {
+            return decodeExtension();
         }
         throw new IllegalArgumentException("Instruction type "
                 + type + " is not supported");
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 b108f7e..3d1ed2f 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
@@ -16,10 +16,16 @@
 package org.onosproject.codec.impl;
 
 import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onlab.osgi.DefaultServiceDirectory;
+import org.onlab.osgi.ServiceDirectory;
 import org.onlab.util.HexString;
 import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.ExtensionTreatmentCodec;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
 import org.onosproject.net.OchSignal;
 import org.onosproject.net.OduSignalId;
+import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.flow.instructions.Instruction;
 import org.onosproject.net.flow.instructions.Instructions;
 import org.onosproject.net.flow.instructions.L0ModificationInstruction;
@@ -28,13 +34,14 @@
 import org.onosproject.net.flow.instructions.L3ModificationInstruction;
 import org.onosproject.net.flow.instructions.L4ModificationInstruction;
 import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+
+import static org.slf4j.LoggerFactory.getLogger;
 
 /**
  * JSON encoding of Instructions.
  */
 public final class EncodeInstructionCodecHelper {
-    protected static final Logger log = LoggerFactory.getLogger(EncodeInstructionCodecHelper.class);
+    protected static final Logger log = getLogger(EncodeInstructionCodecHelper.class);
     private final Instruction instruction;
     private final CodecContext context;
 
@@ -219,14 +226,29 @@
         }
     }
 
+
     /**
-     * Encode a extension instruction.
+     * Encodes a extension instruction.
      *
      * @param result json node that the instruction attributes are added to
      */
     private void encodeExtension(ObjectNode result) {
-        // TODO Support extension in REST API
-        log.info("Cannot convert instruction type of EXTENSION");
+        final Instructions.ExtensionInstructionWrapper extensionInstruction =
+                (Instructions.ExtensionInstructionWrapper) instruction;
+
+        DeviceId deviceId = extensionInstruction.deviceId();
+
+        ServiceDirectory serviceDirectory = new DefaultServiceDirectory();
+        DeviceService deviceService = serviceDirectory.get(DeviceService.class);
+        Device device = deviceService.getDevice(deviceId);
+
+        if (device.is(ExtensionTreatmentCodec.class)) {
+            ExtensionTreatmentCodec treatmentCodec = device.as(ExtensionTreatmentCodec.class);
+            ObjectNode node = treatmentCodec.encode(extensionInstruction.extensionInstruction(), context);
+            result.set(InstructionCodec.EXTENSION, node);
+        } else {
+            log.warn("There is no codec to encode extension for device {}", deviceId.toString());
+        }
     }
 
     /**
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/InstructionCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/InstructionCodec.java
index 3616e54..da41e25 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/InstructionCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/InstructionCodec.java
@@ -57,6 +57,8 @@
     protected static final String TRIBUTARY_PORT_NUMBER = "tributaryPortNumber";
     protected static final String TRIBUTARY_SLOT_LEN = "tributarySlotLength";
     protected static final String TRIBUTARY_SLOT_BITMAP = "tributarySlotBitmap";
+    protected static final String EXTENSION = "extension";
+    protected static final String DEVICE_ID = "deviceId";
 
     protected static final String MISSING_MEMBER_MESSAGE =
             " member is required in Instruction";