2 * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.netvirt.elan.l2gw.listeners;
11 import com.google.common.base.Optional;
12 import java.util.Collections;
13 import java.util.HashSet;
14 import java.util.List;
15 import java.util.Objects;
17 import java.util.concurrent.ExecutionException;
18 import java.util.function.BiPredicate;
19 import java.util.function.Predicate;
20 import javax.annotation.PostConstruct;
21 import javax.inject.Inject;
22 import javax.inject.Singleton;
24 import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener;
25 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
26 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
27 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
28 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
29 import org.opendaylight.genius.datastoreutils.hwvtep.HwvtepAbstractDataTreeChangeListener;
30 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
31 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
32 import org.opendaylight.genius.mdsalutil.MDSALUtil;
33 import org.opendaylight.genius.utils.hwvtep.HwvtepHACache;
34 import org.opendaylight.genius.utils.hwvtep.HwvtepSouthboundConstants;
35 import org.opendaylight.genius.utils.hwvtep.HwvtepSouthboundUtils;
36 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
37 import org.opendaylight.netvirt.elan.l2gw.ha.HwvtepHAUtil;
38 import org.opendaylight.netvirt.elan.l2gw.ha.listeners.HAOpClusteredListener;
39 import org.opendaylight.netvirt.elan.l2gw.utils.ElanL2GatewayUtils;
40 import org.opendaylight.netvirt.elan.l2gw.utils.L2GatewayConnectionUtils;
41 import org.opendaylight.netvirt.elan.l2gw.utils.L2GatewayUtils;
42 import org.opendaylight.netvirt.elan.l2gw.utils.L2gwServiceProvider;
43 import org.opendaylight.netvirt.elan.l2gw.utils.StaleVlanBindingsCleaner;
44 import org.opendaylight.netvirt.elan.utils.ElanClusterUtils;
45 import org.opendaylight.netvirt.elanmanager.utils.ElanL2GwCacheUtils;
46 import org.opendaylight.netvirt.neutronvpn.api.l2gw.L2GatewayCache;
47 import org.opendaylight.netvirt.neutronvpn.api.l2gw.L2GatewayDevice;
48 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.PhysicalSwitchAugmentation;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.PhysicalSwitchAugmentationBuilder;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.physical._switch.attributes.TunnelIps;
53 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
54 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
55 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
56 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
57 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
58 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
59 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
60 import org.slf4j.Logger;
61 import org.slf4j.LoggerFactory;
64 * Listener to handle physical switch updates.
67 public class HwvtepPhysicalSwitchListener
68 extends HwvtepAbstractDataTreeChangeListener<PhysicalSwitchAugmentation, HwvtepPhysicalSwitchListener>
69 implements ClusteredDataTreeChangeListener<PhysicalSwitchAugmentation> {
71 /** The Constant LOG. */
72 private static final Logger LOG = LoggerFactory.getLogger(HwvtepPhysicalSwitchListener.class);
74 private static final BiPredicate<L2GatewayDevice, InstanceIdentifier<Node>> DEVICE_NOT_CACHED_OR_PARENT_CONNECTED =
75 (l2GatewayDevice, globalIid) -> {
76 return l2GatewayDevice == null || l2GatewayDevice.getHwvtepNodeId() == null
77 || !Objects.equals(l2GatewayDevice.getHwvtepNodeId(),
78 globalIid.firstKeyOf(Node.class).getNodeId().getValue());
81 private static final Predicate<PhysicalSwitchAugmentation> TUNNEL_IP_AVAILABLE =
82 phySwitch -> !HwvtepHAUtil.isEmpty(phySwitch.getTunnelIps());
84 private static final Predicate<PhysicalSwitchAugmentation> TUNNEL_IP_NOT_AVAILABLE = TUNNEL_IP_AVAILABLE.negate();
86 private static final BiPredicate<PhysicalSwitchAugmentation, L2GatewayDevice> TUNNEL_IP_CHANGED =
87 (phySwitchAfter, existingDevice) -> {
88 return TUNNEL_IP_AVAILABLE.test(phySwitchAfter)
90 existingDevice.getTunnelIp(), phySwitchAfter.getTunnelIps().get(0).getTunnelIpsKey());
93 /** The data broker. */
94 private final DataBroker dataBroker;
95 private final ManagedNewTransactionRunner txRunner;
97 /** The itm rpc service. */
98 private final ItmRpcService itmRpcService;
100 private final ElanClusterUtils elanClusterUtils;
102 private final HwvtepHACache hwvtepHACache = HwvtepHACache.getInstance();
104 private final L2gwServiceProvider l2gwServiceProvider;
106 private final BiPredicate<L2GatewayDevice, InstanceIdentifier<Node>> childConnectedAfterParent =
107 (l2GwDevice, globalIid) -> {
108 return !hwvtepHACache.isHAParentNode(globalIid)
109 && l2GwDevice != null;
110 // FIXME: The following call to equals compares different types (String and InstanceIdentifier) and
111 // thus will always return false. I don't know what the intention is here so commented out for now.
112 //&& !Objects.equals(l2GwDevice.getHwvtepNodeId(), globalIid);
115 private final Predicate<L2GatewayDevice> alreadyHasL2Gwids =
117 return l2GwDevice != null && HwvtepHAUtil.isEmpty(l2GwDevice.getL2GatewayIds());
120 private final BiPredicate<L2GatewayDevice, InstanceIdentifier<Node>> parentConnectedAfterChild =
121 (l2GwDevice, globalIid) -> {
122 InstanceIdentifier<Node> existingIid = globalIid;
123 if (l2GwDevice != null && l2GwDevice.getHwvtepNodeId() != null) {
124 existingIid = HwvtepHAUtil.convertToInstanceIdentifier(l2GwDevice.getHwvtepNodeId());
126 return hwvtepHACache.isHAParentNode(globalIid)
127 && l2GwDevice != null
128 // FIXME: The following call to equals compares different types (String and InstanceIdentifier) and
129 // thus will always return false. I don't know what the intention is here so commented out for now.
130 //&& !Objects.equals(l2GwDevice.getHwvtepNodeId(), globalIid)
131 && Objects.equals(globalIid, hwvtepHACache.getParent(existingIid));
135 private final HAOpClusteredListener haOpClusteredListener;
137 private final L2GatewayCache l2GatewayCache;
139 private final StaleVlanBindingsCleaner staleVlanBindingsCleaner;
142 * Instantiates a new hwvtep physical switch listener.
143 * @param dataBroker DataBroker
144 * @param itmRpcService ItmRpcService
145 * @param elanClusterUtils ElanClusterUtils
146 * @param l2gwServiceProvider L2gwServiceProvider
147 * @param haListener HAOpClusteredListener
148 * @param l2GatewayCache L2GatewayCache
149 * @param staleVlanBindingsCleaner StaleVlanBindingsCleaner
152 public HwvtepPhysicalSwitchListener(final DataBroker dataBroker, ItmRpcService itmRpcService,
153 ElanClusterUtils elanClusterUtils, L2gwServiceProvider l2gwServiceProvider,
154 HAOpClusteredListener haListener, L2GatewayCache l2GatewayCache,
155 StaleVlanBindingsCleaner staleVlanBindingsCleaner) {
156 super(PhysicalSwitchAugmentation.class, HwvtepPhysicalSwitchListener.class);
157 this.dataBroker = dataBroker;
158 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
159 this.itmRpcService = itmRpcService;
160 this.elanClusterUtils = elanClusterUtils;
161 this.l2gwServiceProvider = l2gwServiceProvider;
162 this.staleVlanBindingsCleaner = staleVlanBindingsCleaner;
163 this.haOpClusteredListener = haListener;
164 this.l2GatewayCache = l2GatewayCache;
170 registerListener(LogicalDatastoreType.OPERATIONAL, dataBroker);
174 protected InstanceIdentifier<PhysicalSwitchAugmentation> getWildCardPath() {
175 return InstanceIdentifier.create(NetworkTopology.class)
176 .child(Topology.class, new TopologyKey(HwvtepSouthboundConstants.HWVTEP_TOPOLOGY_ID)).child(Node.class)
177 .augmentation(PhysicalSwitchAugmentation.class);
181 protected HwvtepPhysicalSwitchListener getDataTreeChangeListener() {
182 return HwvtepPhysicalSwitchListener.this;
186 protected void removed(InstanceIdentifier<PhysicalSwitchAugmentation> identifier,
187 PhysicalSwitchAugmentation phySwitchDeleted) {
188 NodeId nodeId = getNodeId(identifier);
189 String psName = phySwitchDeleted.getHwvtepNodeName().getValue();
190 LOG.info("Received physical switch {} removed event for node {}", psName, nodeId.getValue());
192 L2GatewayDevice l2GwDevice = l2GatewayCache.get(psName);
193 if (l2GwDevice != null) {
194 if (!L2GatewayConnectionUtils.isGatewayAssociatedToL2Device(l2GwDevice)) {
195 l2GatewayCache.remove(psName);
196 LOG.debug("{} details removed from L2Gateway Cache", psName);
197 MDSALUtil.syncDelete(this.dataBroker, LogicalDatastoreType.CONFIGURATION,
198 HwvtepSouthboundUtils.createInstanceIdentifier(nodeId));
200 LOG.debug("{} details are not removed from L2Gateway Cache as it has L2Gateway reference", psName);
203 l2GwDevice.setConnected(false);
204 //ElanL2GwCacheUtils.removeL2GatewayDeviceFromAllElanCache(psName);
206 LOG.error("Unable to find L2 Gateway details for {}", psName);
211 * Upon update checks if the tunnels Ip was null earlier and it got newly added.
212 * In that case simply call add.
213 * If not then check if Tunnel Ip has been updated from an old value to new value.
214 * If yes. delete old ITM tunnels of odl Tunnel Ipand add new ITM tunnels with new Tunnel
215 * IP then call added ().
217 * @param identifier iid
218 * @param phySwitchBefore ps Node before update
219 * @param phySwitchAfter ps Node after update
222 protected void updated(InstanceIdentifier<PhysicalSwitchAugmentation> identifier,
223 PhysicalSwitchAugmentation phySwitchBefore, PhysicalSwitchAugmentation phySwitchAfter) {
224 NodeId nodeId = getNodeId(identifier);
225 LOG.trace("Received PhysicalSwitch Update Event for node {}: PhysicalSwitch Before: {}, "
226 + "PhysicalSwitch After: {}", nodeId.getValue(), phySwitchBefore, phySwitchAfter);
227 String psName = getPsName(identifier);
228 if (psName == null) {
229 LOG.error("Could not find the physical switch name for node {}", nodeId.getValue());
232 L2GatewayDevice existingDevice = l2GatewayCache.get(psName);
233 LOG.info("Received physical switch {} update event for node {}", psName, nodeId.getValue());
234 InstanceIdentifier<Node> globalNodeIid = getManagedByNodeIid(identifier);
236 if (DEVICE_NOT_CACHED_OR_PARENT_CONNECTED.test(existingDevice, globalNodeIid)) {
237 if (TUNNEL_IP_AVAILABLE.test(phySwitchAfter)) {
238 added(identifier, phySwitchAfter);
241 if (!Objects.equals(phySwitchAfter.getTunnelIps(), phySwitchBefore.getTunnelIps())
242 && TUNNEL_IP_CHANGED.test(phySwitchAfter, existingDevice)) {
244 final String hwvtepId = existingDevice.getHwvtepNodeId();
245 elanClusterUtils.runOnlyInOwnerNode(existingDevice.getDeviceName(),
246 "handling Physical Switch add create itm tunnels ",
248 LOG.info("Deleting itm tunnels for device {}", existingDevice.getDeviceName());
249 L2GatewayUtils.deleteItmTunnels(itmRpcService, hwvtepId,
250 existingDevice.getDeviceName(), existingDevice.getTunnelIp());
251 Thread.sleep(10000L);//TODO remove these sleeps
252 LOG.info("Creating itm tunnels for device {}", existingDevice.getDeviceName());
253 ElanL2GatewayUtils.createItmTunnels(dataBroker, itmRpcService, hwvtepId, psName,
254 phySwitchAfter.getTunnelIps().get(0).getTunnelIpsKey());
255 return Collections.emptyList();
259 Thread.sleep(20000L);//TODO remove the sleep by using better itm api to detect finish of prev op
260 } catch (InterruptedException e) {
261 LOG.error("Interrupted ");
263 existingDevice.setTunnelIps(new HashSet<>());
264 added(identifier, phySwitchAfter);
270 protected void added(InstanceIdentifier<PhysicalSwitchAugmentation> identifier,
271 final PhysicalSwitchAugmentation phySwitchAdded) {
272 String globalNodeId = getManagedByNodeId(identifier);
273 final InstanceIdentifier<Node> globalNodeIid = getManagedByNodeIid(identifier);
274 NodeId nodeId = getNodeId(identifier);
275 if (TUNNEL_IP_NOT_AVAILABLE.test(phySwitchAdded)) {
276 LOG.error("Could not find the /tunnel ips for node {}", nodeId.getValue());
279 final String psName = getPsName(identifier);
280 LOG.trace("Received physical switch {} added event received for node {}", psName, nodeId.getValue());
282 haOpClusteredListener.runAfterNodeIsConnected(globalNodeIid, (node) -> {
283 LOG.trace("Running job for node {} ", globalNodeIid);
284 if (!node.isPresent()) {
285 LOG.error("Global node is absent {}", globalNodeId);
288 HAOpClusteredListener.addToCacheIfHAChildNode(globalNodeIid, node.get());
289 if (hwvtepHACache.isHAEnabledDevice(globalNodeIid)) {
290 LOG.trace("Ha enabled device {}", globalNodeIid);
293 LOG.trace("Updating cache for node {}", globalNodeIid);
294 L2GatewayDevice l2GwDevice = l2GatewayCache.get(psName);
295 if (childConnectedAfterParent.test(l2GwDevice, globalNodeIid)) {
296 LOG.trace("Device {} {} is already Connected by ",
297 psName, globalNodeId, l2GwDevice.getHwvtepNodeId());
300 InstanceIdentifier<Node> existingIid = globalNodeIid;
301 if (l2GwDevice != null && l2GwDevice.getHwvtepNodeId() != null) {
302 existingIid = HwvtepHAUtil.convertToInstanceIdentifier(l2GwDevice.getHwvtepNodeId());
304 if (parentConnectedAfterChild.test(l2GwDevice, globalNodeIid)
305 && alreadyHasL2Gwids.test(l2GwDevice)) {
306 LOG.error("Child node {} having l2gw configured became ha node "
307 + " removing the l2device {} from all elan cache and provision parent node {}",
308 existingIid, psName, globalNodeIid);
309 ElanL2GwCacheUtils.removeL2GatewayDeviceFromAllElanCache(l2GwDevice.getHwvtepNodeId());
312 l2GwDevice = l2GatewayCache.addOrGet(psName);
313 l2GwDevice.setConnected(true);
314 l2GwDevice.setHwvtepNodeId(globalNodeId);
316 List<TunnelIps> tunnelIps = phySwitchAdded.getTunnelIps();
317 if (tunnelIps != null) {
318 for (TunnelIps tunnelIp : tunnelIps) {
319 IpAddress tunnelIpAddr = tunnelIp.getTunnelIpsKey();
320 l2GwDevice.addTunnelIp(tunnelIpAddr);
324 handleAdd(l2GwDevice);
325 elanClusterUtils.runOnlyInOwnerNode("Update config tunnels IP ", () -> {
327 updateConfigTunnelIp(identifier, phySwitchAdded);
328 } catch (ReadFailedException e) {
329 LOG.error("Failed to update tunnel ips {}", identifier);
336 boolean updateHACacheIfHANode(InstanceIdentifier<Node> globalNodeId)
337 throws ExecutionException, InterruptedException {
338 try (ReadOnlyTransaction tx = dataBroker.newReadOnlyTransaction()) {
339 tx.read(LogicalDatastoreType.OPERATIONAL, globalNodeId).get().toJavaUtil().ifPresent(
340 node -> HAOpClusteredListener.addToCacheIfHAChildNode(globalNodeId, node));
342 return hwvtepHACache.isHAEnabledDevice(globalNodeId);
351 private void handleAdd(L2GatewayDevice l2GwDevice) {
352 final String psName = l2GwDevice.getDeviceName();
353 final String hwvtepNodeId = l2GwDevice.getHwvtepNodeId();
354 Set<IpAddress> tunnelIps = l2GwDevice.getTunnelIps();
355 for (final IpAddress tunnelIpAddr : tunnelIps) {
356 if (L2GatewayConnectionUtils.isGatewayAssociatedToL2Device(l2GwDevice)) {
357 LOG.debug("L2Gateway {} associated for {} physical switch; creating ITM tunnels for {}",
358 l2GwDevice.getL2GatewayIds(), psName, tunnelIpAddr);
359 l2gwServiceProvider.provisionItmAndL2gwConnection(l2GwDevice, psName, hwvtepNodeId, tunnelIpAddr);
361 LOG.info("l2gw.provision.skip {}", hwvtepNodeId, psName);
364 elanClusterUtils.runOnlyInOwnerNode("Stale entry cleanup", () -> {
365 InstanceIdentifier<Node> globalNodeIid = HwvtepSouthboundUtils.createInstanceIdentifier(
366 new NodeId(hwvtepNodeId));
367 InstanceIdentifier<Node> psIid = HwvtepSouthboundUtils.createInstanceIdentifier(
368 HwvtepSouthboundUtils.createManagedNodeId(new NodeId(hwvtepNodeId), psName));
369 staleVlanBindingsCleaner.scheduleStaleCleanup(psName, globalNodeIid, psIid);
379 * @return the node id
381 private NodeId getNodeId(InstanceIdentifier<PhysicalSwitchAugmentation> identifier) {
382 return identifier.firstKeyOf(Node.class).getNodeId();
385 private String getManagedByNodeId(InstanceIdentifier<PhysicalSwitchAugmentation> identifier) {
386 String psNodeId = identifier.firstKeyOf(Node.class).getNodeId().getValue();
387 if (psNodeId.contains(HwvtepHAUtil.PHYSICALSWITCH)) {
388 return psNodeId.substring(0, psNodeId.indexOf(HwvtepHAUtil.PHYSICALSWITCH));
393 private InstanceIdentifier<Node> getManagedByNodeIid(InstanceIdentifier<PhysicalSwitchAugmentation> identifier) {
394 String psNodeId = identifier.firstKeyOf(Node.class).getNodeId().getValue();
395 if (psNodeId.contains(HwvtepHAUtil.PHYSICALSWITCH)) {
396 psNodeId = psNodeId.substring(0, psNodeId.indexOf(HwvtepHAUtil.PHYSICALSWITCH));
397 return identifier.firstIdentifierOf(Topology.class).child(Node.class, new NodeKey(new NodeId(psNodeId)));
402 private String getPsName(InstanceIdentifier<PhysicalSwitchAugmentation> identifier) {
403 String psNodeId = identifier.firstKeyOf(Node.class).getNodeId().getValue();
404 if (psNodeId.contains(HwvtepHAUtil.PHYSICALSWITCH)) {
405 return psNodeId.substring(psNodeId.indexOf(HwvtepHAUtil.PHYSICALSWITCH) + HwvtepHAUtil.PHYSICALSWITCH
411 private void updateConfigTunnelIp(InstanceIdentifier<PhysicalSwitchAugmentation> identifier,
412 PhysicalSwitchAugmentation phySwitchAdded) throws ReadFailedException {
413 if (phySwitchAdded.getTunnelIps() != null) {
414 ListenableFutures.addErrorLogging(
415 txRunner.callWithNewReadWriteTransactionAndSubmit(tx -> {
416 Optional<PhysicalSwitchAugmentation> existingSwitch = tx.read(
417 LogicalDatastoreType.CONFIGURATION, identifier).checkedGet();
418 PhysicalSwitchAugmentationBuilder psBuilder = new PhysicalSwitchAugmentationBuilder();
419 if (existingSwitch.isPresent()) {
420 psBuilder = new PhysicalSwitchAugmentationBuilder(existingSwitch.get());
422 psBuilder.setTunnelIps(phySwitchAdded.getTunnelIps());
423 tx.put(LogicalDatastoreType.CONFIGURATION, identifier, psBuilder.build(), true);
424 LOG.trace("Updating config tunnel ips {}", identifier);
425 }), LOG, "Failed to update the config tunnel ips {}", identifier);