avoid race in elan bc group update 70/67270/22
authorK.V Suneelu Verma <k.v.suneelu.verma@ericsson.com>
Thu, 18 Jan 2018 06:59:59 +0000 (12:29 +0530)
committerSam Hague <shague@redhat.com>
Sat, 24 Feb 2018 02:28:02 +0000 (02:28 +0000)
elan broad cast group update happens from
1) first elan dpn interface add (runs in oper default shard leader node)
2) l2gw connection add (runs in eos/cluster singleton owner node)

To avoid the race between these two
add the l2gw tep to operational elan external teps and keep the job key as
elan name

handle the tunnel ip change of l2gw gracefully

Change-Id: I2e195dc2c35e75521b31c2a0f358b2034b99874e
Signed-off-by: K.V Suneelu Verma <k.v.suneelu.verma@ericsson.com>
13 files changed:
elanmanager/api/src/main/yang/elan.yang
elanmanager/impl/src/main/java/org/opendaylight/netvirt/elan/evpn/utils/EvpnUtils.java
elanmanager/impl/src/main/java/org/opendaylight/netvirt/elan/internal/ElanDpnToTransportZoneListener.java
elanmanager/impl/src/main/java/org/opendaylight/netvirt/elan/internal/ElanExtnTepConfigListener.java [new file with mode: 0644]
elanmanager/impl/src/main/java/org/opendaylight/netvirt/elan/internal/ElanExtnTepListener.java
elanmanager/impl/src/main/java/org/opendaylight/netvirt/elan/internal/ElanInterfaceManager.java
elanmanager/impl/src/main/java/org/opendaylight/netvirt/elan/l2gw/ha/merge/MergeCommandsAggregator.java
elanmanager/impl/src/main/java/org/opendaylight/netvirt/elan/l2gw/listeners/HwvtepPhysicalSwitchListener.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/ElanL2GatewayUtils.java
elanmanager/impl/src/main/java/org/opendaylight/netvirt/elan/l2gw/utils/L2gwServiceProvider.java
elanmanager/impl/src/main/java/org/opendaylight/netvirt/elan/utils/ElanUtils.java
elanmanager/impl/src/main/java/org/opendaylight/netvirt/elan/utils/TransportZoneNotificationUtil.java

index 5c2af3bc02e0f07bb647ff2e2a5335cde987d57e..e2adc593bdb96689fb9cd90ad05d762f678ccdcc 100644 (file)
@@ -114,6 +114,9 @@ module elan {
                 leaf tep-ip {
                     type inet:ip-address;
                 }
+                leaf nodeid {
+                    type string;
+                }
             }
         }
     }
index cf864485793bdb780a4e976a259b6592b3d02d50..2539146ff3715a83e52ff07b6df763a4fbba3b23 100644 (file)
@@ -220,7 +220,7 @@ public class EvpnUtils {
             return;
         }
         int vpnLabel = 0;
-        long l2vni = elanInfo.getSegmentationId();
+        long l2vni = elanUtils.getVxlanSegmentationId(elanInfo);
         long l3vni = 0;
         String gatewayMacAddr = null;
         String l3VpName = getL3vpnNameFromElan(elanInfo);
