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/DefaultTunnel.java b/src/main/java/org/onosproject/segmentrouting/DefaultTunnel.java
index f1c0216..8c6fbe8 100644
--- a/src/main/java/org/onosproject/segmentrouting/DefaultTunnel.java
+++ b/src/main/java/org/onosproject/segmentrouting/DefaultTunnel.java
@@ -16,16 +16,8 @@
 
 package org.onosproject.segmentrouting;
 
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Link;
-import org.onosproject.segmentrouting.grouphandler.NeighborSet;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Collections;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
+import java.util.Objects;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
@@ -34,31 +26,11 @@
  */
 public class DefaultTunnel implements Tunnel {
 
-    private static final Logger log = LoggerFactory
-            .getLogger(DefaultTunnel.class);
-
-    private final String tunnelId;
+    private final String id;
     private final List<Integer> labelIds;
-    private final SegmentRoutingManager srManager;
-    private final DeviceConfiguration config;
 
     private int groupId;
-
-    /**
-     * Creates a Tunnel reference.
-     *
-     * @param srm SegmentRoutingManager object
-     * @param tid  Tunnel ID
-     * @param labelIds Label stack of the tunnel
-     */
-    public DefaultTunnel(SegmentRoutingManager srm, String tid,
-                         List<Integer> labelIds) {
-        this.srManager = checkNotNull(srm);
-        this.tunnelId = checkNotNull(tid);
-        this.labelIds = Collections.unmodifiableList(labelIds);
-        this.config = srManager.deviceConfiguration;
-        this.groupId = -1;
-    }
+    private boolean allowedToRemoveGroup;
 
     /**
      * Creates a Tunnel reference.
@@ -67,10 +39,10 @@
      * @param labelIds Label stack of the tunnel
      */
     public DefaultTunnel(String tid, List<Integer> labelIds) {
-        this.srManager = null;
-        this.tunnelId = checkNotNull(tid);
-        this.labelIds = Collections.unmodifiableList(labelIds);
-        this.config = null;
+        this.id = checkNotNull(tid);
+        this.labelIds = labelIds;
+        //TODO: need to register the class in Kryo for this
+        //this.labelIds = Collections.unmodifiableList(labelIds);
         this.groupId = -1;
     }
 
@@ -80,16 +52,14 @@
      * @param tunnel DefaultTunnel reference
      */
     public DefaultTunnel(DefaultTunnel tunnel) {
-        this.srManager = tunnel.srManager;
-        this.tunnelId = tunnel.tunnelId;
+        this.id = tunnel.id;
         this.labelIds = tunnel.labelIds;
-        this.config = tunnel.config;
         this.groupId = tunnel.groupId;
     }
 
     @Override
     public String id() {
-        return this.tunnelId;
+        return this.id;
     }
 
     @Override
@@ -98,71 +68,44 @@
     }
 
     @Override
-    public boolean create() {
-
-        if (labelIds.isEmpty() || labelIds.size() < 3) {
-            log.error("More than one router needs to specified to created a tunnel");
-            return false;
-        }
-
-        groupId = createGroupsForTunnel();
-        if (groupId < 0) {
-            log.error("Failed to create groups for the tunnel");
-            return false;
-        }
-
-        return true;
-    }
-
-    @Override
-    public boolean remove() {
-
-        DeviceId deviceId = config.getDeviceId(labelIds.get(0));
-        srManager.removeNextObjective(deviceId, groupId);
-
-        return true;
-    }
-
-    @Override
     public int groupId() {
         return this.groupId;
     }
 
     @Override
