logging issues in elanmanager
[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.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;
23
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;
62
63 /**
64  * Listener to handle physical switch updates.
65  */
66 @Singleton
67 public class HwvtepPhysicalSwitchListener
68         extends HwvtepAbstractDataTreeChangeListener<PhysicalSwitchAugmentation, HwvtepPhysicalSwitchListener>
69         implements ClusteredDataTreeChangeListener<PhysicalSwitchAugmentation> {
70
71     /** The Constant LOG. */
72     private static final Logger LOG = LoggerFactory.getLogger(HwvtepPhysicalSwitchListener.class);
73
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());
79         };
80
81     private static final Predicate<PhysicalSwitchAugmentation> TUNNEL_IP_AVAILABLE =
82         phySwitch -> !HwvtepHAUtil.isEmpty(phySwitch.getTunnelIps());
83
84     private static final Predicate<PhysicalSwitchAugmentation> TUNNEL_IP_NOT_AVAILABLE = TUNNEL_IP_AVAILABLE.negate();
85
86     private static final BiPredicate<PhysicalSwitchAugmentation, L2GatewayDevice> TUNNEL_IP_CHANGED =
87         (phySwitchAfter, existingDevice) -> {
88             return TUNNEL_IP_AVAILABLE.test(phySwitchAfter)
89                     && !Objects.equals(
90                             existingDevice.getTunnelIp(),  phySwitchAfter.getTunnelIps().get(0).getTunnelIpsKey());
91         };
92
93     /** The data broker. */
94     private final DataBroker dataBroker;
95     private final ManagedNewTransactionRunner txRunner;
96
97     /** The itm rpc service. */
98     private final ItmRpcService itmRpcService;
99
100     private final ElanClusterUtils elanClusterUtils;
101
102     private final HwvtepHACache hwvtepHACache = HwvtepHACache.getInstance();
103
104     private final L2gwServiceProvider l2gwServiceProvider;
105
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);
113         };
114
115     private final Predicate<L2GatewayDevice> alreadyHasL2Gwids =
116         (l2GwDevice) -> {
117             return l2GwDevice != null && HwvtepHAUtil.isEmpty(l2GwDevice.getL2GatewayIds());
118         };
119
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());
125             }
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));
132         };
133
134
135     private final HAOpClusteredListener haOpClusteredListener;
136
137     private final L2GatewayCache l2GatewayCache;
138
139     private final StaleVlanBindingsCleaner staleVlanBindingsCleaner;
140
141     /**
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
150      */
151     @Inject
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;
165     }
166
167     @Override
168     @PostConstruct
169     public void init() {
170         registerListener(LogicalDatastoreType.OPERATIONAL, dataBroker);
171     }
172
173     @Override
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);
178     }
179
180     @Override
181     protected HwvtepPhysicalSwitchListener getDataTreeChangeListener() {
182         return HwvtepPhysicalSwitchListener.this;
183     }
184
185     @Override
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());
191
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));
199             } else {
200                 LOG.debug("{} details are not removed from L2Gateway Cache as it has L2Gateway reference", psName);
201             }
202
203             l2GwDevice.setConnected(false);
204             //ElanL2GwCacheUtils.removeL2GatewayDeviceFromAllElanCache(psName);
205         } else {
206             LOG.error("Unable to find L2 Gateway details for {}", psName);
207         }
208     }
209
210     /**
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 ().
216      *
217      * @param identifier iid
218      * @param phySwitchBefore ps Node before update
219      * @param phySwitchAfter ps Node after update
220      */
221     @Override
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());
230             return;
231         }
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);
235
236         if (DEVICE_NOT_CACHED_OR_PARENT_CONNECTED.test(existingDevice, globalNodeIid)) {
237             if (TUNNEL_IP_AVAILABLE.test(phySwitchAfter)) {
238                 added(identifier, phySwitchAfter);
239             }
240         } else {
241             if (!Objects.equals(phySwitchAfter.getTunnelIps(), phySwitchBefore.getTunnelIps())
242                     && TUNNEL_IP_CHANGED.test(phySwitchAfter, existingDevice)) {
243
244                 final String hwvtepId = existingDevice.getHwvtepNodeId();
245                 elanClusterUtils.runOnlyInOwnerNode(existingDevice.getDeviceName(),
246                     "handling Physical Switch add create itm tunnels ",
247                     () -> {
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();
256                     }
257                 );
258                 try {
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 ");
262                 }
263                 existingDevice.setTunnelIps(new HashSet<>());
264                 added(identifier, phySwitchAfter);
265             }
266         }
267     }
268
269     @Override
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());
277             return;
278         }
279         final String psName = getPsName(identifier);
280         LOG.trace("Received physical switch {} added event received for node {}", psName, nodeId.getValue());
281
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);
286                 return;
287             }
288             HAOpClusteredListener.addToCacheIfHAChildNode(globalNodeIid, node.get());
289             if (hwvtepHACache.isHAEnabledDevice(globalNodeIid)) {
290                 LOG.trace("Ha enabled device {}", globalNodeIid);
291                 return;
292             }
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());
298                 return;
299             }
300             InstanceIdentifier<Node> existingIid = globalNodeIid;
301             if (l2GwDevice != null && l2GwDevice.getHwvtepNodeId() != null) {
302                 existingIid = HwvtepHAUtil.convertToInstanceIdentifier(l2GwDevice.getHwvtepNodeId());
303             }
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());
310             }
311
312             l2GwDevice = l2GatewayCache.addOrGet(psName);
313             l2GwDevice.setConnected(true);
314             l2GwDevice.setHwvtepNodeId(globalNodeId);
315
316             List<TunnelIps> tunnelIps = phySwitchAdded.getTunnelIps();
317             if (tunnelIps != null) {
318                 for (TunnelIps tunnelIp : tunnelIps) {
319                     IpAddress tunnelIpAddr = tunnelIp.getTunnelIpsKey();
320                     l2GwDevice.addTunnelIp(tunnelIpAddr);
321                 }
322             }
323
324             handleAdd(l2GwDevice);
325             elanClusterUtils.runOnlyInOwnerNode("Update config tunnels IP ", () -> {
326                 try {
327                     updateConfigTunnelIp(identifier, phySwitchAdded);
328                 } catch (ReadFailedException e) {
329                     LOG.error("Failed to update tunnel ips {}", identifier);
330                 }
331             });
332             return;
333         });
334     }
335
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));
341         }
342         return hwvtepHACache.isHAEnabledDevice(globalNodeId);
343     }
344
345     /**
346      * Handle add.
347      *
348      * @param l2GwDevice
349      *            the l2 gw device
350      */
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);
360             } else {
361                 LOG.info("l2gw.provision.skip {}:{}", hwvtepNodeId, psName);
362             }
363         }
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);
370         });
371     }
372
373
374     /**
375      * Gets the node id.
376      *
377      * @param identifier
378      *            the identifier
379      * @return the node id
380      */
381     private NodeId getNodeId(InstanceIdentifier<PhysicalSwitchAugmentation> identifier) {
382         return identifier.firstKeyOf(Node.class).getNodeId();
383     }
384
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));
389         }
390         return psNodeId;
391     }
392
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)));
398         }
399         return null;
400     }
401
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
406                     .length());
407         }
408         return null;
409     }
410
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());
421                     }
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);
426         }
427     }
428 }