Bug 7988 - Cluster reboot fix 37/53337/10
authorAkash Kumar Sahu <a.k.sahu@ericsson.com>
Wed, 15 Mar 2017 11:18:38 +0000 (16:48 +0530)
committerSam Hague <shague@redhat.com>
Sat, 25 Mar 2017 20:47:11 +0000 (20:47 +0000)
Elan Instance , Physical Switch , L2gateWay and L2gatewayConnection all
have dependency among each other.But if upon cluster reboot the order is
not proper (L2gw cache is updated at the last , then flows are not being programmed as expected.
To take care of the situation
Introduced new ElanInstance Listner (Was already present but not
registered with xml to be instantiated )
Handle scenario if L2gW shows up last after cluster reboot .
Handle scenario if l2gwConnection is updated but Elan instance is not
available.

Change-Id: I6194d7cb22d6659803b17078d57c8ad694c0c201
Signed-off-by: Akash Kumar Sahu <a.k.sahu@ericsson.com>
vpnservice/elanmanager/elanmanager-api/src/main/java/org/opendaylight/netvirt/elanmanager/api/IL2gwService.java [new file with mode: 0644]
vpnservice/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/netvirt/elan/l2gw/listeners/ElanInstanceListener.java
vpnservice/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/netvirt/elan/l2gw/listeners/HwvtepLogicalSwitchListener.java
vpnservice/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/netvirt/elan/l2gw/listeners/HwvtepPhysicalSwitchListener.java
vpnservice/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/netvirt/elan/l2gw/utils/L2GatewayConnectionUtils.java
vpnservice/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/netvirt/elan/l2gw/utils/L2gwServiceProvider.java [new file with mode: 0644]
vpnservice/elanmanager/elanmanager-impl/src/main/resources/org/opendaylight/blueprint/elanmanager.xml
vpnservice/neutronvpn/neutronvpn-api/src/main/java/org/opendaylight/netvirt/neutronvpn/api/l2gw/utils/L2GatewayCacheUtils.java
vpnservice/neutronvpn/neutronvpn-impl/src/main/java/org/opendaylight/netvirt/neutronvpn/l2gw/L2GatewayListener.java
vpnservice/neutronvpn/neutronvpn-impl/src/main/resources/org/opendaylight/blueprint/neutronvpn.xml

diff --git a/vpnservice/elanmanager/elanmanager-api/src/main/java/org/opendaylight/netvirt/elanmanager/api/IL2gwService.java b/vpnservice/elanmanager/elanmanager-api/src/main/java/org/opendaylight/netvirt/elanmanager/api/IL2gwService.java
new file mode 100644 (file)
index 0000000..3933bef
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2017 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.elanmanager.api;
+
+import org.opendaylight.netvirt.neutronvpn.api.l2gw.L2GatewayDevice;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
+
+/**
+ * Created by eaksahu on 3/15/2017.
+ */
+public interface IL2gwService {
+    void provisionItmAndL2gwConnection(L2GatewayDevice l2GwDevice, String psName,
+                                              String hwvtepNodeId, IpAddress tunnelIpAddr) ;
+}
index 5a02321c2c78f7230753356139da53346a697b0f..c232e76d5d9862e9324bc3d2d895e57089b7d906 100644 (file)
@@ -7,7 +7,10 @@
  */
 package org.opendaylight.netvirt.elan.l2gw.listeners;
 
+import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 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;
@@ -30,12 +33,16 @@ public class ElanInstanceListener extends AsyncDataTreeChangeListenerBase<ElanIn
 
     private final DataBroker broker;
     private final L2GatewayConnectionUtils l2GatewayConnectionUtils;
+    private static final Map<String, List<Runnable>> WAITING_JOBS_LIST = new ConcurrentHashMap<>();
 
     public ElanInstanceListener(final DataBroker db, ElanUtils elanUtils) {
         super(ElanInstance.class, ElanInstanceListener.class);
         broker = db;
         this.l2GatewayConnectionUtils = elanUtils.getL2GatewayConnectionUtils();
-        registerListener(LogicalDatastoreType.CONFIGURATION, db);
+    }
+
+    public void init() {
+        registerListener(LogicalDatastoreType.CONFIGURATION, broker);
     }
 
     @Override
@@ -61,7 +68,10 @@ public class ElanInstanceListener extends AsyncDataTreeChangeListenerBase<ElanIn
 
     @Override
     protected void add(InstanceIdentifier<ElanInstance> identifier, ElanInstance add) {
-
+        List<Runnable> runnables = WAITING_JOBS_LIST.get(add.getElanInstanceName());
+        if (runnables != null) {
+            runnables.forEach(Runnable::run);
+        }
     }
 
     @Override
@@ -74,4 +84,9 @@ public class ElanInstanceListener extends AsyncDataTreeChangeListenerBase<ElanIn
         return InstanceIdentifier.create(ElanInstances.class).child(ElanInstance.class);
     }
 
+    public static  void runJobAfterElanIsAvailable(String elanName, Runnable runnable) {
+        WAITING_JOBS_LIST.computeIfAbsent(elanName, (name) -> new ArrayList<>());
+        WAITING_JOBS_LIST.get(elanName).add(runnable);
+    }
+
 }
