Route reprogamming using group substitution during next hop movement

Change-Id: Idf8362dac522722ca67747e245bfd836e6ee6292
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
index 6c72a06..e78b152 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
@@ -27,6 +27,7 @@
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
 import org.onlab.packet.VlanId;
+import org.onlab.packet.MacAddress;
 import org.onlab.util.KryoNamespace;
 import org.onlab.util.Tools;
 import org.onosproject.cfg.ComponentConfigService;
@@ -115,6 +116,7 @@
 import org.onosproject.segmentrouting.storekey.DummyVlanIdStoreKey;
 import org.onosproject.segmentrouting.storekey.PortNextObjectiveStoreKey;
 import org.onosproject.segmentrouting.storekey.VlanNextObjectiveStoreKey;
+import org.onosproject.segmentrouting.storekey.MacVlanNextObjectiveStoreKey;
 import org.onosproject.segmentrouting.storekey.XConnectStoreKey;
 import org.onosproject.segmentrouting.xconnect.api.XconnectService;
 import org.onosproject.store.serializers.KryoNamespaces;
@@ -346,7 +348,7 @@
             vlanNextObjStore = null;
     /**
      * Per device next objective ID store with (device id + port + treatment + meta) as key.
-     * Used to keep track on L2 interface group and L3 unicast group information.
+     * Used to keep track on L2 interface group and L3 unicast group information for direct hosts.
      */
     private EventuallyConsistentMap<PortNextObjectiveStoreKey, Integer>
             portNextObjStore = null;
@@ -358,6 +360,13 @@
     private EventuallyConsistentMap<DummyVlanIdStoreKey, VlanId>
             dummyVlanIdStore = null;
 
+    /**
+     * Per device next objective ID store with (device id + MAC address + vlan) as key.
+     * Used to keep track of L3 unicast group for indirect hosts.
+     */
+    private EventuallyConsistentMap<MacVlanNextObjectiveStoreKey, Integer>
+            macVlanNextObjStore = null;
+
     private EventuallyConsistentMap<String, Tunnel> tunnelStore = null;
     private EventuallyConsistentMap<String, Policy> policyStore = null;
 
@@ -471,6 +480,15 @@
                 .withTimestampProvider((k, v) -> new WallClockTimestamp())
                 .build();
 
+        log.debug("Creating EC map macvlannextobjectivestore");
+        EventuallyConsistentMapBuilder<MacVlanNextObjectiveStoreKey, Integer>
+                macVlanNextObjMapBuilder = storageService.eventuallyConsistentMapBuilder();
+        macVlanNextObjStore = macVlanNextObjMapBuilder
+                .withName("macvlannextobjectivestore")
+                .withSerializer(createSerializer())
+                .withTimestampProvider((k, v) -> new WallClockTimestamp())
+                .build();
+
         log.debug("Creating EC map subnetnextobjectivestore");
         EventuallyConsistentMapBuilder<PortNextObjectiveStoreKey, Integer>
                 portNextObjMapBuilder = storageService.eventuallyConsistentMapBuilder();
@@ -602,7 +620,8 @@
                           L2TunnelPolicy.class,
                           DefaultL2Tunnel.class,
                           DefaultL2TunnelPolicy.class,
-                          DummyVlanIdStoreKey.class
+                          DummyVlanIdStoreKey.class,
+                          MacVlanNextObjectiveStoreKey.class
                 );
     }
 
@@ -650,6 +669,7 @@
 
         dsNextObjStore.destroy();
         vlanNextObjStore.destroy();
+        macVlanNextObjStore.destroy();
         portNextObjStore.destroy();
         dummyVlanIdStore.destroy();
         tunnelStore.destroy();
@@ -947,6 +967,15 @@
     }
 
     @Override
+    public ImmutableMap<MacVlanNextObjectiveStoreKey, Integer> getMacVlanNextObjStore() {
+        if (macVlanNextObjStore != null) {
+            return ImmutableMap.copyOf(macVlanNextObjStore.entrySet());
+        } else {
+            return ImmutableMap.of();
+        }
+    }
+
+    @Override
     public ImmutableMap<PortNextObjectiveStoreKey, Integer> getPortNextObjStore() {
         if (portNextObjStore != null) {
             return ImmutableMap.copyOf(portNextObjStore.entrySet());
@@ -989,6 +1018,13 @@
                 }
             });
         }
