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