Adding support for VLAN_PUSH with EtherType (incl. Q-in-Q)

 - Allowing VLAN_PUSH Instruction to use ethernetType (incl. using REST API)
 - Adding QINQ (0x88a8) Ethernet type
 - Updating InstructionCodec decoders/encoders
 - Updating TrafficTreatment/FlowEntryBuilder

Change-Id: I723cc936a8a49c39da9abe65ba9e5b1bdc1392bf
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 316e9d24..fd8bd74 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
@@ -19,6 +19,7 @@
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import org.onlab.osgi.DefaultServiceDirectory;
 import org.onlab.osgi.ServiceDirectory;
+import org.onlab.packet.EthType;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.MplsLabel;
@@ -48,6 +49,9 @@
 import org.onosproject.net.meter.MeterId;
 import org.slf4j.Logger;
 
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
 import static org.onlab.util.Tools.nullIsIllegal;
 import static org.slf4j.LoggerFactory.getLogger;
 
@@ -58,6 +62,7 @@
     protected static final Logger log = getLogger(DecodeInstructionCodecHelper.class);
     private final ObjectNode json;
     private final CodecContext context;
+    private static final Pattern ETHTYPE_PATTERN = Pattern.compile("0x([0-9a-fA-F]{4})");
 
     /**
      * Creates a decode instruction codec object.
@@ -108,6 +113,9 @@
         } else if (subType.equals(L2ModificationInstruction.L2SubType.VLAN_POP.name())) {
             return Instructions.popVlan();
         } else if (subType.equals(L2ModificationInstruction.L2SubType.VLAN_PUSH.name())) {
+            if (json.has(InstructionCodec.ETHERNET_TYPE)) {
+                return Instructions.pushVlan(getEthType());
+            }
             return Instructions.pushVlan();
         } else if (subType.equals(L2ModificationInstruction.L2SubType.TUNNEL_ID.name())) {
             long tunnelId = nullIsIllegal(json.get(InstructionCodec.TUNNEL_ID),
@@ -312,6 +320,23 @@
     }
 
     /**
+     * Returns Ethernet type.
+     *
+     * @return ethernet type
+     * @throws IllegalArgumentException if the JSON is invalid
+     */
+    private EthType getEthType() {
+        String ethTypeStr = nullIsIllegal(json.get(InstructionCodec.ETHERNET_TYPE),
+                  InstructionCodec.ETHERNET_TYPE + InstructionCodec.MISSING_MEMBER_MESSAGE).asText();
+        Matcher matcher = ETHTYPE_PATTERN.matcher(ethTypeStr);
+        if (!matcher.matches()) {
+            throw new IllegalArgumentException("ETHERNET_TYPE must be a four digit hex string starting with 0x");
+        }
+        short ethernetType = (short) Integer.parseInt(matcher.group(1), 16);
+        return new EthType(ethernetType);
+    }
+
+    /**
      * Decodes the JSON into an instruction object.
      *
      * @return Criterion object
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 2b3c9d7..f523430 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
@@ -136,6 +136,11 @@
                         (L2ModificationInstruction.ModVlanPcpInstruction) l2Instruction;
                 result.put(InstructionCodec.VLAN_PCP, modVlanPcpInstruction.vlanPcp());
                 break;
+            case VLAN_PUSH:
+                final L2ModificationInstruction.ModVlanHeaderInstruction pushVlanInstruction =
+                        (L2ModificationInstruction.ModVlanHeaderInstruction) l2Instruction;
+                result.put(InstructionCodec.ETHERNET_TYPE, pushVlanInstruction.ethernetType().toString());
+                break;
             case MPLS_LABEL:
                 final L2ModificationInstruction.ModMplsLabelInstruction modMplsLabelInstruction =
                         (L2ModificationInstruction.ModMplsLabelInstruction) l2Instruction;