Bulk merge of l2gw changes
[netvirt.git] / elanmanager / impl / src / main / java / org / opendaylight / netvirt / elan / l2gw / listeners / L2GatewayListener.java
1 /*
2  * Copyright (c) 2019 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.netvirt.elan.l2gw.listeners;
9
10 import static org.opendaylight.mdsal.binding.util.Datastore.CONFIGURATION;
11 import static org.opendaylight.mdsal.binding.util.Datastore.OPERATIONAL;
12
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;
23 import java.util.Map;
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;
70
71 @Singleton
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;
85
86     @Inject
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(),
112                 this);
113         init();
114     }
115
116     public void init() {
117         ResourceBatchingManager.getInstance().registerDefaultBatchHandlers(this.dataBroker);
118         LOG.info("{} init", getClass().getSimpleName());
119         // registerListener(); called from L2GatewayConnection listener
120     }
121
122     @Override
123     public void register() {
124         LOG.info("Registering L2Gateway Listener Override Method");
125         super.register();
126     }
127
128     @Override
129     public void registerListener() {
130         LOG.info("Registering L2Gateway Listener");
131         super.register();
132     }
133
134     @Override
135     public void deregisterListener() {
136         LOG.info("Deregistering L2GatewayListener");
137         super.close();
138     }
139
140     @Override
141     @PreDestroy
142     public void close() {
143         super.close();
144         Executors.shutdownAndAwaitTermination(getExecutorService());
145     }
146
147     @Override
148     public void add(final InstanceIdentifier<L2gateway> identifier, final L2gateway input) {
149         LOG.info("Adding L2gateway with ID: {}", input);
150
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);
155         }
156     }
157
158     @Override
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());
163
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());
168                 tx.delete(iid);
169             }
170         });
171
172         Collection<Devices> l2Devices = input.getDevices().values();
173         for (Devices l2Device : l2Devices) {
174             LOG.info("Removing L2gateway device: {}", l2Device);
175             removeL2Device(l2Device, input);
176         }
177     }
178
179     @Override
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());
187             return;
188         }
189         if (original.getDevices() == null) {
190             connections.forEach(
191                 (connection) -> l2gwService.addL2GatewayConnection(connection));
192             return;
193         }
194         elanClusterUtils.runOnlyInOwnerNode("l2gw.update", () -> {
195             DeviceInterfaces updatedDeviceInterfaces = new DeviceInterfaces(update);
196             FluentFuture<?> fts = txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
197                 original.getDevices().values()
198                     .stream()
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()
206                             .stream()
207                             .filter((intf) -> !updatedDeviceInterfaces.containsInterface(
208                                 deviceName, intf.getInterfaceName()))
209                             .forEach((intf) -> {
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);
217                                         }
218                                     } else {
219                                         LOG.info("Deleting vlan binding {} {} {}",
220                                             physicalSwitchNodeId, intf.getInterfaceName(), vlanId);
221                                         HwvtepUtils.deleteVlanBinding(tx, physicalSwitchNodeId,
222                                             intf.getInterfaceName(), vlanId);
223                                     }
224                                 });
225                             });
226                     });
227             });
228             fts.addCallback(new FutureCallback<Object>() {
229                 @Override
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));
234                 }
235
236                 @Override
237                 public void onFailure(Throwable throwable) {
238                     LOG.error("Failed to delete vlan bindings as part of l2gw udpate {}", update);
239                 }
240             }, MoreExecutors.directExecutor());
241         });
242     }
243
244     private synchronized void addL2Device(Devices l2Device, L2gateway input) {
245         String l2DeviceName = l2Device.getDeviceName();
246
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);
254         }
255         l2GwDevice.addL2GatewayId(input.getUuid());
256
257         if (l2GwDevice.getHwvtepNodeId() == null) {
258             LOG.info("L2GW provisioning skipped for device {}",l2DeviceName);
259         } else {
260             transportZoneListener.createZeroDayForL2Device(l2GwDevice);
261             LOG.info("Provisioning l2gw for device {}",l2DeviceName);
262             l2gwService.provisionItmAndL2gwConnection(l2GwDevice, l2DeviceName, l2GwDevice.getHwvtepNodeId(),
263                     l2GwDevice.getTunnelIp());
264         }
265     }
266
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()) {
275                     /*
276                     l2GwDevice.removeL2GatewayId(input.getUuid());
277                     // Delete ITM tunnels
278                     final String hwvtepId = l2GwDevice.getHwvtepNodeId();
279
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);
287                             }
288                         } else {
289                             LOG.info("ITM Tunnels are not deleted on the cluster node as this is not owner for {}",
290                                     l2DeviceName);
291                         }
292
293                         return null;
294                     });
295                     */
296                 } else {
297                     l2GatewayCache.remove(l2DeviceName);
298                 }
299                 l2GwDevice.removeL2GatewayId(input.getUuid());
300                 //Delete itm tunnels
301                 elanClusterUtils.runOnlyInOwnerNode(l2GwDevice.getDeviceName(),
302                     "handling delete of l2gwdevice delete itm tunnels ",
303                     () -> {
304                         if (l2GwDevice.getHwvtepNodeId() == null) {
305                             return Collections.emptyList();
306                         }
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);
313
314                         List<ListenableFuture<?>> result = new ArrayList<>();
315                         result.add(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
316                             tx -> {
317                                 LOG.info("Deleting the zero day config for l2gw delete {}", psNodeIid);
318                                 l2gwZeroDayConfigUtil.deleteZeroDayConfig(tx, globalIid, l2GwDevice);
319                             }));
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);
326                         }
327                         return result;
328                     }
329                 );
330             } else {
331                 l2GwDevice.removeL2GatewayId(input.getUuid());
332                 LOG.info("ITM tunnels are not deleted for {} as this device has other L2gateway associations",
333                         l2DeviceName);
334             }
335         } else {
336             LOG.error("L2GatewayListener Unable to find L2 Gateway details for {}", l2DeviceName);
337         }
338     }
339
340     static class DeviceInterfaces {
341         Map<String, Map<String, Interfaces>> deviceInterfacesMap = new HashMap<>();
342
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));
350                     }
351                 });
352             }
353         }
354
355         boolean containsInterface(String deviceName, String interfaceName) {
356             if (deviceInterfacesMap.containsKey(deviceName)) {
357                 return deviceInterfacesMap.get(deviceName).containsKey(interfaceName);
358             }
359             return false;
360         }
361     }
362
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);
371                 }
372             }
373         });
374
375     }
376
377     private List<Node> readAllOperNodes(TypedReadTransaction<Operational> tx) {
378         Optional<Topology> topologyOptional = null;
379         try {
380
381             topologyOptional = tx.read(HwvtepSouthboundUtils.createHwvtepTopologyInstanceIdentifier()).get();
382         } catch (InterruptedException | ExecutionException e) {
383             LOG.error("Failed to read oper nodes", e);
384         }
385         if (topologyOptional != null && topologyOptional.isPresent() && topologyOptional.get().getNode() != null) {
386             return new ArrayList<>(topologyOptional.get().getNode().values());
387         }
388         return Collections.emptyList();
389     }
390
391
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))) {
396             return;
397         }
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);
402     }
403
404     private void replayGlobalNode(InstanceIdentifier<Node> globalIid,
405                                   final InstanceIdentifier<Node> psIid,
406                                   final Devices l2Device, final L2gateway input,
407                                   HwvtepHACache haCache,
408                                   String hwvtepNodeId,
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);
415                 return;
416             }
417             final Optional<Node> psNode = tx.read(psIid).get();
418
419             haOpClusteredListener.onGlobalNodeAdd(globalIid, globalNode.get(), tx);
420             if (!haCache.isHAEnabledDevice(globalIid)) {
421                 LOG.error("replayGlobalNode Non ha node connected {}", globalId);
422                 return;
423             }
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);
428                 return;
429             }
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()));
437             } else {
438                 LOG.error("replayGlobalNode Failed to find tunnel ips for {}", psIid);
439             }
440         });
441
442     }
443 }