Generic extensions to the treatment API to support protocol extensions like
OF experimenter actions.

Change-Id: I88cc5896d17fdbf89807f911f9c23e4f19f6a5ad
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 2c01e96..4d5b6b2 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
@@ -28,6 +28,11 @@
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.Lambda;
 import org.onosproject.net.PortNumber;
+import org.onosproject.net.driver.DefaultDriverData;
+import org.onosproject.net.driver.DefaultDriverHandler;
+import org.onosproject.net.driver.Driver;
+import org.onosproject.net.driver.DriverHandler;
+import org.onosproject.net.driver.DriverService;
 import org.onosproject.net.flow.DefaultFlowEntry;
 import org.onosproject.net.flow.DefaultFlowRule;
 import org.onosproject.net.flow.DefaultTrafficSelector;
@@ -39,6 +44,7 @@
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.flow.instructions.Instructions;
 import org.onosproject.openflow.controller.Dpid;
+import org.onosproject.openflow.controller.ExtensionInterpreter;
 import org.projectfloodlight.openflow.protocol.OFFlowMod;
 import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
 import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry;
@@ -106,7 +112,9 @@
 
     private final FlowType type;
 
-    public FlowEntryBuilder(Dpid dpid, OFFlowStatsEntry entry) {
+    private final DriverService driverService;
+
+    public FlowEntryBuilder(Dpid dpid, OFFlowStatsEntry entry, DriverService driverService) {
         this.stat = entry;
         this.match = entry.getMatch();
         this.instructions = getInstructions(entry);
@@ -114,9 +122,10 @@
         this.removed = null;
         this.flowMod = null;
         this.type = FlowType.STAT;
+        this.driverService = driverService;
     }
 
-    public FlowEntryBuilder(Dpid dpid, OFFlowRemoved removed) {
+    public FlowEntryBuilder(Dpid dpid, OFFlowRemoved removed, DriverService driverService) {
         this.match = removed.getMatch();
         this.removed = removed;
 
@@ -125,10 +134,10 @@
         this.stat = null;
         this.flowMod = null;
         this.type = FlowType.REMOVED;
-
+        this.driverService = driverService;
     }
 
-    public FlowEntryBuilder(Dpid dpid, OFFlowMod fm) {
+    public FlowEntryBuilder(Dpid dpid, OFFlowMod fm, DriverService driverService) {
         this.match = fm.getMatch();
         this.dpid = dpid;
         this.instructions = getInstructions(fm);
@@ -136,6 +145,7 @@
         this.flowMod = fm;
         this.stat = null;
         this.removed = null;
+        this.driverService = driverService;
     }
 
     public FlowEntry build(FlowEntryState... state) {
@@ -307,7 +317,7 @@
                     break;
                 case SET_FIELD:
                     OFActionSetField setField = (OFActionSetField) act;
-                    handleSetField(builder, setField.getField());
+                    handleSetField(builder, setField);
                     break;
                 case POP_MPLS:
                     OFActionPopMpls popMpls = (OFActionPopMpls) act;
@@ -363,7 +373,8 @@
     }
 
 
-    private void handleSetField(TrafficTreatment.Builder builder, OFOxm<?> oxm) {
+    private void handleSetField(TrafficTreatment.Builder builder, OFActionSetField action) {
+        OFOxm<?> oxm = action.getField();
         switch (oxm.getMatchField().id) {
         case VLAN_PCP:
             @SuppressWarnings("unchecked")
@@ -432,6 +443,13 @@
             OFOxm<TransportPort> udpsrc = (OFOxm<TransportPort>) oxm;
             builder.setUdpSrc(TpPort.tpPort(udpsrc.getValue().getPort()));
             break;
+        case TUNNEL_IPV4_DST:
+            DriverHandler driver = getDriver(dpid);
+            ExtensionInterpreter interpreter = driver.behaviour(ExtensionInterpreter.class);
+            if (interpreter != null) {
+                builder.extension(interpreter.mapAction(action), DeviceId.deviceId(Dpid.uri(dpid)));
+            }
+            break;
         case ARP_OP:
         case ARP_SHA:
         case ARP_SPA:
@@ -697,4 +715,11 @@
         }
         return builder.build();
     }
+
+    private DriverHandler getDriver(Dpid dpid) {
+        DeviceId deviceId = DeviceId.deviceId(Dpid.uri(dpid));
+        Driver driver = driverService.getDriver(deviceId);
+        DriverHandler handler = new DefaultDriverHandler(new DefaultDriverData(driver, deviceId));
+        return handler;
+    }
 }
diff --git a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilder.java b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilder.java
index e050524..7eca492 100644
--- a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilder.java
+++ b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilder.java
@@ -15,16 +15,13 @@
  */
 package org.onosproject.provider.of.flow.impl;
 
-import static org.slf4j.LoggerFactory.getLogger;
-
-import java.util.Optional;
-
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.Ip4Prefix;
 import org.onlab.packet.Ip6Address;
 import org.onlab.packet.Ip6Prefix;
 import org.onlab.packet.VlanId;
 import org.onosproject.net.OchSignal;
+import org.onosproject.net.driver.DriverService;
 import org.onosproject.net.flow.FlowRule;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.criteria.Criterion;
@@ -85,6 +82,10 @@
 import org.projectfloodlight.openflow.types.VlanVid;
 import org.slf4j.Logger;
 
+import java.util.Optional;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
 /**
  * Builder for OpenFlow flow mods based on FlowRules.
  */
@@ -96,6 +97,7 @@
     private final FlowRule flowRule;
     private final TrafficSelector selector;
     protected final Long xid;
+    protected final Optional<DriverService> driverService;
 
     /**
      * Creates a new flow mod builder.
@@ -107,12 +109,13 @@
      */
     public static FlowModBuilder builder(FlowRule flowRule,
                                          OFFactory factory,
-                                         Optional<Long> xid) {
+                                         Optional<Long> xid,
+                                         Optional<DriverService> driverService) {
         switch (factory.getVersion()) {
         case OF_10:
-            return new FlowModBuilderVer10(flowRule, factory, xid);
+            return new FlowModBuilderVer10(flowRule, factory, xid, driverService);
         case OF_13:
-            return new FlowModBuilderVer13(flowRule, factory, xid);
+            return new FlowModBuilderVer13(flowRule, factory, xid, driverService);
         default:
             throw new UnsupportedOperationException(
                     "No flow mod builder for protocol version " + factory.getVersion());
@@ -126,12 +129,13 @@
      * @param factory the OpenFlow factory to use to build the flow mod
      * @param xid the transaction ID
      */
-    protected FlowModBuilder(FlowRule flowRule, OFFactory factory, Optional<Long> xid) {
+    protected FlowModBuilder(FlowRule flowRule, OFFactory factory, Optional<Long> xid,
+                             Optional<DriverService> driverService) {
         this.factory = factory;
         this.flowRule = flowRule;
         this.selector = flowRule.selector();
         this.xid = xid.orElse(0L);
-
+        this.driverService = driverService;
     }
 
     /**
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 f77819d..c789841 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
@@ -17,6 +17,7 @@
 
 import org.onlab.packet.Ip4Address;
 import org.onosproject.net.PortNumber;
+import org.onosproject.net.driver.DriverService;
 import org.onosproject.net.flow.FlowRule;
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.flow.instructions.Instruction;
@@ -68,8 +69,9 @@
      * @param xid the transaction ID
      */
     protected FlowModBuilderVer10(FlowRule flowRule,
-                                  OFFactory factory, Optional<Long> xid) {
-        super(flowRule, factory, xid);
+                                  OFFactory factory, Optional<Long> xid,
+                                  Optional<DriverService> driverService) {
+        super(flowRule, factory, xid, driverService);
 
         this.treatment = flowRule.treatment();
     }
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 64b4360..a99aa81 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
@@ -18,10 +18,16 @@
 import com.google.common.collect.Lists;
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.Ip6Address;
+import org.onosproject.net.DeviceId;
 import org.onosproject.net.OchSignal;
 import org.onosproject.net.PortNumber;
+import org.onosproject.net.driver.DefaultDriverData;
+import org.onosproject.net.driver.DefaultDriverHandler;
+import org.onosproject.net.driver.Driver;
+import org.onosproject.net.driver.DriverService;
 import org.onosproject.net.flow.FlowRule;
 import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.instructions.ExtensionInstruction;
 import org.onosproject.net.flow.instructions.Instruction;
 import org.onosproject.net.flow.instructions.Instructions;
 import org.onosproject.net.flow.instructions.Instructions.GroupInstruction;
@@ -34,15 +40,16 @@
 import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
 import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModMplsBosInstruction;
 import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModMplsLabelInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModTunnelIdInstruction;
 import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
 import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanPcpInstruction;
 import org.onosproject.net.flow.instructions.L2ModificationInstruction.PushHeaderInstructions;
-import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModTunnelIdInstruction;
 import org.onosproject.net.flow.instructions.L3ModificationInstruction;
 import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModIPInstruction;
 import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModIPv6FlowLabelInstruction;
 import org.onosproject.net.flow.instructions.L4ModificationInstruction;
 import org.onosproject.net.flow.instructions.L4ModificationInstruction.ModTransportPortInstruction;
+import org.onosproject.openflow.controller.ExtensionInterpreter;
 import org.projectfloodlight.openflow.protocol.OFFactory;
 import org.projectfloodlight.openflow.protocol.OFFlowAdd;
 import org.projectfloodlight.openflow.protocol.OFFlowDelete;
@@ -88,6 +95,7 @@
     private static final int OFPCML_NO_BUFFER = 0xffff;
 
     private final TrafficTreatment treatment;
+    private final DeviceId deviceId;
 
     /**
      * Constructor for a flow mod builder for OpenFlow 1.3.
@@ -96,10 +104,12 @@
      * @param factory the OpenFlow factory to use to build the flow mod
      * @param xid the transaction ID
      */
-    protected FlowModBuilderVer13(FlowRule flowRule, OFFactory factory, Optional<Long> xid) {
-        super(flowRule, factory, xid);
+    protected FlowModBuilderVer13(FlowRule flowRule, OFFactory factory, Optional<Long> xid,
+                                  Optional<DriverService> driverService) {
+        super(flowRule, factory, xid, driverService);
 
         this.treatment = flowRule.treatment();
+        this.deviceId = flowRule.deviceId();
     }
 
     @Override
@@ -256,6 +266,10 @@
                     //FIXME: should not occur here.
                     tableFound = true;
                     break;
+                case EXTENSION:
+                    actions.add(buildExtensionAction(((Instructions.ExtensionInstructionWrapper) i)
+                            .extensionInstruction()));
+                    break;
                 default:
                     log.warn("Instruction type {} not yet implemented.", i.type());
             }
@@ -467,4 +481,20 @@
         return null;
     }
 
+    private OFAction buildExtensionAction(ExtensionInstruction i) {
+        if (!driverService.isPresent()) {
+            log.error("No driver service present");
+            return null;
+        }
+        Driver driver = driverService.get().getDriver(deviceId);
+        if (driver.hasBehaviour(ExtensionInterpreter.class)) {
+            DefaultDriverHandler handler =
+                    new DefaultDriverHandler(new DefaultDriverData(driver, deviceId));
+            ExtensionInterpreter interpreter = handler.behaviour(ExtensionInterpreter.class);
+            return interpreter.mapInstruction(factory(), i);
+        }
+
+        return null;
+    }
+
 }
diff --git a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/NewAdaptiveFlowStatsCollector.java b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/NewAdaptiveFlowStatsCollector.java
index 487cae9..d5186fa 100644
--- a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/NewAdaptiveFlowStatsCollector.java
+++ b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/NewAdaptiveFlowStatsCollector.java
@@ -16,21 +16,10 @@
 

 package org.onosproject.provider.of.flow.impl;

 

-import java.util.HashSet;

-import java.util.List;

-import java.util.Map;

-import java.util.Optional;

-import java.util.Set;

-import java.util.concurrent.TimeUnit;

-import java.util.concurrent.ScheduledFuture;

-import java.util.concurrent.Executors;

-import java.util.concurrent.ScheduledExecutorService;

-

 import com.google.common.base.Objects;

 import com.google.common.collect.ImmutableSet;

 import com.google.common.collect.Maps;

 import com.google.common.collect.Sets;

-

 import org.onosproject.net.flow.DefaultTypedFlowEntry;

 import org.onosproject.net.flow.FlowEntry;

 import org.onosproject.net.flow.FlowId;

@@ -47,9 +36,19 @@
 import org.projectfloodlight.openflow.types.TableId;

 import org.slf4j.Logger;

 

+import java.util.HashSet;

+import java.util.List;

+import java.util.Map;

+import java.util.Optional;

+import java.util.Set;

+import java.util.concurrent.Executors;

+import java.util.concurrent.ScheduledExecutorService;

+import java.util.concurrent.ScheduledFuture;

+import java.util.concurrent.TimeUnit;

+

 import static com.google.common.base.Preconditions.checkNotNull;

 import static org.onlab.util.Tools.groupedThreads;

-import static org.onosproject.net.flow.TypedStoredFlowEntry.*;

+import static org.onosproject.net.flow.TypedStoredFlowEntry.FlowLiveType;

 import static org.slf4j.LoggerFactory.getLogger;

 

 /**

@@ -232,7 +231,8 @@
     // send openflow flow stats request message with getting the specific flow entry(fe) to a given switch sw

     private void ofFlowStatsRequestFlowSend(FlowEntry fe) {

         // set find match

-        Match match = FlowModBuilder.builder(fe, sw.factory(), Optional.empty()).buildMatch();

+        Match match = FlowModBuilder.builder(fe, sw.factory(), Optional.empty(),

+                Optional.empty()).buildMatch();

         // set find tableId

         TableId tableId = TableId.of(fe.tableId());

         // set output port

diff --git a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java
index 6374ca5..b37cb42 100644
--- a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java
+++ b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java
@@ -21,7 +21,6 @@
 import com.google.common.cache.RemovalNotification;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
-
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -32,6 +31,7 @@
 import org.onosproject.cfg.ComponentConfigService;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.driver.DriverService;
 import org.onosproject.net.flow.CompletedBatchOperation;
 import org.onosproject.net.flow.DefaultTableStatisticsEntry;
 import org.onosproject.net.flow.FlowEntry;
@@ -61,12 +61,12 @@
 import org.projectfloodlight.openflow.protocol.OFFlowMod;
 import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
 import org.projectfloodlight.openflow.protocol.OFFlowStatsReply;
-import org.projectfloodlight.openflow.protocol.OFTableStatsReply;
-import org.projectfloodlight.openflow.protocol.OFTableStatsEntry;
 import org.projectfloodlight.openflow.protocol.OFMessage;
 import org.projectfloodlight.openflow.protocol.OFPortStatus;
 import org.projectfloodlight.openflow.protocol.OFStatsReply;
 import org.projectfloodlight.openflow.protocol.OFStatsType;
+import org.projectfloodlight.openflow.protocol.OFTableStatsEntry;
+import org.projectfloodlight.openflow.protocol.OFTableStatsReply;
 import org.projectfloodlight.openflow.protocol.errormsg.OFBadRequestErrorMsg;
 import org.projectfloodlight.openflow.protocol.errormsg.OFFlowModFailedErrorMsg;
 import org.slf4j.Logger;
@@ -106,6 +106,9 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected ComponentConfigService cfgService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DriverService driverService;
+
     private static final int DEFAULT_POLL_FREQUENCY = 5;
     @Property(name = "flowPollFrequency", intValue = DEFAULT_POLL_FREQUENCY,
             label = "Frequency (in seconds) for polling flow statistics")
@@ -269,7 +272,7 @@
             return;
         }
         sw.sendMsg(FlowModBuilder.builder(flowRule, sw.factory(),
-                                          Optional.empty()).buildFlowAdd());
+                Optional.empty(), Optional.of(driverService)).buildFlowAdd());
 
         if (adaptiveFlowSampling) {
             // Add TypedFlowEntry to deviceFlowEntries in NewAdaptiveFlowStatsCollector
@@ -298,7 +301,7 @@
             return;
         }
         sw.sendMsg(FlowModBuilder.builder(flowRule, sw.factory(),
-                                          Optional.empty()).buildFlowDel());
+                                          Optional.empty(), Optional.of(driverService)).buildFlowDel());
 
         if (adaptiveFlowSampling) {
             // Remove TypedFlowEntry to deviceFlowEntries in NewAdaptiveFlowStatsCollector
@@ -334,7 +337,8 @@
                 continue;
             }
             FlowModBuilder builder =
-                    FlowModBuilder.builder(fbe.target(), sw.factory(), Optional.of(batch.id()));
+                    FlowModBuilder.builder(fbe.target(), sw.factory(),
+                            Optional.of(batch.id()), Optional.of(driverService));
             NewAdaptiveFlowStatsCollector collector = afsCollectors.get(dpid);
             switch (fbe.operator()) {
                 case ADD:
@@ -423,7 +427,7 @@
                 case FLOW_REMOVED:
                     OFFlowRemoved removed = (OFFlowRemoved) msg;
 
-                    FlowEntry fr = new FlowEntryBuilder(dpid, removed).build();
+                    FlowEntry fr = new FlowEntryBuilder(dpid, removed, driverService).build();
                     providerService.flowRemoved(fr);
 
                     if (adaptiveFlowSampling) {
@@ -474,7 +478,7 @@
                             InternalCacheEntry entry =
                                     pendingBatches.getIfPresent(msg.getXid());
                             if (entry != null) {
-                                entry.appendFailure(new FlowEntryBuilder(dpid, fm).build());
+                                entry.appendFailure(new FlowEntryBuilder(dpid, fm, driverService).build());
                             } else {
                                 log.error("No matching batch for this error: {}", error);
                             }
@@ -501,7 +505,7 @@
             DeviceId did = DeviceId.deviceId(Dpid.uri(dpid));
 
             List<FlowEntry> flowEntries = replies.getEntries().stream()
-                    .map(entry -> new FlowEntryBuilder(dpid, entry).build())
+                    .map(entry -> new FlowEntryBuilder(dpid, entry, driverService).build())
                     .collect(Collectors.toList());
 
             if (adaptiveFlowSampling)  {