Mcast population during cluster reboot 73/84373/8
authormanojna v <manojna.vijayakrishna@ericsson.com>
Fri, 13 Sep 2019 11:18:13 +0000 (16:48 +0530)
committerChetan Arakere Gowdru <chetan.arakere@altencalsoftlabs.com>
Thu, 3 Oct 2019 06:18:32 +0000 (06:18 +0000)
these changes make sure that during cluster reboot scenario,
mcast remains intact as it was before cluster reboot.

as the elan interfaces gets replayed mcast gets reconstructed from
scratch and as the final elan interface is added mcast will become
identical to what it was before cluster reboot.

During this period where mcast starts with 1 bucket and reaches to
the correct no of buckets the broadcast traffic will be affected.

while doing mcast update, instead of replacing the group with new set
of buckets , first read existing buckets and add the new buckets to
existing buckets.

Change-Id: I203174ff845eb66a3a6fc685bbbd74966f91e718
Signed-off-by: manojna v <manojna.vijayakrishna@ericsson.com>
elanmanager/impl/src/main/java/org/opendaylight/netvirt/elan/cache/ConfigMcastCache.java [new file with mode: 0644]
elanmanager/impl/src/main/java/org/opendaylight/netvirt/elan/l2gw/jobs/McastUpdateJob.java
elanmanager/impl/src/main/java/org/opendaylight/netvirt/elan/l2gw/utils/ElanL2GatewayMulticastUtils.java
elanmanager/impl/src/main/java/org/opendaylight/netvirt/elan/l2gw/utils/ElanRefUtil.java