@@ -410,7 +410,7 @@ public class EvpnUtils {
     private void programEvpnL2vniFlow(ElanInstance elanInfo, BiConsumer<BigInteger, FlowEntity> flowHandler) {
         long elanTag = elanInfo.getElanTag();
         List<MatchInfo> mkMatches = new ArrayList<>();
-        mkMatches.add(new MatchTunnelId(BigInteger.valueOf(elanInfo.getSegmentationId())));
+        mkMatches.add(new MatchTunnelId(BigInteger.valueOf(elanUtils.getVxlanSegmentationId(elanInfo))));
         NWUtil.getOperativeDPNs(broker).forEach(dpnId -> {
             LOG.debug("Updating tunnel flow to dpnid {}", dpnId);
             List<InstructionInfo> instructions = getInstructionsForExtTunnelTable(elanTag);
index e6062a0c97b9ead45ddafc6cf14246bfe29cc83e..498acc848b9c40ec9bd4c61498bacf67010d525f 100644 (file)
@@ -67,7 +67,7 @@ public class ElanDpnToTransportZoneListener
         BigInteger dpId = dataObjectModification.getDpId();
         String elanInstanceName = key.firstKeyOf(ElanDpnInterfacesList.class).getElanInstanceName();
 
-        if (!ElanUtils.isVxlan(elanInstanceCache.get(elanInstanceName).orNull())) {
+        if (!ElanUtils.isVxlanNetworkOrVxlanSegment(elanInstanceCache.get(elanInstanceName).orNull())) {
             LOG.debug("ElanInstance {} is not vxlan network, nothing to do", elanInstanceName);
             return;
         }
@@ -88,7 +88,7 @@ public class ElanDpnToTransportZoneListener
         BigInteger dpId = dataObjectModification.getDpId();
         String elanInstanceName = key.firstKeyOf(ElanDpnInterfacesList.class).getElanInstanceName();
 
-        if (!ElanUtils.isVxlan(elanInstanceCache.get(elanInstanceName).orNull())) {
+        if (!ElanUtils.isVxlanNetworkOrVxlanSegment(elanInstanceCache.get(elanInstanceName).orNull())) {
             return;
         }
 
diff --git a/elanmanager/impl/src/main/java/org/opendaylight/netvirt/elan/internal/ElanExtnTepConfigListener.java b/elanmanager/impl/src/main/java/org/opendaylight/netvirt/elan/internal/ElanExtnTepConfigListener.java
new file mode 100644 (file)
index 0000000..c954781
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2018 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.internal;
+
+import javax.annotation.PostConstruct;
+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.datastoreutils.AsyncDataTreeChangeListenerBase;
+import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
+import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
+import org.opendaylight.infrautils.utils.concurrent.JdkFutures;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInstances;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.elan.instance.ExternalTeps;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+public class ElanExtnTepConfigListener
+        extends AsyncDataTreeChangeListenerBase<ExternalTeps, ElanExtnTepConfigListener> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ElanExtnTepConfigListener.class);
+
+    private final DataBroker broker;
+    private final ManagedNewTransactionRunner txRunner;
+
+    @Inject
+    public ElanExtnTepConfigListener(DataBroker dataBroker) {
+        super(ExternalTeps.class, ElanExtnTepConfigListener.class);
+        this.broker = dataBroker;
+        this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
+    }
+
+    @Override
+    @PostConstruct
+    public void init() {
+        registerListener(LogicalDatastoreType.CONFIGURATION, broker);
+    }
+
+    @Override
+    public InstanceIdentifier<ExternalTeps> getWildCardPath() {
+        return InstanceIdentifier
+                .builder(ElanInstances.class)
+                .child(ElanInstance.class)
+                .child(ExternalTeps.class).build();
+    }
+
+    @Override
+    protected void add(InstanceIdentifier<ExternalTeps> iid, ExternalTeps tep) {
+        JdkFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
+            tx.put(LogicalDatastoreType.OPERATIONAL, iid, tep, true);
+        }), LOG, "Failed to update operational external teps {}", iid);
+    }
+
+    @Override
+    protected void update(InstanceIdentifier<ExternalTeps> iid, ExternalTeps tep, ExternalTeps t1) {
+    }
+
+    @Override
+    protected void remove(InstanceIdentifier<ExternalTeps> iid, ExternalTeps tep) {
+        JdkFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
+            tx.delete(LogicalDatastoreType.OPERATIONAL, iid);
+        }), LOG, "Failed to update operational external teps {}", iid);
+    }
+
+    @Override
+    protected ElanExtnTepConfigListener getDataTreeChangeListener() {
+        return this;
+    }
+}
index 4da91ca16cc6317a49f1a3c1360a91d6437e757b..15973c439d407facb9645ecb58b2a8373eac5be9 100644 (file)
@@ -49,11 +49,11 @@ public class ElanExtnTepListener extends AsyncDataTreeChangeListenerBase<Externa
     @Override
     @PostConstruct
     public void init() {
-        registerListener(LogicalDatastoreType.CONFIGURATION, broker);
+        registerListener(LogicalDatastoreType.OPERATIONAL, broker);
     }
 
     @Override