+        if (macVlanNextObjStore != null) {
+            macVlanNextObjStore.entrySet().forEach(e -> {
+                if (e.getValue() == nextId) {
+                    macVlanNextObjStore.remove(e.getKey());
+                }
+            });
+        }
         if (portNextObjStore != null) {
             portNextObjStore.entrySet().forEach(e -> {
                 if (e.getValue() == nextId) {
@@ -1109,8 +1145,18 @@
     }
 
     /**
+     * Per device next objective ID store with (device id + MAC address + vlan) as key.
+     * Used to keep track on L3 Unicast group information for indirect hosts.
+     *
+     * @return mac vlan next object store
+     */
+    public EventuallyConsistentMap<MacVlanNextObjectiveStoreKey, Integer> macVlanNextObjStore() {
+        return macVlanNextObjStore;
+    }
+
+    /**
      * Per device next objective ID store with (device id + port + treatment + meta) as key.
-     * Used to keep track on L2 interface group and L3 unicast group information.
+     * Used to keep track on L2 interface group and L3 unicast group information for direct hosts.
      *
      * @return port next object store.
      */
@@ -1254,6 +1300,76 @@
     }
 
     /**
+     * Returns the next Objective ID for the given mac and vlan, given the treatment.
+     * There could be multiple different treatments to the same outport, which
+     * would result in different objectives. If the next object does not exist,
+     * and should be created, a new one is created and its id is returned.
+     *
+     * @param deviceId Device ID
+     * @param macAddr mac of host for which Next ID is required.
+     * @param vlanId vlan of host for which Next ID is required.
+     * @param treatment the actions to apply on the packets (should include outport)
+     * @param meta metadata passed into the creation of a Next Objective if necessary
+     * @param createIfMissing true if a next object should be created if not found
+     * @return next objective ID or -1 if an error occurred during retrieval or creation
+     */
+    public int getMacVlanNextObjectiveId(DeviceId deviceId, MacAddress macAddr, VlanId vlanId,
+                                      TrafficTreatment treatment,
+                                      TrafficSelector meta,
+                                      boolean createIfMissing) {
+        DefaultGroupHandler ghdlr = groupHandlerMap.get(deviceId);
+        if (ghdlr != null) {
+            return ghdlr.getMacVlanNextObjectiveId(macAddr, vlanId, treatment, meta, createIfMissing);
+        } else {
+            log.warn("getMacVlanNextObjectiveId query - groupHandler for device {}"
+                    + " not found", deviceId);
+            return -1;
+        }
+    }
+
+    /**
+     * Returns the next ID for the given host port from the store.
+     *
+     * @param deviceId Device ID
+     * @param hostMac mac of host for which Next ID is required.
+     * @param hostVlanId vlan of host for which Next ID is required.
+     * @param port port of device for which next ID is required.
+     * @return next objective ID or -1 if an error occurred during retrieval
+     */
+    public int getNextIdForHostPort(DeviceId deviceId, MacAddress hostMac,
+                                     VlanId hostVlanId, PortNumber port) {
+        DefaultGroupHandler ghdlr = groupHandlerMap.get(deviceId);
+        if (ghdlr != null) {
+            return ghdlr.getNextIdForHostPort(hostMac, hostVlanId, port);
+        } else {
+            log.warn("getNextIdForHostPort query - groupHandler for device {}"
+                    + " not found", deviceId);
+            return -1;
+        }
+    }
+
+    /**
+     * Updates the next objective for the given nextId .
+     *
+     * @param deviceId Device ID
+     * @param hostMac mac of host for which Next obj is to be updated.
+     * @param hostVlanId vlan of host for which Next obj is to be updated.
+     * @param port port with which to update the Next Obj.
+     * @param nextId of Next Obj which needs to be updated.
+     */
+    public void updateMacVlanTreatment(DeviceId deviceId, MacAddress hostMac,
+                             VlanId hostVlanId, PortNumber port, int nextId) {
+        DefaultGroupHandler ghdlr = groupHandlerMap.get(deviceId);
+        if (ghdlr != null) {
+            ghdlr.updateL3UcastGroupBucket(hostMac, hostVlanId, port, nextId);
+        } else {
+            log.warn("updateL3UcastGroupBucket query - groupHandler for device {}"
+                    + " not found", deviceId);
+        }
+    }
+
+
+    /**
      * Returns the group handler object for the specified device id.
      *
      * @param devId the device identifier
@@ -1584,6 +1700,9 @@
         vlanNextObjStore.entrySet().stream()
                 .filter(entry -> entry.getKey().deviceId().equals(device.id()))
                 .forEach(entry -> vlanNextObjStore.remove(entry.getKey()));
+        macVlanNextObjStore.entrySet().stream()
+                .filter(entry -> entry.getKey().deviceId().equals(device.id()))
+                .forEach(entry -> macVlanNextObjStore.remove(entry.getKey()));
         portNextObjStore.entrySet().stream()
                 .filter(entry -> entry.getKey().deviceId().equals(device.id()))
                 .forEach(entry -> portNextObjStore.remove(entry.getKey()));