index 2c1777b182d555b8ead5931f8302fee245ea81bb..1bd47b0aa672c511549eca2dcb273c190ea0b82d 100644 (file)
@@ -151,7 +151,7 @@ public class HwvtepLogicalSwitchListener extends
         LOG.debug("Received Add DataChange Notification for identifier: {}, LogicalSwitches: {}", identifier,
                 logicalSwitchNew);
         try {
-            L2GatewayDevice elanDevice = L2GatewayConnectionUtils.addL2DeviceToElanL2GwCache(
+            L2GatewayDevice elanDevice = L2GatewayConnectionUtils.addL2DeviceToElanL2GwCache(broker,
                     logicalSwitchNew.getHwvtepNodeName().getValue(), l2GatewayDevice, l2GwConnId,physicalDevice);
 
             LogicalSwitchAddedJob logicalSwitchAddedWorker = new LogicalSwitchAddedJob(broker, elanL2GatewayUtils,
index 41767bbb3de5e9b652a542c3e928dea4cf2cf3c0..a7738495f2f0cdf39caa632d58c7ed90a32368a6 100644 (file)
@@ -8,13 +8,19 @@
 
 package org.opendaylight.netvirt.elan.l2gw.listeners;
 
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL;
+
 import com.google.common.base.Optional;
-import java.util.Collections;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
 import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipService;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
@@ -24,17 +30,16 @@ import org.opendaylight.genius.mdsalutil.MDSALUtil;
 import org.opendaylight.genius.utils.hwvtep.HwvtepHACache;
 import org.opendaylight.genius.utils.hwvtep.HwvtepSouthboundConstants;
 import org.opendaylight.genius.utils.hwvtep.HwvtepSouthboundUtils;
+import org.opendaylight.netvirt.elan.l2gw.ha.HwvtepHAUtil;
 import org.opendaylight.netvirt.elan.l2gw.ha.listeners.HAOpClusteredListener;
-import org.opendaylight.netvirt.elan.l2gw.utils.ElanL2GatewayUtils;
 import org.opendaylight.netvirt.elan.l2gw.utils.L2GatewayConnectionUtils;
-import org.opendaylight.netvirt.elan.utils.ElanClusterUtils;
+import org.opendaylight.netvirt.elan.l2gw.utils.L2gwServiceProvider;
 import org.opendaylight.netvirt.elan.utils.ElanUtils;
 import org.opendaylight.netvirt.elanmanager.utils.ElanL2GwCacheUtils;
 import org.opendaylight.netvirt.neutronvpn.api.l2gw.L2GatewayDevice;
 import org.opendaylight.netvirt.neutronvpn.api.l2gw.utils.L2GatewayCacheUtils;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateway.connections.attributes.l2gatewayconnections.L2gatewayConnection;
 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.HwvtepGlobalAugmentationBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepGlobalRef;
@@ -46,6 +51,7 @@ import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.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.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -73,6 +79,8 @@ public class HwvtepPhysicalSwitchListener
 
     private final HwvtepHACache hwvtepHACache = HwvtepHACache.getInstance();
 
+    protected final L2gwServiceProvider l2gwServiceProvider;
+
     /**
      * Instantiates a new hwvtep physical switch listener.
      *
@@ -84,12 +92,13 @@ public class HwvtepPhysicalSwitchListener
      */
     public HwvtepPhysicalSwitchListener(final DataBroker dataBroker, ItmRpcService itmRpcService,
                                         EntityOwnershipService entityOwnershipService,
-                                        ElanUtils elanUtils) {
+                                        ElanUtils elanUtils, L2gwServiceProvider l2gwServiceProvider) {
         super(PhysicalSwitchAugmentation.class, HwvtepPhysicalSwitchListener.class);
         this.dataBroker = dataBroker;
         this.itmRpcService = itmRpcService;
         this.entityOwnershipService = entityOwnershipService;
         this.l2GatewayConnectionUtils = elanUtils.getL2GatewayConnectionUtils();
+        this.l2gwServiceProvider = l2gwServiceProvider;
     }
 
     public void init() {
@@ -171,13 +180,20 @@ public class HwvtepPhysicalSwitchListener
         NodeId nodeId = getNodeId(identifier);
         LOG.trace("Received PhysicalSwitch Update Event for node {}: PhysicalSwitch Before: {}, "
                 + "PhysicalSwitch After: {}", nodeId.getValue(), phySwitchBefore, phySwitchAfter);
-        String psName = phySwitchBefore.getHwvtepNodeName().getValue();
+        String psName = phySwitchAfter.getHwvtepNodeName() != null
+                ? phySwitchAfter.getHwvtepNodeName().getValue() : null;
+        if (psName == null) {
+            LOG.error("Could not find the physical switch name for node {}", nodeId.getValue());
+            return;
+        }
+        L2GatewayDevice existingDevice = L2GatewayCacheUtils.getL2DeviceFromCache(psName);
         LOG.info("Received physical switch {} update event for node {}", psName, nodeId.getValue());
-
-        if (isTunnelIpNewlyConfigured(phySwitchBefore, phySwitchAfter)) {
-            final L2GatewayDevice l2GwDevice =
-                    updateL2GatewayCache(psName, phySwitchAfter.getManagedBy(), phySwitchAfter.getTunnelIps());
-            handleAdd(l2GwDevice);
+        InstanceIdentifier<Node> globalNodeIid = getManagedByNodeIid(identifier);
+        if (isTunnelIpNewlyConfigured(phySwitchBefore, phySwitchAfter)
+                || existingDevice == null
+                || existingDevice.getHwvtepNodeId() == null
+                || !Objects.equals(existingDevice.getHwvtepNodeId(), globalNodeIid)) {
+            added(identifier, phySwitchAfter);
         } else {
             LOG.debug("Other updates in physical switch {} for node {}", psName, nodeId.getValue());
             // TODO: handle tunnel ip change
@@ -186,32 +202,55 @@ public class HwvtepPhysicalSwitchListener
 
     @Override
     protected void added(InstanceIdentifier<PhysicalSwitchAugmentation> identifier,
-            final PhysicalSwitchAugmentation phySwitchAdded) {
-        if (phySwitchAdded.getManagedBy() == null) {
-            LOG.info("managed by field is missing ");
+                         final PhysicalSwitchAugmentation phySwitchAdded) {
+        LOG.trace("L2gw node added {}", (phySwitchAdded.getHwvtepNodeName() != null
+                ? phySwitchAdded.getHwvtepNodeName() : "Node name doesn't exist"));
+        String globalNodeId = getManagedByNodeId(identifier);
+        final InstanceIdentifier<Node> globalNodeIid = getManagedByNodeIid(identifier);
+        final InstanceIdentifier<Node> wildCard = globalNodeIid.firstIdentifierOf(Topology.class).child(Node.class);
+        NodeId nodeId = getNodeId(identifier);
+        if (phySwitchAdded.getHwvtepNodeName() == null || HwvtepHAUtil.isEmpty(phySwitchAdded.getTunnelIps())) {
+            if (phySwitchAdded.getHwvtepNodeName() == null) {
+                LOG.error("Could not find the physical switch name for node {}", nodeId.getValue());
+            } else {
+                LOG.error("Could not find the /tunnel ips for node {}", nodeId.getValue());
+            }
             return;
         }
-        InstanceIdentifier<Node> globalNodeId = (InstanceIdentifier<Node>)phySwitchAdded.getManagedBy().getValue();
-        NodeId nodeId = getNodeId(identifier);
         final String psName = phySwitchAdded.getHwvtepNodeName().getValue();
-        LOG.info("Received physical switch {} added event received for node {}", psName, nodeId.getValue());
+        LOG.trace("Received physical switch {} added event received for node {}", psName, nodeId.getValue());
+        ReadOnlyTransaction tx = dataBroker.newReadOnlyTransaction();
+        ListenableFuture<Optional<Node>> ft = tx.read(OPERATIONAL, globalNodeIid);
+        Futures.addCallback(ft, new FutureCallback<Optional<Node>>() {
+            @Override
+            public void onSuccess(Optional<Node> globalNodeOptional) {
+                if (globalNodeOptional.isPresent()) {
+                    LOG.trace("Running job for node {} ", globalNodeIid);
+                    HAOpClusteredListener.addToCacheIfHAChildNode(globalNodeIid, (Node) globalNodeOptional.get());
+                    if (hwvtepHACache.isHAEnabledDevice(globalNodeIid)) {
+                        LOG.trace("Ha enabled device {}", globalNodeIid);
+                    }
+                    LOG.trace("Updating cache for node {}", globalNodeIid);
+                    L2GatewayDevice l2GwDevice = L2GatewayCacheUtils.getL2DeviceFromCache(psName);
+                    if (!hwvtepHACache.isHAParentNode(globalNodeIid)
+                            && l2GwDevice != null && l2GwDevice.getHwvtepNodeId() != null
+                            && !Objects.equals(l2GwDevice.getHwvtepNodeId(), globalNodeId)) {
+                        LOG.trace("Device {} {} is already Connected by ",
+                                psName, globalNodeId, l2GwDevice.getHwvtepNodeId());
+                    }
+                    l2GwDevice = L2GatewayCacheUtils.updateCacheUponSwitchConnect(
+                            psName, globalNodeId, phySwitchAdded.getTunnelIps());
+                    handleAdd(l2GwDevice);
+                } else {
+                    LOG.error("Global node doesn't exist for nodeiid {}", globalNodeIid);
+                }
+            }
 
-        try {
-            if (updateHACacheIfHANode(dataBroker, globalNodeId)) {
-                updateL2GatewayCache(psName, new HwvtepGlobalRef(hwvtepHACache.getParent(globalNodeId)),
-                        phySwitchAdded.getTunnelIps());
-                return;
+            @Override
+            public void onFailure(Throwable throwable) {
+                LOG.error("Failed to handle physical switch add {}", identifier.firstKeyOf(Node.class).getNodeId());
             }
-        } catch (ExecutionException e) {
-            LOG.error("Failed to read operational node {}", globalNodeId);
-            //TODO add retry mechanism
-        } catch (InterruptedException e) {
-            LOG.error("Failed to read operational node {}", globalNodeId);
-            //TODO add retry mechanism
-        }
-        L2GatewayDevice l2GwDevice =
-                updateL2GatewayCache(psName, phySwitchAdded.getManagedBy(), phySwitchAdded.getTunnelIps());
-        handleAdd(l2GwDevice);
+        });
     }
 
     boolean updateHACacheIfHANode(DataBroker broker, InstanceIdentifier<Node> globalNodeId)
@@ -237,33 +276,9 @@ public class HwvtepPhysicalSwitchListener
                 if (L2GatewayConnectionUtils.isGatewayAssociatedToL2Device(l2GwDevice)) {
                     LOG.debug("L2Gateway {} associated for {} physical switch; creating ITM tunnels for {}",
                             l2GwDevice.getL2GatewayIds(), psName, tunnelIpAddr);
-
-                    // It's a pre-provision scenario
-                    // Initiate ITM tunnel creation
-                    ElanClusterUtils.runOnlyInLeaderNode(entityOwnershipService,
-                            "handling Physical Switch add create itm tunnels ",
-                        () -> {
-                            ElanL2GatewayUtils.createItmTunnels(itmRpcService,
-                                    hwvtepNodeId, psName, tunnelIpAddr);
-                            return Collections.emptyList();
-                        });
-
-                    // Initiate Logical switch creation for associated L2
-                    // Gateway Connections
-                    List<L2gatewayConnection> l2GwConns = L2GatewayConnectionUtils.getAssociatedL2GwConnections(
-                            dataBroker, l2GwDevice.getL2GatewayIds());
-                    if (l2GwConns != null) {
-                        LOG.debug("L2GatewayConnections associated for {} physical switch", psName);
-
-                        for (L2gatewayConnection l2GwConn : l2GwConns) {
-                            LOG.trace("L2GatewayConnection {} changes executed on physical switch {}",
-                                    l2GwConn.getL2gatewayId(), psName);
-
-                            l2GatewayConnectionUtils.addL2GatewayConnection(l2GwConn, psName);
-                        }
-                    }
-                    // TODO handle deleted l2gw connections while the device is
-                    // offline
+                    l2gwServiceProvider.provisionItmAndL2gwConnection(l2GwDevice, psName, hwvtepNodeId, tunnelIpAddr);
+                } else {
+                    LOG.info("l2gw.provision.skip {}", hwvtepNodeId, psName);
                 }
             }
         }
@@ -295,18 +310,6 @@ public class HwvtepPhysicalSwitchListener
         return l2GwDevice;
     }
 
-    /**
-     * Gets the managed by node id.
-     *
-     * @param globalRef
-     *            the global ref
-     * @return the managed by node id
-     */
-    private String getManagedByNodeId(HwvtepGlobalRef globalRef) {
-        InstanceIdentifier<?> instId = globalRef.getValue();
-        return instId.firstKeyOf(Node.class).getNodeId().getValue();
-    }
-
     /**
      * Gets the node id.
      *
@@ -333,4 +336,33 @@ public class HwvtepPhysicalSwitchListener
                 && phySwitchAfter.getTunnelIps() != null && !phySwitchAfter.getTunnelIps().isEmpty();
     }
 
+    /**
+     * Gets the managed by node id.
+     *
+     * @param globalRef
+     *            the global ref
+     * @return the managed by node id
+     */
+    private String getManagedByNodeId(HwvtepGlobalRef globalRef) {
+        InstanceIdentifier<?> instId = globalRef.getValue();
+        return instId.firstKeyOf(Node.class).getNodeId().getValue();
+    }
+
+    private String getManagedByNodeId(InstanceIdentifier<PhysicalSwitchAugmentation> identifier) {
+        String psNodeId = identifier.firstKeyOf(Node.class).getNodeId().getValue();
+        if (psNodeId.contains(HwvtepHAUtil.PHYSICALSWITCH)) {
+            return psNodeId.substring(0, psNodeId.indexOf(HwvtepHAUtil.PHYSICALSWITCH));
+        }
+        return psNodeId;
+    }
+
+    private InstanceIdentifier<Node> getManagedByNodeIid(InstanceIdentifier<PhysicalSwitchAugmentation> identifier) {
+        String psNodeId = identifier.firstKeyOf(Node.class).getNodeId().getValue();
+        if (psNodeId.contains(HwvtepHAUtil.PHYSICALSWITCH)) {
+            psNodeId = psNodeId.substring(0, psNodeId.indexOf(HwvtepHAUtil.PHYSICALSWITCH));
+            return identifier.firstIdentifierOf(Topology.class).child(Node.class, new NodeKey(new NodeId(psNodeId)));
+        }
+        return null;
+    }
+
 }
index e44dbabaf32c063b33866d4c160554ce2e80bbde..5f3948a07a390475426d61ac1316bbe72c04575b 100644 (file)
@@ -8,17 +8,24 @@
 
 package org.opendaylight.netvirt.elan.l2gw.utils;
 
+import com.google.common.base.Optional;
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.SettableFuture;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipService;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.genius.datastoreutils.DataStoreJobCoordinator;
 import org.opendaylight.genius.mdsalutil.MDSALUtil;
+import org.opendaylight.genius.utils.hwvtep.HwvtepSouthboundUtils;
 import org.opendaylight.genius.utils.hwvtep.HwvtepUtils;
 import org.opendaylight.netvirt.elan.internal.ElanInstanceManager;
 import org.opendaylight.netvirt.elan.l2gw.jobs.AssociateHwvtepToElanJob;
 import org.opendaylight.netvirt.elan.l2gw.jobs.DisAssociateHwvtepFromElanJob;
+import org.opendaylight.netvirt.elan.l2gw.listeners.ElanInstanceListener;
 import org.opendaylight.netvirt.elan.l2gw.listeners.HwvtepLogicalSwitchListener;
 import org.opendaylight.netvirt.elan.utils.ElanClusterUtils;
 import org.opendaylight.netvirt.elan.utils.ElanUtils;
@@ -34,8 +41,11 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev15071
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateways.attributes.l2gateways.L2gateway;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateways.attributes.l2gateways.L2gatewayKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
+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.LocalUcastMacs;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.LogicalSwitches;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+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;
@@ -140,8 +150,18 @@ public class L2GatewayConnectionUtils {
 
         Uuid networkUuid = input.getNetworkId();
         ElanInstance elanInstance = elanInstanceManager.getElanInstanceByName(networkUuid.getValue());
-        if (elanInstance == null || (!ElanUtils.isVxlan(elanInstance) && !ElanUtils.isVxlanSegment(elanInstance))) {
-            LOG.error("Neutron network with id {} is not present", networkUuid.getValue());
+        //Taking cluster reboot scenario , if Elan instance is not available when l2GatewayConnection add events
+        //comes we need to wait for elaninstance to resolve. Hence updating the map with the runnable .
+        //When elanInstance add comes , it look in to the map and run the associated runnable associated with it.
+        if (elanInstance == null) {
+            LOG.info("Waiting for elan {}", networkUuid.getValue());
+            ElanInstanceListener.runJobAfterElanIsAvailable(networkUuid.getValue(), () -> {
+                addL2GatewayConnection(input, l2GwDeviceName);
+            });
+            return;
+        }
+        if (!ElanUtils.isVxlan(elanInstance) && !ElanUtils.isVxlanSegment(elanInstance)) {
+            LOG.error("Neutron network with id {} is not VxlanNetwork", networkUuid.getValue());
         } else {
             Uuid l2GatewayId = input.getL2gatewayId();
             L2gateway l2Gateway = getNeutronL2gateway(broker, l2GatewayId);
@@ -172,6 +192,17 @@ public class L2GatewayConnectionUtils {
                         .get(input.getKey().getUuid()));
             }
         }
+        if (l2gwDevicesToBeDeleted.isEmpty()) {
+            //delete logical switch
+            Uuid l2GatewayId = input.getL2gatewayId();
+            L2gateway l2Gateway = L2GatewayConnectionUtils.getNeutronL2gateway(broker, l2GatewayId);
+            if (l2Gateway == null) {
+                LOG.error("Failed to find the l2gateway for the connection {}", input.getUuid());
+                return;
+            } else {
+                l2gwDevicesToBeDeleted.addAll(l2Gateway.getDevices());
+            }
+        }
         for (Devices l2Device : l2gwDevicesToBeDeleted) {
             String l2DeviceName = l2Device.getDeviceName();
             L2GatewayDevice l2GatewayDevice = L2GatewayCacheUtils.getL2DeviceFromCache(l2DeviceName);
@@ -187,7 +218,11 @@ public class L2GatewayConnectionUtils {
                 Uuid l2GwConnId = input.getKey().getUuid();
                 LOG.debug("Elan L2Gw Conn cache with id {} is being referred by other L2Gw Conns; so only "
                         + "L2 Gw Conn {} reference is removed", hwvtepNodeId, l2GwConnId);
-                elanL2GwDevice.removeL2GatewayId(l2GwConnId);
+                if (elanL2GwDevice != null) {
+                    elanL2GwDevice.removeL2GatewayId(l2GwConnId);
+                } else {
+                    isLastL2GwConnDeleted = true;
+                }
             }
 
             DisAssociateHwvtepFromElanJob disAssociateHwvtepToElanJob =
@@ -238,7 +273,7 @@ public class L2GatewayConnectionUtils {
                     hwVTEPLogicalSwitchListener.registerListener(LogicalDatastoreType.OPERATIONAL, broker);
                     createLogicalSwitch = true;
                 } else {
-                    addL2DeviceToElanL2GwCache(elanName, l2GatewayDevice, l2GwConnId, l2Device);
+                    addL2DeviceToElanL2GwCache(broker ,elanName, l2GatewayDevice, l2GwConnId, l2Device);
                     createLogicalSwitch = false;
                 }
                 AssociateHwvtepToElanJob associateHwvtepToElanJob = new AssociateHwvtepToElanJob(broker,
@@ -255,7 +290,8 @@ public class L2GatewayConnectionUtils {
         }
     }
 
-    public static L2GatewayDevice addL2DeviceToElanL2GwCache(String elanName, L2GatewayDevice l2GatewayDevice,
+    public static L2GatewayDevice addL2DeviceToElanL2GwCache(final DataBroker broker, String elanName,
+                                                             L2GatewayDevice l2GatewayDevice,
             Uuid l2GwConnId, Devices l2Device) {
         String l2gwDeviceNodeId = l2GatewayDevice.getHwvtepNodeId();
         L2GatewayDevice elanL2GwDevice = ElanL2GwCacheUtils.getL2GatewayDeviceFromCache(elanName, l2gwDeviceNodeId);
@@ -273,6 +309,7 @@ public class L2GatewayConnectionUtils {
         elanL2GwDevice.addL2GatewayId(l2GwConnId);
         elanL2GwDevice.getL2gwConnectionIdToDevices().computeIfAbsent(l2GwConnId, key -> new ArrayList<>()).add(
                 l2Device);
+        readAndCopyLocalUcastMacsToCache(broker, elanName, l2GatewayDevice);
 
         LOG.trace("Elan L2GwConn cache updated with below details: {}", elanL2GwDevice);
         return elanL2GwDevice;
@@ -285,4 +322,34 @@ public class L2GatewayConnectionUtils {
     protected static boolean isLastL2GwConnBeingDeleted(L2GatewayDevice l2GwDevice) {
         return l2GwDevice.getL2GatewayIds().size() == 1;
     }
+
+    private static void readAndCopyLocalUcastMacsToCache(final DataBroker broker,
+                                                         final String elanName,
+                                                         final L2GatewayDevice l2GatewayDevice) {
+
+        final InstanceIdentifier<Node> nodeIid = HwvtepSouthboundUtils.createInstanceIdentifier(
+                new NodeId(l2GatewayDevice.getHwvtepNodeId()));
+        DataStoreJobCoordinator.getInstance().enqueueJob(elanName + ":" + l2GatewayDevice.getDeviceName(), () -> {
+            final SettableFuture settableFuture = SettableFuture.create();
+            Futures.addCallback(broker.newReadOnlyTransaction().read(LogicalDatastoreType.OPERATIONAL,
+                    nodeIid),
+                    new SettableFutureCallback<Optional<Node>>(settableFuture) {
+                        @Override
+                        public void onSuccess(Optional<Node> resultNode) {
+                            Optional<Node> nodeOptional = (Optional<Node>) resultNode;
+                            if (nodeOptional.isPresent()) {
+                                Node node = nodeOptional.get();
+                                if (node.getAugmentation(HwvtepGlobalAugmentation.class) != null) {
+                                    List<LocalUcastMacs> localUcastMacs =
+                                            node.getAugmentation(HwvtepGlobalAugmentation.class).getLocalUcastMacs();
+                                    if (localUcastMacs != null) {
+                                        localUcastMacs.forEach((mac) -> l2GatewayDevice.addUcastLocalMac(mac));
+                                    }
+                                }
+                            }
+                        }
+                    });
+            return Lists.newArrayList(settableFuture);
+        } , 5);
+    }
 }
diff --git a/vpnservice/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/netvirt/elan/l2gw/utils/L2gwServiceProvider.java b/vpnservice/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/netvirt/elan/l2gw/utils/L2gwServiceProvider.java
new file mode 100644 (file)
index 0000000..d21bf1a
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2017 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.l2gw.utils;
+
+import java.util.Collections;
+import java.util.List;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipService;
+import org.opendaylight.infrautils.inject.AbstractLifecycle;
+import org.opendaylight.netvirt.elan.utils.ElanClusterUtils;
+import org.opendaylight.netvirt.elan.utils.ElanUtils;
+import org.opendaylight.netvirt.elanmanager.api.IL2gwService;
+import org.opendaylight.netvirt.neutronvpn.api.l2gw.L2GatewayDevice;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateway.connections.attributes.l2gatewayconnections.L2gatewayConnection;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Created by eaksahu on 3/15/2017.
+ */
+public class L2gwServiceProvider extends AbstractLifecycle implements IL2gwService {
+
+    private static final Logger LOG = LoggerFactory.getLogger(L2gwServiceProvider.class);
+
+    private final DataBroker dataBroker;
+    private final ItmRpcService itmRpcService;
+    private final ElanUtils elanUtils;
+    private final EntityOwnershipService entityOwnershipService;
+
+    public L2gwServiceProvider(final DataBroker dataBroker, final EntityOwnershipService entityOwnershipService,
+                               ItmRpcService itmRpcService, ElanUtils elanUtils) {
+        this.dataBroker = dataBroker;
+        this.entityOwnershipService = entityOwnershipService;
+        this.itmRpcService = itmRpcService;
+        this.elanUtils = elanUtils;
+    }
+
+    @Override
+    public void provisionItmAndL2gwConnection(L2GatewayDevice l2GwDevice, String psName,
+                                              String hwvtepNodeId, IpAddress tunnelIpAddr) {
+        ElanClusterUtils.runOnlyInLeaderNode(entityOwnershipService,
+                "Handling Physical Switch add create itm tunnels ", () -> {
+                ElanL2GatewayUtils.createItmTunnels(itmRpcService, hwvtepNodeId, psName, tunnelIpAddr);
+                return Collections.emptyList();
+            });
+
+        List<L2gatewayConnection> l2GwConns = L2GatewayConnectionUtils.getAssociatedL2GwConnections(
+                dataBroker, l2GwDevice.getL2GatewayIds());
+        if (l2GwConns != null) {
+            LOG.debug("L2GatewayConnections associated for {} physical switch", psName);
+            for (L2gatewayConnection l2GwConn : l2GwConns) {
+                LOG.trace("L2GatewayConnection {} changes executed on physical switch {}",
+                        l2GwConn.getL2gatewayId(), psName);
+                elanUtils.getL2GatewayConnectionUtils().addL2GatewayConnection(l2GwConn, psName);
+            }
+        }
+    }
+
+    @Override
+    protected void start() throws Exception {
+        LOG.info("Starting L2gwServiceProvider");
+    }
+
+    @Override
+    protected void stop() throws Exception {
+
+    }
+}
index 7f12dde06fb533a258c3351019cb267a93d06419..bd7f41ec9c9475e7b2b16d0e80bc7b328bdce183 100644 (file)
     <argument ref="itmRpcService" />
     <argument ref="entityOwnershipService" />
     <argument ref="elanUtils" />
+    <argument ref="l2gwService" />
   </bean>
 
   <bean id="hwvtepTerminationPointListener"
     <argument ref="TransportZoneNotificationUtil" />
   </bean>
 
+  <bean id="l2gwService"
+        class="org.opendaylight.netvirt.elan.l2gw.utils.L2gwServiceProvider"
+        init-method="init">
+    <argument ref="dataBroker" />
+    <argument ref="entityOwnershipService" />
+    <argument ref="itmRpcService" />
+    <argument ref="elanUtils" />
+  </bean>
+  <service ref="l2gwService" odl:type="default"
+           interface="org.opendaylight.netvirt.elanmanager.api.IL2gwService" />
+
+  <bean id="elanInstanceListener"
+        class="org.opendaylight.netvirt.elan.l2gw.listeners.ElanInstanceListener"
+        init-method="init" destroy-method="close">
+    <argument ref="dataBroker" />
+    <argument ref="elanUtils" />
+  </bean>
+
 </blueprint>
index 95630b074d4c4a30596c557e916828b12fbfc1ea..b5af9ecb23d58effbf6421136fb1e93fdda6758e 100644 (file)
@@ -7,9 +7,13 @@
  */
 package org.opendaylight.netvirt.neutronvpn.api.l2gw.utils;
 
+import java.util.List;
 import java.util.concurrent.ConcurrentMap;
 import org.opendaylight.genius.utils.cache.CacheUtil;
 import org.opendaylight.netvirt.neutronvpn.api.l2gw.L2GatewayDevice;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.physical._switch.attributes.TunnelIps;
 
 public class L2GatewayCacheUtils {
     public static final String L2GATEWAY_CACHE_NAME = "L2GW";
@@ -47,4 +51,36 @@ public class L2GatewayCacheUtils {
                 .getCache(L2GatewayCacheUtils.L2GATEWAY_CACHE_NAME);
     }
 
+    public static synchronized  L2GatewayDevice updateCacheUponL2GatewayAdd(final String psName, final Uuid l2gwUuid) {
+        L2GatewayDevice l2GwDevice = L2GatewayCacheUtils.getL2DeviceFromCache(psName);
+        if (l2GwDevice == null) {
+            l2GwDevice = new L2GatewayDevice();
+            l2GwDevice.setDeviceName(psName);
+            l2GwDevice.addL2GatewayId(l2gwUuid);
+        } else {
+            l2GwDevice.addL2GatewayId(l2gwUuid);
+        }
+        addL2DeviceToCache(psName, l2GwDevice);
+        return l2GwDevice;
+    }
+
+    public static synchronized  L2GatewayDevice updateCacheUponSwitchConnect(final String psName, final String
+            hwvtepNodeId, final List<TunnelIps> tunnelIps) {
+        L2GatewayDevice l2GwDevice = L2GatewayCacheUtils.getL2DeviceFromCache(psName);
+        if (l2GwDevice == null) {
+            l2GwDevice = new L2GatewayDevice();
+            l2GwDevice.setDeviceName(psName);
+        }
+        l2GwDevice.setConnected(true);
+        l2GwDevice.setHwvtepNodeId(hwvtepNodeId);
+
+        if (tunnelIps != null && !tunnelIps.isEmpty()) {
+            for (TunnelIps tunnelIp : tunnelIps) {
+                IpAddress tunnelIpAddr = tunnelIp.getTunnelIpsKey();
+                l2GwDevice.addTunnelIp(tunnelIpAddr);
+            }
+        }
+        addL2DeviceToCache(psName, l2GwDevice);
+        return l2GwDevice;
+    }
 }
index 6aed7254bd51b76c44a4d37d87789c38d306d2f9..651262da3d15b123fa9a2ed99fce51ffb89dda46 100644 (file)
@@ -20,6 +20,7 @@ import org.opendaylight.genius.mdsalutil.MDSALUtil;
 import org.opendaylight.genius.utils.clustering.ClusteringUtils;
 import org.opendaylight.genius.utils.hwvtep.HwvtepSouthboundConstants;
 import org.opendaylight.genius.utils.hwvtep.HwvtepSouthboundUtils;
+import org.opendaylight.netvirt.elanmanager.api.IL2gwService;
 import org.opendaylight.netvirt.neutronvpn.api.l2gw.L2GatewayDevice;
 import org.opendaylight.netvirt.neutronvpn.api.l2gw.utils.L2GatewayCacheUtils;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
@@ -39,13 +40,15 @@ public class L2GatewayListener extends AsyncClusteredDataTreeChangeListenerBase<
     private static final Logger LOG = LoggerFactory.getLogger(L2GatewayListener.class);
     private final DataBroker dataBroker;
     private final ItmRpcService itmRpcService;
+    private final IL2gwService l2gwService;
     private final EntityOwnershipService entityOwnershipService;
 
     public L2GatewayListener(final DataBroker dataBroker, final EntityOwnershipService entityOwnershipService,
-                             ItmRpcService itmRpcService) {
+                             ItmRpcService itmRpcService, IL2gwService l2gwService) {
         this.dataBroker = dataBroker;
         this.entityOwnershipService = entityOwnershipService;
         this.itmRpcService = itmRpcService;
+        this.l2gwService = l2gwService;
     }
 
     public void start() {
@@ -86,50 +89,16 @@ public class L2GatewayListener extends AsyncClusteredDataTreeChangeListenerBase<
         LOG.trace("Updating L2gateway : key: {}, original value={}, update value={}", identifier, original, update);
     }
 
-    private void addL2Device(Devices l2Device, L2gateway input) {
-        final String l2DeviceName = l2Device.getDeviceName();
-        L2GatewayDevice l2GwDevice = L2GatewayCacheUtils.getL2DeviceFromCache(l2DeviceName);
-        if (l2GwDevice != null) {
-            if (!L2GatewayUtils.isGatewayAssociatedToL2Device(l2GwDevice)
-                    && l2GwDevice.isConnected()) {
-                // VTEP already discovered; create ITM tunnels
-                final String hwvtepId = l2GwDevice.getHwvtepNodeId();
-                InstanceIdentifier<Node> iid = HwvtepSouthboundUtils.createInstanceIdentifier(new NodeId(hwvtepId));
-                ListenableFuture<Boolean> checkEntityOwnerFuture = ClusteringUtils.checkNodeEntityOwner(
-                        entityOwnershipService, HwvtepSouthboundConstants.ELAN_ENTITY_TYPE,
-                        HwvtepSouthboundConstants.ELAN_ENTITY_NAME);
-                final Set<IpAddress> tunnelIps = l2GwDevice.getTunnelIps();
-                Futures.addCallback(checkEntityOwnerFuture, new FutureCallback<Boolean>() {
-                    @Override
-                    public void onSuccess(Boolean isOwner) {
-                        if (isOwner) {
-                            LOG.info("Creating ITM Tunnels for {} connected to cluster node owner", l2DeviceName);
-                            for (IpAddress tunnelIp : tunnelIps) {
-                                L2GatewayUtils.createItmTunnels(itmRpcService, hwvtepId, l2DeviceName, tunnelIp);
-                            }
-                        } else {
-                            LOG.info("ITM Tunnels are not created on the cluster node as this is not owner for {}",
-                                    l2DeviceName);
-                        }
-                    }
-
-                    @Override
-                    public void onFailure(Throwable error) {
-                        LOG.error("Failed to create ITM tunnels", error);
-                    }
-                });
-            } else {
-                LOG.trace("ITM tunnels are already created for device {}", l2DeviceName);
-            }
+    private synchronized void addL2Device(Devices l2Device, L2gateway input) {
+        String l2DeviceName = l2Device.getDeviceName();
+        L2GatewayDevice l2GwDevice = L2GatewayCacheUtils.updateCacheUponL2GatewayAdd(l2DeviceName, input.getUuid());
+        if (l2GwDevice.getHwvtepNodeId() == null) {
+            LOG.info("L2GW provisioning skipped for device {}",l2DeviceName);
         } else {
-            LOG.trace("{} is not connected; ITM tunnels will be created when device comes up", l2DeviceName);
-            // Pre-provision scenario. Create L2GatewayDevice without VTEP
-            // details for pushing configurations as soon as device discovered
-            l2GwDevice = new L2GatewayDevice();
-            l2GwDevice.setDeviceName(l2DeviceName);
-            L2GatewayCacheUtils.addL2DeviceToCache(l2DeviceName, l2GwDevice);
+            LOG.info("Provisioning l2gw for device {}",l2DeviceName);
+            l2gwService.provisionItmAndL2gwConnection(l2GwDevice, l2DeviceName, l2GwDevice.getHwvtepNodeId(),
+                    l2GwDevice.getTunnelIp());
         }
-        l2GwDevice.addL2GatewayId(input.getUuid());
     }
 
     private void removeL2Device(Devices l2Device, L2gateway input) {
index a99473731a32f197f23840e4adee8319e3922388..f52261f3a09e7d1980d37822cb404fd90051474a 100644 (file)
@@ -8,6 +8,8 @@
              odl:type="default" />
   <reference id="elanService"
              interface="org.opendaylight.netvirt.elanmanager.api.IElanService" />
+  <reference id="l2gwService"
+             interface="org.opendaylight.netvirt.elanmanager.api.IL2gwService" />
   <reference id="entityOwnershipService"
              interface="org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipService" />
   <reference id="notificationPublishService"
     <argument ref="dataBroker" />
     <argument ref="entityOwnershipService" />
     <argument ref="itmRpcService" />
+    <argument ref="l2gwService" />
   </bean>
 
   <bean id="l2GwTransportZoneListener"