-    public DeviceId source() {
-        return config.getDeviceId(labelIds.get(0));
+    public void setGroupId(int id) {
+        this.groupId = id;
     }
 
-    private int createGroupsForTunnel() {
-
-        List<Integer> portNumbers;
-
-        int groupId;
-
-        DeviceId deviceId = config.getDeviceId(labelIds.get(0));
-        if (deviceId == null) {
-            log.warn("No device found for SID {}", labelIds.get(0));
-            return -1;
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
         }
-        Set<DeviceId> deviceIds = new HashSet<>();
-        int sid = labelIds.get(1);
-        if (config.isAdjacencySid(deviceId, sid)) {
-            portNumbers = config.getPortsForAdjacencySid(deviceId, sid);
-            for (Link link: srManager.linkService.getDeviceEgressLinks(deviceId)) {
-                for (Integer port: portNumbers) {
-                    if (link.src().port().toLong() == port) {
-                        deviceIds.add(link.dst().deviceId());
-                    }
-                }
+
+        if (o instanceof DefaultTunnel) {
+            DefaultTunnel tunnel = (DefaultTunnel) o;
+            // We compare only the tunnel paths.
+            if (tunnel.labelIds.equals(this.labelIds)) {
+                return true;
             }
-        } else {
-            deviceIds.add(config.getDeviceId(sid));
         }
 
-        NeighborSet ns = new NeighborSet(deviceIds, labelIds.get(2));
-        groupId = srManager.getNextObjectiveId(deviceId, ns);
-
-        return groupId;
+        return false;
     }
 
+    @Override
+    public int hashCode() {
+        return Objects.hash(labelIds);
+    }
+
+    @Override
+    public boolean isAllowedToRemoveGroup() {
+        return this.allowedToRemoveGroup;
+    }
+
+    @Override
+    public void allowToRemoveGroup(boolean b) {
+        this.allowedToRemoveGroup = b;
+    }
 }
diff --git a/src/main/java/org/onosproject/segmentrouting/Policy.java b/src/main/java/org/onosproject/segmentrouting/Policy.java
index d47bc2e..2e41795 100644
--- a/src/main/java/org/onosproject/segmentrouting/Policy.java
+++ b/src/main/java/org/onosproject/segmentrouting/Policy.java
@@ -16,8 +16,6 @@
 
 package org.onosproject.segmentrouting;
 
-import org.onosproject.net.flow.TrafficSelector;
-
 /**
  * Interface for Segment Routing Policy.
  */
@@ -47,13 +45,6 @@
     String id();
 
     /**
-     * Returns the traffic selector object.
-     *
-     * @return TrafficSelector object
-     */
-    TrafficSelector selector();
-
-    /**
      * Returns the priority of the policy.
      *
      * @return priority
@@ -68,16 +59,38 @@
     Type type();
 
     /**
-     * Creates a policy.
+     * Returns the source IP address of the policy.
      *
-     * @return true if succeeds, false otherwise
+     * @return source IP address
      */
-    boolean create();
+    String srcIp();
 
     /**
-     * Removes the policy.
+     * Returns the destination IP address of the policy.
      *
-     * @return true if succeeds, false otherwise
+     * @return destination IP address
      */
-    boolean remove();
+    String dstIp();
+
+    /**
+     * Returns the IP protocol of the policy.
+     *
+     * @return IP protocol
+     */
+    String ipProto();
+
+    /**
+     * Returns the source port of the policy.
+     *
+     * @return source port
+     */
+    short srcPort();
+
+    /**
+     * Returns the destination of the policy.
+     *
+     * @return destination port
+     */
+    short dstPort();
+
 }
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();
+    }
+
 }
diff --git a/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
index 02ed88a..8a7aae9 100644
--- a/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
+++ b/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
@@ -128,6 +128,9 @@
     // Per device next objective ID store with (device id + neighbor set) as key
     private EventuallyConsistentMap<NeighborSetNextObjectiveStoreKey,
         Integer> nsNextObjStore = null;
+    private EventuallyConsistentMap<String, Tunnel> tunnelStore = null;
+    private EventuallyConsistentMap<String, Policy> policyStore = null;
+
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected StorageService storageService;
 
@@ -148,13 +151,18 @@
 
         kryoBuilder = new KryoNamespace.Builder()
             .register(NeighborSetNextObjectiveStoreKey.class,
-                  NeighborSet.class,
-                  DeviceId.class,
-                  URI.class,
-                  WallClockTimestamp.class,
-                  org.onosproject.cluster.NodeId.class,
-                  HashSet.class
-                );
+                    NeighborSet.class,
+                    DeviceId.class,
+                    URI.class,
+                    WallClockTimestamp.class,
+                    org.onosproject.cluster.NodeId.class,
+                    HashSet.class,
+                    Tunnel.class,
+                    DefaultTunnel.class,
+                    Policy.class,
+                    TunnelPolicy.class,
+                    Policy.Type.class
+            );
 
         log.debug("Creating EC map nsnextobjectivestore");
         EventuallyConsistentMapBuilder<NeighborSetNextObjectiveStoreKey, Integer>
@@ -167,6 +175,24 @@
                 .build();
         log.trace("Current size {}", nsNextObjStore.size());
 
+        EventuallyConsistentMapBuilder<String, Tunnel> tunnelMapBuilder =
+                storageService.eventuallyConsistentMapBuilder();
+
+        tunnelStore = tunnelMapBuilder
+                .withName("tunnelstore")
+                .withSerializer(kryoBuilder)
+                .withClockService(new WallclockClockManager<>())
+                .build();
+
+        EventuallyConsistentMapBuilder<String, Policy> policyMapBuilder =
+                storageService.eventuallyConsistentMapBuilder();
+
+        policyStore = policyMapBuilder
+                .withName("policystore")
+                .withSerializer(kryoBuilder)
+                .withClockService(new WallclockClockManager<>())
+                .build();
+
         networkConfigService.init();
         deviceConfiguration = new DeviceConfiguration(networkConfigService);
         arpHandler = new ArpHandler(this);