diff --git a/elanmanager/impl/src/main/java/org/opendaylight/netvirt/elan/cache/ConfigMcastCache.java b/elanmanager/impl/src/main/java/org/opendaylight/netvirt/elan/cache/ConfigMcastCache.java
new file mode 100644 (file)
index 0000000..757e965
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2019 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.netvirt.elan.cache;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.genius.mdsalutil.cache.InstanceIdDataObjectCache;
+import org.opendaylight.genius.utils.hwvtep.HwvtepSouthboundConstants;
+import org.opendaylight.infrautils.caches.CacheProvider;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepGlobalAugmentation;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.RemoteMcastMacs;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+public class ConfigMcastCache extends InstanceIdDataObjectCache<RemoteMcastMacs> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ConfigMcastCache.class);
+
+    @Inject
+    public ConfigMcastCache(DataBroker dataBroker, CacheProvider cacheProvider) {
+        super(RemoteMcastMacs.class, dataBroker, LogicalDatastoreType.CONFIGURATION,
+                InstanceIdentifier.create(NetworkTopology.class)
+                        .child(Topology.class, new TopologyKey(HwvtepSouthboundConstants.HWVTEP_TOPOLOGY_ID))
+                        .child(Node.class).augmentation(HwvtepGlobalAugmentation.class)
+                        .child(RemoteMcastMacs.class), cacheProvider);
+    }
+}
index fcdddd66bf2305244c558c055847da1df3f0ccb3..5886ffd3f9cbffa2ade5b89936520ce741de96cc 100644 (file)
@@ -30,6 +30,8 @@ public class McastUpdateJob implements Callable<List<ListenableFuture<Void>>> {
     private ElanClusterUtils elanClusterUtils;
     boolean add;
     protected String jobKey;
+    private IpAddress removedDstTep;
+    private boolean dpnOrConnectionRemoved;
 
     public McastUpdateJob(String elanName,
                           String nodeId,
@@ -54,7 +56,8 @@ public class McastUpdateJob implements Callable<List<ListenableFuture<Void>>> {
         ListenableFuture<Void> ft = null;
         //TODO: make prepareRemoteMcastMacUpdateOnDevice return a ListenableFuture<Void>
         if (add) {
-            ft = mcastUtils.prepareRemoteMcastMacUpdateOnDevice(elanName, device);
+            ft = mcastUtils.prepareRemoteMcastMacUpdateOnDevice(elanName, device, !dpnOrConnectionRemoved ,
+                    removedDstTep);
         } else {
             ft =  mcastUtils.deleteRemoteMcastMac(new NodeId(nodeId), elanName);
         }
@@ -86,6 +89,16 @@ public class McastUpdateJob implements Callable<List<ListenableFuture<Void>>> {
                 elanClusterUtils).submit();
     }
 
+    private McastUpdateJob setRemovedDstTep(IpAddress removedDstTep) {
+        this.removedDstTep = removedDstTep;
+        return this;
+    }
+
+    private McastUpdateJob setDpnOrconnectionRemoved() {
+        this.dpnOrConnectionRemoved = true;
+        return this;
+    }
+
     public static void updateAllMcastsForConnectionAdd(String elanName,
                                                        ElanL2GatewayMulticastUtils mcastUtils,
                                                        ElanClusterUtils elanClusterUtils) {
@@ -101,6 +114,8 @@ public class McastUpdateJob implements Callable<List<ListenableFuture<Void>>> {
         ElanL2GwCacheUtils.getInvolvedL2GwDevices(elanName).forEach(device -> {
             IpAddress deletedTep = deletedDevice.getTunnelIp();
             new McastUpdateJob(elanName, device.getHwvtepNodeId(), true , mcastUtils, elanClusterUtils)
+                    .setDpnOrconnectionRemoved()
+                    .setRemovedDstTep(deletedTep)
                     .submit();
         });
     }
@@ -121,6 +136,8 @@ public class McastUpdateJob implements Callable<List<ListenableFuture<Void>>> {
         ElanL2GwCacheUtils.getInvolvedL2GwDevices(elanName).forEach(device -> {
             IpAddress deletedTep = elanItmUtils.getSourceDpnTepIp(srcDpnId, new NodeId(device.getHwvtepNodeId()));
             new McastUpdateJob(elanName, device.getHwvtepNodeId(), true , mcastUtils, elanClusterUtils)
+                    .setDpnOrconnectionRemoved()
+                    .setRemovedDstTep(deletedTep)
                     .submit();
         });
     }
index 2f5cf5db91c95e2d7dae038b2cc979f57f12349a..5a0067ec51ebb32bdee82739451f9609bc0a705d 100644 (file)
@@ -11,13 +11,17 @@ import static java.util.Collections.emptyList;
 import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
 import static org.opendaylight.netvirt.elan.utils.ElanUtils.isVxlanNetworkOrVxlanSegment;
 
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import java.math.BigInteger;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 import javax.inject.Inject;
 import javax.inject.Singleton;
 import org.eclipse.jdt.annotation.NonNull;
@@ -101,23 +105,24 @@ public class ElanL2GatewayMulticastUtils {
     private final ElanUtils elanUtils;
     private final IMdsalApiManager mdsalManager;
     private final IInterfaceManager interfaceManager;
+    private final ElanRefUtil elanRefUtil;
     private final ElanClusterUtils elanClusterUtils;
     private final Scheduler scheduler;
 
+
     @Inject
-    public ElanL2GatewayMulticastUtils(DataBroker broker, ElanItmUtils elanItmUtils, JobCoordinator jobCoordinator,
-                                       ElanUtils elanUtils, IMdsalApiManager mdsalManager,
-                                       IInterfaceManager interfaceManager, ElanClusterUtils elanClusterUtils,
-                                       Scheduler scheduler) {
-        this.broker = broker;
-        this.txRunner = new ManagedNewTransactionRunnerImpl(broker);
+    public ElanL2GatewayMulticastUtils(ElanItmUtils elanItmUtils, ElanUtils elanUtils, IMdsalApiManager mdsalManager,
+                                       IInterfaceManager interfaceManager, ElanRefUtil elanRefUtil) {
+        this.elanRefUtil = elanRefUtil;
+        this.broker = elanRefUtil.getDataBroker();
+        this.txRunner = new ManagedNewTransactionRunnerImpl(elanRefUtil.getDataBroker());
         this.elanItmUtils = elanItmUtils;
-        this.jobCoordinator = jobCoordinator;
+        this.jobCoordinator = elanRefUtil.getJobCoordinator();
         this.elanUtils = elanUtils;
         this.mdsalManager = mdsalManager;
         this.interfaceManager = interfaceManager;
-        this.elanClusterUtils = elanClusterUtils;
-        this.scheduler = scheduler;
+        this.elanClusterUtils = elanRefUtil.getElanClusterUtils();
+        this.scheduler = elanRefUtil.getScheduler();
     }
 
     /**
@@ -152,7 +157,7 @@ public class ElanL2GatewayMulticastUtils {
      */
     public void updateRemoteMcastMacOnElanL2GwDevices(String elanName) {
         for (L2GatewayDevice device : ElanL2GwCacheUtils.getInvolvedL2GwDevices(elanName)) {
-            prepareRemoteMcastMacUpdateOnDevice(elanName, device);
+            prepareRemoteMcastMacUpdateOnDevice(elanName, device, false, null);
         }
     }
 
@@ -170,15 +175,58 @@ public class ElanL2GatewayMulticastUtils {
      *            the device
      */
     public void updateRemoteMcastMacOnElanL2GwDevice(String elanName, L2GatewayDevice device) {
-        prepareRemoteMcastMacUpdateOnDevice(elanName, device);
+        prepareRemoteMcastMacUpdateOnDevice(elanName, device, false, null);
     }
 
-    public ListenableFuture<Void> prepareRemoteMcastMacUpdateOnDevice(String elanName, L2GatewayDevice device) {
+    public ListenableFuture<Void> prepareRemoteMcastMacUpdateOnDevice(String elanName, L2GatewayDevice device,
+                                                                      boolean addCase, IpAddress removedDstTep) {
+        NodeId dstNodeId = new NodeId(device.getHwvtepNodeId());
+        RemoteMcastMacs existingMac = null;
+        try {
+            Optional<RemoteMcastMacs> mac  = elanRefUtil.getConfigMcastCache().get(getRemoteMcastIid(dstNodeId,
+                    elanName));
+            if (mac.isPresent()) {
+                existingMac = mac.get();
+            }
+        } catch (ReadFailedException e) {
+            LOG.error("Failed to read iid for elan {}", elanName, e);
+        }
+
+        if (!addCase && removedDstTep != null) {
+            LOG.debug(" RemoteMcast update delete tep {} of elan {} ", removedDstTep.getIpv4Address().getValue(),
+                    elanName);
+            //incase of dpn flap immediately after cluster reboot just remove its tep alone
+            if (existingMac != null) {
+                return deleteLocatorFromMcast(elanName, dstNodeId, removedDstTep, existingMac);
+            }
+        }
         Collection<L2GatewayDevice> elanL2gwDevices = ElanL2GwCacheUtils.getInvolvedL2GwDevices(elanName);
-        List<DpnInterfaces> dpns = elanUtils.getElanDPNByName(elanName);
+        Collection<DpnInterfaces> dpns = elanRefUtil.getElanInstanceDpnsCache().get(elanName);
         List<IpAddress> dpnsTepIps = getAllTepIpsOfDpns(device, dpns);
         List<IpAddress> l2GwDevicesTepIps = getAllTepIpsOfL2GwDevices(elanL2gwDevices);
-        return prepareRemoteMcastMacEntry(elanName, device, dpnsTepIps, l2GwDevicesTepIps);
+        return prepareRemoteMcastMacEntry(elanName, device, dpnsTepIps, l2GwDevicesTepIps, addCase);
+    }
+
+    private ListenableFuture<Void> deleteLocatorFromMcast(String elanName, NodeId dstNodeId,
+                                                          IpAddress removedDstTep,
+                                                          RemoteMcastMacs existingMac) {
+        InstanceIdentifier<RemoteMcastMacs> macIid = HwvtepSouthboundUtils
+                .createRemoteMcastMacsInstanceIdentifier(dstNodeId, existingMac.key());
+        LocatorSet tobeDeleted = buildLocatorSet(dstNodeId, removedDstTep);
+        RemoteMcastMacsBuilder newMacBuilder = new RemoteMcastMacsBuilder(existingMac);
+        newMacBuilder.getLocatorSet().remove(tobeDeleted);
+        RemoteMcastMacs mac = newMacBuilder.build();
+        //configMcastCache.add(macIid, mac);
+        return ResourceBatchingManager.getInstance().put(
+                ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY, macIid, mac);
+    }
+
+    LocatorSet buildLocatorSet(NodeId nodeId, IpAddress tepIp) {
+        HwvtepPhysicalLocatorAugmentation phyLocatorAug = HwvtepSouthboundUtils
+                .createHwvtepPhysicalLocatorAugmentation(tepIp.stringValue());
+        HwvtepPhysicalLocatorRef phyLocRef = new HwvtepPhysicalLocatorRef(
+                HwvtepSouthboundUtils.createPhysicalLocatorInstanceIdentifier(nodeId, phyLocatorAug));
+        return new LocatorSetBuilder().setLocatorRef(phyLocRef).build();
     }
 
     /**
@@ -417,7 +465,7 @@ public class ElanL2GatewayMulticastUtils {
      */
     private ListenableFuture<Void> prepareRemoteMcastMacEntry(String elanName,
                                              L2GatewayDevice device, List<IpAddress> dpnsTepIps,
-                                             List<IpAddress> l2GwDevicesTepIps) {
+                                             List<IpAddress> l2GwDevicesTepIps, boolean addCase) {
         NodeId nodeId = new NodeId(device.getHwvtepNodeId());
 
         ArrayList<IpAddress> remoteTepIps = new ArrayList<>(l2GwDevicesTepIps);
@@ -453,7 +501,7 @@ public class ElanL2GatewayMulticastUtils {
         String logicalSwitchName = ElanL2GatewayUtils.getLogicalSwitchFromElan(elanName);
         LOG.info("Adding RemoteMcastMac for node: {} with physical locators: {}", device.getHwvtepNodeId(),
                 remoteTepIps);
-        return putRemoteMcastMac(nodeId, logicalSwitchName, remoteTepIps);
+        return putRemoteMcastMac(nodeId, logicalSwitchName, remoteTepIps, addCase);
     }
 
     /**
@@ -466,8 +514,8 @@ public class ElanL2GatewayMulticastUtils {
      * @param tepIps
      *            the tep ips
      */
-    private static ListenableFuture<Void> putRemoteMcastMac(NodeId nodeId, String logicalSwitchName,
-            ArrayList<IpAddress> tepIps) {
+    private ListenableFuture<Void> putRemoteMcastMac(NodeId nodeId, String logicalSwitchName,
+            ArrayList<IpAddress> tepIps, boolean addCase) {
         List<LocatorSet> locators = new ArrayList<>();
         for (IpAddress tepIp : tepIps) {
             HwvtepPhysicalLocatorAugmentation phyLocatorAug = HwvtepSouthboundUtils
@@ -479,14 +527,47 @@ public class ElanL2GatewayMulticastUtils {
 
         HwvtepLogicalSwitchRef lsRef = new HwvtepLogicalSwitchRef(HwvtepSouthboundUtils
                 .createLogicalSwitchesInstanceIdentifier(nodeId, new HwvtepNodeName(logicalSwitchName)));
-        RemoteMcastMacs remoteMcastMac = new RemoteMcastMacsBuilder()
+        RemoteMcastMacs newRemoteMcastMac = new RemoteMcastMacsBuilder()
                 .setMacEntryKey(new MacAddress(ElanConstants.UNKNOWN_DMAC)).setLogicalSwitchRef(lsRef)
                 .setLocatorSet(locators).build();
         InstanceIdentifier<RemoteMcastMacs> iid = HwvtepSouthboundUtils.createRemoteMcastMacsInstanceIdentifier(nodeId,
-                remoteMcastMac.key());
+                newRemoteMcastMac.key());
+        RemoteMcastMacs existingRemoteMcastMac = null;
+        try {
+            Optional<RemoteMcastMacs> mac  = elanRefUtil.getConfigMcastCache().get(iid);
+            if (mac.isPresent()) {
+                existingRemoteMcastMac = mac.get();
+            }
+        } catch (ReadFailedException e) {
+            LOG.error("Failed to read iid {}", iid, e);
+        }
+
+        if (addCase && areLocatorsAlreadyConfigured(existingRemoteMcastMac, newRemoteMcastMac)) {
+            return Futures.immediateFuture(null);
+        }
+
         return ResourceBatchingManager.getInstance().put(ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY,
-                iid, remoteMcastMac);
+                iid, newRemoteMcastMac);
+
+    }
+
+    private boolean areLocatorsAlreadyConfigured(RemoteMcastMacs existingMac, RemoteMcastMacs newMac) {
+        if (existingMac == null) {
+            return false;
+        }
+        Set existingLocators = new HashSet<>(existingMac.getLocatorSet());
+        List newLocators = newMac.getLocatorSet();
+        return existingLocators.containsAll(newLocators);
+    }
 
+    private InstanceIdentifier<RemoteMcastMacs> getRemoteMcastIid(NodeId nodeId, String logicalSwitchName) {
+        HwvtepLogicalSwitchRef lsRef = new HwvtepLogicalSwitchRef(HwvtepSouthboundUtils
+                .createLogicalSwitchesInstanceIdentifier(nodeId, new HwvtepNodeName(logicalSwitchName)));
+        RemoteMcastMacs remoteMcastMac = new RemoteMcastMacsBuilder()
+                .setMacEntryKey(new MacAddress(ElanConstants.UNKNOWN_DMAC)).setLogicalSwitchRef(lsRef)
+                .build();
+        return HwvtepSouthboundUtils.createRemoteMcastMacsInstanceIdentifier(nodeId,
+                remoteMcastMac.key());
     }
 
     /**
@@ -498,7 +579,7 @@ public class ElanL2GatewayMulticastUtils {
      *            the dpns
      * @return the all tep ips of dpns and devices
      */
-    private List<IpAddress> getAllTepIpsOfDpns(L2GatewayDevice l2GwDevice, List<DpnInterfaces> dpns) {
+    private List<IpAddress> getAllTepIpsOfDpns(L2GatewayDevice l2GwDevice, Collection<DpnInterfaces> dpns) {
         List<IpAddress> tepIps = new ArrayList<>();
         for (DpnInterfaces dpn : dpns) {
             IpAddress internalTunnelIp = elanItmUtils.getSourceDpnTepIp(dpn.getDpId(),
index 17e874a1eb78315ad0324a8943b55633d7d37513..86d4027591b905fa549d714b87cf1f9e53e14860 100644 (file)
@@ -12,6 +12,7 @@ import javax.inject.Singleton;
 
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
+import org.opendaylight.netvirt.elan.cache.ConfigMcastCache;
 import org.opendaylight.netvirt.elan.cache.ElanInstanceCache;
 import org.opendaylight.netvirt.elan.cache.ElanInstanceDpnsCache;
 import org.opendaylight.netvirt.elan.cache.ElanInterfaceCache;
@@ -28,6 +29,7 @@ public class ElanRefUtil {
     private final ElanInstanceCache elanInstanceCache;
     private final ElanInstanceDpnsCache elanInstanceDpnsCache;
     private final ElanInterfaceCache elanInterfaceCache;
+    private final ConfigMcastCache configMcastCache;
 
     @Inject
     public ElanRefUtil(DataBroker dataBroker,
@@ -35,6 +37,7 @@ public class ElanRefUtil {
                        ElanInstanceCache elanInstanceCache,
                        ElanInstanceDpnsCache elanInstanceDpnsCache,
                        ElanInterfaceCache elanInterfaceCache,
+                       ConfigMcastCache configMcastCache,
                        JobCoordinator jobCoordinator,
                        Scheduler scheduler) {
         this.dataBroker = dataBroker;
@@ -42,6 +45,7 @@ public class ElanRefUtil {
         this.elanInstanceCache = elanInstanceCache;
         this.elanInstanceDpnsCache = elanInstanceDpnsCache;
         this.elanInterfaceCache = elanInterfaceCache;
+        this.configMcastCache = configMcastCache;
         this.jobCoordinator = jobCoordinator;
         this.scheduler = scheduler;
     }
@@ -73,4 +77,8 @@ public class ElanRefUtil {
     public Scheduler getScheduler() {
         return scheduler;
     }
+
+    public ConfigMcastCache getConfigMcastCache() {
+        return configMcastCache;
+    }
 }
\ No newline at end of file