--- /dev/null
+/*
+ * 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) ;
+}
*/
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;
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
@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
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);
+ }
+
}
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,
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;
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;
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;
private final HwvtepHACache hwvtepHACache = HwvtepHACache.getInstance();
+ protected final L2gwServiceProvider l2gwServiceProvider;
+
/**
* Instantiates a new hwvtep physical switch listener.
*
*/
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() {
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
@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)
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);
}
}
}
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.
*
&& 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;
+ }
+
}
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;
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;
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);
.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);
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 =
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,
}
}
- 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);
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;
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);
+ }
}
--- /dev/null
+/*
+ * 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 {
+
+ }
+}
<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>
*/
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";
.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;
+ }
}
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;
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() {
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) {
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"