mdsalutil datastore-constrained migration, itm 1
[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 flowCapableNodeConnectorOld) {
86         String portName = flowCapableNodeConnectorOld.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, flowCapableNodeConnectorOld);
107         NodeConnectorId nodeConnectorId = InstanceIdentifier.keyOf(key.firstIdentifierOf(NodeConnector.class)).getId();
108         remove(nodeConnectorId, null, flowCapableNodeConnectorOld, portName);
109     }
110
111     private void remove(NodeConnectorId nodeConnectorIdNew, NodeConnectorId nodeConnectorIdOld,
112                         FlowCapableNodeConnector fcNodeConnectorNew, String portName) {
113         LOG.debug("InterfaceInventoryState REMOVE for {}", portName);
114         InterfaceStateRemoveWorker portStateRemoveWorker = new InterfaceStateRemoveWorker(nodeConnectorIdNew,
115                 nodeConnectorIdOld, 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 nodeConnectorIdNew,
277         NodeConnectorId nodeConnectorIdOld, String interfaceName, FlowCapableNodeConnector fcNodeConnectorOld) {
278         List<ListenableFuture<Void>> futures = new ArrayList<>();
279
280         NodeConnectorId nodeConnectorId = (nodeConnectorIdOld != null && !nodeConnectorIdNew.equals(nodeConnectorIdOld))
281                 ? nodeConnectorIdOld : nodeConnectorIdNew;
282
283         BigInteger dpId = DirectTunnelUtils.getDpnFromNodeConnectorId(nodeConnectorId);
284         // In a genuine port delete scenario, the reason will be there in the incoming event, for all remaining
285         // cases treat the event as DPN disconnect, if old and new ports are same. Else, this is a VM migration
286         // scenario, and should be treated as port removal.
287         if (fcNodeConnectorOld.getReason() != PortReason.Delete) {
288             //Remove event is because of connection lost between controller and switch, or switch shutdown.
289             // Hence, dont remove the interface but set the status as "unknown"
290             futures.add(txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL,
291                 tx -> updateInterfaceStateOnNodeRemove(tx, interfaceName, fcNodeConnectorOld)));
292         } else {
293             LOG.debug("removing interface state for interface: {}", interfaceName);
294             directTunnelUtils.deleteTunnelStateEntry(interfaceName);
295             DpnTepInterfaceInfo dpnTepInfo = dpnTepStateCache.getTunnelFromCache(interfaceName);
296             if (dpnTepInfo != null) {
297                 futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
298                     //SF 419 This will only be tunnel interface
299                     directTunnelUtils.removeLportTagInterfaceMap(interfaceName);
300                     directTunnelUtils.removeTunnelIngressFlow(tx, dpId, interfaceName);
301                 }));
302             } else {
303                 LOG.error("DPNTEPInfo is null for Tunnel Interface {}", interfaceName);
304             }
305         }
306         return futures;
307     }
308
309     private class InterfaceStateAddWorker implements Callable {
310         private final InstanceIdentifier<FlowCapableNodeConnector> key;
311         private final FlowCapableNodeConnector fcNodeConnectorNew;
312         private final String interfaceName;
313
314         InterfaceStateAddWorker(InstanceIdentifier<FlowCapableNodeConnector> key,
315                                 FlowCapableNodeConnector fcNodeConnectorNew, String portName) {
316             this.key = key;
317             this.fcNodeConnectorNew = fcNodeConnectorNew;
318             this.interfaceName = portName;
319         }
320
321         @Override
322         public Object call() throws Exception {
323             // If another renderer(for eg : OVS) needs to be supported, check can be performed here
324             // to call the respective helpers.
325             return addState(key, interfaceName, fcNodeConnectorNew);
326         }
327
328         @Override
329         public String toString() {
330             return "InterfaceStateAddWorker{fcNodeConnectorIdentifier=" + key + ", fcNodeConnectorNew="
331                     + fcNodeConnectorNew + ", interfaceName='" + interfaceName + '\'' + '}';
332         }
333     }
334
335     private class InterfaceStateUpdateWorker implements Callable {
336         private final InstanceIdentifier<FlowCapableNodeConnector> key;
337         private final FlowCapableNodeConnector fcNodeConnectorOld;
338         private final FlowCapableNodeConnector fcNodeConnectorNew;
339         private final String interfaceName;
340
341         InterfaceStateUpdateWorker(InstanceIdentifier<FlowCapableNodeConnector> key,
342                                    FlowCapableNodeConnector fcNodeConnectorOld,
343                                    FlowCapableNodeConnector fcNodeConnectorNew, String portName) {
344             this.key = key;
345             this.fcNodeConnectorOld = fcNodeConnectorOld;
346             this.fcNodeConnectorNew = fcNodeConnectorNew;
347             this.interfaceName = portName;
348         }
349
350         @Override
351         public Object call() {
352             // If another renderer(for eg : OVS) needs to be supported, check can be performed here
353             // to call the respective helpers.
354             return updateState(interfaceName, fcNodeConnectorNew, fcNodeConnectorOld);
355         }
356
357         @Override
358         public String toString() {
359             return "InterfaceStateUpdateWorker{key=" + key + ", fcNodeConnectorOld=" + fcNodeConnectorOld
360                     + ", fcNodeConnectorNew=" + fcNodeConnectorNew + ", interfaceName='" + interfaceName + '\'' + '}';
361         }
362     }
363
364     private class InterfaceStateRemoveWorker implements Callable {
365         private final NodeConnectorId nodeConnectorIdNew;
366         private final NodeConnectorId nodeConnectorIdOld;
367         private final FlowCapableNodeConnector fcNodeConnectorOld;
368         private final String interfaceName;
369
370         InterfaceStateRemoveWorker(NodeConnectorId nodeConnectorIdNew,
371             NodeConnectorId nodeConnectorIdOld, FlowCapableNodeConnector fcNodeConnectorOld,
372             String interfaceName) {
373             this.nodeConnectorIdNew = nodeConnectorIdNew;
374             this.nodeConnectorIdOld = nodeConnectorIdOld;
375             this.fcNodeConnectorOld = fcNodeConnectorOld;
376             this.interfaceName = interfaceName;
377         }
378
379         @Override
380         public Object call() {
381             // If another renderer(for eg : OVS) needs to be supported, check can be performed here
382             // to call the respective helpers.
383             return removeInterfaceStateConfiguration(nodeConnectorIdNew, nodeConnectorIdOld, interfaceName,
384                     fcNodeConnectorOld);
385         }
386
387         @Override
388         public String toString() {
389             return "InterfaceStateRemoveWorker{nodeConnectorIdNew=" + nodeConnectorIdNew + ", nodeConnectorIdOld="
390                     + nodeConnectorIdOld + ", fcNodeConnectorOld=" + fcNodeConnectorOld + ", interfaceName='"
391                     + interfaceName + '\'' + '}';
392         }
393     }
394 }