-    protected InstanceIdentifier<ExternalTeps> getWildCardPath() {
+    public InstanceIdentifier<ExternalTeps> getWildCardPath() {
         return InstanceIdentifier.create(ElanInstances.class).child(ElanInstance.class).child(ExternalTeps.class);
     }
 
index d0aeb1f75c6dafce6003ddea45b2dab907d5d99a..ffb0acf076653c4a391ed5005a002be384935b8a 100644 (file)
@@ -7,7 +7,6 @@
  */
 package org.opendaylight.netvirt.elan.internal;
 
-import static org.opendaylight.netvirt.elan.utils.ElanUtils.isVxlan;
 import static org.opendaylight.netvirt.elan.utils.ElanUtils.isVxlanNetworkOrVxlanSegment;
 
 import com.google.common.base.Optional;
@@ -261,7 +260,8 @@ public class ElanInterfaceManager extends AsyncDataTreeChangeListenerBase<ElanIn
                 removeEtreeBroadcastGrups(elanInfo, interfaceInfo, flowTx);
                 if (isVxlanNetworkOrVxlanSegment(elanInfo)) {
                     if (elanUtils.isOpenstackVniSemanticsEnforced()) {
-                        elanUtils.removeTerminatingServiceAction(dpId, elanInfo.getSegmentationId().intValue());
+                        elanUtils.removeTerminatingServiceAction(dpId,
+                                elanUtils.getVxlanSegmentationId(elanInfo).intValue());
                     }
                     unsetExternalTunnelTable(dpId, elanInfo);
                 }
@@ -823,7 +823,7 @@ public class ElanInterfaceManager extends AsyncDataTreeChangeListenerBase<ElanIn
             // Terminating Service , UnknownDMAC Table.
             // The 1st ELAN Interface in a DPN must program the INTERNAL_TUNNEL_TABLE, but only if the network type
             // for ELAN Instance is VxLAN
-            if (isVxlan(elanInstance)) {
+            if (isVxlanNetworkOrVxlanSegment(elanInstance)) {
                 setupTerminateServiceTable(elanInstance, dpId, writeFlowGroupTx);
             }
             setupUnknownDMacTable(elanInstance, dpId, writeFlowGroupTx);
@@ -947,8 +947,8 @@ public class ElanInterfaceManager extends AsyncDataTreeChangeListenerBase<ElanIn
                             try {
                                 List<Action> remoteListActionInfo = elanItmUtils.getInternalTunnelItmEgressAction(
                                         dpnInterface.getDpId(), otherFes.getDpId(),
-                                        elanUtils.isOpenstackVniSemanticsEnforced() ? elanInfo.getSegmentationId()
-                                                : elanTag);
+                                        elanUtils.isOpenstackVniSemanticsEnforced()
+                                                ? elanUtils.getVxlanSegmentationId(elanInfo) : elanTag);
                                 if (!remoteListActionInfo.isEmpty()) {
                                     remoteListBucketInfo.add(MDSALUtil.buildBucket(remoteListActionInfo, MDSALUtil
                                             .GROUP_WEIGHT, bucketId, MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP));
@@ -1252,7 +1252,7 @@ public class ElanInterfaceManager extends AsyncDataTreeChangeListenerBase<ElanIn
             listMatchInfoBase = ElanUtils.getTunnelMatchesForServiceId((int) elanTag);
             instructionInfos = getInstructionsForOutGroup(ElanUtils.getElanLocalBCGId(elanTag));
         } else {
-            serviceId = elanInfo.getSegmentationId();
+            serviceId = elanUtils.getVxlanSegmentationId(elanInfo);
             listMatchInfoBase = buildMatchesForVni(serviceId);
             instructionInfos = getInstructionsIntOrExtTunnelTable(elanTag);
         }
index cede32ad6d81d95646562cb9e4a214b2ac70674f..235994bca97c0dd080f383130584265b2daecfce 100644 (file)
@@ -114,9 +114,9 @@ public abstract class MergeCommandsAggregator<BuilderTypeT extends Builder, AugT
 
                 String destination = datastoreType == CONFIGURATION ? "child" : "parent";
                 if (create) {
-                    if (isDataUpdated(existingDataOptional, data)) {
+                    if (isDataUpdated(existingDataOptional, transformedItem)) {
                         LOG.debug("Copy to {} {} {}", destination, datastoreType, transformedId);
-                        tx.put(datastoreType, transformedId, data, true);
+                        tx.put(datastoreType, transformedId, transformedItem, true);
                     } else {
                         LOG.debug("Data not updated skip copy to {}", transformedId);
                     }
index de467bbc4d1bf4de8156af62bca38e77495916bd..d7a5079744f283835857497cb2220e4589c16899 100644 (file)
@@ -8,6 +8,7 @@
 
 package org.opendaylight.netvirt.elan.l2gw.listeners;
 
+import com.google.common.base.Optional;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
@@ -24,6 +25,7 @@ import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeLis
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
 import org.opendaylight.genius.datastoreutils.hwvtep.HwvtepAbstractDataTreeChangeListener;
 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
@@ -248,7 +250,7 @@ public class HwvtepPhysicalSwitchListener
                                 existingDevice.getDeviceName(), existingDevice.getTunnelIp());
                         Thread.sleep(10000L);//TODO remove these sleeps
                         LOG.info("Creating itm tunnels for device {}", existingDevice.getDeviceName());
-                        ElanL2GatewayUtils.createItmTunnels(itmRpcService, hwvtepId, psName,
+                        ElanL2GatewayUtils.createItmTunnels(dataBroker, itmRpcService, hwvtepId, psName,
                                 phySwitchAfter.getTunnelIps().get(0).getTunnelIpsKey());
                         return Collections.emptyList();
                     }
@@ -321,7 +323,11 @@ public class HwvtepPhysicalSwitchListener
 
             handleAdd(l2GwDevice);
             elanClusterUtils.runOnlyInOwnerNode("Update config tunnels IP ", () -> {
-                updateConfigTunnelIp(identifier, phySwitchAdded);
+                try {
+                    updateConfigTunnelIp(identifier, phySwitchAdded);
+                } catch (ReadFailedException e) {
+                    LOG.error("Failed to update tunnel ips {}", identifier);
+                }
             });
             return;
         });
@@ -403,16 +409,20 @@ public class HwvtepPhysicalSwitchListener
     }
 
     private void updateConfigTunnelIp(InstanceIdentifier<PhysicalSwitchAugmentation> identifier,
-                                      PhysicalSwitchAugmentation phySwitchAdded) {
+                                      PhysicalSwitchAugmentation phySwitchAdded) throws ReadFailedException {
         if (phySwitchAdded.getTunnelIps() != null) {
             ListenableFutures.addErrorLogging(
                 txRunner.callWithNewReadWriteTransactionAndSubmit(tx -> {
+                    Optional<PhysicalSwitchAugmentation> existingSwitch = tx.read(
+                            LogicalDatastoreType.CONFIGURATION, identifier).checkedGet();
                     PhysicalSwitchAugmentationBuilder psBuilder = new PhysicalSwitchAugmentationBuilder();
+                    if (existingSwitch.isPresent()) {
+                        psBuilder = new PhysicalSwitchAugmentationBuilder(existingSwitch.get());
+                    }
                     psBuilder.setTunnelIps(phySwitchAdded.getTunnelIps());
-                    tx.merge(LogicalDatastoreType.CONFIGURATION, identifier, psBuilder.build());
+                    tx.put(LogicalDatastoreType.CONFIGURATION, identifier, psBuilder.build(), true);
                     LOG.trace("Updating config tunnel ips {}", identifier);
-                }),
-                LOG, "Failed to update config tunnel ip for iid {}", identifier);
+                }), LOG, "Failed to update the config tunnel ips {}", identifier);
         }
     }
 }
