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