ONOS-1930 : Tunnel and policy failover with multi-instances
 - Prevents tunnel delete from removing groups used for default flows
 - Removes SegmentRoutingManager reference from Tunnel and Policy class
 - Adds some error checks such as duplicates tunnel IDs or duplicate polices

Change-Id: I0e7d5e2eff0aea6dad13137a872fee58e083b11c
diff --git a/src/main/java/org/onosproject/segmentrouting/PolicyHandler.java b/src/main/java/org/onosproject/segmentrouting/PolicyHandler.java
index 234dd20..5913e5b 100644
--- a/src/main/java/org/onosproject/segmentrouting/PolicyHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/PolicyHandler.java
@@ -16,10 +16,18 @@
 
 package org.onosproject.segmentrouting;
 
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IpPrefix;
+import org.onosproject.cli.net.IpProtocol;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flowobjective.DefaultForwardingObjective;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.store.service.EventuallyConsistentMap;
 import org.slf4j.Logger;
 
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
 
 import static org.slf4j.LoggerFactory.getLogger;
@@ -31,13 +39,17 @@
 
     protected final Logger log = getLogger(getClass());
 
-    private final HashMap<String, Policy> policyMap;
+    private final SegmentRoutingManager srManager;
+    private final EventuallyConsistentMap<String, Policy> policyStore;
 
     /**
      * Creates a reference.
+     * @param policyStore
      */
-    public PolicyHandler() {
-        policyMap = new HashMap<>();
+    public PolicyHandler(SegmentRoutingManager srManager,
+                         EventuallyConsistentMap<String, Policy> policyStore) {
+        this.srManager = srManager;
+        this.policyStore = policyStore;
     }
 
     /**
@@ -47,7 +59,7 @@
      */
     public List<Policy> getPolicies() {
         List<Policy> policies = new ArrayList<>();
-        policyMap.values().forEach(policy -> policies.add(
+        policyStore.values().forEach(policy -> policies.add(
                 new TunnelPolicy((TunnelPolicy) policy)));
 
         return policies;
@@ -59,8 +71,43 @@
      * @param policy policy reference to create
      */
     public void createPolicy(Policy policy) {
-        policy.create();
-        policyMap.put(policy.id(), policy);
+
+        if (policyStore.containsKey(policy.id())) {
+            log.warn("The policy id {} exists already", policy.id());
+            return;
+        }
+
+        if (policyStore.containsValue(policy)) {
+            log.warn("The same policy exists already");
+            return;
+        }
+
+        if (policy.type() == Policy.Type.TUNNEL_FLOW) {
+
+            TunnelPolicy tunnelPolicy = (TunnelPolicy) policy;
+            Tunnel tunnel = srManager.getTunnel(tunnelPolicy.tunnelId());
+            if (tunnel == null) {
+                log.error("Tunnel {} does not exists");
+                return;
+            }
+
+            ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
+                    .builder()
+                    .fromApp(srManager.appId)
+                    .makePermanent()
+                    .nextStep(tunnel.groupId())
+                    .withPriority(tunnelPolicy.priority())
+                    .withSelector(buildSelector(policy))
+                    .withFlag(ForwardingObjective.Flag.VERSATILE);
+
+            DeviceId source = srManager.deviceConfiguration.getDeviceId(tunnel.labelIds().get(0));
+            srManager.flowObjectiveService.forward(source, fwdBuilder.add());
+
+        } else {
+            log.warn("Policy type {} is not supported yet.", policy.type());
+        }
+
+        policyStore.put(policy.id(), policy);
     }
 
     /**
@@ -69,15 +116,64 @@
      * @param policyInfo policy information to remove
      */
     public void removePolicy(Policy policyInfo) {
-        if (policyMap.get(policyInfo.id()) != null) {
-            if (policyMap.get(policyInfo.id()).remove()) {
-                policyMap.remove(policyInfo.id());
-            } else {
-                log.error("Failed to remove the policy {}", policyInfo.id());
+
+        if (policyStore.get(policyInfo.id()) != null) {
+            Policy policy = policyStore.get(policyInfo.id());
+            if (policy.type() == Policy.Type.TUNNEL_FLOW) {
+                TunnelPolicy tunnelPolicy = (TunnelPolicy) policy;
+                Tunnel tunnel = srManager.getTunnel(tunnelPolicy.tunnelId());
+
+                ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
+                        .builder()
+                        .fromApp(srManager.appId)
+                        .makePermanent()
+                        .withSelector(buildSelector(policy))
+                        .withPriority(tunnelPolicy.priority())
+                        .nextStep(tunnel.groupId())
+                        .withFlag(ForwardingObjective.Flag.VERSATILE);
+
+                DeviceId source = srManager.deviceConfiguration.getDeviceId(tunnel.labelIds().get(0));
+                srManager.flowObjectiveService.forward(source, fwdBuilder.remove());
+
+                policyStore.remove(policyInfo.id());
             }
         } else {
             log.warn("Policy {} was not found", policyInfo.id());
         }
     }
 
+
+    private TrafficSelector buildSelector(Policy policy) {
+
+        TrafficSelector.Builder tsb = DefaultTrafficSelector.builder();
+        tsb.matchEthType(Ethernet.TYPE_IPV4);
+        if (policy.dstIp() != null && !policy.dstIp().isEmpty()) {
+            tsb.matchIPDst(IpPrefix.valueOf(policy.dstIp()));
+        }
+        if (policy.srcIp() != null && !policy.srcIp().isEmpty()) {
+            tsb.matchIPSrc(IpPrefix.valueOf(policy.srcIp()));
+        }
+        if (policy.ipProto() != null && !policy.ipProto().isEmpty()) {
+            Short ipProto = Short.valueOf(IpProtocol.valueOf(policy.ipProto()).value());
+            tsb.matchIPProtocol(ipProto.byteValue());
+            if (IpProtocol.valueOf(policy.ipProto()).equals(IpProtocol.TCP)) {
+                if (policy.srcPort() != 0) {
+                    tsb.matchTcpSrc(policy.srcPort());
+                }
+                if (policy.dstPort() != 0) {
+                    tsb.matchTcpDst(policy.dstPort());
+                }
+            } else if (IpProtocol.valueOf(policy.ipProto()).equals(IpProtocol.UDP)) {
+                if (policy.srcPort() != 0) {
+                    tsb.matchUdpSrc(policy.srcPort());
+                }
+                if (policy.dstPort() != 0) {
+                    tsb.matchUdpDst(policy.dstPort());
+                }
+            }
+        }
+
+        return tsb.build();
+    }
+
 }