Adding support for writing metadata instruction.
Needed by Centec driver.

Change-Id: I705015c4d7ac881c273a01b36b05d39d9cd9ba87
diff --git a/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficTreatment.java b/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficTreatment.java
index 049f49a..31148ab 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficTreatment.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficTreatment.java
@@ -43,6 +43,7 @@
     private final List<Instruction> deferred;
     private final List<Instruction> all;
     private final Instructions.TableTypeTransition table;
+    private final Instructions.MetadataInstruction meta;
 
     private final boolean hasClear;
 
@@ -60,6 +61,7 @@
         this.all = this.immediate;
         this.hasClear = false;
         this.table = null;
+        this.meta = null;
     }
 
     /**
@@ -73,13 +75,14 @@
     private DefaultTrafficTreatment(List<Instruction> deferred,
                                    List<Instruction> immediate,
                                    Instructions.TableTypeTransition table,
-                                   boolean clear) {
+                                   boolean clear,
+                                   Instructions.MetadataInstruction meta) {
         this.immediate = ImmutableList.copyOf(checkNotNull(immediate));
         this.deferred = ImmutableList.copyOf(checkNotNull(deferred));
         this.all = ListUtils.union(this.immediate, this.deferred);
         this.table = table;
+        this.meta = meta;
         this.hasClear = clear;
-
     }
 
     @Override
@@ -107,6 +110,11 @@
         return hasClear;
     }
 
+    @Override
+    public Instructions.MetadataInstruction writeMetadata() {
+        return meta;
+    }
+
     /**
      * Returns a new traffic treatment builder.
      *
@@ -139,7 +147,7 @@
     //FIXME: Order of instructions may affect hashcode
     @Override
     public int hashCode() {
-        return Objects.hash(immediate, deferred, table);
+        return Objects.hash(immediate, deferred, table, meta);
     }
 
     @Override
@@ -151,7 +159,8 @@
             DefaultTrafficTreatment that = (DefaultTrafficTreatment) obj;
             return Objects.equals(immediate, that.immediate) &&
                     Objects.equals(deferred, that.deferred) &&
-                    Objects.equals(table, that.table);
+                    Objects.equals(table, that.table) &&
+                    Objects.equals(meta, that.meta);
 
         }
         return false;
@@ -164,6 +173,7 @@
                 .add("deferred", deferred)
                 .add("transition", table == null ? "None" : table.toString())
                 .add("cleared", hasClear)
+                .add("metadata", meta)
                 .toString();
     }
 
@@ -177,6 +187,8 @@
 
         Instructions.TableTypeTransition table;
 
+        Instructions.MetadataInstruction meta;
+
         List<Instruction> deferred = Lists.newLinkedList();
 
         List<Instruction> immediate = Lists.newLinkedList();
@@ -213,6 +225,9 @@
                 case TABLE:
                     table = (Instructions.TableTypeTransition) instruction;
                     break;
+                case METADATA:
+                    meta = (Instructions.MetadataInstruction) instruction;
+                    break;
                 default:
                     throw new IllegalArgumentException("Unknown instruction type: " +
                                                                instruction.type());
@@ -355,6 +370,11 @@
         }
 
         @Override
+        public Builder writeMetadata(long metadata, long metadataMask) {
+            return add(Instructions.writeMetadata(metadata, metadataMask));
+        }
+
+        @Override
         public TrafficTreatment build() {
             //Don't add DROP instruction by default when instruction
             //set is empty. This will be handled in DefaultSingleTablePipeline
@@ -364,7 +384,7 @@
             //        && table == null && !clear) {
             //    drop();
             //}
-            return new DefaultTrafficTreatment(deferred, immediate, table, clear);
+            return new DefaultTrafficTreatment(deferred, immediate, table, clear, meta);
         }
 
     }
diff --git a/core/api/src/main/java/org/onosproject/net/flow/TrafficTreatment.java b/core/api/src/main/java/org/onosproject/net/flow/TrafficTreatment.java
index 281e395..a82919f 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/TrafficTreatment.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/TrafficTreatment.java
@@ -67,6 +67,13 @@
     boolean clearedDeferred();
 
     /**
+     * Returns the metadata instruction if there is one.
+     *
+     * @return a metadata instruction that may be null
+     */
+    Instructions.MetadataInstruction writeMetadata();
+
+    /**
      * Builder of traffic treatment entities.
      */
     interface Builder {
@@ -276,6 +283,20 @@
         Builder wipeDeferred();
 
         /**
+         * Writes metadata to associate with a packet.
+         * <pre>
+         * {@code
+         * new_metadata = (old_metadata &  ̃mask) | (value & mask)
+         * }
+         * </pre>
+         *
+         * @param value the metadata to write
+         * @param mask  the masked bits for the value
+         * @return a treatment builder
+         */
+        Builder writeMetadata(long value, long mask);
+
+        /**
          * Builds an immutable traffic treatment descriptor.
          * <p>
          * If the treatment is empty when build() is called, it will add a default
@@ -286,5 +307,7 @@
          * @return traffic treatment
          */
         TrafficTreatment build();
+
     }
