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/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/TunnelHandler.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/TunnelHandler.java
index 4a498c6..1c07093 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/TunnelHandler.java
+++ b/apps/segmentrouting/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;
+    }
+
 }