NETVIRT-1241 : Preprovisioning of l2gw conf. fails
[netvirt.git] / elanmanager / impl / src / main / java / org / opendaylight / netvirt / elan / l2gw / listeners / HwvtepPhysicalSwitchListener.java
1 /*
2  * Copyright (c) 2016 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
9 package org.opendaylight.netvirt.elan.l2gw.listeners;
10
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;
16 import java.util.Set;
17 import java.util.function.BiPredicate;
18 import java.util.function.Predicate;
19 import javax.annotation.PostConstruct;
20 import javax.inject.Inject;
21 import javax.inject.Singleton;
22 import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener;
23 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
24 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
25 import org.opendaylight.genius.datastoreutils.hwvtep.HwvtepAbstractDataTreeChangeListener;
26 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
27 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
28 import org.opendaylight.genius.mdsalutil.MDSALUtil;
29 import org.opendaylight.genius.utils.hwvtep.HwvtepNodeHACache;
30 import org.opendaylight.genius.utils.hwvtep.HwvtepSouthboundConstants;
31 import org.opendaylight.genius.utils.hwvtep.HwvtepSouthboundUtils;
32 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
33 import org.opendaylight.netvirt.elan.l2gw.ha.HwvtepHAUtil;
34 import org.opendaylight.netvirt.elan.l2gw.ha.listeners.HAOpClusteredListener;
35 import org.opendaylight.netvirt.elan.l2gw.utils.ElanL2GatewayUtils;
36 import org.opendaylight.netvirt.elan.l2gw.utils.L2GatewayConnectionUtils;
37 import org.opendaylight.netvirt.elan.l2gw.utils.L2GatewayUtils;
38 import org.opendaylight.netvirt.elan.l2gw.utils.L2gwServiceProvider;
39 import org.opendaylight.netvirt.elan.l2gw.utils.StaleVlanBindingsCleaner;
40 import org.opendaylight.netvirt.elan.utils.ElanClusterUtils;
41 import org.opendaylight.netvirt.elanmanager.utils.ElanL2GwCacheUtils;
42 import org.opendaylight.netvirt.neutronvpn.api.l2gw.L2GatewayCache;
43 import org.opendaylight.netvirt.neutronvpn.api.l2gw.L2GatewayDevice;
44 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.PhysicalSwitchAugmentation;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.PhysicalSwitchAugmentationBuilder;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.physical._switch.attributes.TunnelIps;
49 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
50 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
51 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
52 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
53 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
54 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
55 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
58
59 /**
60  * Listener to handle physical switch updates.
61  */
62 @Singleton
63 public class HwvtepPhysicalSwitchListener
64         extends HwvtepAbstractDataTreeChangeListener<PhysicalSwitchAugmentation, HwvtepPhysicalSwitchListener>
65         implements ClusteredDataTreeChangeListener<PhysicalSwitchAugmentation> {
66
67     /** The Constant LOG. */
68     private static final Logger LOG = LoggerFactory.getLogger(HwvtepPhysicalSwitchListener.class);
69
70     private static final BiPredicate<L2GatewayDevice, InstanceIdentifier<Node>> DEVICE_NOT_CACHED_OR_PARENT_CONNECTED =
71         (l2GatewayDevice, globalIid) -> l2GatewayDevice == null || l2GatewayDevice.getHwvtepNodeId() == null
72                 || !Objects.equals(l2GatewayDevice.getHwvtepNodeId(),
73                         globalIid.firstKeyOf(Node.class).getNodeId().getValue());
74
75     private static final Predicate<PhysicalSwitchAugmentation> TUNNEL_IP_AVAILABLE =
76         phySwitch -> !HwvtepHAUtil.isEmpty(phySwitch.getTunnelIps());
77
78     private static final Predicate<PhysicalSwitchAugmentation> TUNNEL_IP_NOT_AVAILABLE = TUNNEL_IP_AVAILABLE.negate();
79
80     private static final BiPredicate<PhysicalSwitchAugmentation, L2GatewayDevice> TUNNEL_IP_CHANGED =
81         (phySwitchAfter, existingDevice) -> TUNNEL_IP_AVAILABLE.test(phySwitchAfter)
82                 && !Objects.equals(
83                         existingDevice.getTunnelIp(),  phySwitchAfter.getTunnelIps().get(0).getTunnelIpsKey());
84
85     /** The data broker. */
86     private final DataBroker dataBroker;
87     private final ManagedNewTransactionRunner txRunner;
88
89     /** The itm rpc service. */
90     private final ItmRpcService itmRpcService;
91
92     private final ElanClusterUtils elanClusterUtils;
93
94     private final HwvtepNodeHACache hwvtepNodeHACache;
95
96     private final L2gwServiceProvider l2gwServiceProvider;
97
98     private final BiPredicate<L2GatewayDevice, InstanceIdentifier<Node>> childConnectedAfterParent;
99
100     private final Predicate<L2GatewayDevice> alreadyHasL2Gwids =
101         (l2GwDevice) -> l2GwDevice != null && HwvtepHAUtil.isEmpty(l2GwDevice.getL2GatewayIds());
102
103     private final BiPredicate<L2GatewayDevice, InstanceIdentifier<Node>> parentConnectedAfterChild;
104     private final HAOpClusteredListener haOpClusteredListener;
105
106     private final L2GatewayCache l2GatewayCache;
107
108     private final StaleVlanBindingsCleaner staleVlanBindingsCleaner;
109
110     /**
111      * Instantiates a new hwvtep physical switch listener.
112      */
113     @Inject
114     public HwvtepPhysicalSwitchListener(final DataBroker dataBroker, ItmRpcService itmRpcService,
115             ElanClusterUtils elanClusterUtils, L2gwServiceProvider l2gwServiceProvider,
116             HAOpClusteredListener haListener, L2GatewayCache l2GatewayCache,
117             StaleVlanBindingsCleaner staleVlanBindingsCleaner,
118             HwvtepNodeHACache hwvtepNodeHACache) {
119         super(PhysicalSwitchAugmentation.class, HwvtepPhysicalSwitchListener.class, hwvtepNodeHACache);
120         this.dataBroker = dataBroker;
121         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
122         this.itmRpcService = itmRpcService;
123         this.elanClusterUtils = elanClusterUtils;
124         this.l2gwServiceProvider = l2gwServiceProvider;
125         this.staleVlanBindingsCleaner = staleVlanBindingsCleaner;
126         this.haOpClusteredListener = haListener;
127         this.l2GatewayCache = l2GatewayCache;
128         this.hwvtepNodeHACache = hwvtepNodeHACache;
129
130         childConnectedAfterParent = (l2GwDevice, globalIid) -> {
131             return !hwvtepNodeHACache.isHAParentNode(globalIid)
132                     && l2GwDevice != null && l2GwDevice.getHwvtepNodeId() != null
133                     && !Objects.equals(l2GwDevice.getHwvtepNodeId(), globalIid.firstKeyOf(Node.class)
134                     .getNodeId().getValue());
135         };
136
137         parentConnectedAfterChild = (l2GwDevice, globalIid) -> {
138             InstanceIdentifier<Node> existingIid = globalIid;
139             if (l2GwDevice != null && l2GwDevice.getHwvtepNodeId() != null) {
140                 existingIid = HwvtepHAUtil.convertToInstanceIdentifier(l2GwDevice.getHwvtepNodeId());
141             }
142             return hwvtepNodeHACache.isHAParentNode(globalIid)
143                     && l2GwDevice != null && l2GwDevice.getHwvtepNodeId() != null
144                     && !Objects.equals(l2GwDevice.getHwvtepNodeId(), globalIid.firstKeyOf(Node.class)
145                     .getNodeId().getValue())
146                     && Objects.equals(globalIid, hwvtepNodeHACache.getParent(existingIid));
147         };
148     }
149
150     @Override
151     @PostConstruct
152     public void init() {
153         registerListener(LogicalDatastoreType.OPERATIONAL, dataBroker);
154     }
155
156     @Override
157     protected InstanceIdentifier<PhysicalSwitchAugmentation> getWildCardPath() {
158         return InstanceIdentifier.create(NetworkTopology.class)
159                 .child(Topology.class, new TopologyKey(HwvtepSouthboundConstants.HWVTEP_TOPOLOGY_ID)).child(Node.class)
160                 .augmentation(PhysicalSwitchAugmentation.class);
161     }
162
163     @Override
164     protected HwvtepPhysicalSwitchListener getDataTreeChangeListener() {
165         return HwvtepPhysicalSwitchListener.this;
166     }
167
168     @Override
169     protected void removed(InstanceIdentifier<PhysicalSwitchAugmentation> identifier,
170             PhysicalSwitchAugmentation phySwitchDeleted) {
171         NodeId nodeId = getNodeId(identifier);
172         String psName = phySwitchDeleted.getHwvtepNodeName().getValue();
173         LOG.info("Received physical switch {} removed event for node {}", psName, nodeId.getValue());
174
175         L2GatewayDevice l2GwDevice = l2GatewayCache.get(psName);
176         if (l2GwDevice != null) {
177             if (!L2GatewayConnectionUtils.isGatewayAssociatedToL2Device(l2GwDevice)) {
178                 l2GatewayCache.remove(psName);
179                 LOG.debug("{} details removed from L2Gateway Cache", psName);
180                 MDSALUtil.syncDelete(this.dataBroker, LogicalDatastoreType.CONFIGURATION,
181                         HwvtepSouthboundUtils.createInstanceIdentifier(nodeId));
182             } else {
183                 LOG.debug("{} details are not removed from L2Gateway Cache as it has L2Gateway reference", psName);
184             }
185
186             l2GwDevice.setConnected(false);
187             //ElanL2GwCacheUtils.removeL2GatewayDeviceFromAllElanCache(psName);
188         } else {
189             LOG.error("Unable to find L2 Gateway details for {}", psName);
190         }
191     }
192
193     /**
194      * Upon update checks if the tunnels Ip was null earlier and it got newly added.
195      * In that case simply call add.
196      * If not then check if Tunnel Ip has been updated from an old value to new value.
197      * If yes. delete old ITM tunnels of odl Tunnel Ipand add new ITM tunnels with new Tunnel
198      * IP then call added ().
199      *
200      * @param identifier iid
201      * @param phySwitchBefore ps Node before update
202      * @param phySwitchAfter ps Node after update
203      */
204     @Override
205     protected void updated(InstanceIdentifier<PhysicalSwitchAugmentation> identifier,
206             PhysicalSwitchAugmentation phySwitchBefore, PhysicalSwitchAugmentation phySwitchAfter) {
207         NodeId nodeId = getNodeId(identifier);
208         LOG.trace("Received PhysicalSwitch Update Event for node {}: PhysicalSwitch Before: {}, "
209                 + "PhysicalSwitch After: {}", nodeId.getValue(), phySwitchBefore, phySwitchAfter);
210         String psName = getPsName(identifier);
211         if (psName == null) {
212             LOG.error("Could not find the physical switch name for node {}", nodeId.getValue());
213             return;
214         }
215         L2GatewayDevice existingDevice = l2GatewayCache.get(psName);
216         LOG.info("Received physical switch {} update event for node {}", psName, nodeId.getValue());
217         InstanceIdentifier<Node> globalNodeIid = getManagedByNodeIid(identifier);
218
219         if (DEVICE_NOT_CACHED_OR_PARENT_CONNECTED.test(existingDevice, globalNodeIid)) {
220             if (TUNNEL_IP_AVAILABLE.test(phySwitchAfter)) {
221                 added(identifier, phySwitchAfter);
222             }
223         } else {
224             if (!Objects.equals(phySwitchAfter.getTunnelIps(), phySwitchBefore.getTunnelIps())
225                     && TUNNEL_IP_CHANGED.test(phySwitchAfter, existingDevice)) {
226
227                 final String hwvtepId = existingDevice.getHwvtepNodeId();
228                 elanClusterUtils.runOnlyInOwnerNode(existingDevice.getDeviceName(),
229                     "handling Physical Switch add create itm tunnels ",
230                     () -> {
231                         LOG.info("Deleting itm tunnels for device {}", existingDevice.getDeviceName());
232                         L2GatewayUtils.deleteItmTunnels(itmRpcService, hwvtepId,
233                                 existingDevice.getDeviceName(), existingDevice.getTunnelIp());
234                         Thread.sleep(10000L);//TODO remove these sleeps
235                         LOG.info("Creating itm tunnels for device {}", existingDevice.getDeviceName());
236                         ElanL2GatewayUtils.createItmTunnels(dataBroker, itmRpcService, hwvtepId, psName,
237                                 phySwitchAfter.getTunnelIps().get(0).getTunnelIpsKey());
238                         return Collections.emptyList();
239                     }
240                 );
241                 try {
242                     Thread.sleep(20000L);//TODO remove the sleep by using better itm api to detect finish of prev op
243                 } catch (InterruptedException e) {
244                     LOG.error("Interrupted ");
245                 }
246                 existingDevice.setTunnelIps(new HashSet<>());
247                 added(identifier, phySwitchAfter);
248             }
249         }
250     }
251
252     @Override
253     protected void added(InstanceIdentifier<PhysicalSwitchAugmentation> identifier,
254                          final PhysicalSwitchAugmentation phySwitchAdded) {
255         String globalNodeId = getManagedByNodeId(identifier);
256         final InstanceIdentifier<Node> globalNodeIid = getManagedByNodeIid(identifier);
257         NodeId nodeId = getNodeId(identifier);
258         if (TUNNEL_IP_NOT_AVAILABLE.test(phySwitchAdded)) {
259             LOG.error("Could not find the /tunnel ips for node {}", nodeId.getValue());
260             return;
261         }
262         final String psName = getPsName(identifier);
263         LOG.trace("Received physical switch {} added event received for node {}", psName, nodeId.getValue());
264
265         haOpClusteredListener.runAfterNodeIsConnected(globalNodeIid, (node) -> {
266             LOG.trace("Running job for node {} ", globalNodeIid);
267             if (!node.isPresent()) {
268                 LOG.error("Global node is absent {}", globalNodeId);
269                 return;
270             }
271             HwvtepHAUtil.addToCacheIfHAChildNode(globalNodeIid, node.get(), hwvtepNodeHACache);
272             if (hwvtepNodeHACache.isHAEnabledDevice(globalNodeIid)) {
273                 LOG.trace("Ha enabled device {}", globalNodeIid);
274                 return;
275             }
276             LOG.trace("Updating cache for node {}", globalNodeIid);
277             L2GatewayDevice l2GwDevice = l2GatewayCache.get(psName);
278             if (childConnectedAfterParent.test(l2GwDevice, globalNodeIid)) {
279                 LOG.trace("Device {} {} is already Connected by {}",
280                         psName, globalNodeId, l2GwDevice.getHwvtepNodeId());
281                 return;
282             }
283             InstanceIdentifier<Node> existingIid = globalNodeIid;
284             if (l2GwDevice != null && l2GwDevice.getHwvtepNodeId() != null) {
285                 existingIid = HwvtepHAUtil.convertToInstanceIdentifier(l2GwDevice.getHwvtepNodeId());
286             }
287             if (parentConnectedAfterChild.test(l2GwDevice, globalNodeIid)
288                     && alreadyHasL2Gwids.test(l2GwDevice)) {
289                 LOG.error("Child node {} having l2gw configured became ha node "
290                                 + " removing the l2device {} from all elan cache and provision parent node {}",
291                           existingIid, psName, globalNodeIid);
292                 ElanL2GwCacheUtils.removeL2GatewayDeviceFromAllElanCache(l2GwDevice.getHwvtepNodeId());
293             }
294
295             l2GwDevice = l2GatewayCache.addOrGet(psName);
296             l2GwDevice.setConnected(true);
297             l2GwDevice.setHwvtepNodeId(globalNodeId);
298
299             List<TunnelIps> tunnelIps = phySwitchAdded.getTunnelIps();
300             if (tunnelIps != null) {
301                 for (TunnelIps tunnelIp : tunnelIps) {
302                     IpAddress tunnelIpAddr = tunnelIp.getTunnelIpsKey();
303                     l2GwDevice.addTunnelIp(tunnelIpAddr);
304                 }
305             }
306
307             handleAdd(l2GwDevice);
308             elanClusterUtils.runOnlyInOwnerNode("Update config tunnels IP ",
309                 () -> updateConfigTunnelIp(identifier, phySwitchAdded));
310         });
311     }
312
313     /**
314      * Handle add.
315      *
316      * @param l2GwDevice
317      *            the l2 gw device
318      */
319     private void handleAdd(L2GatewayDevice l2GwDevice) {
320         final String psName = l2GwDevice.getDeviceName();
321         final String hwvtepNodeId = l2GwDevice.getHwvtepNodeId();
322         Set<IpAddress> tunnelIps = l2GwDevice.getTunnelIps();
323         for (final IpAddress tunnelIpAddr : tunnelIps) {
324             if (L2GatewayConnectionUtils.isGatewayAssociatedToL2Device(l2GwDevice)) {
325                 LOG.debug("L2Gateway {} associated for {} physical switch; creating ITM tunnels for {}",
326                         l2GwDevice.getL2GatewayIds(), psName, tunnelIpAddr);
327                 l2gwServiceProvider.provisionItmAndL2gwConnection(l2GwDevice, psName, hwvtepNodeId, tunnelIpAddr);
328             } else {
329                 LOG.info("l2gw.provision.skip {}:{}", hwvtepNodeId, psName);
330             }
331         }
332         elanClusterUtils.runOnlyInOwnerNode("Stale entry cleanup", () -> {
333             InstanceIdentifier<Node> globalNodeIid = HwvtepSouthboundUtils.createInstanceIdentifier(
334                     new NodeId(hwvtepNodeId));
335             InstanceIdentifier<Node> psIid = HwvtepSouthboundUtils.createInstanceIdentifier(
336                     HwvtepSouthboundUtils.createManagedNodeId(new NodeId(hwvtepNodeId), psName));
337             staleVlanBindingsCleaner.scheduleStaleCleanup(psName, globalNodeIid, psIid);
338         });
339     }
340
341
342     /**
343      * Gets the node id.
344      *
345      * @param identifier
346      *            the identifier
347      * @return the node id
348      */
349     private NodeId getNodeId(InstanceIdentifier<PhysicalSwitchAugmentation> identifier) {
350         return identifier.firstKeyOf(Node.class).getNodeId();
351     }
352
353     private String getManagedByNodeId(InstanceIdentifier<PhysicalSwitchAugmentation> identifier) {
354         String psNodeId = identifier.firstKeyOf(Node.class).getNodeId().getValue();
355         if (psNodeId.contains(HwvtepHAUtil.PHYSICALSWITCH)) {
356             return psNodeId.substring(0, psNodeId.indexOf(HwvtepHAUtil.PHYSICALSWITCH));
357         }
358         return psNodeId;
359     }
360
361     private InstanceIdentifier<Node> getManagedByNodeIid(InstanceIdentifier<PhysicalSwitchAugmentation> identifier) {
362         String psNodeId = identifier.firstKeyOf(Node.class).getNodeId().getValue();
363         if (psNodeId.contains(HwvtepHAUtil.PHYSICALSWITCH)) {
364             psNodeId = psNodeId.substring(0, psNodeId.indexOf(HwvtepHAUtil.PHYSICALSWITCH));
365             return identifier.firstIdentifierOf(Topology.class).child(Node.class, new NodeKey(new NodeId(psNodeId)));
366         }
367         return null;
368     }
369
370     private String getPsName(InstanceIdentifier<PhysicalSwitchAugmentation> identifier) {
371         String psNodeId = identifier.firstKeyOf(Node.class).getNodeId().getValue();
372         if (psNodeId.contains(HwvtepHAUtil.PHYSICALSWITCH)) {
373             return psNodeId.substring(psNodeId.indexOf(HwvtepHAUtil.PHYSICALSWITCH) + HwvtepHAUtil.PHYSICALSWITCH
374                     .length());
375         }
376         return null;
377     }
378
379     private void updateConfigTunnelIp(InstanceIdentifier<PhysicalSwitchAugmentation> identifier,
380                                       PhysicalSwitchAugmentation phySwitchAdded) {
381         if (phySwitchAdded.getTunnelIps() != null) {
382             ListenableFutures.addErrorLogging(
383                 txRunner.callWithNewReadWriteTransactionAndSubmit(tx -> {
384                     Optional<PhysicalSwitchAugmentation> existingSwitch = tx.read(
385                             LogicalDatastoreType.CONFIGURATION, identifier).checkedGet();
386                     PhysicalSwitchAugmentationBuilder psBuilder = new PhysicalSwitchAugmentationBuilder();
387                     if (existingSwitch.isPresent()) {
388                         psBuilder = new PhysicalSwitchAugmentationBuilder(existingSwitch.get());
389                     }
390                     psBuilder.setTunnelIps(phySwitchAdded.getTunnelIps());
391                     tx.put(LogicalDatastoreType.CONFIGURATION, identifier, psBuilder.build(), true);
392                     LOG.trace("Updating config tunnel ips {}", identifier);
393                 }), LOG, "Failed to update the config tunnel ips {}", identifier);
394         }
395     }
396 }