Bug 7988 - Cluster reboot fix 59/54759/7
authorAkash Kumar Sahu <a.k.sahu@ericsson.com>
Wed, 15 Mar 2017 11:18:38 +0000 (16:48 +0530)
committerSam Hague <shague@redhat.com>
Sun, 11 Jun 2017 15:23:15 +0000 (15:23 +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>
15 files changed:
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/internal/ElanDpnInterfaceClusteredListener.java
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/HwvtepLocalUcastMacListener.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/ElanL2GatewayUtils.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/java/org/opendaylight/netvirt/elan/utils/ElanUtils.java
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/L2GatewayDevice.java
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 8a715022a6fb1bdf054a0f79d3b3c10a14d27a87..3aadc48d1125aa94f4ba9a10b3cb36c00fb8f1d2 100644 (file)
@@ -84,6 +84,7 @@ public class ElanDpnInterfaceClusteredListener
         final String elanName = getElanName(identifier);
         LOG.debug("Received ElanDpnInterface removed for for elan {} with Dp Id ", elanName,
             dpnInterfaces.getDpId());
+        ElanUtils.removeDPNInterfaceFromElanInCache(getElanName(identifier), dpnInterfaces);
 
         if (ElanL2GwCacheUtils.getInvolvedL2GwDevices(elanName).isEmpty()) {
             LOG.debug("dpnInterface removed, no external l2 devices to update for elan {} with Dp Id:", elanName,
@@ -106,6 +107,7 @@ public class ElanDpnInterfaceClusteredListener
                           final DpnInterfaces dpnInterfaces) {
         LOG.debug("dpninterfaces update fired new size {}", dpnInterfaces.getInterfaces().size());
         if (dpnInterfaces.getInterfaces().size() == 0) {
+            ElanUtils.removeDPNInterfaceFromElanInCache(getElanName(identifier), dpnInterfaces);
             LOG.debug("dpninterfaces last dpn interface on this elan {} ", dpnInterfaces.getKey());
             // this is the last dpn interface on this elan
             handleUpdate(identifier, dpnInterfaces);
@@ -114,6 +116,7 @@ public class ElanDpnInterfaceClusteredListener
 
     @Override
     protected void add(InstanceIdentifier<DpnInterfaces> identifier, final DpnInterfaces dpnInterfaces) {
+        ElanUtils.addDPNInterfaceToElanInCache(getElanName(identifier), dpnInterfaces);
         if (dpnInterfaces.getInterfaces().size() == 1) {
             LOG.debug("dpninterfaces first dpn interface on this elan {} {} ", dpnInterfaces.getKey(),
                 dpnInterfaces.getInterfaces().get(0));
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 d45ae453b44a73e38a8fad4afe75dc6fc5955957..d3ef315355e5767fd13d159f001e96b0f860c0aa 100644 (file)
@@ -18,6 +18,7 @@ import org.opendaylight.netvirt.elanmanager.utils.ElanL2GwCacheUtils;
 import org.opendaylight.netvirt.neutronvpn.api.l2gw.L2GatewayDevice;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
 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.network.topology.topology.Node;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.slf4j.Logger;
@@ -54,17 +55,13 @@ public class HwvtepLocalUcastMacListener extends
     @Override
     protected void removed(InstanceIdentifier<LocalUcastMacs> identifier, LocalUcastMacs macRemoved) {
         String hwvtepNodeId = identifier.firstKeyOf(Node.class).getNodeId().getValue();
-        String macAddress = macRemoved.getMacEntryKey().getValue();
+        String macAddress = macRemoved.getMacEntryKey().getValue().toLowerCase();
 
         LOG.trace("LocalUcastMacs {} removed from {}", macAddress, hwvtepNodeId);
 
-        ElanInstance elan = elanL2GatewayUtils.getElanInstanceForUcastLocalMac(macRemoved);
-        if (elan == null) {
-            LOG.warn("Could not find ELAN for mac {} being deleted", macAddress);
-            return;
-        }
+        String elanName = getElanName(macRemoved);
+        ElanInstance elan = ElanUtils.getElanInstanceByName(broker, elanName);
 
-        String elanName = elan.getElanInstanceName();
         L2GatewayDevice elanL2GwDevice = ElanL2GwCacheUtils.getL2GatewayDeviceFromCache(elanName, hwvtepNodeId);
         if (elanL2GwDevice == null) {
             LOG.warn("Could not find L2GatewayDevice for ELAN: {}, nodeID:{} from cache", elanName, hwvtepNodeId);
@@ -78,6 +75,11 @@ public class HwvtepLocalUcastMacListener extends
                 Lists.newArrayList(macRemoved.getMacEntryKey()));
     }
 
+    protected String getElanName(LocalUcastMacs mac) {
+        return ((InstanceIdentifier<LogicalSwitches>) mac.getLogicalSwitchRef().getValue())
+                .firstKeyOf(LogicalSwitches.class).getHwvtepNodeName().getValue();
+    }
+
     @Override
     protected void updated(InstanceIdentifier<LocalUcastMacs> identifier, LocalUcastMacs original,
             LocalUcastMacs update) {
@@ -108,7 +110,7 @@ public class HwvtepLocalUcastMacListener extends
         // Cache MAC for furthur processing later
         elanL2GwDevice.addUcastLocalMac(macAdded);
 
-        elanL2GatewayUtils.installL2GwUcastMacInElan(elan, elanL2GwDevice, macAddress, null);
+        elanL2GatewayUtils.installL2GwUcastMacInElan(elan, elanL2GwDevice, macAddress.toLowerCase(), macAdded, null);
     }
 
     @Override
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 8e9e9b409a3ca9cfc73c3627608db484fb8efa59..a7738495f2f0cdf39caa632d58c7ed90a32368a6 100644 (file)
@@ -8,15 +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 com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
-import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
-import java.util.concurrent.Callable;
 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;
@@ -26,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;
@@ -48,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;
@@ -75,6 +79,8 @@ public class HwvtepPhysicalSwitchListener
 
     private final HwvtepHACache hwvtepHACache = HwvtepHACache.getInstance();
 
+    protected final L2gwServiceProvider l2gwServiceProvider;
+
     /**
      * Instantiates a new hwvtep physical switch listener.
      *
@@ -86,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() {
@@ -173,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
@@ -188,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)
@@ -239,36 +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 ",
-                            new Callable<List<ListenableFuture<Void>>>() {
-                                @Override
-                                public List<ListenableFuture<Void>> call() throws Exception {
-                                    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);
                 }
             }
         }
@@ -300,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.
      *
@@ -338,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 f933db2be1543ab6a91cce29d4e93a8b8a4a737c..4946ab80e86963bde59b425e042d78e4d29661aa 100644 (file)
@@ -374,21 +374,21 @@ public class ElanL2GatewayUtils {
     }
 
     public void installL2GwUcastMacInElan(final ElanInstance elan, final L2GatewayDevice extL2GwDevice,
-            final String macToBeAdded, String interfaceName) {
+            final String macToBeAdded, final LocalUcastMacs localUcastMacs, String interfaceName) {
         final String extDeviceNodeId = extL2GwDevice.getHwvtepNodeId();
         final String elanInstanceName = elan.getElanInstanceName();
+        final List<DpnInterfaces> elanDpns = getElanDpns(elanInstanceName);
 
         // Retrieve all participating DPNs in this Elan. Populate this MAC in
         // DMAC table.
         // Looping through all DPNs in order to add/remove mac flows in their
         // DMAC table
-        final List<DpnInterfaces> elanDpns = elanUtils.getInvolvedDpnsInElan(elanInstanceName);
         if (elanDpns != null && elanDpns.size() > 0) {
             String jobKey = elan.getElanInstanceName() + ":" + macToBeAdded;
             ElanClusterUtils.runOnlyInLeaderNode(entityOwnershipService, jobKey, "install l2gw macs in dmac table",
                 () -> {
-                    List<ListenableFuture<Void>> fts = Lists.newArrayList();
-                    if (doesLocalUcastMacExistsInCache(extL2GwDevice, macToBeAdded)) {
+                    List<ListenableFuture<Void>> fts = new ArrayList<>();
+                    if (doesLocalUcastMacExistsInCache(extL2GwDevice, localUcastMacs)) {
                         for (DpnInterfaces elanDpn : elanDpns) {
                             // TODO batch the below call
                             fts.addAll(elanUtils.installDmacFlowsToExternalRemoteMac(elanDpn.getDpId(),
@@ -409,8 +409,8 @@ public class ElanL2GatewayUtils {
         String jobKey = "hwvtep:" + elan.getElanInstanceName() + ":" + macToBeAdded;
         ElanClusterUtils.runOnlyInLeaderNode(entityOwnershipService, jobKey, "install remote ucast macs in l2gw device",
             () -> {
-                List<ListenableFuture<Void>> fts = Lists.newArrayList();
-                if (!doesLocalUcastMacExistsInCache(extL2GwDevice, macToBeAdded)) {
+                List<ListenableFuture<Void>> fts = new ArrayList<>();
+                if (!doesLocalUcastMacExistsInCache(extL2GwDevice, localUcastMacs)) {
                     LOG.trace(
                             "Skipping install of remote ucast macs {} in l2gw device as it is not found in cache",
                             macToBeAdded);
@@ -459,10 +459,8 @@ public class ElanL2GatewayUtils {
      *            the mac address to be verified
      * @return true, if successful
      */
-    private static boolean doesLocalUcastMacExistsInCache(L2GatewayDevice elanL2GwDevice, String macAddress) {
-        java.util.Optional<LocalUcastMacs> macExistsInCache = elanL2GwDevice.getUcastLocalMacs().stream()
-                .filter(mac -> mac.getMacEntryKey().getValue().equalsIgnoreCase(macAddress)).findFirst();
-        return macExistsInCache.isPresent();
+    private static boolean doesLocalUcastMacExistsInCache(L2GatewayDevice elanL2GwDevice, LocalUcastMacs macAddress) {
+        return elanL2GwDevice.containsUcastMac(macAddress);
     }
 
     /**
@@ -482,11 +480,12 @@ public class ElanL2GatewayUtils {
         }
         final String elanName = elan.getElanInstanceName();
 
+        final List<DpnInterfaces> elanDpns = getElanDpns(elanName);
+
         // Retrieve all participating DPNs in this Elan. Populate this MAC in
         // DMAC table. Looping through all DPNs in order to add/remove mac flows
         // in their DMAC table
         for (final MacAddress mac : macAddresses) {
-            final List<DpnInterfaces> elanDpns = elanUtils.getInvolvedDpnsInElan(elanName);
             if (elanDpns != null && !elanDpns.isEmpty()) {
                 String jobKey = elanName + ":" + mac.getValue();
                 ElanClusterUtils.runOnlyInLeaderNode(entityOwnershipService, jobKey, "delete l2gw macs from dmac table",
@@ -496,7 +495,7 @@ public class ElanL2GatewayUtils {
                             BigInteger dpnId = elanDpn.getDpId();
                             // never batch deletes
                             fts.addAll(elanUtils.deleteDmacFlowsToExternalMac(elan.getElanTag(), dpnId,
-                                    l2GwDevice.getHwvtepNodeId(), mac.getValue()));
+                                    l2GwDevice.getHwvtepNodeId(), mac.getValue().toLowerCase()));
                         }
                         return fts;
                     });
@@ -710,7 +709,7 @@ public class ElanL2GatewayUtils {
                                     .createHwvtepPhysicalLocatorAugmentation(
                                             String.valueOf(otherDevice.getTunnelIp().getValue()));
                             RemoteUcastMacs remoteUcastMac = HwvtepSouthboundUtils.createRemoteUcastMac(hwVtepNodeId,
-                                    localUcastMac.getMacEntryKey().getValue(), localUcastMac.getIpaddr(),
+                                    localUcastMac.getMacEntryKey().getValue().toLowerCase(), localUcastMac.getIpaddr(),
                                     logicalSwitchName, physLocatorAug);
                             lstRemoteUcastMacs.add(remoteUcastMac);
                         }
@@ -780,7 +779,7 @@ public class ElanL2GatewayUtils {
             // MAC
             IpAddress ipAddress = null;
             RemoteUcastMacs remoteUcastMac = HwvtepSouthboundUtils.createRemoteUcastMac(hwVtepNodeId,
-                    macEntry.getMacAddress().getValue(), ipAddress, logicalSwitchName, physLocatorAug);
+                    macEntry.getMacAddress().getValue().toLowerCase(), ipAddress, logicalSwitchName, physLocatorAug);
             lstRemoteUcastMacs.add(remoteUcastMac);
         }
         return lstRemoteUcastMacs;
@@ -1119,4 +1118,12 @@ public class ElanL2GatewayUtils {
             logicalSwitchDeletedTasks.remove(nodeIdLogicalSwitchNamePair);
         }
     }
+
+    public List<DpnInterfaces> getElanDpns(String elanName) {
+        Set<DpnInterfaces> dpnInterfaces = ElanUtils.getElanInvolvedDPNsFromCache(elanName);
+        if (dpnInterfaces == null) {
+            return elanUtils.getInvolvedDpnsInElan(elanName);
+        }
+        return new ArrayList(dpnInterfaces);
+    }
 }
index 55df42ba8a7820b7b5eeae85c21e4fb15a00b490..26332e72bbe6732ca1ef52d7bbae6273018d5faa 100644 (file)
@@ -10,17 +10,23 @@ 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.HashSet;
 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;
@@ -36,8 +42,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;
@@ -153,8 +162,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);
@@ -185,6 +204,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);
@@ -200,7 +230,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 =
@@ -251,7 +285,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,
@@ -268,7 +302,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);
@@ -284,10 +319,9 @@ public class L2GatewayConnectionUtils {
                     l2gwDeviceNodeId, l2GwConnId);
         }
         elanL2GwDevice.addL2GatewayId(l2GwConnId);
-        if (elanL2GwDevice.getL2gwConnectionIdToDevices().get(l2GwConnId) == null) {
-            elanL2GwDevice.getL2gwConnectionIdToDevices().put(l2GwConnId, new ArrayList<Devices>());
-        }
-        elanL2GwDevice.getL2gwConnectionIdToDevices().get(l2GwConnId).add(l2Device);
+        elanL2GwDevice.getL2gwConnectionIdToDevices().computeIfAbsent(l2GwConnId, key -> new HashSet<>()).add(
+                l2Device);
+        readAndCopyLocalUcastMacsToCache(broker, elanName, l2GatewayDevice);
 
         LOG.trace("Elan L2GwConn cache updated with below details: {}", elanL2GwDevice);
         return elanL2GwDevice;
@@ -300,4 +334,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 ac66c05f11838791584ee2090f5a2d04111d0619..2339b14917c4eb0f1b8b2d877b4be3ec169f6a71 100755 (executable)
@@ -8,6 +8,8 @@
 package org.opendaylight.netvirt.elan.utils;
 
 import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
 import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
@@ -15,7 +17,9 @@ import com.google.common.util.concurrent.ListenableFuture;
 
 import java.math.BigInteger;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -24,6 +28,8 @@ import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
 
 import org.apache.commons.lang3.StringUtils;
+import org.opendaylight.controller.liblldp.NetUtils;
+import org.opendaylight.controller.liblldp.PacketException;
 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.WriteTransaction;
@@ -45,8 +51,12 @@ import org.opendaylight.genius.mdsalutil.MDSALUtil.MdsalOp;
 import org.opendaylight.genius.mdsalutil.MatchFieldType;
 import org.opendaylight.genius.mdsalutil.MatchInfo;
 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
+import org.opendaylight.genius.mdsalutil.NWUtil;
 import org.opendaylight.genius.mdsalutil.NwConstants;
 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
+import org.opendaylight.genius.mdsalutil.packet.ARP;
+import org.opendaylight.genius.mdsalutil.packet.Ethernet;
+import org.opendaylight.genius.mdsalutil.packet.IPv4;
 import org.opendaylight.genius.utils.ServiceIndex;
 import org.opendaylight.netvirt.elan.ElanException;
 import org.opendaylight.netvirt.elan.internal.ElanInstanceManager;
@@ -54,6 +64,8 @@ import org.opendaylight.netvirt.elan.internal.ElanInterfaceManager;
 import org.opendaylight.netvirt.elan.l2gw.utils.ElanL2GatewayMulticastUtils;
 import org.opendaylight.netvirt.elan.l2gw.utils.ElanL2GatewayUtils;
 import org.opendaylight.netvirt.elan.l2gw.utils.L2GatewayConnectionUtils;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddressBuilder;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesState;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.AdminStatus;
@@ -163,6 +175,7 @@ public class ElanUtils {
 
     private static Map<String, ElanInstance> elanInstanceLocalCache = new ConcurrentHashMap<>();
     private static Map<String, ElanInterface> elanInterfaceLocalCache = new ConcurrentHashMap<>();
+    private static Map<String, Set<DpnInterfaces>> elanInstancToDpnsCache = new ConcurrentHashMap<>();
 
     private final DataBroker broker;
     private final IMdsalApiManager mdsalManager;
@@ -2242,4 +2255,82 @@ public class ElanUtils {
             futures.add(Futures.immediateFailedCheckedFuture((TransactionCommitFailedException) cause));
         }
     }
+
+    public static List<PhysAddress> getPhysAddress(List<String> macAddress) {
+        Preconditions.checkNotNull(macAddress, "macAddress cannot be null");
+        List<PhysAddress> physAddresses = new ArrayList<>();
+        for (String mac : macAddress) {
+            physAddresses.add(new PhysAddress(mac));
+        }
+        return physAddresses;
+    }
+
+    public Optional<IpAddress> getSourceIpV4Address(byte[] data) {
+        IPv4 ip = new IPv4();
+        try {
+            ip.deserialize(data, 0, data.length * NetUtils.NumBitsInAByte);
+        } catch (PacketException e) {
+            LOG.error("ip.deserialize throws exception  {}", e);
+            return Optional.absent();
+        }
+        return Optional.of(IpAddressBuilder.getDefaultInstance(Integer.toString(ip.getSourceAddress())));
+    }
+
+    public Optional<IpAddress> getSrcIpAddrFromArp(byte[] data) {
+        ARP arp = new ARP();
+        try {
+            arp.deserialize(data, 0, data.length * NetUtils.NumBitsInAByte);
+        } catch (PacketException e) {
+            LOG.error("ip.deserialize throws exception  {}", e);
+            return Optional.absent();
+        }
+        return Optional.of(IpAddressBuilder.getDefaultInstance(
+                NWUtil.toStringIpAddress(arp.getSenderProtocolAddress())));
+    }
+
+    public Optional<IpAddress> getSourceIpAddress(Ethernet ethernet, byte[] data) {
+        /*IPV6 is not yet present in genius, hence V6 case ignored*/
+        Optional<IpAddress> srcIpAddress = Optional.absent();
+        if (NwConstants.ETHTYPE_IPV4 == ethernet.getEtherType()) {
+            srcIpAddress = getSourceIpV4Address(data);
+        } else if (NwConstants.ETHTYPE_ARP == ethernet.getEtherType()) {
+            srcIpAddress = getSrcIpAddrFromArp(data);
+        }
+        return srcIpAddress;
+    }
+
+    public static <T> List<T> diffOf(List<T> orig, List<T> updated) {
+        if (isEmpty(orig)) {
+            return Collections.EMPTY_LIST;
+        }
+        List<T> diff = Lists.newArrayList(orig);
+        if (isNotEmpty(updated)) {
+            diff.removeAll(updated);
+        }
+        return diff;
+    }
+
+    public static boolean isEmpty(Collection collection) {
+        return collection == null || collection.isEmpty();
+    }
+
+    public static boolean isNotEmpty(Collection collection) {
+        return (!isEmpty(collection));
+    }
+
+    public static void setElanInstancToDpnsCache(Map<String, Set<DpnInterfaces>> elanInstancToDpnsCache) {
+        ElanUtils.elanInstancToDpnsCache = elanInstancToDpnsCache;
+    }
+
+    public static Set<DpnInterfaces> getElanInvolvedDPNsFromCache(String elanName) {
+        return elanInstancToDpnsCache.get(elanName);
+    }
+
+    public static void addDPNInterfaceToElanInCache(String elanName, DpnInterfaces dpnInterfaces) {
+        elanInstancToDpnsCache.computeIfAbsent(elanName, key -> new HashSet<>()).add(dpnInterfaces);
+    }
+
+    public static void removeDPNInterfaceFromElanInCache(String elanName, DpnInterfaces dpnInterfaces) {
+        elanInstancToDpnsCache.computeIfAbsent(elanName, key -> new HashSet<DpnInterfaces>()).remove(dpnInterfaces);
+    }
 }
index bfc3912d61d496790e3a90df635e7bdbbc8034f5..53313c804256b830403b23346b1cf82eef8c6c00 100644 (file)
     <argument ref="itmRpcService" />
     <argument ref="entityOwnershipService" />
     <argument ref="elanUtils" />
+    <argument ref="l2gwService" />
   </bean>
 
   <bean id="hwvtepTerminationPointListener"
     <argument ref="elanUtils" />
     <argument ref="entityOwnershipService" />
   </bean>
+
+  <bean id="TransportZoneNotificationUtil"
+    class="org.opendaylight.netvirt.elan.utils.TransportZoneNotificationUtil">
+    <argument ref="dataBroker" />
+    <argument ref="interfaceManager" />
+    <argument ref="elanService" />
+    <argument ref="elanConfig" />
+  </bean>
+
+  <bean id="ElanDpnToTransportZoneListener"
+    class="org.opendaylight.netvirt.elan.internal.ElanDpnToTransportZoneListener"
+    init-method="start" destroy-method="close">
+    <argument ref="dataBroker" />
+    <argument ref="interfaceManager" />
+    <argument ref="elanConfig" />
+    <argument ref="TransportZoneNotificationUtil" />
+  </bean>
+
+  <bean id="VpnDpnToTransportZoneListener"
+    class="org.opendaylight.netvirt.elan.internal.VpnDpnToTransportZoneListener"
+    init-method="start" destroy-method="close">
+    <argument ref="dataBroker" />
+    <argument ref="interfaceManager" />
+    <argument ref="elanConfig" />
+    <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 e65e3e952d67406c7ace32d610a6576a3a168f09..1e0f58a7b438fb76b457513b7ac8608e3aab0a2e 100644 (file)
@@ -48,9 +48,7 @@ public class L2GatewayDevice {
 
     /** the status of this device connectin */
     AtomicBoolean connected = new AtomicBoolean(false);
-
-    /** Connection Id to Devices */
-    Map<Uuid,List<Devices>> l2gwConnectionIdToDevices = new HashMap<>();
+    Map<Uuid,Set<Devices>> l2gwConnectionIdToDevices = new HashMap<>();
 
     /**
      * VTEP device name mentioned with L2 Gateway.
@@ -99,11 +97,11 @@ public class L2GatewayDevice {
         return tunnelIps;
     }
 
-    public Map<Uuid, List<Devices>> getL2gwConnectionIdToDevices() {
+    public Map<Uuid, Set<Devices>> getL2gwConnectionIdToDevices() {
         return l2gwConnectionIdToDevices;
     }
 
-    public void setL2gwConnectionIdToDevices(Map<Uuid, List<Devices>> l2gwConnectionIdToDevices) {
+    public void setL2gwConnectionIdToDevices(Map<Uuid, Set<Devices>> l2gwConnectionIdToDevices) {
         this.l2gwConnectionIdToDevices = l2gwConnectionIdToDevices;
     }
 
@@ -285,6 +283,10 @@ public class L2GatewayDevice {
         return true;
     }
 
+    public boolean containsUcastMac(LocalUcastMacs mac) {
+        return ucastLocalMacs.contains(mac);
+    }
+
     /*
      * (non-Javadoc)
      *
index 26afd60796101948b55e5d49aaeee0e247db0f11..40a5434df3d3bfa1468d8e85302d5fd6f80d469f 100644 (file)
@@ -7,10 +7,14 @@
  */
 package org.opendaylight.netvirt.neutronvpn.api.l2gw.utils;
 
+import java.util.List;
 import java.util.concurrent.ConcurrentMap;
 
 import org.opendaylight.netvirt.neutronvpn.api.l2gw.L2GatewayDevice;
 import org.opendaylight.genius.utils.cache.CacheUtil;
+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";
@@ -48,4 +52,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 7da13c64466eb140955251a4ff2b678e669bc922..eee66218a4569e42dd256a2e95d92522dda420fb 100644 (file)
@@ -25,6 +25,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;
@@ -45,14 +46,16 @@ public class L2GatewayListener extends AsyncClusteredDataChangeListenerBase<L2ga
     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) {
         super(L2gateway.class, L2GatewayListener.class);
         this.dataBroker = dataBroker;
         this.entityOwnershipService = entityOwnershipService;
         this.itmRpcService = itmRpcService;
+        this.l2gwService = l2gwService;
     }
 
     public void start() {
@@ -93,50 +96,16 @@ public class L2GatewayListener extends AsyncClusteredDataChangeListenerBase<L2ga
         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 e21b1f510b944f6ae76b8a894fef045f5e22a3e1..cf8baf13e1b258f5ebc6792d35e81992be60297e 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"