@@ -174,8 +200,8 @@
         ipHandler = new IpHandler(this);
         routingRulePopulator = new RoutingRulePopulator(this);
         defaultRoutingHandler = new DefaultRoutingHandler(this);
-        tunnelHandler = new TunnelHandler();
-        policyHandler = new PolicyHandler();
+        tunnelHandler = new TunnelHandler(this, tunnelStore);
+        policyHandler = new PolicyHandler(this, policyStore);
 
         packetService.addProcessor(processor, PacketProcessor.ADVISOR_MAX + 2);
         linkService.addListener(new InternalLinkListener());
@@ -295,6 +321,24 @@
     }
 
     /**
+     * Checks if the next objective ID (group) for the neighbor set exists or not in the device.
+     *
+     * @param deviceId Device ID to check
+     * @param ns neighbor set to check
+     * @return true if it exists, false otherwise
+     */
+    public boolean hasNextObjectiveId(DeviceId deviceId, NeighborSet ns) {
+        if (groupHandlerMap.get(deviceId) != null) {
+            log.trace("getNextObjectiveId query in device {}", deviceId);
+            return groupHandlerMap
+                    .get(deviceId).hasNextObjectiveId(ns);
+        } else {
+            log.warn("getNextObjectiveId query in device {} not found", deviceId);
+            return false;
+        }
+    }
+
+    /**
      * Removes the next objective ID.
      *
      * @param deviceId Device ID
diff --git a/src/main/java/org/onosproject/segmentrouting/Tunnel.java b/src/main/java/org/onosproject/segmentrouting/Tunnel.java
index 0c35fba..c56ae70 100644
--- a/src/main/java/org/onosproject/segmentrouting/Tunnel.java
+++ b/src/main/java/org/onosproject/segmentrouting/Tunnel.java
@@ -16,8 +16,6 @@
 
 package org.onosproject.segmentrouting;
 
-import org.onosproject.net.DeviceId;
-
 import java.util.List;
 
 /**
@@ -40,20 +38,6 @@
     List<Integer> labelIds();
 
     /**
-     * Creates a tunnel.
-     *
-     * @return true if succeeds, false otherwise
-     */
-    boolean create();
-
-    /**
-     * Removes the tunnel.
-     *
-     * @return true if succeeds, false otherwise.
-     */
-    boolean remove();
-
-    /**
      * Returns the group ID for the tunnel.
      *
      * @return group ID
@@ -61,9 +45,22 @@
     int groupId();
 
     /**
-     * Returns the source device Id of the tunnel.
+     * Sets group ID for the tunnel.
      *
-     * @return source device Id
      */
-    DeviceId source();
+    void setGroupId(int groupId);
+
+    /**
+     * Sets the flag to allow to remove the group or not.
+     *
+     * @param ok the flag; true - allow to remove
+     */
+    void allowToRemoveGroup(boolean ok);
+
+    /**
+     * Checks if it is allowed to remove the group for the tunnel.
+     *
+     * @return true if allowed, false otherwise
+     */
+    boolean isAllowedToRemoveGroup();
 }
diff --git a/src/main/java/org/onosproject/segmentrouting/TunnelHandler.java b/src/main/java/org/onosproject/segmentrouting/TunnelHandler.java
index 4a498c6..1c07093 100644
--- a/src/main/java/org/onosproject/segmentrouting/TunnelHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/TunnelHandler.java
@@ -15,12 +15,18 @@
  */
 package org.onosproject.segmentrouting;
 
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.segmentrouting.grouphandler.NeighborSet;
+import org.onosproject.store.service.EventuallyConsistentMap;
 import org.slf4j.Logger;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
-import java.util.HashMap;
+import java.util.Set;
 
