ONOS-688 (ONOS-1835,1836,1837) :
 - Implements features to create and remove tunnels and tunnel flow policies.
 - Implements REST API to create/show/delete tunnels and policies.
 - Supports only single instance for now.
 - Fix "apply" actions to "write" actions of all flow rules
Change-Id: I3740ed82fed8eab4ab8b03839192da72d3e223f1
diff --git a/drivers/src/main/java/org/onosproject/driver/pipeline/SpringOpenTTP.java b/drivers/src/main/java/org/onosproject/driver/pipeline/SpringOpenTTP.java
index bb2315d..68db4ce 100644
--- a/drivers/src/main/java/org/onosproject/driver/pipeline/SpringOpenTTP.java
+++ b/drivers/src/main/java/org/onosproject/driver/pipeline/SpringOpenTTP.java
@@ -49,6 +49,7 @@
 import org.onosproject.net.flow.criteria.EthCriterion;
 import org.onosproject.net.flow.criteria.EthTypeCriterion;
 import org.onosproject.net.flow.criteria.IPCriterion;
+import org.onosproject.net.flow.criteria.IPProtocolCriterion;
 import org.onosproject.net.flow.criteria.MplsCriterion;
 import org.onosproject.net.flow.criteria.PortCriterion;
 import org.onosproject.net.flow.criteria.VlanIdCriterion;
@@ -215,7 +216,7 @@
     public void next(NextObjective nextObjective) {
 
         if (nextObjective.op() == Objective.Operation.REMOVE) {
-            if (nextObjective.next() == null) {
+            if (nextObjective.next().isEmpty()) {
                 removeGroup(nextObjective);
             } else {
                 removeBucketFromGroup(nextObjective);
@@ -369,8 +370,89 @@
     }
 
     private Collection<FlowRule> processVersatile(ForwardingObjective fwd) {
-        fail(fwd, ObjectiveError.UNSUPPORTED);
-        return Collections.emptySet();
+        log.debug("Processing versatile forwarding objective");
+        TrafficSelector selector = fwd.selector();
+
+        EthTypeCriterion ethType =
+                (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
+        if (ethType == null) {
+            log.error("Versatile forwarding objective must include ethType");
+            fail(fwd, ObjectiveError.UNKNOWN);
+            return Collections.emptySet();
+        }
+
+        TrafficSelector.Builder filteredSelectorBuilder =
+                DefaultTrafficSelector.builder();
+        if (ethType.ethType() == Ethernet.TYPE_IPV4) {
+            IPCriterion ipSrc = (IPCriterion) selector
+                    .getCriterion(Criterion.Type.IPV4_SRC);
+            IPCriterion ipDst = (IPCriterion) selector
+                    .getCriterion(Criterion.Type.IPV4_DST);
+            IPProtocolCriterion ipProto = (IPProtocolCriterion) selector
+                    .getCriterion(Criterion.Type.IP_PROTO);
+
+            filteredSelectorBuilder
+                    .matchEthType(Ethernet.TYPE_IPV4);
+
+            if (ipSrc != null) {
+                filteredSelectorBuilder.matchIPSrc(ipSrc.ip());
+            }
+            if (ipDst != null) {
+                filteredSelectorBuilder.matchIPDst(ipDst.ip());
+            }
+            if (ipProto != null) {
+                filteredSelectorBuilder.matchIPProtocol(
+                        Short.valueOf(ipProto.protocol()).byteValue());
+            }
+
+            log.debug("processing IPv4 specific forwarding objective");
+        } else {
+            log.warn("VERSATILE forwarding objective does not support {} yet.",
+                    ethType.ethType());
+            return Collections.emptySet();
+        }
+
+        TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment
+                .builder();
+        treatmentBuilder.wipeDeferred();
+
+        if (fwd.nextId() != null) {
+            NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
+
+            if (next != null) {
+                GroupKey key = appKryo.deserialize(next.data());
+
+                Group group = groupService.getGroup(deviceId, key);
+
+                if (group == null) {
+                    log.warn("The group left!");
+                    fail(fwd, ObjectiveError.GROUPMISSING);
+                    return Collections.emptySet();
+                }
+                treatmentBuilder.deferred().group(group.id());
+                log.debug("Adding OUTGROUP action");
+            }
+        } else {
+            log.warn("VERSATILE forwarding objective need next objective ID.");
+            return Collections.emptySet();
+        }
+
+        TrafficSelector filteredSelector = filteredSelectorBuilder.build();
+        TrafficTreatment treatment = treatmentBuilder.build();
+
+        FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
+                .fromApp(fwd.appId()).withPriority(fwd.priority())
+                .forDevice(deviceId).withSelector(filteredSelector)
+                .withTreatment(treatment);
+
+        if (fwd.permanent()) {
+            ruleBuilder.makePermanent();
+        } else {
+            ruleBuilder.makeTemporary(fwd.timeout());
+        }
+
+        ruleBuilder.forTable(aclTableId);
+        return Collections.singletonList(ruleBuilder.build());
     }
 
     protected Collection<FlowRule> processSpecific(ForwardingObjective fwd) {
@@ -434,7 +516,7 @@
                     fail(fwd, ObjectiveError.GROUPMISSING);
                     return Collections.emptySet();
                 }
-                treatmentBuilder.group(group.id());
+                treatmentBuilder.deferred().group(group.id());
                 log.debug("Adding OUTGROUP action");
             } else {
                 log.warn("processSpecific: No associated next objective object");