Refactor InterfaceMetaUtils to non-static
[genius.git] / interfacemanager / interfacemanager-impl / src / main / java / org / opendaylight / genius / interfacemanager / listeners / InterfaceInventoryStateListener.java
1 /*
2  * Copyright (c) 2016, 2017 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.interfacemanager.listeners;
9
10 import com.google.common.util.concurrent.ListenableFuture;
11 import java.math.BigInteger;
12 import java.util.ArrayList;
13 import java.util.List;
14 import java.util.Objects;
15 import java.util.concurrent.Callable;
16 import javax.inject.Inject;
17 import javax.inject.Singleton;
18 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
19 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
20 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
21 import org.opendaylight.genius.datastoreutils.AsyncClusteredDataTreeChangeListenerBase;
22 import org.opendaylight.genius.interfacemanager.IfmConstants;
23 import org.opendaylight.genius.interfacemanager.IfmUtil;
24 import org.opendaylight.genius.interfacemanager.commons.AlivenessMonitorUtils;
25 import org.opendaylight.genius.interfacemanager.commons.InterfaceManagerCommonUtils;
26 import org.opendaylight.genius.interfacemanager.commons.InterfaceMetaUtils;
27 import org.opendaylight.genius.interfacemanager.renderer.ovs.statehelpers.OvsInterfaceStateAddHelper;
28 import org.opendaylight.genius.interfacemanager.renderer.ovs.statehelpers.OvsInterfaceStateUpdateHelper;
29 import org.opendaylight.genius.interfacemanager.servicebindings.flowbased.utilities.FlowBasedServicesUtils;
30 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
31 import org.opendaylight.genius.utils.clustering.EntityOwnershipUtils;
32 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.PortReason;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.alivenessmonitor.rev160411.AlivenessMonitorService;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406._interface.child.info.InterfaceParentEntry;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406._interface.child.info._interface.parent.entry.InterfaceChildEntry;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfTunnel;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
44 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47
48 /**
49  * This Class is a Data Change Listener for FlowCapableNodeConnector updates.
50  * This creates an entry in the interface-state OperDS for every node-connector
51  * used.
52  *
53  * <p>
54  * NOTE: This class just creates an ifstate entry whose interface-name will be
55  * the same as the node-connector portname. If PortName is not unique across
56  * DPNs, this implementation can have problems.
57  */
58 @Singleton
59 public class InterfaceInventoryStateListener
60         extends AsyncClusteredDataTreeChangeListenerBase<FlowCapableNodeConnector, InterfaceInventoryStateListener> {
61     private static final Logger LOG = LoggerFactory.getLogger(InterfaceInventoryStateListener.class);
62     private final DataBroker dataBroker;
63     private final IdManagerService idManager;
64     private final EntityOwnershipUtils entityOwnershipUtils;
65     private final JobCoordinator coordinator;
66     private final InterfaceManagerCommonUtils interfaceManagerCommonUtils;
67     private final AlivenessMonitorUtils alivenessMonitorUtils;
68     private final OvsInterfaceStateUpdateHelper ovsInterfaceStateUpdateHelper;
69     private final OvsInterfaceStateAddHelper ovsInterfaceStateAddHelper;
70     private final InterfaceMetaUtils interfaceMetaUtils;
71
72     @Inject
73     public InterfaceInventoryStateListener(final DataBroker dataBroker, final IdManagerService idManagerService,
74             final IMdsalApiManager mdsalApiManager, final AlivenessMonitorService alivenessMonitorService,
75             final EntityOwnershipUtils entityOwnershipUtils, final JobCoordinator coordinator,
76             final InterfaceManagerCommonUtils interfaceManagerCommonUtils,
77             final OvsInterfaceStateAddHelper ovsInterfaceStateAddHelper,
78             final OvsInterfaceStateUpdateHelper ovsInterfaceStateUpdateHelper,
79             final AlivenessMonitorUtils alivenessMonitorUtils,
80             final InterfaceMetaUtils interfaceMetaUtils) {
81         super(FlowCapableNodeConnector.class, InterfaceInventoryStateListener.class);
82         this.dataBroker = dataBroker;
83         this.idManager = idManagerService;
84         this.entityOwnershipUtils = entityOwnershipUtils;
85         this.coordinator = coordinator;
86         this.interfaceManagerCommonUtils = interfaceManagerCommonUtils;
87         this.alivenessMonitorUtils = alivenessMonitorUtils;
88         this.ovsInterfaceStateUpdateHelper = ovsInterfaceStateUpdateHelper;
89         this.ovsInterfaceStateAddHelper = ovsInterfaceStateAddHelper;
90         this.interfaceMetaUtils = interfaceMetaUtils;
91         this.registerListener(LogicalDatastoreType.OPERATIONAL, dataBroker);
92     }
93
94     @Override
95     protected InstanceIdentifier<FlowCapableNodeConnector> getWildCardPath() {
96         return InstanceIdentifier.create(Nodes.class).child(Node.class).child(NodeConnector.class)
97                 .augmentation(FlowCapableNodeConnector.class);
98     }
99
100     @Override
101     protected InterfaceInventoryStateListener getDataTreeChangeListener() {
102         return InterfaceInventoryStateListener.this;
103     }
104
105     @Override
106     protected void remove(InstanceIdentifier<FlowCapableNodeConnector> key,
107             FlowCapableNodeConnector flowCapableNodeConnectorOld) {
108         if (!entityOwnershipUtils.isEntityOwner(IfmConstants.INTERFACE_CONFIG_ENTITY,
109                 IfmConstants.INTERFACE_CONFIG_ENTITY)) {
110             return;
111         }
112
113         LOG.debug("Received NodeConnector Remove Event: {}, {}", key, flowCapableNodeConnectorOld);
114         NodeConnectorId nodeConnectorId = InstanceIdentifier.keyOf(key.firstIdentifierOf(NodeConnector.class))
115             .getId();
116         String portName = InterfaceManagerCommonUtils.getPortNameForInterface(nodeConnectorId,
117             flowCapableNodeConnectorOld.getName());
118
119         remove(nodeConnectorId, null, flowCapableNodeConnectorOld, portName, true);
120     }
121
122     private void remove(NodeConnectorId nodeConnectorIdNew, NodeConnectorId nodeConnectorIdOld,
123             FlowCapableNodeConnector fcNodeConnectorNew, String portName, boolean isNetworkEvent) {
124         boolean isNodePresent = interfaceManagerCommonUtils.isNodePresent(nodeConnectorIdNew);
125         InterfaceStateRemoveWorker portStateRemoveWorker = new InterfaceStateRemoveWorker(idManager, nodeConnectorIdNew,
126                 nodeConnectorIdOld, fcNodeConnectorNew, portName, portName, isNodePresent, isNetworkEvent, true);
127         coordinator.enqueueJob(portName, portStateRemoveWorker, IfmConstants.JOB_MAX_RETRIES);
128     }
129
130     @Override
131     protected void update(InstanceIdentifier<FlowCapableNodeConnector> key, FlowCapableNodeConnector fcNodeConnectorOld,
132             FlowCapableNodeConnector fcNodeConnectorNew) {
133         if (!entityOwnershipUtils.isEntityOwner(IfmConstants.INTERFACE_CONFIG_ENTITY,
134                 IfmConstants.INTERFACE_CONFIG_ENTITY)) {
135             return;
136         }
137
138         LOG.debug("Received NodeConnector Update Event: {}, {}, {}", key, fcNodeConnectorOld, fcNodeConnectorNew);
139         NodeConnectorId nodeConnectorId = InstanceIdentifier.keyOf(key.firstIdentifierOf(NodeConnector.class))
140             .getId();
141         String portName = InterfaceManagerCommonUtils.getPortNameForInterface(nodeConnectorId,
142             fcNodeConnectorNew.getName());
143
144         InterfaceStateUpdateWorker portStateUpdateWorker = new InterfaceStateUpdateWorker(key, fcNodeConnectorOld,
145             fcNodeConnectorNew, portName);
146         coordinator.enqueueJob(portName, portStateUpdateWorker, IfmConstants.JOB_MAX_RETRIES);
147     }
148
149     @Override
150     protected void add(InstanceIdentifier<FlowCapableNodeConnector> key, FlowCapableNodeConnector fcNodeConnectorNew) {
151         if (!entityOwnershipUtils.isEntityOwner(IfmConstants.INTERFACE_CONFIG_ENTITY,
152                 IfmConstants.INTERFACE_CONFIG_ENTITY)) {
153             return;
154         }
155
156         LOG.debug("Received NodeConnector Add Event: {}, {}", key, fcNodeConnectorNew);
157         NodeConnectorId nodeConnectorId = InstanceIdentifier.keyOf(key.firstIdentifierOf(NodeConnector.class))
158             .getId();
159         String portName = InterfaceManagerCommonUtils.getPortNameForInterface(nodeConnectorId,
160             fcNodeConnectorNew.getName());
161
162         if (InterfaceManagerCommonUtils.isNovaPort(portName)) {
163             NodeConnectorId nodeConnectorIdOld =
164                     FlowBasedServicesUtils.getNodeConnectorIdFromInterface(portName, interfaceManagerCommonUtils);
165             if (nodeConnectorIdOld != null && !nodeConnectorId.equals(nodeConnectorIdOld)) {
166                 BigInteger dpnIdOld = IfmUtil.getDpnFromNodeConnectorId(nodeConnectorIdOld);
167                 BigInteger dpnIdNew = IfmUtil.getDpnFromNodeConnectorId(nodeConnectorId);
168                 if (!Objects.equals(dpnIdOld, dpnIdNew)) {
169                     if (fcNodeConnectorNew.getReason() != PortReason.Add) {
170                         LOG.error("Dropping Port update event for {}, as DPN id is changed from {} to {}",
171                             fcNodeConnectorNew.getName(), dpnIdOld, dpnIdNew);
172                         return;
173                     }
174                 } else {
175                     LOG.warn("Port number update detected for {}", fcNodeConnectorNew.getName());
176                 }
177                 //VM Migration or Port Number Update: Delete existing interface entry for older DPN
178                 LOG.debug("Triggering NodeConnector Remove Event for the interface: {}, {}, {}", portName,
179                     nodeConnectorId, nodeConnectorIdOld);
180                 remove(nodeConnectorId, nodeConnectorIdOld, fcNodeConnectorNew, portName, false);
181                 // Adding a delay of 10sec for VM migration, so applications will have sufficient time
182                 // for processing remove before add
183                 try {
184                     Thread.sleep(IfmConstants.DELAY_TIME_IN_MILLISECOND);
185                 } catch (InterruptedException e) {
186                     LOG.error("Error while waiting for the vm migration remove events to get processed");
187                 }
188             }
189         }
190
191         InterfaceStateAddWorker ifStateAddWorker = new InterfaceStateAddWorker(idManager, nodeConnectorId,
192             fcNodeConnectorNew, portName);
193         coordinator.enqueueJob(portName, ifStateAddWorker, IfmConstants.JOB_MAX_RETRIES);
194     }
195
196
197     private class InterfaceStateAddWorker implements Callable {
198         private final NodeConnectorId nodeConnectorId;
199         private final FlowCapableNodeConnector fcNodeConnectorNew;
200         private final String interfaceName;
201         private final IdManagerService idManager;
202
203         InterfaceStateAddWorker(IdManagerService idManager, NodeConnectorId nodeConnectorId,
204                 FlowCapableNodeConnector fcNodeConnectorNew, String portName) {
205             this.nodeConnectorId = nodeConnectorId;
206             this.fcNodeConnectorNew = fcNodeConnectorNew;
207             this.interfaceName = portName;
208             this.idManager = idManager;
209         }
210
211         @Override
212         public Object call() {
213             List<ListenableFuture<Void>> futures = ovsInterfaceStateAddHelper.addState(nodeConnectorId, interfaceName,
214                     fcNodeConnectorNew);
215             List<InterfaceChildEntry> interfaceChildEntries = getInterfaceChildEntries(interfaceName);
216             for (InterfaceChildEntry interfaceChildEntry : interfaceChildEntries) {
217                 InterfaceStateAddWorker interfaceStateAddWorker = new InterfaceStateAddWorker(idManager,
218                         nodeConnectorId, fcNodeConnectorNew, interfaceChildEntry.getChildInterface());
219                 coordinator.enqueueJob(interfaceName, interfaceStateAddWorker);
220             }
221             return futures;
222         }
223
224         @Override
225         public String toString() {
226             return "InterfaceStateAddWorker{" + "nodeConnectorId=" + nodeConnectorId + ", fcNodeConnectorNew="
227                     + fcNodeConnectorNew + ", interfaceName='" + interfaceName + '\'' + '}';
228         }
229     }
230
231     private class InterfaceStateUpdateWorker implements Callable {
232         private final InstanceIdentifier<FlowCapableNodeConnector> key;
233         private final FlowCapableNodeConnector fcNodeConnectorOld;
234         private final FlowCapableNodeConnector fcNodeConnectorNew;
235         private final String interfaceName;
236
237         InterfaceStateUpdateWorker(InstanceIdentifier<FlowCapableNodeConnector> key,
238                 FlowCapableNodeConnector fcNodeConnectorOld, FlowCapableNodeConnector fcNodeConnectorNew,
239                 String portName) {
240             this.key = key;
241             this.fcNodeConnectorOld = fcNodeConnectorOld;
242             this.fcNodeConnectorNew = fcNodeConnectorNew;
243             this.interfaceName = portName;
244         }
245
246         @Override
247         public Object call() {
248             List<ListenableFuture<Void>> futures = ovsInterfaceStateUpdateHelper.updateState(
249                     interfaceName, fcNodeConnectorNew, fcNodeConnectorOld);
250             List<InterfaceChildEntry> interfaceChildEntries = getInterfaceChildEntries(interfaceName);
251             for (InterfaceChildEntry interfaceChildEntry : interfaceChildEntries) {
252                 InterfaceStateUpdateWorker interfaceStateUpdateWorker = new InterfaceStateUpdateWorker(key,
253                         fcNodeConnectorOld, fcNodeConnectorNew, interfaceChildEntry.getChildInterface());
254                 coordinator.enqueueJob(interfaceName, interfaceStateUpdateWorker);
255             }
256             return futures;
257         }
258
259         @Override
260         public String toString() {
261             return "InterfaceStateUpdateWorker{" + "key=" + key + ", fcNodeConnectorOld=" + fcNodeConnectorOld
262                     + ", fcNodeConnectorNew=" + fcNodeConnectorNew + ", interfaceName='" + interfaceName + '\'' + '}';
263         }
264     }
265
266     private class InterfaceStateRemoveWorker implements Callable {
267         private final NodeConnectorId nodeConnectorIdNew;
268         private NodeConnectorId nodeConnectorIdOld;
269         FlowCapableNodeConnector fcNodeConnectorOld;
270         private final String interfaceName;
271         private final String parentInterface;
272         private final IdManagerService idManager;
273         private final boolean isNodePresent;
274         private final boolean isNetworkEvent;
275         private final boolean isParentInterface;
276
277         InterfaceStateRemoveWorker(IdManagerService idManager, NodeConnectorId nodeConnectorIdNew,
278                 NodeConnectorId nodeConnectorIdOld, FlowCapableNodeConnector fcNodeConnectorOld, String interfaceName,
279                 String parentInterface, boolean isNodePresent, boolean isNetworkEvent, boolean isParentInterface) {
280             this.nodeConnectorIdNew = nodeConnectorIdNew;
281             this.nodeConnectorIdOld = nodeConnectorIdOld;
282             this.fcNodeConnectorOld = fcNodeConnectorOld;
283             this.interfaceName = interfaceName;
284             this.parentInterface = parentInterface;
285             this.idManager = idManager;
286             this.isNodePresent = isNodePresent;
287             this.isNetworkEvent = isNetworkEvent;
288             this.isParentInterface = isParentInterface;
289         }
290
291         @Override
292         public Object call() {
293             List<ListenableFuture<Void>> futures = null;
294             // VM Migration: Skip OFPPR_DELETE event received after OFPPR_ADD
295             // for same interface from Older DPN
296             if (isParentInterface && isNetworkEvent) {
297                 nodeConnectorIdOld = FlowBasedServicesUtils.getNodeConnectorIdFromInterface(interfaceName,
298                         interfaceManagerCommonUtils);
299                 if (nodeConnectorIdOld != null && !nodeConnectorIdNew.equals(nodeConnectorIdOld)) {
300                     LOG.debug("Dropping the NodeConnector Remove Event for the interface: {}, {}, {}", interfaceName,
301                             nodeConnectorIdNew, nodeConnectorIdOld);
302                     return futures;
303                 }
304             }
305
306             futures = removeInterfaceStateConfiguration(nodeConnectorIdNew, nodeConnectorIdOld, interfaceName,
307                     fcNodeConnectorOld, isNodePresent);
308
309             List<InterfaceChildEntry> interfaceChildEntries = getInterfaceChildEntries(interfaceName);
310             for (InterfaceChildEntry interfaceChildEntry : interfaceChildEntries) {
311                 // Fetch all interfaces on this port and trigger remove worker
312                 // for each of them
313                 InterfaceStateRemoveWorker interfaceStateRemoveWorker = new InterfaceStateRemoveWorker(idManager,
314                         nodeConnectorIdNew, nodeConnectorIdOld, fcNodeConnectorOld,
315                         interfaceChildEntry.getChildInterface(), interfaceName, isNodePresent, isNetworkEvent, false);
316                 coordinator.enqueueJob(interfaceName, interfaceStateRemoveWorker);
317             }
318             return futures;
319         }
320
321         private List<ListenableFuture<Void>> removeInterfaceStateConfiguration(NodeConnectorId nodeConnectorIdNew,
322                 NodeConnectorId nodeConnectorIdOld, String interfaceName, FlowCapableNodeConnector fcNodeConnectorOld,
323                 boolean isNodePresent) {
324             LOG.debug("Removing interface state information for interface: {} {}", interfaceName, isNodePresent);
325             List<ListenableFuture<Void>> futures = new ArrayList<>();
326             WriteTransaction defaultOperationalShardTransaction = dataBroker.newWriteOnlyTransaction();
327
328             //VM Migration: Use old nodeConnectorId to delete the interface entry
329             NodeConnectorId nodeConnectorId = nodeConnectorIdOld != null
330                     && !nodeConnectorIdNew.equals(nodeConnectorIdOld) ? nodeConnectorIdOld : nodeConnectorIdNew;
331             // delete the port entry from interface operational DS
332             BigInteger dpId = IfmUtil.getDpnFromNodeConnectorId(nodeConnectorId);
333
334             //VM Migration: Update the interface state to unknown only if remove event received for same switch
335             if (!isNodePresent && nodeConnectorIdNew.equals(nodeConnectorIdOld)) {
336                 //Remove event is because of connection lost between controller and switch, or switch shutdown.
337                 // Hence, don't remove the interface but set the status as "unknown"
338                 ovsInterfaceStateUpdateHelper.updateInterfaceStateOnNodeRemove(interfaceName, fcNodeConnectorOld,
339                         defaultOperationalShardTransaction);
340             } else {
341                 InterfaceManagerCommonUtils.deleteStateEntry(interfaceName, defaultOperationalShardTransaction);
342                 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces
343                     .Interface iface = interfaceManagerCommonUtils.getInterfaceFromConfigDS(interfaceName);
344
345                 if (iface != null) {
346                     // If this interface is a tunnel interface, remove the tunnel ingress flow and stop LLDP monitoring
347                     if (InterfaceManagerCommonUtils.isTunnelInterface(iface)) {
348                         interfaceMetaUtils.removeLportTagInterfaceMap(defaultOperationalShardTransaction,
349                                 interfaceName);
350                         handleTunnelMonitoringRemoval(dpId, iface.getName(), iface.getAugmentation(IfTunnel.class),
351                                 defaultOperationalShardTransaction, futures);
352                         return futures;
353                     }
354                 }
355                 // remove ingress flow only for northbound configured interfaces
356                 // skip this check for non-unique ports(Ex: br-int,br-ex)
357                 if (iface != null || iface == null && !interfaceName.contains(fcNodeConnectorOld.getName())) {
358                     FlowBasedServicesUtils.removeIngressFlow(interfaceName, dpId, dataBroker, futures);
359                 }
360
361                 // Delete the Vpn Interface from DpnToInterface Op DS.
362                 interfaceManagerCommonUtils.deleteDpnToInterface(dpId, interfaceName,
363                         defaultOperationalShardTransaction);
364             }
365             futures.add(defaultOperationalShardTransaction.submit());
366             return futures;
367         }
368
369         private void handleTunnelMonitoringRemoval(BigInteger dpId, String interfaceName,
370                 IfTunnel ifTunnel, WriteTransaction transaction, List<ListenableFuture<Void>> futures) {
371             interfaceManagerCommonUtils.removeTunnelIngressFlow(ifTunnel, dpId, interfaceName);
372
373             IfmUtil.unbindService(dataBroker, coordinator, interfaceName,
374                     FlowBasedServicesUtils.buildDefaultServiceId(interfaceName));
375
376             futures.add(transaction.submit());
377             alivenessMonitorUtils.stopLLDPMonitoring(ifTunnel, interfaceName);
378         }
379
380         @Override
381         public String toString() {
382             return "InterfaceStateRemoveWorker{" + "nodeConnectorIdNew=" + nodeConnectorIdNew + ", nodeConnectorIdOld="
383                     + nodeConnectorIdOld + ", fcNodeConnectorOld=" + fcNodeConnectorOld + ", interfaceName='"
384                     + interfaceName + '\'' + '}';
385         }
386     }
387
388     public List<InterfaceChildEntry> getInterfaceChildEntries(String interfaceName) {
389         InterfaceParentEntry interfaceParentEntry =
390                 interfaceMetaUtils.getInterfaceParentEntryFromConfigDS(interfaceName);
391         if (interfaceParentEntry != null && interfaceParentEntry.getInterfaceChildEntry() != null) {
392             return interfaceParentEntry.getInterfaceChildEntry();
393         }
394         return new ArrayList<>();
395     }
396 }