+
 }
diff --git a/core/api/src/main/java/org/onosproject/net/flow/instructions/Instruction.java b/core/api/src/main/java/org/onosproject/net/flow/instructions/Instruction.java
index e3fe3f3..16a9464 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/instructions/Instruction.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/instructions/Instruction.java
@@ -35,7 +35,7 @@
         OUTPUT,
 
         /**
-         * Signifies that.... (do we need this?)
+         * Signifies that traffic should be sent out of a group.
          */
         GROUP,
 
@@ -57,7 +57,12 @@
         /**
          * Signifies that the traffic should be modified in L3 way.
          */
-        L3MODIFICATION
+        L3MODIFICATION,
+
+        /**
+         * Signifies that metadata be attached to traffic.
+         */
+        METADATA
     }
 
     /**
diff --git a/core/api/src/main/java/org/onosproject/net/flow/instructions/Instructions.java b/core/api/src/main/java/org/onosproject/net/flow/instructions/Instructions.java
index c673ae0..53d0528 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/instructions/Instructions.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/instructions/Instructions.java
@@ -323,6 +323,17 @@
     }
 
     /**
+     * Writes metadata to associate with a packet.
+     *
+     * @param metadata the metadata value to write
+     * @param metadataMask the bits to mask for the metadata value
+     * @return metadata instruction
+     */
+    public static Instruction writeMetadata(long metadata, long metadataMask) {
+        return new MetadataInstruction(metadata, metadataMask);
+    }
+
+    /**
      *  Drop instruction.
      */
     public static final class DropInstruction implements Instruction {
@@ -443,7 +454,9 @@
         }
     }
 
-
+    /**
+     *  Transition instruction.
+     */
     public static class TableTypeTransition implements Instruction {
         private final Integer tableId;
 
@@ -483,7 +496,59 @@
             }
             return false;
         }
+    }
 
+    /**
+     *  Metadata instruction.
+     */
+    public static class MetadataInstruction implements Instruction {
+        private final long metadata;
+        private final long metadataMask;
+
+        MetadataInstruction(long metadata, long metadataMask) {
+            this.metadata = metadata;
+            this.metadataMask = metadataMask;
+        }
+
+        @Override
+        public Type type() {
+            return Type.METADATA;
+        }
+
+        public long metadata() {
+            return this.metadata;
+        }
+
+        public long metadataMask() {
+            return this.metadataMask;
+        }
+
+        @Override
+        public String toString() {
+            return toStringHelper(type().toString())
+                    .add("metadata", Long.toHexString(this.metadata))
+                    .add("metadata mask", Long.toHexString(this.metadataMask))
+                    .toString();
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(type(), metadata, metadataMask);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj instanceof MetadataInstruction) {
+                MetadataInstruction that = (MetadataInstruction) obj;
+                return Objects.equals(metadata, that.metadata) &&
+                        Objects.equals(metadataMask, that.metadataMask);
+
+            }
+            return false;
+        }
     }
 }
 
diff --git a/core/api/src/test/java/org/onosproject/net/intent/IntentTestsMocks.java b/core/api/src/test/java/org/onosproject/net/intent/IntentTestsMocks.java
index 6c7fe13..e47d1aa 100644
--- a/core/api/src/test/java/org/onosproject/net/intent/IntentTestsMocks.java
+++ b/core/api/src/test/java/org/onosproject/net/intent/IntentTestsMocks.java
@@ -36,6 +36,7 @@
 import org.onosproject.net.flow.criteria.Criterion.Type;
 import org.onosproject.net.flow.instructions.Instruction;
 import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.flow.instructions.Instructions.MetadataInstruction;
 import org.onosproject.net.resource.link.BandwidthResource;
 import org.onosproject.net.resource.link.BandwidthResourceRequest;
 import org.onosproject.net.resource.link.LambdaResource;
@@ -118,6 +119,11 @@
         public boolean clearedDeferred() {
             return false;
         }