index 6563f72bc455ed1f92a5b76d0b250c9a31beb3a2..2f0fe43fcf057ddc57b97a4a3b1ee0cab3475b93 100644 (file)
@@ -7,7 +7,7 @@
  */
 package org.opendaylight.netvirt.elan.l2gw.utils;
 
-import static org.opendaylight.netvirt.elan.utils.ElanUtils.isVxlan;
+import static org.opendaylight.netvirt.elan.utils.ElanUtils.isVxlanNetworkOrVxlanSegment;
 
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.SettableFuture;
@@ -24,6 +24,8 @@ import javax.inject.Singleton;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
@@ -34,6 +36,7 @@ import org.opendaylight.genius.utils.batching.ResourceBatchingManager;
 import org.opendaylight.genius.utils.hwvtep.HwvtepSouthboundUtils;
 import org.opendaylight.genius.utils.hwvtep.HwvtepUtils;
 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
+import org.opendaylight.infrautils.utils.concurrent.JdkFutures;
 import org.opendaylight.netvirt.elan.l2gw.jobs.HwvtepDeviceMcastMacUpdateJob;
 import org.opendaylight.netvirt.elan.utils.ElanConstants;
 import org.opendaylight.netvirt.elan.utils.ElanItmUtils;
@@ -50,10 +53,14 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.dhcp.rev160428.Desi
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.dhcp.rev160428.designated.switches._for.external.tunnels.DesignatedSwitchForTunnel;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.dhcp.rev160428.designated.switches._for.external.tunnels.DesignatedSwitchForTunnelKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.etree.rev160614.EtreeInstance;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInstances;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.ElanDpnInterfacesList;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.elan.dpn.interfaces.list.DpnInterfaces;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstanceKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.elan.instance.ExternalTeps;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.elan.instance.ExternalTepsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.elan.instance.ExternalTepsKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepLogicalSwitchRef;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepNodeName;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepPhysicalLocatorAugmentation;
@@ -111,9 +118,22 @@ public class ElanL2GatewayMulticastUtils {
      */
     public ListenableFuture<Void> handleMcastForElanL2GwDeviceAdd(String elanName, ElanInstance elanInstance,
             L2GatewayDevice device) {
+        InstanceIdentifier<ExternalTeps> tepPath = buildExternalTepPath(elanName, device.getTunnelIp());
+        JdkFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
+            tx.put(LogicalDatastoreType.CONFIGURATION, tepPath, buildExternalTeps(device));
+        }), LOG, "Failed to write to config external tep {}", tepPath);
         return updateMcastMacsForAllElanDevices(elanName, elanInstance, device, true/* updateThisDevice */);
     }
 
