Fix: Update following an add port creates transaction conflict
[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.mdsal.binding.util.Datastore.CONFIGURATION;
11 import static org.opendaylight.mdsal.binding.util.Datastore.OPERATIONAL;
12
13 import com.google.common.util.concurrent.ListenableFuture;
14 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
15 import java.util.ArrayList;
16 import java.util.Collections;
17 import java.util.List;
18 import java.util.Objects;
19 import java.util.Optional;
20 import java.util.concurrent.Callable;
21 import java.util.concurrent.ConcurrentHashMap;
22 import java.util.concurrent.ConcurrentMap;
23 import org.eclipse.jdt.annotation.NonNull;
24 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
25 import org.opendaylight.genius.itm.cache.DPNTEPsInfoCache;
26 import org.opendaylight.genius.itm.cache.DpnTepStateCache;
27 import org.opendaylight.genius.itm.cache.OfDpnTepConfigCache;
28 import org.opendaylight.genius.itm.cache.TunnelStateCache;
29 import org.opendaylight.genius.itm.cache.UnprocessedNodeConnectorCache;
30 import org.opendaylight.genius.itm.cache.UnprocessedNodeConnectorEndPointCache;
31 import org.opendaylight.genius.itm.cache.UnprocessedOFNodeConnectorCache;
32 import org.opendaylight.genius.itm.globals.ITMConstants;
33 import org.opendaylight.genius.itm.impl.ItmUtils;
34 import org.opendaylight.genius.itm.itmdirecttunnels.renderer.ovs.utilities.DirectTunnelUtils;
35 import org.opendaylight.genius.itm.itmdirecttunnels.workers.OfPortStateAddWorker;
36 import org.opendaylight.genius.itm.itmdirecttunnels.workers.OfPortStateAddWorkerForNodeConnector;
37 import org.opendaylight.genius.itm.itmdirecttunnels.workers.TunnelStateAddWorker;
38 import org.opendaylight.genius.itm.itmdirecttunnels.workers.TunnelStateAddWorkerForNodeConnector;
39 import org.opendaylight.genius.itm.utils.DpnTepInterfaceInfo;
40 import org.opendaylight.genius.itm.utils.NodeConnectorInfo;
41 import org.opendaylight.genius.itm.utils.NodeConnectorInfoBuilder;
42 import org.opendaylight.genius.itm.utils.TunnelEndPointInfo;
43 import org.opendaylight.genius.itm.utils.TunnelStateInfo;
44 import org.opendaylight.genius.itm.utils.TunnelStateInfoBuilder;
45 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
46 import org.opendaylight.infrautils.utils.concurrent.NamedSimpleReentrantLock.Acquired;
47 import org.opendaylight.mdsal.binding.api.DataBroker;
48 import org.opendaylight.mdsal.binding.util.Datastore.Operational;
49 import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunner;
50 import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunnerImpl;
51 import org.opendaylight.mdsal.binding.util.TypedReadWriteTransaction;
52 import org.opendaylight.mdsal.binding.util.TypedWriteTransaction;
53 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
54 import org.opendaylight.mdsal.common.api.ReadFailedException;
55 import org.opendaylight.serviceutils.tools.listener.AbstractClusteredSyncDataTreeChangeListener;
56 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
57 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.PortReason;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.dpn.tep.config.OfDpnTep;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.of.teps.state.OfTep;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.of.teps.state.OfTepBuilder;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.of.teps.state.OfTepKey;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.tunnels_state.StateTunnelList;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.tunnels_state.StateTunnelListBuilder;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.tunnels_state.StateTunnelListKey;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
71 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
72 import org.opendaylight.yangtools.yang.common.Uint64;
73 import org.slf4j.Logger;
74 import org.slf4j.LoggerFactory;
75
76 /**
77  * This Class is a Data Change Listener for FlowCapableNodeConnector updates.
78  * This creates an entry in the tunnels-state OperDS for every node-connector used.
79  */
80 public class TunnelInventoryStateListener extends
81     AbstractClusteredSyncDataTreeChangeListener<FlowCapableNodeConnector> {
82
83     private static final Logger LOG = LoggerFactory.getLogger(TunnelInventoryStateListener.class);
84     private static final Logger EVENT_LOGGER = LoggerFactory.getLogger("GeniusEventLogger");
85
86     private final JobCoordinator coordinator;
87     private final ManagedNewTransactionRunner txRunner;
88     private final TunnelStateCache tunnelStateCache;
89     private final DpnTepStateCache dpnTepStateCache;
90     private final DPNTEPsInfoCache dpntePsInfoCache;
91     private final UnprocessedNodeConnectorCache unprocessedNCCache;
92     private final UnprocessedNodeConnectorEndPointCache unprocessedNodeConnectorEndPointCache;
93     private final DirectTunnelUtils directTunnelUtils;
94     private final ConcurrentMap<String, NodeConnectorInfo> meshedMap = new ConcurrentHashMap<>();
95     private final UnprocessedOFNodeConnectorCache unprocessedOFNCCache;
96     private final OfDpnTepConfigCache ofDpnTepConfigCache;
97     private final IInterfaceManager interfaceManager;
98
99     public TunnelInventoryStateListener(final DataBroker dataBroker,
100                                         final JobCoordinator coordinator,
101                                         final TunnelStateCache tunnelStateCache,
102                                         final DpnTepStateCache dpnTepStateCache,
103                                         final DPNTEPsInfoCache dpntePsInfoCache,
104                                         final UnprocessedNodeConnectorCache unprocessedNCCache,
105                                         final UnprocessedNodeConnectorEndPointCache
106                                             unprocessedNodeConnectorEndPointCache,
107                                         final DirectTunnelUtils directTunnelUtils,
108                                         UnprocessedOFNodeConnectorCache unprocessedOFNCCache,
109                                         final OfDpnTepConfigCache ofDpnTepConfigCache,
110                                         final IInterfaceManager interfaceManager) {
111         super(dataBroker, LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.create(Nodes.class).child(Node.class)
112             .child(NodeConnector.class).augmentation(FlowCapableNodeConnector.class));
113         this.coordinator = coordinator;
114         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
115         this.tunnelStateCache = tunnelStateCache;
116         this.dpnTepStateCache = dpnTepStateCache;
117         this.dpntePsInfoCache = dpntePsInfoCache;
118         this.unprocessedNCCache = unprocessedNCCache;
119         this.unprocessedNodeConnectorEndPointCache = unprocessedNodeConnectorEndPointCache;
120         this.directTunnelUtils = directTunnelUtils;
121         this.unprocessedOFNCCache = unprocessedOFNCCache;
122         this.ofDpnTepConfigCache = ofDpnTepConfigCache;
123         this.interfaceManager = interfaceManager;
124         super.register();
125     }
126
127     @Override
128     public void remove(@NonNull InstanceIdentifier<FlowCapableNodeConnector> key,
129                        @NonNull FlowCapableNodeConnector flowCapableNodeConnector) {
130         String portName = flowCapableNodeConnector.getName();
131         NodeConnectorId nodeConnectorId = InstanceIdentifier.keyOf(key.firstIdentifierOf(NodeConnector.class)).getId();
132         LOG.debug("InterfaceInventoryState Remove for {}", portName);
133         EVENT_LOGGER.debug("ITM-TunnelInventoryState,REMOVE DTCN received for {}",
134                 flowCapableNodeConnector.getName());
135
136         if (!directTunnelUtils.isEntityOwner()) {
137             return;
138         }
139         // ITM Direct Tunnels Return if its not tunnel port or of-ports
140         if (portName.startsWith("of") && interfaceManager.isItmOfTunnelsEnabled()) {
141             LOG.debug("OfPortState REMOVE for {}", portName);
142             EVENT_LOGGER.debug("ITM-OfPortStateState Entity Owner, REMOVE {} {}", nodeConnectorId.getValue(), portName);
143             OfPortStateRemoveWorker ofPortStateRemoveWorker = new OfPortStateRemoveWorker(nodeConnectorId,
144                     null, flowCapableNodeConnector, portName);
145             coordinator.enqueueJob(portName, ofPortStateRemoveWorker, ITMConstants.JOB_MAX_RETRIES);
146             return;
147         } else if (!DirectTunnelUtils.TUNNEL_PORT_PREDICATE.test(portName)) {
148             LOG.debug("Node Connector Remove - {} Interface is not a tunnel I/f, so no-op", portName);
149             return;
150         }
151
152         try {
153             if (!tunnelStateCache.isInternalBasedOnState(portName)) {
154                 LOG.debug("Node Connector Remove {} Interface is not a internal tunnel I/f, so no-op", portName);
155                 return;
156             }
157         } catch (ReadFailedException e) {
158             LOG.error("Tunnel {} is not present in operational DS ", portName);
159             return;
160         }
161         LOG.debug("Received NodeConnector Remove Event: {}, {}", key, flowCapableNodeConnector);
162         remove(nodeConnectorId, flowCapableNodeConnector, portName);
163     }
164
165     private void remove(NodeConnectorId nodeConnectorId,
166                         FlowCapableNodeConnector fcNodeConnectorNew, String portName) {
167         LOG.debug("TunnelInventoryState REMOVE for {}", portName);
168         LOG.debug("InterfaceInventoryState REMOVE for {}", portName);
169         EVENT_LOGGER.debug("ITM-TunnelInventoryState Entity Owner, REMOVE {} {}", nodeConnectorId.getValue(),
170                 portName);
171         TunnelInterfaceStateRemoveWorker portStateRemoveWorker = new TunnelInterfaceStateRemoveWorker(nodeConnectorId,
172                 fcNodeConnectorNew, portName);
173         coordinator.enqueueJob(portName, portStateRemoveWorker, ITMConstants.JOB_MAX_RETRIES);
174     }
175
176     @Override
177     public void update(@NonNull InstanceIdentifier<FlowCapableNodeConnector> key,
178                        @NonNull FlowCapableNodeConnector fcNodeConnectorOld,
179                        @NonNull FlowCapableNodeConnector fcNodeConnectorNew) {
180         EVENT_LOGGER.debug("ITM-TunnelInventoryState,UPDATE DTCN received for {}", fcNodeConnectorOld.getName());
181         String portName = fcNodeConnectorNew.getName();
182         if (!DirectTunnelUtils.TUNNEL_PORT_PREDICATE.test(portName) && !portName.startsWith("of")) {
183             LOG.debug("Node Connector Update - {} Interface is not a tunnel I/f, so no-op", portName);
184             return;
185         } else if (!dpnTepStateCache.isInternal(portName)) {
186             LOG.debug("Node Connector Update {} Interface is not a internal tunnel I/f, so no-op", portName);
187             return;
188         }
189
190         if (fcNodeConnectorNew.getReason() == PortReason.Delete
191                 || fcNodeConnectorNew.getReason() == PortReason.Update || !directTunnelUtils.isEntityOwner()) {
192             EVENT_LOGGER.debug("ITM-TunnelInventoryState,UPDATE with reason {}, is entity owner? {}",
193                     fcNodeConnectorNew.getReason(), directTunnelUtils.isEntityOwner());
194             return;
195         }
196         LOG.debug("Received NodeConnector Update Event: {}, {}, {}", key, fcNodeConnectorOld, fcNodeConnectorNew);
197
198         TunnelInterfaceStateUpdateWorker portStateUpdateWorker =
199                 new TunnelInterfaceStateUpdateWorker(key, fcNodeConnectorOld, fcNodeConnectorNew, portName);
200         EVENT_LOGGER.debug("ITM-TunnelInventoryState Entity Owner, UPDATE {} {} Reason {}",
201                 fcNodeConnectorNew.getName(), portName, fcNodeConnectorNew.getReason());
202         coordinator.enqueueJob(portName, portStateUpdateWorker, ITMConstants.JOB_MAX_RETRIES);
203     }
204
205     @Override
206     public void add(@NonNull InstanceIdentifier<FlowCapableNodeConnector> key,
207                     @NonNull FlowCapableNodeConnector fcNodeConnectorNew) {
208         LOG.info("Received NodeConnector Add Event: {}, {}", key, fcNodeConnectorNew);
209         EVENT_LOGGER.debug("ITM-TunnelInventoryState,ADD DTCN received for {}", fcNodeConnectorNew.getName());
210         String portName = fcNodeConnectorNew.getName();
211
212         // Return if its not tunnel port and if its not Internal
213         if (!DirectTunnelUtils.TUNNEL_PORT_PREDICATE.test(portName) && !portName.startsWith("of")) {
214             LOG.debug("Node Connector Add {} Interface is not a tunnel I/f, so no-op", portName);
215             return;
216         }
217
218         if (!directTunnelUtils.isEntityOwner()) {
219             LOG.debug("Not an entity owner.");
220             return;
221         }
222
223         //Optional<OfDpnTep> dpnTepOptional = Optional.ofNullable(null);
224         NodeConnectorInfo nodeConnectorInfo =
225                 new NodeConnectorInfoBuilder().setNodeConnectorId(key).setNodeConnector(fcNodeConnectorNew).build();
226
227         if (portName.startsWith("of") && interfaceManager.isItmOfTunnelsEnabled()) {
228             NodeConnectorId nodeConnectorId = InstanceIdentifier.keyOf(key.firstIdentifierOf(NodeConnector.class))
229                     .getId();
230             Uint64 srcDpn = DirectTunnelUtils.getDpnFromNodeConnectorId(nodeConnectorId);
231
232             OfDpnTep dpntep = null;
233             try (Acquired lock = directTunnelUtils.lockTunnel(portName)) {
234                 try {
235                     Optional<OfDpnTep> dpnTepOptional = ofDpnTepConfigCache.get(srcDpn.toJava());
236                     if (!dpnTepOptional.isPresent()) {
237                         // Park the notification
238                         LOG.debug("Unable to process the NodeConnector ADD event for {} as Config not available."
239                                 + "Hence parking it", portName);
240                         unprocessedOFNCCache.add(portName, nodeConnectorInfo);
241                         return;
242                     } else {
243                         dpntep = dpnTepOptional.get();
244                     }
245                 } catch (ReadFailedException e) {
246                     LOG.error("unable to get ofDpnTepConfigCache");
247                 }
248             }
249
250             if (dpntep != null) {
251                 OfPortStateAddWorkerForNodeConnector ifOfStateAddWorker =
252                         new OfPortStateAddWorkerForNodeConnector(new OfPortStateAddWorker(directTunnelUtils,
253                                 dpntep, txRunner), nodeConnectorInfo);
254                 EVENT_LOGGER.debug("ITM-Of-tepInventoryState Entity Owner,ADD {} {}",
255                         nodeConnectorId.getValue(), portName);
256                 coordinator.enqueueJob(portName, ifOfStateAddWorker, ITMConstants.JOB_MAX_RETRIES);
257             }
258         } else {
259             addTunnelState(nodeConnectorInfo, portName);
260         }
261     }
262
263     private void addTunnelState(NodeConnectorInfo nodeConnectorInfo, String portName) {
264
265         TunnelStateInfo tunnelStateInfo = null;
266         TunnelEndPointInfo tunnelEndPtInfo = null;
267         try (Acquired lock = directTunnelUtils.lockTunnel(portName)) {
268             if (!dpnTepStateCache.isConfigAvailable(portName)) {
269                 // Park the notification
270                 LOG.debug("Unable to process the NodeConnector ADD event for {} as Config not available."
271                     + "Hence parking it", portName);
272                 unprocessedNCCache.add(portName,
273                     new TunnelStateInfoBuilder().setNodeConnectorInfo(nodeConnectorInfo).build());
274                 return;
275             } else if (!dpnTepStateCache.isInternal(portName)) {
276                 LOG.debug("{} Interface is not a internal tunnel I/f, so no-op", portName);
277                 return;
278             }
279         }
280
281         // Check if tunnels State has an entry for this interface.
282         // If so, then this Inventory Add is due to compute re-connection. Then, ONLY update the state
283         // to UP as previously the compute would have disconnected and so the state will be UNKNOWN.
284         try {
285             long portNo = tunnelStateCache.getNodeConnectorIdFromInterface(portName);
286             if (portNo != ITMConstants.INVALID_PORT_NO) {
287                 coordinator.enqueueJob(portName,
288                         new TunnelInterfaceNodeReconnectWorker(portName), ITMConstants.JOB_MAX_RETRIES);
289                 return;
290             }
291         } catch (ReadFailedException e) {
292             LOG.error("Exception occurred in reconnect for portName {}, reason: {}.",
293                      portName, e.getMessage());
294         }
295
296         if (DirectTunnelUtils.TUNNEL_PORT_PREDICATE.test(portName) && dpnTepStateCache.isInternal(portName)) {
297             tunnelEndPtInfo = dpnTepStateCache.getTunnelEndPointInfoFromCache(portName);
298             TunnelStateInfoBuilder builder = new TunnelStateInfoBuilder().setNodeConnectorInfo(nodeConnectorInfo);
299             dpntePsInfoCache.getDPNTepFromDPNId(Uint64.valueOf(tunnelEndPtInfo.getSrcEndPointInfo()))
300                 .ifPresent(builder::setSrcDpnTepsInfo);
301             dpntePsInfoCache.getDPNTepFromDPNId(Uint64.valueOf(tunnelEndPtInfo.getDstEndPointInfo()))
302                 .ifPresent(builder::setDstDpnTepsInfo);
303             tunnelStateInfo = builder.setTunnelEndPointInfo(tunnelEndPtInfo)
304                 .setDpnTepInterfaceInfo(dpnTepStateCache.getTunnelFromCache(portName)).build();
305
306             if (tunnelStateInfo.getSrcDpnTepsInfo() == null) {
307                 try (Acquired lock = directTunnelUtils.lockTunnel(tunnelEndPtInfo.getSrcEndPointInfo())) {
308                     LOG.debug("Source DPNTepsInfo is null for tunnel {}. Hence Parking with key {}",
309                         portName, tunnelEndPtInfo.getSrcEndPointInfo());
310                     unprocessedNodeConnectorEndPointCache.add(tunnelEndPtInfo.getSrcEndPointInfo(), tunnelStateInfo);
311                 }
312             }
313             if (tunnelStateInfo.getDstDpnTepsInfo() == null) {
314                 try (Acquired lock = directTunnelUtils.lockTunnel(tunnelEndPtInfo.getDstEndPointInfo())) {
315                     LOG.debug("Destination DPNTepsInfo is null for tunnel {}. Hence Parking with key {}",
316                         portName, tunnelEndPtInfo.getDstEndPointInfo());
317                     unprocessedNodeConnectorEndPointCache.add(tunnelEndPtInfo.getDstEndPointInfo(), tunnelStateInfo);
318                 }
319             }
320         }
321
322         if (tunnelEndPtInfo != null && tunnelStateInfo.getSrcDpnTepsInfo() != null
323             && tunnelStateInfo.getDstDpnTepsInfo() != null) {
324             EVENT_LOGGER.debug("ITM-TunnelInventoryState Entity Owner,ADD {}", portName);
325             coordinator.enqueueJob(portName,
326                 new TunnelStateAddWorkerForNodeConnector(new TunnelStateAddWorker(directTunnelUtils, txRunner),
327                     tunnelStateInfo), ITMConstants.JOB_MAX_RETRIES);
328         }
329     }
330
331     @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
332             justification = "https://github.com/spotbugs/spotbugs/issues/811")
333     private List<? extends ListenableFuture<?>> updateState(String interfaceName,
334         FlowCapableNodeConnector flowCapableNodeConnectorNew,
335         FlowCapableNodeConnector flowCapableNodeConnectorOld) {
336         LOG.debug("Updating interface state for port: {}", interfaceName);
337
338         // Hardware updates can be ignored
339         Interface.OperStatus operStatusNew = getOpState(flowCapableNodeConnectorNew);
340         MacAddress macAddressNew = flowCapableNodeConnectorNew.getHardwareAddress();
341
342         Interface.OperStatus operStatusOld = getOpState(flowCapableNodeConnectorOld);
343         MacAddress macAddressOld = flowCapableNodeConnectorOld.getHardwareAddress();
344
345         boolean opstateModified = false;
346         boolean hardwareAddressModified = false;
347         if (!operStatusNew.equals(operStatusOld)) {
348             opstateModified = true;
349         }
350         if (!Objects.equals(macAddressNew, macAddressOld)) {
351             hardwareAddressModified = true;
352         }
353
354         if (!opstateModified && !hardwareAddressModified) {
355             LOG.debug("If State entry for port: {} Not Modified.", interfaceName);
356             return Collections.emptyList();
357         }
358
359         DpnTepInterfaceInfo dpnTepInfo = dpnTepStateCache.getTunnelFromCache(interfaceName);
360
361         // For monitoring enabled tunnels, skip opstate updation
362         if (!modifyTunnelOpState(dpnTepInfo, opstateModified)) {
363             LOG.debug("skipping Tunnel-state update for monitoring enabled tunnel interface {}", interfaceName);
364             opstateModified = false;
365         }
366
367         if (!opstateModified && !hardwareAddressModified) {
368             LOG.debug("If State entry for port: {} Not Modified.", interfaceName);
369             return Collections.emptyList();
370         }
371         if (opstateModified) {
372             return Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL, tx -> {
373                 // modify the attributes in interface operational DS
374                 handleInterfaceStateUpdates(tx, dpnTepInfo, true, interfaceName, flowCapableNodeConnectorNew.getName(),
375                         operStatusNew);
376                 EVENT_LOGGER.debug("ITM-TunnelInventoryState, UPDATE {} CHGED {} completed", interfaceName,
377                         operStatusNew.getName());
378             }));
379         } else {
380             return Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL, tx -> {
381                 // modify the attributes in interface operational DS
382                 handleInterfaceStateUpdates(tx, dpnTepInfo, false, interfaceName,
383                         flowCapableNodeConnectorNew.getName(), operStatusNew);
384                 EVENT_LOGGER.debug("ITM-TunnelInventoryState, UPDATE {} completed", interfaceName);
385             }));
386         }
387     }
388
389     private void updateInterfaceStateOnNodeRemove(TypedWriteTransaction<Operational> tx, String interfaceName,
390         FlowCapableNodeConnector flowCapableNodeConnector) {
391         LOG.debug("Updating interface oper-status to UNKNOWN for : {}", interfaceName);
392         DpnTepInterfaceInfo dpnTepInfo = dpnTepStateCache.getTunnelFromCache(interfaceName);
393
394         handleInterfaceStateUpdates(tx, dpnTepInfo, true, interfaceName, flowCapableNodeConnector.getName(),
395                 Interface.OperStatus.Unknown);
396     }
397
398     private void updateOfTepStateOnNodeRemove(Uint64 srcDpn, String ofTepName,
399                                               FlowCapableNodeConnector flowCapableNodeConnector,
400                                               TypedReadWriteTransaction<Operational> tx) throws ReadFailedException {
401         LOG.debug("Updating oftep oper-status to UNKNOWN for : {}", ofTepName);
402         Optional<OfDpnTep> dpnTepInfo = ofDpnTepConfigCache.get(srcDpn.toJava());
403         if (dpnTepInfo == null || !ofTepName.equals(flowCapableNodeConnector.getName())) {
404             return;
405         }
406         handleOfTepStateUpdates(dpnTepInfo, tx, true, ofTepName,
407                 Interface.OperStatus.Unknown);
408     }
409
410     private void handleOfTepStateUpdates(Optional<OfDpnTep> dpnTepInfo, TypedReadWriteTransaction<Operational> tx,
411                                          boolean opStateModified, String ofTepName, Interface.OperStatus opState) {
412         LOG.debug("updating oftep state entry for {}", ofTepName);
413         InstanceIdentifier<OfTep> ofTepStateId = ItmUtils.buildStateOfTepListId(new OfTepKey(ofTepName));
414         OfTepBuilder ofTepBuilder = new OfTepBuilder();
415         ofTepBuilder.withKey(new OfTepKey(ofTepName));
416         if (opStateModified) {
417             LOG.debug("updating oftep oper status as {} for {}", opState.getName(), ofTepName);
418             ofTepBuilder.setOfTepState(DirectTunnelUtils.convertInterfaceToTunnelOperState(opState));
419             LOG.trace("updated to {} for {}",opState.getName(), ofTepName);
420         }
421         tx.merge(ofTepStateId, ofTepBuilder.build());
422     }
423
424     private Interface.OperStatus getOpState(FlowCapableNodeConnector flowCapableNodeConnector) {
425         return flowCapableNodeConnector.getState().isLive()
426                 && !flowCapableNodeConnector.getConfiguration().isPORTDOWN()
427                 ? Interface.OperStatus.Up : Interface.OperStatus.Down;
428     }
429
430     private void handleInterfaceStateUpdates(TypedWriteTransaction<Operational> tx,
431         DpnTepInterfaceInfo dpnTepInfo,
432         boolean opStateModified, String interfaceName, String portName,
433         Interface.OperStatus opState) {
434         if (dpnTepInfo == null && !portName.startsWith("of") && !interfaceName.equals(portName)) {
435             return;
436         }
437         LOG.debug("updating interface state entry for {}", interfaceName);
438         InstanceIdentifier<StateTunnelList> tnlStateId = ItmUtils.buildStateTunnelListId(
439                 new StateTunnelListKey(interfaceName));
440         StateTunnelListBuilder stateTnlBuilder = new StateTunnelListBuilder();
441         stateTnlBuilder.withKey(new StateTunnelListKey(interfaceName));
442         if (modifyOpState(dpnTepInfo, opStateModified)) {
443             LOG.debug("updating interface oper status as {} for {}", opState.name(), interfaceName);
444             boolean tunnelState = opState.equals(Interface.OperStatus.Up);
445             stateTnlBuilder.setTunnelState(tunnelState);
446             stateTnlBuilder.setOperState(DirectTunnelUtils.convertInterfaceToTunnelOperState(opState));
447         }
448         tx.merge(tnlStateId, stateTnlBuilder.build());
449     }
450
451     private boolean modifyOpState(DpnTepInterfaceInfo dpnTepInterfaceInfo, boolean opStateModified) {
452         return opStateModified && dpnTepInterfaceInfo != null;
453     }
454
455     private boolean modifyTunnelOpState(DpnTepInterfaceInfo dpnTepInterfaceInfo, boolean opStateModified) {
456         return !dpnTepInterfaceInfo.isMonitoringEnabled() && modifyOpState(dpnTepInterfaceInfo, opStateModified);
457     }
458
459     @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
460             justification = "https://github.com/spotbugs/spotbugs/issues/811")
461     private List<? extends ListenableFuture<?>> removeInterfaceStateConfiguration(NodeConnectorId nodeConnectorId,
462                                                                                   String interfaceName,
463                                                                                   FlowCapableNodeConnector
464                                                                                       flowCapableNodeConnector) {
465
466
467         List<ListenableFuture<?>> futures = new ArrayList<>();
468         Uint64 dpId = DirectTunnelUtils.getDpnFromNodeConnectorId(nodeConnectorId);
469         // In a genuine port delete scenario, the reason will be there in the incoming event, for all remaining
470         // cases treat the event as DPN disconnect, if old and new ports are same. Else, this is a VM migration
471         // scenario, and should be treated as port removal.
472         if (flowCapableNodeConnector.getReason() != PortReason.Delete) {
473             //Remove event is because of connection lost between controller and switch, or switch shutdown.
474             // Hence, dont remove the interface but set the status as "unknown"
475
476             futures.add(txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL,
477                 tx -> updateInterfaceStateOnNodeRemove(tx, interfaceName, flowCapableNodeConnector)));
478
479         } else {
480             LOG.debug("removing interface state for interface: {}", interfaceName);
481             directTunnelUtils.deleteTunnelStateEntry(interfaceName);
482             DpnTepInterfaceInfo dpnTepInfo = dpnTepStateCache.getTunnelFromCache(interfaceName);
483             if (dpnTepInfo != null) {
484                 futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
485                     // Do if-index and ingress flow clean-up only for tunnel-interfaces
486                     directTunnelUtils.removeLportTagInterfaceMap(interfaceName);
487                     directTunnelUtils.removeTunnelIngressFlow(tx, dpId, interfaceName);
488                     directTunnelUtils.removeTunnelEgressFlow(tx, dpId, interfaceName);
489                 }));
490             } else {
491                 LOG.error("DPNTEPInfo is null for Tunnel Interface {}", interfaceName);
492             }
493             EVENT_LOGGER.debug("ITM-TunnelInventoryState,REMOVE Table 0 flow for {} completed", interfaceName);
494         }
495         return futures;
496     }
497
498     @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
499             justification = "https://github.com/spotbugs/spotbugs/issues/811")
500     private List<? extends ListenableFuture<?>> removeOfTepStateConfiguration(NodeConnectorId nodeConnectorIdNew,
501                                                                        NodeConnectorId nodeConnectorIdOld,
502                                                                        String ofTepName,
503                                                                        FlowCapableNodeConnector
504                                                                                fcNodeConnectorOld) {
505         List<ListenableFuture<?>> futures = new ArrayList<>();
506
507         NodeConnectorId nodeConnectorId = (nodeConnectorIdOld != null && !nodeConnectorIdNew.equals(nodeConnectorIdOld))
508                 ? nodeConnectorIdOld : nodeConnectorIdNew;
509
510         Uint64 dpId = DirectTunnelUtils.getDpnFromNodeConnectorId(nodeConnectorId);
511
512             // In a genuine port delete scenario, the reason will be there in the incoming event, for all remaining
513             // cases treat the event as DPN disconnect, if old and new ports are same. Else, this is a VM migration
514             // scenario, and should be treated as port removal.
515         if (fcNodeConnectorOld.getReason() != PortReason.Delete) {
516             //Remove event is because of connection lost between controller and switch, or switch shutdown.
517             // Hence, dont remove the interface but set the status as "unknown"
518             futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL, tx -> {
519                 updateOfTepStateOnNodeRemove(dpId, ofTepName, fcNodeConnectorOld, tx);
520             }));
521         } else {
522             LOG.debug("removing oftep state for oftep: {}", ofTepName);
523             futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
524                 directTunnelUtils.deleteOfTepStateEntry(ofTepName);
525                 directTunnelUtils.removeLportTagInterfaceMap(ofTepName);
526                 directTunnelUtils.removeTunnelIngressFlow(tx, dpId, ofTepName);
527             }));
528             EVENT_LOGGER.debug("ITM-OfTepInventoryState,REMOVE Table 0 flow for {} completed", ofTepName);
529         }
530
531         return futures;
532     }
533
534     private class TunnelInterfaceStateUpdateWorker implements Callable<List<? extends ListenableFuture<?>>> {
535         private final InstanceIdentifier<FlowCapableNodeConnector> key;
536         private final FlowCapableNodeConnector fcNodeConnectorOld;
537         private final FlowCapableNodeConnector fcNodeConnectorNew;
538         private final String interfaceName;
539
540         TunnelInterfaceStateUpdateWorker(InstanceIdentifier<FlowCapableNodeConnector> key,
541                                          FlowCapableNodeConnector fcNodeConnectorOld,
542                                          FlowCapableNodeConnector fcNodeConnectorNew, String portName) {
543             this.key = key;
544             this.fcNodeConnectorOld = fcNodeConnectorOld;
545             this.fcNodeConnectorNew = fcNodeConnectorNew;
546             this.interfaceName = portName;
547         }
548
549         @Override
550         public List<? extends ListenableFuture<?>> call() {
551             // If another renderer(for eg : OVS) needs to be supported, check can be performed here
552             // to call the respective helpers.
553             return updateState(interfaceName, fcNodeConnectorNew, fcNodeConnectorOld);
554         }
555
556         @Override
557         public String toString() {
558             return "TunnelInterfaceStateUpdateWorker{key=" + key + ", fcNodeConnectorOld=" + fcNodeConnectorOld
559                     + ", fcNodeConnectorNew=" + fcNodeConnectorNew + ", interfaceName='" + interfaceName + '\'' + '}';
560         }
561     }
562
563     private class TunnelInterfaceStateRemoveWorker implements Callable<List<? extends ListenableFuture<?>>> {
564         private final NodeConnectorId nodeConnectorId;
565         private final FlowCapableNodeConnector flowCapableNodeConnector;
566         private final String interfaceName;
567
568         TunnelInterfaceStateRemoveWorker(NodeConnectorId nodeConnectorId,
569                                          FlowCapableNodeConnector flowCapableNodeConnector,
570                                          String interfaceName) {
571             this.nodeConnectorId = nodeConnectorId;
572             this.flowCapableNodeConnector = flowCapableNodeConnector;
573             this.interfaceName = interfaceName;
574         }
575
576         @Override
577         public List<? extends ListenableFuture<?>> call() {
578             // If another renderer(for eg : OVS) needs to be supported, check can be performed here
579             // to call the respective helpers.
580             return removeInterfaceStateConfiguration(nodeConnectorId, interfaceName, flowCapableNodeConnector);
581         }
582
583         @Override
584         public String toString() {
585             return "TunnelInterfaceStateRemoveWorker{nodeConnectorId=" + nodeConnectorId + ", fcNodeConnector"
586                     + flowCapableNodeConnector + ", interfaceName='" + interfaceName + '\'' + '}';
587         }
588     }
589
590     private class TunnelInterfaceNodeReconnectWorker implements Callable<List<? extends ListenableFuture<?>>> {
591         private final String tunnelName;
592
593         TunnelInterfaceNodeReconnectWorker(String tunnelName) {
594             this.tunnelName = tunnelName;
595         }
596
597         @Override
598         public List<? extends ListenableFuture<?>> call() throws Exception {
599             // If another renderer(for eg : OVS) needs to be supported, check can be performed here
600             // to call the respective helpers.
601             EVENT_LOGGER.debug("ITM-TunnelInventoryState, Compute Re-connected, ADD received for {} ", tunnelName);
602
603             return handleInterfaceStateOnReconnect(tunnelName);
604         }
605
606         @Override
607         public String toString() {
608             return "TunnelInterfaceNodeReconnectWorker{tunnelName=" + tunnelName + '\'' + '}';
609         }
610     }
611
612     @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
613             justification = "https://github.com/spotbugs/spotbugs/issues/811")
614     private List<? extends ListenableFuture<?>> handleInterfaceStateOnReconnect(String interfaceName) {
615         List<ListenableFuture<?>> futures = new ArrayList<>();
616
617         futures.add(txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL, tx -> {
618             DpnTepInterfaceInfo dpnTepInfo = dpnTepStateCache.getTunnelFromCache(interfaceName);
619
620             handleInterfaceStateUpdates(tx, dpnTepInfo, true, interfaceName, interfaceName,
621                     Interface.OperStatus.Up);
622         }));
623         return futures;
624     }
625
626     private class OfPortStateRemoveWorker implements Callable<List<? extends ListenableFuture<?>>> {
627
628         private final NodeConnectorId nodeconnectorIdNew;
629         private final NodeConnectorId nodeconnectorIdOld;
630         private final FlowCapableNodeConnector fcNodeConnectorOld;
631         private final String ofTepName;
632
633         OfPortStateRemoveWorker(NodeConnectorId nodeconnectorIdNew, NodeConnectorId nodeconnectorIdOld,
634                                 FlowCapableNodeConnector fcNodeConnectorOld, String ofTepName) {
635             this.nodeconnectorIdNew = nodeconnectorIdNew;
636             this.nodeconnectorIdOld = nodeconnectorIdOld;
637             this.fcNodeConnectorOld = fcNodeConnectorOld;
638             this.ofTepName = ofTepName;
639         }
640
641         @Override
642         public List<? extends ListenableFuture<?>> call() throws Exception {
643             // If another renderer(for eg : OVS) needs to be supported, check can be performed here
644             // to call the respective helpers.
645             return removeOfTepStateConfiguration(nodeconnectorIdNew, nodeconnectorIdOld, ofTepName, fcNodeConnectorOld);
646         }
647
648         @Override
649         public String toString() {
650             return "OfTepStateRemoveWorker{nodeConnectorInfo=" + nodeconnectorIdNew + ", nodeConnectorIdOld="
651                     + nodeconnectorIdOld + ", fcNodeConnectorOld=" + fcNodeConnectorOld + ", ofTepName='"
652                     + ofTepName + '\'' + '}';
653         }
654     }
655 }