2 * Copyright (c) 2019 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
8 package org.opendaylight.netvirt.elan.l2gw.listeners;
10 import static org.opendaylight.mdsal.binding.util.Datastore.CONFIGURATION;
11 import static org.opendaylight.mdsal.binding.util.Datastore.OPERATIONAL;
13 import com.google.common.collect.Sets;
14 import com.google.common.util.concurrent.FluentFuture;
15 import com.google.common.util.concurrent.FutureCallback;
16 import com.google.common.util.concurrent.ListenableFuture;
17 import com.google.common.util.concurrent.MoreExecutors;
18 import java.util.ArrayList;
19 import java.util.Collection;
20 import java.util.Collections;
21 import java.util.HashMap;
22 import java.util.List;
24 import java.util.Objects;
25 import java.util.Optional;
26 import java.util.concurrent.ExecutionException;
27 import javax.annotation.PreDestroy;
28 import javax.inject.Inject;
29 import javax.inject.Singleton;
30 import org.opendaylight.genius.utils.batching.ResourceBatchingManager;
31 import org.opendaylight.genius.utils.hwvtep.HwvtepHACache;
32 import org.opendaylight.genius.utils.hwvtep.HwvtepSouthboundUtils;
33 import org.opendaylight.genius.utils.hwvtep.HwvtepUtils;
34 import org.opendaylight.infrautils.utils.concurrent.Executors;
35 import org.opendaylight.mdsal.binding.api.DataBroker;
36 import org.opendaylight.mdsal.binding.util.Datastore.Operational;
37 import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunner;
38 import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunnerImpl;
39 import org.opendaylight.mdsal.binding.util.TypedReadTransaction;
40 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
41 import org.opendaylight.netvirt.elan.l2gw.ha.HwvtepHAUtil;
42 import org.opendaylight.netvirt.elan.l2gw.ha.listeners.HAOpClusteredListener;
43 import org.opendaylight.netvirt.elan.l2gw.ha.listeners.HAOpNodeListener;
44 import org.opendaylight.netvirt.elan.l2gw.recovery.impl.L2GatewayInstanceRecoveryHandler;
45 import org.opendaylight.netvirt.elan.l2gw.utils.L2GatewayUtils;
46 import org.opendaylight.netvirt.elan.l2gw.utils.L2gwZeroDayConfigUtil;
47 import org.opendaylight.netvirt.elan.utils.ElanClusterUtils;
48 import org.opendaylight.netvirt.elanmanager.api.IL2gwService;
49 import org.opendaylight.netvirt.neutronvpn.api.l2gw.L2GatewayCache;
50 import org.opendaylight.netvirt.neutronvpn.api.l2gw.L2GatewayDevice;
51 import org.opendaylight.serviceutils.srm.RecoverableListener;
52 import org.opendaylight.serviceutils.srm.ServiceRecoveryRegistry;
53 import org.opendaylight.serviceutils.tools.listener.AbstractClusteredAsyncDataTreeChangeListener;
54 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateway.attributes.Devices;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateway.attributes.devices.Interfaces;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateway.connections.attributes.L2gatewayConnections;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateway.connections.attributes.l2gatewayconnections.L2gatewayConnection;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateways.attributes.L2gateways;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateways.attributes.l2gateways.L2gateway;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.PhysicalSwitchAugmentation;
64 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
65 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
66 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
67 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
68 import org.slf4j.Logger;
69 import org.slf4j.LoggerFactory;
72 public class L2GatewayListener extends AbstractClusteredAsyncDataTreeChangeListener<L2gateway>
73 implements RecoverableListener {
74 private static final Logger LOG = LoggerFactory.getLogger(L2GatewayListener.class);
75 private final DataBroker dataBroker;
76 private final IL2gwService l2gwService;
77 private final L2GatewayCache l2GatewayCache;
78 private final HAOpNodeListener haOpNodeListener;
79 private final HAOpClusteredListener haOpClusteredListener;
80 private final ElanClusterUtils elanClusterUtils;
81 private final L2gwZeroDayConfigUtil l2gwZeroDayConfigUtil;
82 private final L2GwTransportZoneListener transportZoneListener;
83 private final ManagedNewTransactionRunner txRunner;
84 private final ItmRpcService itmRpcService;
87 public L2GatewayListener(final DataBroker dataBroker,
88 final IL2gwService l2gwService,
89 final L2GatewayCache l2GatewayCache,
90 HAOpNodeListener haOpNodeListener,
91 HAOpClusteredListener haOpClusteredListener,
92 final ItmRpcService itmRpcService,
93 L2GatewayInstanceRecoveryHandler l2GatewayInstanceRecoveryHandler,
94 ServiceRecoveryRegistry serviceRecoveryRegistry,
95 L2gwZeroDayConfigUtil l2gwZeroDayConfigUtil,
96 L2GwTransportZoneListener transportZoneListener,
97 ElanClusterUtils elanClusterUtils) {
98 super(dataBroker, LogicalDatastoreType.CONFIGURATION, InstanceIdentifier.create(Neutron.class)
99 .child(L2gateways.class).child(L2gateway.class),
100 Executors.newListeningSingleThreadExecutor("L2GatewayListener", LOG));
101 this.dataBroker = dataBroker;
102 this.l2gwService = l2gwService;
103 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
104 this.l2gwZeroDayConfigUtil = l2gwZeroDayConfigUtil;
105 this.transportZoneListener = transportZoneListener;
106 this.l2GatewayCache = l2GatewayCache;
107 this.haOpClusteredListener = haOpClusteredListener;
108 this.haOpNodeListener = haOpNodeListener;
109 this.elanClusterUtils = elanClusterUtils;
110 this.itmRpcService = itmRpcService;
111 serviceRecoveryRegistry.addRecoverableListener(l2GatewayInstanceRecoveryHandler.buildServiceRegistryKey(),
117 ResourceBatchingManager.getInstance().registerDefaultBatchHandlers(this.dataBroker);
118 LOG.info("{} init", getClass().getSimpleName());
119 // registerListener(); called from L2GatewayConnection listener
123 public void register() {
124 LOG.info("Registering L2Gateway Listener Override Method");
129 public void registerListener() {
130 LOG.info("Registering L2Gateway Listener");
135 public void deregisterListener() {
136 LOG.info("Deregistering L2GatewayListener");
142 public void close() {
144 Executors.shutdownAndAwaitTermination(getExecutorService());
148 public void add(final InstanceIdentifier<L2gateway> identifier, final L2gateway input) {
149 LOG.info("Adding L2gateway with ID: {}", input);
151 List<Devices> l2Devices = new ArrayList<>(input.getDevices().values());
152 for (Devices l2Device : l2Devices) {
153 LOG.info("Adding L2gateway device: {}", l2Device);
154 addL2Device(l2Device, input);
159 public void remove(final InstanceIdentifier<L2gateway> identifier, final L2gateway input) {
160 LOG.info("Removing L2gateway with ID: {}", input);
161 List<L2gatewayConnection> connections = l2gwService
162 .getL2GwConnectionsByL2GatewayId(input.getUuid());
164 txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
165 for (L2gatewayConnection connection : connections) {
166 InstanceIdentifier<L2gatewayConnection> iid = InstanceIdentifier.create(Neutron.class)
167 .child(L2gatewayConnections.class).child(L2gatewayConnection.class, connection.key());
172 Collection<Devices> l2Devices = input.getDevices().values();
173 for (Devices l2Device : l2Devices) {
174 LOG.info("Removing L2gateway device: {}", l2Device);
175 removeL2Device(l2Device, input);
180 public void update(InstanceIdentifier<L2gateway> identifier, L2gateway original, L2gateway update) {
181 LOG.info("Updating L2gateway : key: {}, original value={}, update value={}", identifier, original, update);
182 List<L2gatewayConnection> connections = l2gwService.getAssociatedL2GwConnections(
183 Sets.newHashSet(update.getUuid()));
184 if (connections == null) {
185 LOG.warn("There are no connections associated with l2 gateway uuid {} name {}",
186 update.getUuid(), update.getName());
189 if (original.getDevices() == null) {
191 (connection) -> l2gwService.addL2GatewayConnection(connection));
194 elanClusterUtils.runOnlyInOwnerNode("l2gw.update", () -> {
195 DeviceInterfaces updatedDeviceInterfaces = new DeviceInterfaces(update);
196 FluentFuture<?> fts = txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
197 original.getDevices().values()
199 .filter((originalDevice) -> originalDevice.getInterfaces() != null)
200 .forEach((originalDevice) -> {
201 String deviceName = originalDevice.getDeviceName();
202 L2GatewayDevice l2GwDevice = l2GatewayCache.get(deviceName);
203 NodeId physicalSwitchNodeId = HwvtepSouthboundUtils.createManagedNodeId(
204 new NodeId(l2GwDevice.getHwvtepNodeId()), deviceName);
205 originalDevice.getInterfaces().values()
207 .filter((intf) -> !updatedDeviceInterfaces.containsInterface(
208 deviceName, intf.getInterfaceName()))
210 connections.forEach((connection) -> {
211 Integer vlanId = connection.getSegmentId();
212 if (intf.getSegmentationIds() != null
213 && !intf.getSegmentationIds().isEmpty()) {
214 for (Integer vlan : intf.getSegmentationIds()) {
215 HwvtepUtils.deleteVlanBinding(tx,
216 physicalSwitchNodeId, intf.getInterfaceName(), vlan);
219 LOG.info("Deleting vlan binding {} {} {}",
220 physicalSwitchNodeId, intf.getInterfaceName(), vlanId);
221 HwvtepUtils.deleteVlanBinding(tx, physicalSwitchNodeId,
222 intf.getInterfaceName(), vlanId);
228 fts.addCallback(new FutureCallback<Object>() {
230 public void onSuccess(Object success) {
231 LOG.info("Successfully deleted vlan bindings for l2gw update {}", update);
232 connections.forEach((l2GwConnection) ->
233 l2gwService.addL2GatewayConnection(l2GwConnection, null, update));
237 public void onFailure(Throwable throwable) {
238 LOG.error("Failed to delete vlan bindings as part of l2gw udpate {}", update);
240 }, MoreExecutors.directExecutor());
244 private synchronized void addL2Device(Devices l2Device, L2gateway input) {
245 String l2DeviceName = l2Device.getDeviceName();
247 L2GatewayDevice l2GwDevice = l2GatewayCache.addOrGet(l2DeviceName);
248 String hwvtepNodeId = l2GwDevice.getHwvtepNodeId();
249 HwvtepHACache haCache = HwvtepHACache.getInstance();
250 if (hwvtepNodeId == null) {
251 scanNodesAndReplayDeviceGlobalNode(l2Device, input, l2DeviceName);
252 } else if (!haCache.isHAParentNode(HwvtepHAUtil.convertToInstanceIdentifier(hwvtepNodeId))) {
253 replayGlobalNode(l2Device, input, l2DeviceName, hwvtepNodeId);
255 l2GwDevice.addL2GatewayId(input.getUuid());
257 if (l2GwDevice.getHwvtepNodeId() == null) {
258 LOG.info("L2GW provisioning skipped for device {}",l2DeviceName);
260 transportZoneListener.createZeroDayForL2Device(l2GwDevice);
261 LOG.info("Provisioning l2gw for device {}",l2DeviceName);
262 l2gwService.provisionItmAndL2gwConnection(l2GwDevice, l2DeviceName, l2GwDevice.getHwvtepNodeId(),
263 l2GwDevice.getTunnelIp());
267 private void removeL2Device(Devices l2Device, L2gateway input) {
268 final String l2DeviceName = l2Device.getDeviceName();
269 L2GatewayDevice l2GwDevice = l2GatewayCache.get(l2DeviceName);
270 if (l2GwDevice != null) {
271 // Delete ITM tunnels if it's last Gateway deleted and device is connected
272 // Also, do not delete device from cache if it's connected
273 if (L2GatewayUtils.isLastL2GatewayBeingDeleted(l2GwDevice)) {
274 if (l2GwDevice.isConnected()) {
276 l2GwDevice.removeL2GatewayId(input.getUuid());
277 // Delete ITM tunnels
278 final String hwvtepId = l2GwDevice.getHwvtepNodeId();
280 final Set<IpAddress> tunnelIps = l2GwDevice.getTunnelIps();
281 jobCoordinator.enqueueJob(hwvtepId, () -> {
282 if (entityOwnershipUtils.isEntityOwner(HwvtepSouthboundConstants.ELAN_ENTITY_TYPE,
283 HwvtepSouthboundConstants.ELAN_ENTITY_NAME)) {
284 LOG.info("Deleting ITM Tunnels for {} connected to cluster node owner", l2DeviceName);
285 for (IpAddress tunnelIp : tunnelIps) {
286 L2GatewayUtils.deleteItmTunnels(itmRpcService, hwvtepId, l2DeviceName, tunnelIp);
289 LOG.info("ITM Tunnels are not deleted on the cluster node as this is not owner for {}",
297 l2GatewayCache.remove(l2DeviceName);
299 l2GwDevice.removeL2GatewayId(input.getUuid());
301 elanClusterUtils.runOnlyInOwnerNode(l2GwDevice.getDeviceName(),
302 "handling delete of l2gwdevice delete itm tunnels ",
304 if (l2GwDevice.getHwvtepNodeId() == null) {
305 return Collections.emptyList();
307 // Cleaning up the config DS
308 NodeId nodeId = new NodeId(l2GwDevice.getHwvtepNodeId());
309 LOG.info("L2GatewayListener deleting the config nodes {} {}", nodeId, l2DeviceName);
310 NodeId psNodeId = HwvtepSouthboundUtils.createManagedNodeId(nodeId, l2DeviceName);
311 InstanceIdentifier<Node> psNodeIid = HwvtepSouthboundUtils.createInstanceIdentifier(psNodeId);
312 InstanceIdentifier<Node> globalIid = HwvtepSouthboundUtils.createInstanceIdentifier(nodeId);
314 List<ListenableFuture<?>> result = new ArrayList<>();
315 result.add(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
317 LOG.info("Deleting the zero day config for l2gw delete {}", psNodeIid);
318 l2gwZeroDayConfigUtil.deleteZeroDayConfig(tx, globalIid, l2GwDevice);
320 LOG.info("L2GatewayListener Deleting itm tunnels for {}", l2GwDevice.getDeviceName());
321 for (final IpAddress tunnelIpAddr : l2GwDevice.getTunnelIps()) {
322 L2GatewayUtils.deleteItmTunnels(itmRpcService, l2GwDevice.getHwvtepNodeId(),
323 l2DeviceName, tunnelIpAddr);
324 //result.add(ElanL2GatewayUtils.deleteItmTunnels(tunnelIpAddr, dataBroker));
325 LOG.info("L2GatewayListener Deleting itm tunnel {}", tunnelIpAddr);
331 l2GwDevice.removeL2GatewayId(input.getUuid());
332 LOG.info("ITM tunnels are not deleted for {} as this device has other L2gateway associations",
336 LOG.error("L2GatewayListener Unable to find L2 Gateway details for {}", l2DeviceName);
340 static class DeviceInterfaces {
341 Map<String, Map<String, Interfaces>> deviceInterfacesMap = new HashMap<>();
343 DeviceInterfaces(L2gateway l2gateway) {
344 if (l2gateway.getDevices() != null) {
345 l2gateway.getDevices().values().forEach((device) -> {
346 deviceInterfacesMap.putIfAbsent(device.getDeviceName(), new HashMap<>());
347 if (device.getInterfaces() != null) {
348 device.getInterfaces().values().forEach((intf) ->
349 deviceInterfacesMap.get(device.getDeviceName()).put(intf.getInterfaceName(), intf));
355 boolean containsInterface(String deviceName, String interfaceName) {
356 if (deviceInterfacesMap.containsKey(deviceName)) {
357 return deviceInterfacesMap.get(deviceName).containsKey(interfaceName);
363 private void scanNodesAndReplayDeviceGlobalNode(Devices l2Device, L2gateway input, String l2DeviceName) {
364 txRunner.callWithNewReadOnlyTransactionAndClose(OPERATIONAL, tx -> {
365 List<Node> allNodes = readAllOperNodes(tx);
366 for (Node psNode : allNodes) {
367 if (Objects.equals(HwvtepHAUtil.getPsName(psNode), l2DeviceName)) {
368 String globalNodeId = HwvtepHAUtil.getGlobalNodePathFromPSNode(psNode)
369 .firstKeyOf(Node.class).getNodeId().getValue();
370 replayGlobalNode(l2Device, input, l2DeviceName, globalNodeId);
377 private List<Node> readAllOperNodes(TypedReadTransaction<Operational> tx) {
378 Optional<Topology> topologyOptional = null;
381 topologyOptional = tx.read(HwvtepSouthboundUtils.createHwvtepTopologyInstanceIdentifier()).get();
382 } catch (InterruptedException | ExecutionException e) {
383 LOG.error("Failed to read oper nodes", e);
385 if (topologyOptional != null && topologyOptional.isPresent() && topologyOptional.get().getNode() != null) {
386 return new ArrayList<>(topologyOptional.get().getNode().values());
388 return Collections.emptyList();
392 private void replayGlobalNode(Devices l2Device, L2gateway input,
393 String l2DeviceName, String hwvtepNodeId) {
394 HwvtepHACache haCache = HwvtepHACache.getInstance();
395 if (haCache.isHAParentNode(HwvtepHAUtil.convertToInstanceIdentifier(hwvtepNodeId))) {
398 InstanceIdentifier<Node> globalIid = HwvtepHAUtil.convertToInstanceIdentifier(hwvtepNodeId);
399 InstanceIdentifier<Node> psIid = HwvtepHAUtil.convertToInstanceIdentifier(
400 hwvtepNodeId + "/physicalswitch/" + l2DeviceName);
401 replayGlobalNode(globalIid, psIid, l2Device, input, haCache, hwvtepNodeId, l2DeviceName);
404 private void replayGlobalNode(InstanceIdentifier<Node> globalIid,
405 final InstanceIdentifier<Node> psIid,
406 final Devices l2Device, final L2gateway input,
407 HwvtepHACache haCache,
409 String l2DeviceName) {
410 txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL, tx -> {
411 String globalId = hwvtepNodeId;
412 final Optional<Node> globalNode = tx.read(globalIid).get();
413 if (!globalNode.isPresent()) {
414 LOG.error("replayGlobalNode Global Node not present in oper store {}", globalId);
417 final Optional<Node> psNode = tx.read(psIid).get();
419 haOpClusteredListener.onGlobalNodeAdd(globalIid, globalNode.get(), tx);
420 if (!haCache.isHAEnabledDevice(globalIid)) {
421 LOG.error("replayGlobalNode Non ha node connected {}", globalId);
424 globalId = haCache.getParent(globalIid).firstKeyOf(Node.class).getNodeId().getValue();
425 haOpNodeListener.onGlobalNodeAdd(globalIid, globalNode.get(), tx);
426 if (!psNode.isPresent()) {
427 LOG.error("replayGlobalNode ps node not present in oper store {}", psIid);
430 haOpNodeListener.onPsNodeAdd(psIid, psNode.get(), tx);
431 PhysicalSwitchAugmentation psAugmentation = psNode.get().augmentation(
432 PhysicalSwitchAugmentation.class);
433 if (psAugmentation != null
434 && psAugmentation.getTunnelIps() != null && !psAugmentation.getTunnelIps().isEmpty()) {
435 l2GatewayCache.updateL2GatewayCache(
436 l2DeviceName, globalId, new ArrayList<>(psAugmentation.nonnullTunnelIps().values()));
438 LOG.error("replayGlobalNode Failed to find tunnel ips for {}", psIid);