+    public static InstanceIdentifier<ExternalTeps> buildExternalTepPath(String elan, IpAddress tepIp) {
+        return InstanceIdentifier.builder(ElanInstances.class).child(ElanInstance.class, new ElanInstanceKey(elan))
+                .child(ExternalTeps.class, new ExternalTepsKey(tepIp)).build();
+    }
+
+    protected ExternalTeps buildExternalTeps(L2GatewayDevice device) {
+        return new ExternalTepsBuilder().setTepIp(device.getTunnelIp()).setNodeid(device.getHwvtepNodeId()).build();
+    }
+
     /**
      * Updates the remote mcast mac table for all the devices in this elan
      * includes all the dpn tep ips and other devices tep ips in broadcast
@@ -182,8 +202,6 @@ public class ElanL2GatewayMulticastUtils {
         SettableFuture<Void> ft = SettableFuture.create();
         ft.set(null);
 
-        updateRemoteBroadcastGroupForAllElanDpns(elanInstance);
-
         List<DpnInterfaces> dpns = elanUtils.getElanDPNByName(elanName);
 
         ConcurrentMap<String, L2GatewayDevice> devices = ElanL2GwCacheUtils
@@ -308,14 +326,13 @@ public class ElanL2GatewayMulticastUtils {
         List<Bucket> listBucketInfo = new ArrayList<>();
         ElanDpnInterfacesList elanDpns = elanUtils.getElanDpnInterfacesList(elanInfo.getElanInstanceName());
 
-        if (isVxlan(elanInfo)) {
+        if (isVxlanNetworkOrVxlanSegment(elanInfo)) {
             listBucketInfo.addAll(getRemoteBCGroupTunnelBuckets(elanDpns, dpnId, bucketId,
-                    elanUtils.isOpenstackVniSemanticsEnforced() ? elanInfo.getSegmentationId() : elanTag));
+                    elanUtils.isOpenstackVniSemanticsEnforced()
+                            ? elanUtils.getVxlanSegmentationId(elanInfo) : elanTag));
         }
         listBucketInfo.addAll(getRemoteBCGroupExternalPortBuckets(elanDpns, dpnInterfaces, dpnId,
-            getNextAvailableBucketId(listBucketInfo.size())));
-        listBucketInfo.addAll(getRemoteBCGroupBucketsOfElanL2GwDevices(elanInfo, dpnId,
-            getNextAvailableBucketId(listBucketInfo.size())));
+                getNextAvailableBucketId(listBucketInfo.size())));
         listBucketInfo.addAll(getRemoteBCGroupBucketsOfElanExternalTeps(elanInfo, dpnId,
                 getNextAvailableBucketId(listBucketInfo.size())));
         return listBucketInfo;
@@ -344,19 +361,29 @@ public class ElanL2GatewayMulticastUtils {
     public List<Bucket> getRemoteBCGroupBucketsOfElanExternalTeps(ElanInstance elanInfo, BigInteger dpnId,
             int bucketId) {
         List<Bucket> listBucketInfo = new ArrayList<>();
-        List<ExternalTeps> teps = elanInfo.getExternalTeps();
+        ElanInstance operElanInstance = null;
+        try {
+            operElanInstance = new SingleTransactionDataBroker(broker).syncRead(LogicalDatastoreType.OPERATIONAL,
+                    InstanceIdentifier.builder(ElanInstances.class).child(ElanInstance.class, elanInfo.getKey())
+                            .build());
+        } catch (ReadFailedException e) {
+            LOG.error("Failed to read elan instance operational path {}", elanInfo);
+            return Collections.emptyList();
+        }
+        List<ExternalTeps> teps = operElanInstance.getExternalTeps();
         if (teps == null || teps.isEmpty()) {
             return listBucketInfo;
         }
         for (ExternalTeps tep : teps) {
+            String externalTep = tep.getNodeid() != null ? tep.getNodeid() : tep.getTepIp().toString();
             String interfaceName = elanItmUtils.getExternalTunnelInterfaceName(String.valueOf(dpnId),
-                    tep.getTepIp().toString());
+                    externalTep);
             if (interfaceName == null) {
                 LOG.error("Could not get interface name to ext tunnel {} {}", dpnId, tep.getTepIp());
                 continue;
             }
             List<Action> listActionInfo = elanItmUtils.buildTunnelItmEgressActions(interfaceName,
-                    elanInfo.getSegmentationId());
+                    elanUtils.getVxlanSegmentationId(elanInfo));
             listBucketInfo.add(MDSALUtil.buildBucket(listActionInfo, MDSALUtil.GROUP_WEIGHT, bucketId,
                     MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP));
             bucketId++;
@@ -538,11 +565,15 @@ public class ElanL2GatewayMulticastUtils {
      */
     public List<ListenableFuture<Void>> handleMcastForElanL2GwDeviceDelete(String elanName,
             ElanInstance elanInstance, L2GatewayDevice l2GatewayDevice) {
+        ListenableFuture<Void> deleteTepFuture = txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
+            tx.delete(LogicalDatastoreType.CONFIGURATION,
+                    buildExternalTepPath(elanName, l2GatewayDevice.getTunnelIp()));
+        });
         ListenableFuture<Void> updateMcastMacsFuture = updateMcastMacsForAllElanDevices(
                 elanName, elanInstance, l2GatewayDevice, false/* updateThisDevice */);
         ListenableFuture<Void> deleteRemoteMcastMacFuture = deleteRemoteMcastMac(
                 new NodeId(l2GatewayDevice.getHwvtepNodeId()), elanName);