+import static com.google.common.base.Preconditions.checkNotNull;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
@@ -29,10 +35,15 @@
 public class TunnelHandler {
     protected final Logger log = getLogger(getClass());
 
-    private final HashMap<String, Tunnel> tunnelMap;
+    private final SegmentRoutingManager srManager;
+    private final DeviceConfiguration config;
+    private final EventuallyConsistentMap<String, Tunnel> tunnelStore;
 
-    public TunnelHandler() {
-        tunnelMap = new HashMap<>();
+    public TunnelHandler(SegmentRoutingManager srm,
+                         EventuallyConsistentMap<String, Tunnel> tunnelStore) {
+        this.srManager = checkNotNull(srm);
+        this.config = srm.deviceConfiguration;
+        this.tunnelStore = tunnelStore;
     }
 
     /**
@@ -40,9 +51,33 @@
      *
      * @param tunnel tunnel reference to create a tunnel
      */
-    public void createTunnel(Tunnel tunnel) {
-        tunnel.create();
-        tunnelMap.put(tunnel.id(), tunnel);
+    public boolean createTunnel(Tunnel tunnel) {
+
+        if (tunnel.labelIds().isEmpty() || tunnel.labelIds().size() < 3) {
+            log.error("More than one router needs to specified to created a tunnel");
+            return false;
+        }
+
+        if (tunnelStore.containsKey(tunnel.id())) {
+            log.warn("The same tunnel ID exists already");
+            return false;
+        }
+
+        if (tunnelStore.containsValue(tunnel)) {
+            log.warn("The same tunnel exists already");
+            return false;
+        }
+
+        int groupId = createGroupsForTunnel(tunnel);
+        if (groupId < 0) {
+            log.error("Failed to create groups for the tunnel");
+            return false;
+        }
+
+        tunnel.setGroupId(groupId);
+        tunnelStore.put(tunnel.id(), tunnel);
+
+        return true;
     }
 
     /**
@@ -52,10 +87,19 @@
      */
     public void removeTunnel(Tunnel tunnelInfo) {
 
-        Tunnel tunnel = tunnelMap.get(tunnelInfo.id());
+        Tunnel tunnel = tunnelStore.get(tunnelInfo.id());
         if (tunnel != null) {
-            tunnel.remove();
-            tunnelMap.remove(tunnel.id());
+            DeviceId deviceId = config.getDeviceId(tunnel.labelIds().get(0));
+            if (tunnel.isAllowedToRemoveGroup()) {
+                if (srManager.removeNextObjective(deviceId, tunnel.groupId())) {
+                    tunnelStore.remove(tunnel.id());
+                } else {
+                    log.error("Failed to remove the tunnel {}", tunnelInfo.id());
+                }
+            } else {
+                log.debug("The group is not removed because it is being used.");
+                tunnelStore.remove(tunnel.id());
+            }
         } else {
             log.warn("No tunnel found for tunnel ID {}", tunnelInfo.id());
         }
@@ -68,7 +112,7 @@
      * @return Tunnel reference
      */
     public Tunnel getTunnel(String tid) {
-        return tunnelMap.get(tid);
+        return tunnelStore.get(tid);
     }
 
     /**
@@ -78,9 +122,50 @@
      */
     public List<Tunnel> getTunnels() {
         List<Tunnel> tunnels = new ArrayList<>();
-        tunnelMap.values().forEach(tunnel -> tunnels.add(
+        tunnelStore.values().forEach(tunnel -> tunnels.add(
                 new DefaultTunnel((DefaultTunnel) tunnel)));
 
         return tunnels;
     }
+
+    private int createGroupsForTunnel(Tunnel tunnel) {
+
+        List<Integer> portNumbers;
+
+        int groupId;
+
+        DeviceId deviceId = config.getDeviceId(tunnel.labelIds().get(0));
+        if (deviceId == null) {
+            log.warn("No device found for SID {}", tunnel.labelIds().get(0));
+            return -1;
+        }
+        Set<DeviceId> deviceIds = new HashSet<>();
+        int sid = tunnel.labelIds().get(1);
+        if (config.isAdjacencySid(deviceId, sid)) {
+            portNumbers = config.getPortsForAdjacencySid(deviceId, sid);
+            for (Link link: srManager.linkService.getDeviceEgressLinks(deviceId)) {
+                for (Integer port: portNumbers) {
+                    if (link.src().port().toLong() == port) {
+                        deviceIds.add(link.dst().deviceId());
+                    }
+                }
+            }
+        } else {
+            deviceIds.add(config.getDeviceId(sid));
+        }
+
+        NeighborSet ns = new NeighborSet(deviceIds, tunnel.labelIds().get(2));
+
+        // If the tunnel reuses any existing groups, then tunnel handler
+        // should not remove the group.
+        if (srManager.hasNextObjectiveId(deviceId, ns)) {
+            tunnel.allowToRemoveGroup(false);
+        } else {
+            tunnel.allowToRemoveGroup(true);
+        }
+        groupId = srManager.getNextObjectiveId(deviceId, ns);
+
+        return groupId;
+    }
+
 }
diff --git a/src/main/java/org/onosproject/segmentrouting/TunnelPolicy.java b/src/main/java/org/onosproject/segmentrouting/TunnelPolicy.java
index 134828b..06dbdb2 100644
--- a/src/main/java/org/onosproject/segmentrouting/TunnelPolicy.java
+++ b/src/main/java/org/onosproject/segmentrouting/TunnelPolicy.java
@@ -16,9 +16,7 @@
 
 package org.onosproject.segmentrouting;
 
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flowobjective.DefaultForwardingObjective;
-import org.onosproject.net.flowobjective.ForwardingObjective;
+import java.util.Objects;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
@@ -27,24 +25,28 @@
  */
 public final class TunnelPolicy implements Policy {
 
-    // FIXME: We should avoid passing around references to implementation objects
-    // Instead, if some operational context is required, we should abstract it to
-    // a bare minimum.
-    private final SegmentRoutingManager srManager;
     private final Type type;
     private final String id;
-    private final TrafficSelector selector;
     private final int priority;
     private final String tunnelId;
+    private String dstIp;
+    private String srcIp;
+    private String ipProto;
+    private short srcPort;
+    private short dstPort;
 
-    private TunnelPolicy(SegmentRoutingManager srm, String policyId, Type type,
-                         TrafficSelector selector, int priority, String tunnelId) {
-        this.srManager = srm;
+    private TunnelPolicy(String policyId, Type type, int priority, String tunnelId, String srcIp,
+                         String dstIp, String ipProto, short srcPort, short dstPort) {
         this.id = checkNotNull(policyId);
         this.type = type;
         this.tunnelId = tunnelId;
         this.priority = priority;
-        this.selector = selector;
+        this.dstIp = dstIp;
+        this.srcIp = srcIp;
+        this.ipProto = ipProto;
+        this.srcPort = srcPort;
+        this.dstPort = dstPort;
+
     }
 
     /**
@@ -53,27 +55,15 @@
      * @param p TunnelPolicy reference
      */
     public TunnelPolicy(TunnelPolicy p) {
-        this.srManager = p.srManager;
         this.id = p.id;
         this.type = p.type;
         this.tunnelId = p.tunnelId;
         this.priority = p.priority;
-        this.selector = p.selector;
-    }
-
-    /**
-     * Creates a TunnelPolicy reference.
-     *
-     * @param srm reference to the segment routing component
-     * @param p TunnelPolicy reference
-     */
-    public TunnelPolicy(SegmentRoutingManager srm, TunnelPolicy p) {
-        this.srManager = srm;
-        this.id = p.id;
-        this.type = p.type;
-        this.tunnelId = p.tunnelId;
-        this.priority = p.priority;
-        this.selector = p.selector;
+        this.srcIp = p.srcIp;
+        this.dstIp = p.dstIp;
+        this.ipProto = p.ipProto;
+        this.srcPort = p.srcPort;
+        this.dstPort = p.dstPort;
     }
 
     /**
@@ -91,11 +81,6 @@
     }
 
     @Override
-    public TrafficSelector selector() {
-        return selector;
-    }
-
-    @Override
     public int priority() {
         return priority;
     }
@@ -106,41 +91,58 @@
     }
 
     @Override
-    public boolean create() {
-
-        Tunnel tunnel = srManager.getTunnel(tunnelId);
-
-        ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
-                .builder()
-                .fromApp(srManager.appId)
-                .makePermanent()
-                .nextStep(tunnel.groupId())
-                .withPriority(priority)
-                .withSelector(selector)
-                .withFlag(ForwardingObjective.Flag.VERSATILE);
-
-        srManager.flowObjectiveService.forward(tunnel.source(), fwdBuilder.add());
-
-        return true;
+    public String srcIp() {
+        return srcIp;
     }
 
     @Override
-    public boolean remove() {
+    public String dstIp() {
+        return dstIp;
+    }
 
-        Tunnel tunnel = srManager.getTunnel(tunnelId);
+    @Override
+    public String ipProto() {
+        return ipProto;
+    }
 
-        ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
-                .builder()
-                .fromApp(srManager.appId)
-                .makePermanent()
-                .withSelector(selector)
-                .withPriority(priority)
-                .nextStep(tunnel.groupId())
-                .withFlag(ForwardingObjective.Flag.VERSATILE);
+    @Override
+    public short srcPort() {
+        return srcPort;
+    }
 
-        srManager.flowObjectiveService.forward(tunnel.source(), fwdBuilder.remove());
+    @Override
+    public short dstPort() {
+        return dstPort;
+    }
 
-        return true;
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (o instanceof TunnelPolicy) {
+            TunnelPolicy that = (TunnelPolicy) o;
+            // We do not compare the policy ID
+            if (this.type.equals(that.type) &&
+                    this.tunnelId.equals(that.tunnelId) &&
+                    this.priority == that.priority &&
+                    this.srcIp.equals(that.srcIp) &&
+                    this.dstIp.equals(that.dstIp) &&
+                    this.srcPort == that.srcPort &&
+                    this.dstPort == that.dstPort &&
+                    this.ipProto.equals(that.ipProto)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(type, tunnelId, srcIp, dstIp, ipProto,
+                srcPort, dstPort, priority);
     }
 
     /**
@@ -152,17 +154,21 @@
         return this.tunnelId;
     }
 
+
     /**
      * Tunnel Policy Builder.
      */
     public static final class Builder {
 
-        private SegmentRoutingManager srManager;
         private String id;
         private Type type;
-        private TrafficSelector selector;
         private int priority;
         private String tunnelId;
+        private String dstIp;
+        private String srcIp;
+        private String ipProto;
+        private short srcPort;
+        private short dstPort;
 
         /**
          * Sets the policy Id.
@@ -189,13 +195,61 @@
         }
 
         /**
-         * Sets the TrafficSelector.
+         * Sets the source IP address.
          *
-         * @param selector TrafficSelector
+         * @param srcIp source IP address
          * @return Builder object
          */
-        public Builder setSelector(TrafficSelector selector) {
-            this.selector = selector;
+        public Builder setSrcIp(String srcIp) {
+            this.srcIp = srcIp;
+
+            return this;
+        }
+
+        /**
+         * Sets the destination IP address.
+         *
+         * @param dstIp destination IP address
+         * @return Builder object
+         */
+        public Builder setDstIp(String dstIp) {
+            this.dstIp = dstIp;
+
+            return this;
+        }
+
+        /**
+         * Sets the IP protocol.
+         *
+         * @param proto IP protocol
+         * @return Builder object
+         */
+        public Builder setIpProto(String proto) {
+            this.ipProto = proto;
+
+            return this;
+        }
+
+        /**
+         * Sets the source port.
+         *
+         * @param srcPort source port
+         * @return Builder object
+         */
+        public Builder setSrcPort(short srcPort) {
+            this.srcPort = srcPort;
+
+            return this;
+        }
+
+        /**
+         * Sets the destination port.
+         *
+         * @param dstPort destination port
+         * @return Builder object
+         */
+        public Builder setDstPort(short dstPort) {
+            this.dstPort = dstPort;
 
             return this;
         }
@@ -225,24 +279,13 @@
         }
 
         /**
-         * Sets the Segment Routing Manager reference.
-         *
-         * @param srm Segment Routing Manager reference
-         * @return Builder object
-         */
-        public Builder setManager(SegmentRoutingManager srm) {
-            this.srManager = srm;
-
-            return this;
-        }
-
-        /**
          * Builds the policy.
          *
          * @return Tunnel Policy reference
          */
         public Policy build() {
-            return new TunnelPolicy(srManager, id, type, selector, priority, tunnelId);
+            return new TunnelPolicy(id, type, priority, tunnelId, srcIp, dstIp,
+                    ipProto, srcPort, dstPort);
         }
     }
 }
diff --git a/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java b/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
index 4a25438..7226fd8 100644
--- a/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
@@ -181,8 +181,8 @@
                         .contains(newLink.dst().deviceId())))
                 .collect(Collectors.toSet());
         log.trace("linkUp: nsNextObjStore contents for device {}:",
-                  deviceId,
-                  nsSet);
+                deviceId,
+                nsSet);
         for (NeighborSet ns : nsSet) {
             // Create the new bucket to be updated
             TrafficTreatment.Builder tBuilder =
@@ -317,6 +317,22 @@
         return nextId.intValue();
     }
 
+    /**
+     * Checks if the next objective ID (group) for the neighbor set exists or not.
+     *
+     * @param ns neighbor set to check
+     * @return true if it exists, false otherwise
+     */
+    public boolean hasNextObjectiveId(NeighborSet ns) {
+        Integer nextId = nsNextObjStore.
+                get(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
+        if (nextId == null) {
+            return false;
+        }
+
+        return true;
+    }
+
     // Empty implementation
     protected void newNeighbor(Link newLink) {
     }
@@ -489,6 +505,7 @@
                     break;
                 }
             }
+            return true;
         }
 
         return false;
diff --git a/src/main/java/org/onosproject/segmentrouting/web/PolicyCodec.java b/src/main/java/org/onosproject/segmentrouting/web/PolicyCodec.java
index 3e2524d..8e50887 100644
--- a/src/main/java/org/onosproject/segmentrouting/web/PolicyCodec.java
+++ b/src/main/java/org/onosproject/segmentrouting/web/PolicyCodec.java
@@ -15,19 +15,9 @@
  */
 package org.onosproject.segmentrouting.web;
 
-import org.onlab.packet.Ethernet;
-import org.onlab.packet.IpPrefix;
-import org.onosproject.cli.net.IpProtocol;
 import org.onosproject.codec.CodecContext;
 import org.onosproject.codec.JsonCodec;
 import com.fasterxml.jackson.databind.node.ObjectNode;
-import org.onosproject.net.flow.DefaultTrafficSelector;
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flow.criteria.Criterion;
-import org.onosproject.net.flow.criteria.IPCriterion;
-import org.onosproject.net.flow.criteria.IPProtocolCriterion;
-import org.onosproject.net.flow.criteria.TcpPortCriterion;
-import org.onosproject.net.flow.criteria.UdpPortCriterion;
 import org.onosproject.segmentrouting.Policy;
 import org.onosproject.segmentrouting.TunnelPolicy;
 
@@ -52,45 +42,24 @@
         result.put(PRIORITY, policy.priority());
         result.put(TYPE, policy.type().toString());
 
-        if (policy.selector().getCriterion(Criterion.Type.IPV4_DST) != null) {
-            IPCriterion criterion = (IPCriterion) policy.selector().getCriterion(
-                    Criterion.Type.IPV4_DST);
-            result.put(DST_IP, criterion.ip().toString());
+        if (policy.dstIp() != null) {
+            result.put(DST_IP, policy.dstIp());
         }
-        if (policy.selector().getCriterion(Criterion.Type.IPV4_SRC) != null) {
-            IPCriterion criterion = (IPCriterion) policy.selector().getCriterion(
-                    Criterion.Type.IPV4_SRC);
-            result.put(SRC_IP, criterion.ip().toString());
+        if (policy.srcIp() != null) {
+            result.put(SRC_IP, policy.srcIp());
         }
-        if (policy.selector().getCriterion(Criterion.Type.IP_PROTO) != null) {
-            IPProtocolCriterion protocolCriterion =
-                    (IPProtocolCriterion) policy.selector().getCriterion(Criterion.Type.IP_PROTO);
-            result.put(PROTO_TYPE, protocolCriterion.protocol());
-        }
-        if (policy.selector().getCriterion(Criterion.Type.TCP_SRC) != null) {
-            TcpPortCriterion tcpPortCriterion =
-                    (TcpPortCriterion) policy.selector().getCriterion(Criterion.Type.TCP_SRC);
-            result.put(SRC_PORT, tcpPortCriterion.toString());
-        } else if (policy.selector().getCriterion(Criterion.Type.UDP_SRC) != null) {
-            UdpPortCriterion udpPortCriterion =
-                    (UdpPortCriterion) policy.selector().getCriterion(Criterion.Type.UDP_SRC);
-            result.put(SRC_PORT, udpPortCriterion.toString());
-        }
-        if (policy.selector().getCriterion(Criterion.Type.TCP_DST) != null) {
-            TcpPortCriterion tcpPortCriterion =
-                    (TcpPortCriterion) policy.selector().getCriterion(Criterion.Type.TCP_DST);
-            result.put(DST_PORT, tcpPortCriterion.toString());
-        } else if (policy.selector().getCriterion(Criterion.Type.UDP_DST) != null) {
-            UdpPortCriterion udpPortCriterion =
-                    (UdpPortCriterion) policy.selector().getCriterion(Criterion.Type.UDP_DST);
-            result.put(DST_PORT, udpPortCriterion.toString());
-        }
-        if (policy.selector().getCriterion(Criterion.Type.IP_PROTO) != null) {
-            IPProtocolCriterion protocolCriterion =
-                    (IPProtocolCriterion) policy.selector().getCriterion(Criterion.Type.IP_PROTO);
-            result.put(PROTO_TYPE, protocolCriterion.toString());
+        if (policy.ipProto() != null) {
+            result.put(PROTO_TYPE, policy.ipProto());
         }
 
+        int srcPort = policy.srcPort() & 0xffff;
+        if (policy.srcPort() != 0) {
+            result.put(SRC_PORT, srcPort);
+        }
+        int dstPort = policy.dstPort() & 0xffff;
+        if (policy.dstPort() != 0) {
+            result.put(DST_PORT, dstPort);
+        }
         if (policy.type() == Policy.Type.TUNNEL_FLOW) {
             result.put(TUNNEL_ID, ((TunnelPolicy) policy).tunnelId());
         }
@@ -111,53 +80,43 @@
         short srcPort = json.path(SRC_PORT).shortValue();
         short dstPort = json.path(DST_PORT).shortValue();
 
-        if (tunnelId != null) {
-            TrafficSelector.Builder tsb = DefaultTrafficSelector.builder();
-            tsb.matchEthType(Ethernet.TYPE_IPV4);
-            if (dstIp != null && !dstIp.isEmpty()) {
-                tsb.matchIPDst(IpPrefix.valueOf(dstIp));
-            }
-            if (srcIp != null && !srcIp.isEmpty()) {
-                tsb.matchIPSrc(IpPrefix.valueOf(srcIp));
-            }
-            if (protoType != null && !protoType.isEmpty()) {
-                Short ipProto = Short.valueOf(IpProtocol.valueOf(protoType).value());
-                tsb.matchIPProtocol(ipProto.byteValue());
-                if (IpProtocol.valueOf(protoType).equals(IpProtocol.TCP)) {
-                    if (srcPort != 0) {
-                        tsb.matchTcpSrc(srcPort);
-                    }
-                    if (dstPort != 0) {
-                        tsb.matchTcpDst(dstPort);
-                    }
-                } else if (IpProtocol.valueOf(protoType).equals(IpProtocol.UDP)) {
-                    if (srcPort != 0) {
-                        tsb.matchUdpSrc(srcPort);
-                    }
-                    if (dstPort != 0) {
-                        tsb.matchUdpDst(dstPort);
-                    }
-                }
-            }
-            TunnelPolicy.Builder tpb = TunnelPolicy.builder().setPolicyId(pid);
-            if (tunnelId != null) {
-                tpb.setTunnelId(tunnelId);
-            }
-            if (!json.path(PRIORITY).isMissingNode()) {
-                tpb.setPriority(priority);
-            }
-            if (!json.path(TYPE).isMissingNode()) {
-                tpb.setType(Policy.Type.valueOf(type));
-            }
-            tpb.setSelector(tsb.build());
-
-            return tpb.build();
-        } else {
-            // TODO: handle more policy types
+        if (json.path(POLICY_ID).isMissingNode() || pid == null) {
+            // TODO: handle errors
             return null;
         }
 
+        TunnelPolicy.Builder tpb = TunnelPolicy.builder().setPolicyId(pid);
+        if (!json.path(TYPE).isMissingNode() && type != null &&
+                Policy.Type.valueOf(type).equals(Policy.Type.TUNNEL_FLOW)) {
 
+            if (json.path(TUNNEL_ID).isMissingNode() || tunnelId == null) {
+                return null;
+            }
+
+            tpb.setTunnelId(tunnelId);
+            tpb.setType(Policy.Type.valueOf(type));
+
+            if (!json.path(PRIORITY).isMissingNode()) {
+                tpb.setPriority(priority);
+            }
+            if (dstIp != null) {
+                tpb.setDstIp(dstIp);
+            }
+            if (srcIp != null) {
+                tpb.setSrcIp(srcIp);
+            }
+            if (protoType != null) {
+                tpb.setIpProto(protoType);
+            }
+            if (dstPort != 0) {
+                tpb.setDstPort(dstPort);
+            }
+            if (srcPort != 0) {
+                tpb.setSrcPort(srcPort);
+            }
+        }
+
+        return tpb.build();
     }
 
 }
diff --git a/src/main/java/org/onosproject/segmentrouting/web/PolicyWebResource.java b/src/main/java/org/onosproject/segmentrouting/web/PolicyWebResource.java
index 97e836a..384ff0f 100644
--- a/src/main/java/org/onosproject/segmentrouting/web/PolicyWebResource.java
+++ b/src/main/java/org/onosproject/segmentrouting/web/PolicyWebResource.java
@@ -20,9 +20,7 @@
 
 import org.onosproject.rest.AbstractWebResource;
 import org.onosproject.segmentrouting.Policy;
-import org.onosproject.segmentrouting.SegmentRoutingManager;
 import org.onosproject.segmentrouting.SegmentRoutingService;
-import org.onosproject.segmentrouting.TunnelPolicy;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
@@ -59,10 +57,9 @@
         ObjectNode policyJson = (ObjectNode) mapper.readTree(input);
         SegmentRoutingService srService = get(SegmentRoutingService.class);
         Policy policyInfo = POLICY_CODEC.decode(policyJson, this);
-        if (policyInfo.type() == Policy.Type.TUNNEL_FLOW) {
-            TunnelPolicy policy = new TunnelPolicy((SegmentRoutingManager) srService, (TunnelPolicy) policyInfo);
-            srService.createPolicy(policy);
 
+        if (policyInfo.type() == Policy.Type.TUNNEL_FLOW) {
+            srService.createPolicy(policyInfo);
             return Response.ok().build();
         } else {
             return Response.serverError().build();
@@ -78,6 +75,7 @@
         Policy policyInfo = POLICY_CODEC.decode(policyJson, this);
         // TODO: Check the result
         srService.removePolicy(policyInfo);
+
         return Response.ok().build();
 
     }
diff --git a/src/main/java/org/onosproject/segmentrouting/web/TunnelWebResource.java b/src/main/java/org/onosproject/segmentrouting/web/TunnelWebResource.java
index c531e3f..4ed4b8c 100644
--- a/src/main/java/org/onosproject/segmentrouting/web/TunnelWebResource.java
+++ b/src/main/java/org/onosproject/segmentrouting/web/TunnelWebResource.java
@@ -19,8 +19,6 @@
 import com.fasterxml.jackson.databind.node.ObjectNode;
 
 import org.onosproject.rest.AbstractWebResource;
-import org.onosproject.segmentrouting.DefaultTunnel;
-import org.onosproject.segmentrouting.SegmentRoutingManager;
 import org.onosproject.segmentrouting.SegmentRoutingService;
 import org.onosproject.segmentrouting.Tunnel;
 
@@ -59,9 +57,7 @@
         ObjectNode tunnelJson = (ObjectNode) mapper.readTree(input);
         SegmentRoutingService srService = get(SegmentRoutingService.class);
         Tunnel tunnelInfo = TUNNEL_CODEC.decode(tunnelJson, this);
-        Tunnel tunnel = new DefaultTunnel((SegmentRoutingManager) srService,
-                tunnelInfo.id(), tunnelInfo.labelIds());
-        srService.createTunnel(tunnel);
+        srService.createTunnel(tunnelInfo);
 
         return Response.ok().build();
     }