Cleaning up InterfacestateRemoveWorker
[genius.git] / itm / itm-impl / src / main / java / org / opendaylight / genius / itm / itmdirecttunnels / listeners / TunnelInventoryStateListener.java
1 /*
2  * Copyright (c) 2018 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.genius.itm.itmdirecttunnels.listeners;
9
10 import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
11 import static org.opendaylight.genius.infra.Datastore.OPERATIONAL;
12
13 import com.google.common.util.concurrent.ListenableFuture;
14 import java.math.BigInteger;
15 import java.util.ArrayList;
16 import java.util.Collections;
17 import java.util.List;
18 import java.util.concurrent.Callable;
19 import javax.annotation.Nonnull;
20 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
21 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
22 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
23 import org.opendaylight.genius.infra.Datastore.Operational;
24 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
25 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
26 import org.opendaylight.genius.infra.TypedWriteTransaction;
27 import org.opendaylight.genius.itm.cache.DPNTEPsInfoCache;
28 import org.opendaylight.genius.itm.cache.DpnTepStateCache;
29 import org.opendaylight.genius.itm.cache.TunnelStateCache;
30 import org.opendaylight.genius.itm.cache.UnprocessedNodeConnectorCache;
31 import org.opendaylight.genius.itm.globals.ITMConstants;
32 import org.opendaylight.genius.itm.impl.ItmUtils;
33 import org.opendaylight.genius.itm.itmdirecttunnels.renderer.ovs.utilities.DirectTunnelUtils;
34 import org.opendaylight.genius.itm.utils.DpnTepInterfaceInfo;
35 import org.opendaylight.genius.itm.utils.NodeConnectorInfo;
36 import org.opendaylight.genius.itm.utils.NodeConnectorInfoBuilder;
37 import org.opendaylight.genius.utils.clustering.EntityOwnershipUtils;
38 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
39 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
40 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.PortReason;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.tunnels_state.StateTunnelList;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.tunnels_state.StateTunnelListBuilder;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.tunnels_state.StateTunnelListKey;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
50 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
53
54 /**
55  * This Class is a Data Change Listener for FlowCapableNodeConnector updates.
56  * This creates an entry in the tunnels-state OperDS for every node-connector used.
57  */
58 public class TunnelInventoryStateListener extends AbstractTunnelListenerBase<FlowCapableNodeConnector> {
59
60     private static final Logger LOG = LoggerFactory.getLogger(TunnelInventoryStateListener.class);
61
62     private final JobCoordinator coordinator;
63     private final ManagedNewTransactionRunner txRunner;
64     private final TunnelStateCache tunnelStateCache;
65
66     public TunnelInventoryStateListener(final DataBroker dataBroker,
67                                         final JobCoordinator coordinator,
68                                         final EntityOwnershipUtils entityOwnershipUtils,
69                                         final TunnelStateCache tunnelStateCache,
70                                         final DpnTepStateCache dpnTepStateCache,
71                                         final DPNTEPsInfoCache dpntePsInfoCache,
72                                         final UnprocessedNodeConnectorCache unprocessedNCCache,
73                                         final DirectTunnelUtils directTunnelUtils) {
74         super(dataBroker, LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.create(Nodes.class).child(Node.class)
75                 .child(NodeConnector.class).augmentation(FlowCapableNodeConnector.class), dpnTepStateCache,
76                 dpntePsInfoCache, unprocessedNCCache, entityOwnershipUtils, directTunnelUtils);
77         this.coordinator = coordinator;
78         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
79         this.tunnelStateCache = tunnelStateCache;
80         super.register();
81     }
82
83     @Override
84     public void remove(@Nonnull InstanceIdentifier<FlowCapableNodeConnector> key,
85                        @Nonnull FlowCapableNodeConnector flowCapableNodeConnector) {
86         String portName = flowCapableNodeConnector.getName();
87         LOG.debug("InterfaceInventoryState Remove for {}", portName);
88         // ITM Direct Tunnels Return if its not tunnel port and if its not Internal
89         if (!DirectTunnelUtils.TUNNEL_PORT_PREDICATE.test(portName)) {
90             LOG.debug("Node Connector Remove - {} Interface is not a tunnel I/f, so no-op", portName);
91             return;
92         } else {
93             try {
94                 if (!tunnelStateCache.isInternalBasedOnState(portName)) {
95                     LOG.debug("Node Connector Remove {} Interface is not a internal tunnel I/f, so no-op", portName);
96                     return;
97                 }
98             } catch (ReadFailedException e) {
99                 LOG.error("Tunnel {} is not present in operational DS ", portName);
100                 return;
101             }
102         }
103         if (!entityOwner()) {
104             return;
105         }
106         LOG.debug("Received NodeConnector Remove Event: {}, {}", key, flowCapableNodeConnector);
107         NodeConnectorId nodeConnectorId = InstanceIdentifier.keyOf(key.firstIdentifierOf(NodeConnector.class)).getId();
108         remove(nodeConnectorId, flowCapableNodeConnector, portName);
109     }
110
111     private void remove(NodeConnectorId nodeConnectorId,
112                         FlowCapableNodeConnector fcNodeConnectorNew, String portName) {
113         LOG.debug("InterfaceInventoryState REMOVE for {}", portName);
114         InterfaceStateRemoveWorker portStateRemoveWorker = new InterfaceStateRemoveWorker(nodeConnectorId,
115                 fcNodeConnectorNew, portName);
116         coordinator.enqueueJob(portName, portStateRemoveWorker, ITMConstants.JOB_MAX_RETRIES);
117     }
118
119     @Override
120     public void update(@Nonnull InstanceIdentifier<FlowCapableNodeConnector> key,
121                        @Nonnull FlowCapableNodeConnector fcNodeConnectorOld,
122                        @Nonnull FlowCapableNodeConnector fcNodeConnectorNew) {
123         String portName = fcNodeConnectorNew.getName();
124         if (DirectTunnelUtils.TUNNEL_PORT_PREDICATE.test(portName)) {
125             LOG.debug("Node Connector Update - {} Interface is not a tunnel I/f, so no-op", portName);
126             return;
127         } else if (!dpnTepStateCache.isInternal(portName)) {
128             LOG.debug("Node Connector Update {} Interface is not a internal tunnel I/f, so no-op", portName);
129             return;
130         }
131         if (fcNodeConnectorNew.getReason() == PortReason.Delete || !entityOwner()) {
132             return;
133         }
134         LOG.debug("Received NodeConnector Update Event: {}, {}, {}", key, fcNodeConnectorOld, fcNodeConnectorNew);
135
136         InterfaceStateUpdateWorker portStateUpdateWorker = new InterfaceStateUpdateWorker(key, fcNodeConnectorOld,
137                 fcNodeConnectorNew, portName);
138         coordinator.enqueueJob(portName, portStateUpdateWorker, ITMConstants.JOB_MAX_RETRIES);
139     }
140
141     @Override
142     public void add(@Nonnull InstanceIdentifier<FlowCapableNodeConnector> key,
143                     @Nonnull FlowCapableNodeConnector fcNodeConnectorNew) {
144         String portName = fcNodeConnectorNew.getName();
145         LOG.debug("InterfaceInventoryState ADD for {}", portName);
146         // Return if its not tunnel port and if its not Internal
147         if (!DirectTunnelUtils.TUNNEL_PORT_PREDICATE.test(portName)) {
148             LOG.debug("Node Connector Add {} Interface is not a tunnel I/f, so no-op", portName);
149             return;
150         }
151         if (!entityOwner()) {
152             return;
153         }
154         if (!dpnTepStateCache.isConfigAvailable(portName)) {
155             // Park the notification
156             LOG.debug("Unable to process the NodeConnector ADD event for {} as Config not available."
157                     + "Hence parking it", portName);
158             NodeConnectorInfo nodeConnectorInfo = new NodeConnectorInfoBuilder().setNodeConnectorId(key)
159                     .setNodeConnector(fcNodeConnectorNew).build();
160             unprocessedNCCache.add(portName, nodeConnectorInfo);
161             return;
162         } else if (!dpnTepStateCache.isInternal(portName)) {
163             LOG.debug("{} Interface is not a internal tunnel I/f, so no-op", portName);
164             return;
165         }
166
167         LOG.debug("Received NodeConnector Add Event: {}, {}", key, fcNodeConnectorNew);
168         if (DirectTunnelUtils.TUNNEL_PORT_PREDICATE.test(portName) && dpnTepStateCache.isInternal(portName)) {
169             //NodeConnectorId nodeConnectorId =
170             // InstanceIdentifier.keyOf(key.firstIdentifierOf(NodeConnector.class)).getId();
171             InterfaceStateAddWorker ifStateAddWorker = new InterfaceStateAddWorker(key,
172                     fcNodeConnectorNew, portName);
173             coordinator.enqueueJob(portName, ifStateAddWorker, ITMConstants.JOB_MAX_RETRIES);
174         }
175     }
176
177     private List<ListenableFuture<Void>> updateState(String interfaceName,
178         FlowCapableNodeConnector flowCapableNodeConnectorNew,
179         FlowCapableNodeConnector flowCapableNodeConnectorOld) {
180         LOG.debug("Updating interface state for port: {}", interfaceName);
181
182         // Hardware updates can be ignored
183         Interface.OperStatus operStatusNew = getOpState(flowCapableNodeConnectorNew);
184         MacAddress macAddressNew = flowCapableNodeConnectorNew.getHardwareAddress();
185
186         Interface.OperStatus operStatusOld = getOpState(flowCapableNodeConnectorOld);
187         MacAddress macAddressOld = flowCapableNodeConnectorOld.getHardwareAddress();
188
189         boolean opstateModified = false;
190         boolean hardwareAddressModified = false;
191         if (!operStatusNew.equals(operStatusOld)) {
192             opstateModified = true;
193         }
194         if (!macAddressNew.equals(macAddressOld)) {
195             hardwareAddressModified = true;
196         }
197
198         if (!opstateModified && !hardwareAddressModified) {
199             LOG.debug("If State entry for port: {} Not Modified.", interfaceName);
200             return Collections.emptyList();
201         }
202
203         DpnTepInterfaceInfo dpnTepInfo = dpnTepStateCache.getTunnelFromCache(interfaceName);
204
205         // For monitoring enabled tunnels, skip opstate updation
206         if (!modifyTunnelOpState(dpnTepInfo, opstateModified)) {
207             LOG.debug("skipping Tunnel-state update for monitoring enabled tunnel interface {}", interfaceName);
208             opstateModified = false;
209         }
210
211         if (!opstateModified && !hardwareAddressModified) {
212             LOG.debug("If State entry for port: {} Not Modified.", interfaceName);
213             return Collections.emptyList();
214         }
215         if (opstateModified) {
216             return Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL, tx -> {
217                 // modify the attributes in interface operational DS
218                 handleInterfaceStateUpdates(tx, dpnTepInfo, true, interfaceName, flowCapableNodeConnectorNew.getName(),
219                         operStatusNew);
220
221             }));
222         } else {
223             return Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL, tx -> {
224                 // modify the attributes in interface operational DS
225                 handleInterfaceStateUpdates(tx, dpnTepInfo, false, interfaceName,
226                         flowCapableNodeConnectorNew.getName(), operStatusNew);
227
228             }));
229         }
230     }
231
232     private void updateInterfaceStateOnNodeRemove(TypedWriteTransaction<Operational> tx, String interfaceName,
233         FlowCapableNodeConnector flowCapableNodeConnector) {
234         LOG.debug("Updating interface oper-status to UNKNOWN for : {}", interfaceName);
235         DpnTepInterfaceInfo dpnTepInfo = dpnTepStateCache.getTunnelFromCache(interfaceName);
236
237         handleInterfaceStateUpdates(tx, dpnTepInfo, true, interfaceName, flowCapableNodeConnector.getName(),
238                 Interface.OperStatus.Unknown);
239     }
240
241     private Interface.OperStatus getOpState(FlowCapableNodeConnector flowCapableNodeConnector) {
242         return flowCapableNodeConnector.getState().isLive()
243                 && !flowCapableNodeConnector.getConfiguration().isPORTDOWN()
244                 ? Interface.OperStatus.Up : Interface.OperStatus.Down;
245     }
246
247     private void handleInterfaceStateUpdates(TypedWriteTransaction<Operational> tx,
248         DpnTepInterfaceInfo dpnTepInfo,
249         boolean opStateModified, String interfaceName, String portName,
250         Interface.OperStatus opState) {
251         if (dpnTepInfo == null && !interfaceName.equals(portName)) {
252             return;
253         }
254         LOG.debug("updating interface state entry for {}", interfaceName);
255         InstanceIdentifier<StateTunnelList> tnlStateId = ItmUtils.buildStateTunnelListId(
256                 new StateTunnelListKey(interfaceName));
257         StateTunnelListBuilder stateTnlBuilder = new StateTunnelListBuilder();
258         stateTnlBuilder.withKey(new StateTunnelListKey(interfaceName));
259         if (modifyOpState(dpnTepInfo, opStateModified)) {
260             LOG.debug("updating interface oper status as {} for {}", opState.name(), interfaceName);
261             boolean tunnelState = opState.equals(Interface.OperStatus.Up);
262             stateTnlBuilder.setTunnelState(tunnelState);
263             stateTnlBuilder.setOperState(DirectTunnelUtils.convertInterfaceToTunnelOperState(opState));
264         }
265         tx.merge(tnlStateId, stateTnlBuilder.build());
266     }
267
268     private boolean modifyOpState(DpnTepInterfaceInfo dpnTepInterfaceInfo, boolean opStateModified) {
269         return opStateModified && (dpnTepInterfaceInfo != null);
270     }
271
272     private boolean modifyTunnelOpState(DpnTepInterfaceInfo dpnTepInterfaceInfo, boolean opStateModified) {
273         return !dpnTepInterfaceInfo.isMonitoringEnabled() && modifyOpState(dpnTepInterfaceInfo, opStateModified);
274     }
275
276     private List<ListenableFuture<Void>> removeInterfaceStateConfiguration(NodeConnectorId nodeConnectorId,
277                                                                            String interfaceName,
278                                                                            FlowCapableNodeConnector
279                                                                                    flowCapableNodeConnector) {
280         List<ListenableFuture<Void>> futures = new ArrayList<>();
281
282         BigInteger dpId = DirectTunnelUtils.getDpnFromNodeConnectorId(nodeConnectorId);
283         // In a genuine port delete scenario, the reason will be there in the incoming event, for all remaining
284         // cases treat the event as DPN disconnect, if old and new ports are same. Else, this is a VM migration
285         // scenario, and should be treated as port removal.
286         if (flowCapableNodeConnector.getReason() != PortReason.Delete) {
287             //Remove event is because of connection lost between controller and switch, or switch shutdown.
288             // Hence, dont remove the interface but set the status as "unknown"
289             futures.add(txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL,
290                 tx -> updateInterfaceStateOnNodeRemove(tx, interfaceName, flowCapableNodeConnector)));
291         } else {
292             LOG.debug("removing interface state for interface: {}", interfaceName);
293             directTunnelUtils.deleteTunnelStateEntry(interfaceName);
294             DpnTepInterfaceInfo dpnTepInfo = dpnTepStateCache.getTunnelFromCache(interfaceName);
295             if (dpnTepInfo != null) {
296                 futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
297                     //SF 419 This will only be tunnel interface
298                     directTunnelUtils.removeLportTagInterfaceMap(interfaceName);
299                     directTunnelUtils.removeTunnelIngressFlow(tx, dpId, interfaceName);
300                 }));
301             } else {
302                 LOG.error("DPNTEPInfo is null for Tunnel Interface {}", interfaceName);
303             }
304         }
305         return futures;
306     }
307
308     private class InterfaceStateAddWorker implements Callable {
309         private final InstanceIdentifier<FlowCapableNodeConnector> key;
310         private final FlowCapableNodeConnector fcNodeConnectorNew;
311         private final String interfaceName;
312
313         InterfaceStateAddWorker(InstanceIdentifier<FlowCapableNodeConnector> key,
314                                 FlowCapableNodeConnector fcNodeConnectorNew, String portName) {
315             this.key = key;
316             this.fcNodeConnectorNew = fcNodeConnectorNew;
317             this.interfaceName = portName;
318         }
319
320         @Override
321         public Object call() throws Exception {
322             // If another renderer(for eg : OVS) needs to be supported, check can be performed here
323             // to call the respective helpers.
324             return addState(key, interfaceName, fcNodeConnectorNew);
325         }
326
327         @Override
328         public String toString() {
329             return "InterfaceStateAddWorker{fcNodeConnectorIdentifier=" + key + ", fcNodeConnectorNew="
330                     + fcNodeConnectorNew + ", interfaceName='" + interfaceName + '\'' + '}';
331         }
332     }
333
334     private class InterfaceStateUpdateWorker implements Callable {
335         private final InstanceIdentifier<FlowCapableNodeConnector> key;
336         private final FlowCapableNodeConnector fcNodeConnectorOld;
337         private final FlowCapableNodeConnector fcNodeConnectorNew;
338         private final String interfaceName;
339
340         InterfaceStateUpdateWorker(InstanceIdentifier<FlowCapableNodeConnector> key,
341                                    FlowCapableNodeConnector fcNodeConnectorOld,
342                                    FlowCapableNodeConnector fcNodeConnectorNew, String portName) {
343             this.key = key;
344             this.fcNodeConnectorOld = fcNodeConnectorOld;
345             this.fcNodeConnectorNew = fcNodeConnectorNew;
346             this.interfaceName = portName;
347         }
348
349         @Override
350         public Object call() {
351             // If another renderer(for eg : OVS) needs to be supported, check can be performed here
352             // to call the respective helpers.
353             return updateState(interfaceName, fcNodeConnectorNew, fcNodeConnectorOld);
354         }
355
356         @Override
357         public String toString() {
358             return "InterfaceStateUpdateWorker{key=" + key + ", fcNodeConnectorOld=" + fcNodeConnectorOld
359                     + ", fcNodeConnectorNew=" + fcNodeConnectorNew + ", interfaceName='" + interfaceName + '\'' + '}';
360         }
361     }
362
363     private class InterfaceStateRemoveWorker implements Callable {
364         private final NodeConnectorId nodeConnectorId;
365         private final FlowCapableNodeConnector flowCapableNodeConnector;
366         private final String interfaceName;
367
368         InterfaceStateRemoveWorker(NodeConnectorId nodeConnectorId, FlowCapableNodeConnector flowCapableNodeConnector,
369                                    String interfaceName) {
370             this.nodeConnectorId = nodeConnectorId;
371             this.flowCapableNodeConnector = flowCapableNodeConnector;
372             this.interfaceName = interfaceName;
373         }
374
375         @Override
376         public Object call() {
377             // If another renderer(for eg : OVS) needs to be supported, check can be performed here
378             // to call the respective helpers.
379             return removeInterfaceStateConfiguration(nodeConnectorId, interfaceName, flowCapableNodeConnector);
380         }
381
382         @Override
383         public String toString() {
384             return "InterfaceStateRemoveWorker{nodeConnectorId=" + nodeConnectorId + ", fcNodeConnector"
385                     + flowCapableNodeConnector + ", interfaceName='" + interfaceName + '\'' + '}';
386         }
387     }
388 }