-        return Arrays.asList(updateMcastMacsFuture, deleteRemoteMcastMacFuture);
+        return Arrays.asList(updateMcastMacsFuture, deleteRemoteMcastMacFuture, deleteTepFuture);
     }
 
     /**
index 0d41dfb3118fa7501759190d616267b19173c952..d6a39be726e22217e6878b89fc9f7c2907d5142a 100644 (file)
@@ -48,9 +48,11 @@ import org.opendaylight.genius.utils.hwvtep.HwvtepSouthboundConstants;
 import org.opendaylight.genius.utils.hwvtep.HwvtepSouthboundUtils;
 import org.opendaylight.genius.utils.hwvtep.HwvtepUtils;
 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
+import org.opendaylight.infrautils.utils.concurrent.JdkFutures;
 import org.opendaylight.netvirt.elan.ElanException;
 import org.opendaylight.netvirt.elan.cache.ElanInstanceCache;
 import org.opendaylight.netvirt.elan.cache.ElanInstanceDpnsCache;
+import org.opendaylight.netvirt.elan.l2gw.ha.HwvtepHAUtil;
 import org.opendaylight.netvirt.elan.l2gw.jobs.DeleteL2GwDeviceMacsFromElanJob;
 import org.opendaylight.netvirt.elan.l2gw.jobs.DeleteLogicalSwitchJob;
 import org.opendaylight.netvirt.elan.utils.ElanClusterUtils;
@@ -70,13 +72,17 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetDpidFromInterfaceInputBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetDpidFromInterfaceOutput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.TransportZones;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.transport.zone.subnets.DeviceVteps;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.AddL2GwDeviceInputBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.config.rev150710.ElanConfig;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInstances;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan._interface.forwarding.entries.ElanInterfaceMac;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.elan.dpn.interfaces.list.DpnInterfaces;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.forwarding.tables.MacTable;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.elan.instance.ExternalTeps;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.forwarding.entries.MacEntry;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateway.attributes.Devices;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepGlobalAugmentation;
@@ -974,13 +980,14 @@ public class ElanL2GatewayUtils {
         unInstallL2GwUcastMacFromElanDpns(elan, l2GatewayDevice, localMacs);
     }
 
-    public static void createItmTunnels(ItmRpcService itmRpcService, String hwvtepId, String psName,
-            IpAddress tunnelIp) {
+    public static void createItmTunnels(DataBroker dataBroker, ItmRpcService itmRpcService,
+                                        String hwvtepId, String psName, IpAddress tunnelIp) {
         AddL2GwDeviceInputBuilder builder = new AddL2GwDeviceInputBuilder();
         builder.setTopologyId(HwvtepSouthboundConstants.HWVTEP_TOPOLOGY_ID.getValue());
         builder.setNodeId(HwvtepSouthboundUtils.createManagedNodeId(new NodeId(hwvtepId), psName).getValue());
         builder.setIpAddress(tunnelIp);
         try {
+            deleteStaleTunnelsOfHwvtepInITM(dataBroker, itmRpcService, hwvtepId, psName, tunnelIp);
             Future<RpcResult<Void>> result = itmRpcService.addL2GwDevice(builder.build());
             RpcResult<Void> rpcResult = result.get();
             if (rpcResult.isSuccessful()) {
@@ -993,6 +1000,74 @@ public class ElanL2GatewayUtils {
         }
     }
 
+    private static void deleteStaleTunnelsOfHwvtepInITM(DataBroker dataBroker,
+                                                        ItmRpcService itmRpcService,
+                                                        String globalNodeId,
+                                                        String psName,
+                                                        IpAddress tunnelIp) {
+        try {
+            Optional<TransportZones> tzonesoptional = readTransportZone(dataBroker);
+            if (!tzonesoptional.isPresent() || tzonesoptional.get().getTransportZone() == null) {
+                return;
+            }
+            String psNodeId = globalNodeId + HwvtepHAUtil.PHYSICALSWITCH + psName;
+            tzonesoptional.get().getTransportZone().stream()
+                .filter(transportZone -> transportZone.getSubnets() != null)
+                .flatMap(transportZone -> transportZone.getSubnets().stream())
+                .filter(subnet -> subnet.getDeviceVteps() != null)
+                .flatMap(subnet -> subnet.getDeviceVteps().stream())
+                .filter(deviceVteps -> Objects.equals(getPsName(deviceVteps), psName)) //get device with same ps name
+                .filter(deviceVteps -> !Objects.equals(psNodeId, deviceVteps.getNodeId())
+                        || !Objects.equals(tunnelIp, deviceVteps.getIpAddress()))//node id or tunnel ip is changed
+                .forEach(deviceVteps -> deleteStaleL2gwTep(dataBroker, itmRpcService, deviceVteps));
+        } catch (ReadFailedException e) {
+            LOG.error("Failed delete stale tunnels for {}", globalNodeId);
+        }
+    }
+
+    private static Optional<TransportZones> readTransportZone(DataBroker dataBroker) throws ReadFailedException {
+        return new SingleTransactionDataBroker(dataBroker).syncReadOptional(LogicalDatastoreType.CONFIGURATION,
+                InstanceIdentifier.builder(TransportZones.class).build());
+    }
+
+    private static Optional<ElanInstances> readElanInstances(DataBroker dataBroker) throws ReadFailedException {
+        return new SingleTransactionDataBroker(dataBroker).syncReadOptional(LogicalDatastoreType.CONFIGURATION,
+                InstanceIdentifier.builder(ElanInstances.class).build());
+    }
+
+    private static String getPsName(DeviceVteps deviceVteps) {
+        return HwvtepHAUtil.getPsName(HwvtepHAUtil.convertToInstanceIdentifier(deviceVteps.getNodeId()));
+    }
+
+    private static void deleteStaleL2gwTep(DataBroker dataBroker,
+                                           ItmRpcService itmRpcService,
+                                           DeviceVteps deviceVteps) {
+        String psName = HwvtepHAUtil.getPsName(HwvtepHAUtil.convertToInstanceIdentifier(deviceVteps.getNodeId()));
+        String globalNodeId = HwvtepHAUtil.convertToGlobalNodeId(deviceVteps.getNodeId());
+        try {
+            LOG.info("Deleting stale tep {} ", deviceVteps);
+            L2GatewayUtils.deleteItmTunnels(itmRpcService, globalNodeId, psName, deviceVteps.getIpAddress());
+            Optional<ElanInstances> optionalElan = readElanInstances(dataBroker);
+            if (!optionalElan.isPresent()) {
+                return;
+            }
+            JdkFutures.addErrorLogging(
+                new ManagedNewTransactionRunnerImpl(dataBroker).callWithNewReadWriteTransactionAndSubmit(tx -> {
+                    optionalElan.get().getElanInstance().stream()
+                        .flatMap(elan -> elan.getExternalTeps().stream()
+                                .map(externalTep -> ElanL2GatewayMulticastUtils.buildExternalTepPath(
+                                        elan.getElanInstanceName(), externalTep.getTepIp())))
+                        .filter(externalTepIid -> Objects.equals(
+                                deviceVteps.getIpAddress(), externalTepIid.firstKeyOf(ExternalTeps.class).getTepIp()))
+                        .peek(externalTepIid -> LOG.info("Deleting stale external tep {}", externalTepIid))
+                        .forEach(externalTepIid -> tx.delete(LogicalDatastoreType.CONFIGURATION, externalTepIid));
+                }), LOG, "Failed to delete stale external teps {}", deviceVteps);
+            Thread.sleep(10000);//TODO remove the sleep currently it waits for interfacemgr to finish the cleanup
+        } catch (ReadFailedException | InterruptedException e) {
+            LOG.error("Failed to delete stale l2gw tep {}", deviceVteps, e);
+        }
+    }
+
     public static String getNodeIdFromDpnId(BigInteger dpnId) {
         return MDSALUtil.NODE_PREFIX + MDSALUtil.SEPARATOR + dpnId.toString();
     }
index 7f179419bd7085de7bf54fcbe383415571cd1cc8..1ae9495368fd098bfdfd3c5f23de15f7046246b9 100644 (file)
@@ -53,7 +53,7 @@ public class L2gwServiceProvider extends AbstractLifecycle implements IL2gwServi
                                               String hwvtepNodeId, IpAddress tunnelIpAddr) {
         elanClusterUtils.runOnlyInOwnerNode(hwvtepNodeId, "Handling Physical Switch add create itm tunnels ",
             () -> {
-                ElanL2GatewayUtils.createItmTunnels(itmRpcService, hwvtepNodeId, psName, tunnelIpAddr);
+                ElanL2GatewayUtils.createItmTunnels(dataBroker, itmRpcService, hwvtepNodeId, psName, tunnelIpAddr);
                 return Collections.emptyList();
             });
 
index 5c7d00a49e697ae2afd97326dcec564e3cb5b005..fec60d8e96a84588c77e9faed1ca6459175141c3 100755 (executable)
@@ -943,8 +943,8 @@ public class ElanUtils {
         Flow flowEntity;
         // if openstack-vni-semantics are enforced, segmentation ID is passed as network VNI for VxLAN based provider
         // networks, 0 otherwise
-        long lportTagOrVni = !isOpenstackVniSemanticsEnforced() ? lportTag : isVxlan(elanInstance)
-                ? elanInstance.getSegmentationId() : 0;
+        long lportTagOrVni = !isOpenstackVniSemanticsEnforced() ? lportTag : isVxlanNetworkOrVxlanSegment(elanInstance)
+                ? getVxlanSegmentationId(elanInstance) : 0;
         flowEntity = buildRemoteDmacFlowEntry(srcDpId, destDpId, lportTagOrVni, elanTag, macAddress, displayName,
                 elanInstance);
         mdsalManager.addFlowToTx(srcDpId, flowEntity, writeFlowGroupTx);
@@ -1011,7 +1011,7 @@ public class ElanUtils {
                         elanInstance);
                 }
                 actions = getEgressActionsForInterface(interfaceName, null);
-            } else if (isVxlan(elanInstance)) {
+            } else if (isVxlanNetworkOrVxlanSegment(elanInstance)) {
                 actions = elanItmUtils.getInternalTunnelItmEgressAction(srcDpId, destDpId, lportTagOrVni);
             }
             mkInstructions.add(MDSALUtil.buildApplyActionsInstruction(actions));
index aa3049372b2c9a64a5d29192e81f5b8621ad3dc7..687c76dafe42860d52733fb1b32839c75cdbeb9e 100644 (file)
@@ -108,7 +108,8 @@ public class TransportZoneNotificationUtil {
                 continue;
             }
 
-            if (ElanUtils.isVxlan(elanInstanceCache.get(elanInt.getElanInstanceName()).orNull())) {
+            if (ElanUtils.isVxlanNetworkOrVxlanSegment(
+                    elanInstanceCache.get(elanInt.getElanInstanceName()).orNull())) {
                 return true;
             } else {
                 LOG.debug("Non-VXLAN elanInstance: " + elanInt.getElanInstanceName());