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