+
+        @Override
+        public MetadataInstruction writeMetadata() {
+            return null;
+        }
     }
 
     /**
diff --git a/core/net/src/test/java/org/onosproject/net/flow/impl/FlowRuleManagerTest.java b/core/net/src/test/java/org/onosproject/net/flow/impl/FlowRuleManagerTest.java
index 1113b2b..5097bb8 100644
--- a/core/net/src/test/java/org/onosproject/net/flow/impl/FlowRuleManagerTest.java
+++ b/core/net/src/test/java/org/onosproject/net/flow/impl/FlowRuleManagerTest.java
@@ -21,6 +21,7 @@
 import com.google.common.collect.Sets;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.MoreExecutors;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -58,6 +59,7 @@
 import org.onosproject.net.flow.criteria.Criterion;
 import org.onosproject.net.flow.instructions.Instruction;
 import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.flow.instructions.Instructions.MetadataInstruction;
 import org.onosproject.net.provider.AbstractProvider;
 import org.onosproject.net.provider.ProviderId;
 import org.onosproject.store.trivial.impl.SimpleFlowRuleStore;
@@ -595,6 +597,11 @@
             return false;
         }
 
+        @Override
+        public MetadataInstruction writeMetadata() {
+            return null;
+        }
+
     }
 
     public class TestApplicationId extends DefaultApplicationId {
diff --git a/drivers/src/main/java/org/onosproject/driver/pipeline/CentecV350Pipeline.java b/drivers/src/main/java/org/onosproject/driver/pipeline/CentecV350Pipeline.java
index 4c9700f..7c29773 100644
--- a/drivers/src/main/java/org/onosproject/driver/pipeline/CentecV350Pipeline.java
+++ b/drivers/src/main/java/org/onosproject/driver/pipeline/CentecV350Pipeline.java
@@ -78,6 +78,7 @@
     protected static final int ROUTE_TABLE = 3;
 
     private static final long DEFAULT_METADATA = 100;
+    private static final long DEFAULT_METADATA_MASK = 0xff;
 
     // Priority used in PORT_VLAN Table, the only priority accepted is PORT_VLAN_TABLE_PRIORITY.
     // The packet passed PORT+VLAN check will goto FILTER Table.
@@ -408,10 +409,10 @@
                 selector.matchInPort(p.port());
                 // Although the accepted packets will be sent to filter table, we must
                 // explicitly set goto_table instruction here.
+                treatment.writeMetadata(DEFAULT_METADATA, DEFAULT_METADATA_MASK);
+                // set default metadata written by PORT_VLAN Table.
                 treatment.transition(FILTER_TABLE);
                 // We do not support strip vlan here, treatment.deferred().popVlan();
-                // XXX: write_metadata seems not supported by ONOS now, use the switch CLI to
-                // set default metadata written by PORT_VLAN Table.
                 // PORT_VLAN table only accept 0xffff priority since it does exact match only.
                 FlowRule rule = DefaultFlowRule.builder()
                         .forDevice(deviceId)
diff --git a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java
index 61bda98..61f669c 100644
--- a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java
+++ b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java
@@ -16,6 +16,7 @@
 package org.onosproject.provider.of.flow.impl;
 
 import com.google.common.collect.Lists;
+
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.Ip4Prefix;
 import org.onlab.packet.Ip6Address;
@@ -56,6 +57,7 @@
 import org.projectfloodlight.openflow.protocol.instruction.OFInstructionApplyActions;
 import org.projectfloodlight.openflow.protocol.instruction.OFInstructionGotoTable;
 import org.projectfloodlight.openflow.protocol.instruction.OFInstructionWriteActions;
+import org.projectfloodlight.openflow.protocol.instruction.OFInstructionWriteMetadata;
 import org.projectfloodlight.openflow.protocol.match.Match;
 import org.projectfloodlight.openflow.protocol.match.MatchField;
 import org.projectfloodlight.openflow.protocol.oxm.OFOxm;
@@ -219,6 +221,9 @@
                             .getTableId().getValue()));
                     break;
                 case WRITE_METADATA:
+                    OFInstructionWriteMetadata m = (OFInstructionWriteMetadata) in;
+                    builder.writeMetadata(m.getMetadata().getValue(),
+                                          m.getMetadataMask().getValue());
                     break;
                 case WRITE_ACTIONS:
                     builder.deferred();
diff --git a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer10.java b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer10.java
index 6d7e2e1..c9de450 100644
--- a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer10.java
+++ b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer10.java
@@ -167,6 +167,8 @@
                 break;
             case L0MODIFICATION:
             case GROUP:
+            case TABLE:
+            case METADATA:
                 log.warn("Instruction type {} not supported with protocol version {}",
                         i.type(), factory().getVersion());
                 break;
diff --git a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java
index a636a76..67c16fa 100644
--- a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java
+++ b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java
@@ -117,6 +117,9 @@
         if (treatment.tableTransition() != null) {
             instructions.add(buildTableGoto(treatment.tableTransition()));
         }
+        if (treatment.writeMetadata() != null) {
+            instructions.add(buildMetadata(treatment.writeMetadata()));
+        }
 
         long cookie = flowRule().id().value();
 
@@ -154,6 +157,9 @@
         if (treatment.tableTransition() != null) {
             instructions.add(buildTableGoto(treatment.tableTransition()));
         }
+        if (treatment.writeMetadata() != null) {
+            instructions.add(buildMetadata(treatment.writeMetadata()));
+        }
 
         long cookie = flowRule().id().value();
 
@@ -247,6 +253,12 @@
         return instruction;
     }
 
+    private OFInstruction buildMetadata(Instructions.MetadataInstruction m) {
+        OFInstruction instruction = factory().instructions().writeMetadata(
+                U64.of(m.metadata()), U64.of(m.metadataMask()));
+        return instruction;
+    }
+
     private OFAction buildL0Modification(Instruction i) {
         L0ModificationInstruction l0m = (L0ModificationInstruction) i;
         switch (l0m.subtype()) {