2 * Copyright (c) 2018 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
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
8 package org.opendaylight.genius.itm.itmdirecttunnels.listeners;
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;
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.
55 public class TunnelInventoryStateListener extends AbstractTunnelListenerBase<FlowCapableNodeConnector> {
57 private static final Logger LOG = LoggerFactory.getLogger(TunnelInventoryStateListener.class);
59 private final JobCoordinator coordinator;
60 private final ManagedNewTransactionRunner txRunner;
61 private final TunnelStateCache tunnelStateCache;
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;
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);
91 if (!tunnelStateCache.isInternalBasedOnState(portName)) {
92 LOG.debug("Node Connector Remove {} Interface is not a internal tunnel I/f, so no-op", portName);
95 } catch (ReadFailedException e) {
96 LOG.error("Tunnel {} is not present in operational DS ", portName);
100 if (!entityOwner()) {
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);
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);
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);
124 } else if (!dpnTepStateCache.isInternal(portName)) {
125 LOG.debug("Node Connector Update {} Interface is not a internal tunnel I/f, so no-op", portName);
128 if (fcNodeConnectorNew.getReason() == PortReason.Delete || !entityOwner()) {
131 LOG.debug("Received NodeConnector Update Event: {}, {}, {}", key, fcNodeConnectorOld, fcNodeConnectorNew);
133 InterfaceStateUpdateWorker portStateUpdateWorker = new InterfaceStateUpdateWorker(key, fcNodeConnectorOld,
134 fcNodeConnectorNew, portName);
135 coordinator.enqueueJob(portName, portStateUpdateWorker, ITMConstants.JOB_MAX_RETRIES);
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);
148 if (!entityOwner()) {
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);
159 } else if (!dpnTepStateCache.isInternal(portName)) {
160 LOG.debug("{} Interface is not a internal tunnel I/f, so no-op", portName);
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);
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);
180 // Hardware updates can be ignored
181 Interface.OperStatus operStatusNew = getOpState(flowCapableNodeConnectorNew);
182 MacAddress macAddressNew = flowCapableNodeConnectorNew.getHardwareAddress();
184 Interface.OperStatus operStatusOld = getOpState(flowCapableNodeConnectorOld);
185 MacAddress macAddressOld = flowCapableNodeConnectorOld.getHardwareAddress();
187 boolean opstateModified = false;
188 boolean hardwareAddressModified = false;
189 if (!operStatusNew.equals(operStatusOld)) {
190 opstateModified = true;
192 if (!macAddressNew.equals(macAddressOld)) {
193 hardwareAddressModified = true;
196 if (!opstateModified && !hardwareAddressModified) {
197 LOG.debug("If State entry for port: {} Not Modified.", interfaceName);
198 return Collections.emptyList();
201 DpnTepInterfaceInfo dpnTepInfo = dpnTepStateCache.getTunnelFromCache(interfaceName);
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;
209 if (!opstateModified && !hardwareAddressModified) {
210 LOG.debug("If State entry for port: {} Not Modified.", interfaceName);
211 return Collections.emptyList();
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(),
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);
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);
236 handleInterfaceStateUpdates(dpnTepInfo, transaction, true, interfaceName, flowCapableNodeConnector.getName(),
237 Interface.OperStatus.Unknown);
240 private Interface.OperStatus getOpState(FlowCapableNodeConnector flowCapableNodeConnector) {
241 return flowCapableNodeConnector.getState().isLive()
242 && !flowCapableNodeConnector.getConfiguration().isPORTDOWN()
243 ? Interface.OperStatus.Up : Interface.OperStatus.Down;
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)) {
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.setKey(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));
263 transaction.merge(LogicalDatastoreType.OPERATIONAL, tnlStateId, stateTnlBuilder.build(), false);
266 private boolean modifyOpState(DpnTepInterfaceInfo dpnTepInterfaceInfo, boolean opStateModified) {
267 return opStateModified && (dpnTepInterfaceInfo == null || dpnTepInterfaceInfo.isMonitoringEnabled());
270 private boolean modifyTunnelOpState(DpnTepInterfaceInfo dpnTepInterfaceInfo, boolean opStateModified) {
271 return !dpnTepInterfaceInfo.isMonitoringEnabled() && modifyOpState(dpnTepInterfaceInfo, opStateModified);
274 private List<ListenableFuture<Void>> removeInterfaceStateConfiguration(NodeConnectorId nodeConnectorIdNew,
275 NodeConnectorId nodeConnectorIdOld,
276 String interfaceName,
277 FlowCapableNodeConnector
279 String parentInterface) {
280 List<ListenableFuture<Void>> futures = new ArrayList<>();
282 NodeConnectorId nodeConnectorId = (nodeConnectorIdOld != null && !nodeConnectorIdNew.equals(nodeConnectorIdOld))
283 ? nodeConnectorIdOld : nodeConnectorIdNew;
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 && nodeConnectorIdNew.equals(nodeConnectorIdOld)) {
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);
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);
305 LOG.error("DPNTEPInfo is null for Tunnel Interface {}", interfaceName);
312 private class InterfaceStateAddWorker implements Callable {
313 private final InstanceIdentifier<FlowCapableNodeConnector> key;
314 private final FlowCapableNodeConnector fcNodeConnectorNew;
315 private final String interfaceName;
317 InterfaceStateAddWorker(InstanceIdentifier<FlowCapableNodeConnector> key,
318 FlowCapableNodeConnector fcNodeConnectorNew, String portName) {
320 this.fcNodeConnectorNew = fcNodeConnectorNew;
321 this.interfaceName = portName;
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);
332 public String toString() {
333 return "InterfaceStateAddWorker{fcNodeConnectorIdentifier=" + key + ", fcNodeConnectorNew="
334 + fcNodeConnectorNew + ", interfaceName='" + interfaceName + '\'' + '}';
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;
344 InterfaceStateUpdateWorker(InstanceIdentifier<FlowCapableNodeConnector> key,
345 FlowCapableNodeConnector fcNodeConnectorOld,
346 FlowCapableNodeConnector fcNodeConnectorNew, String portName) {
348 this.fcNodeConnectorOld = fcNodeConnectorOld;
349 this.fcNodeConnectorNew = fcNodeConnectorNew;
350 this.interfaceName = portName;
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);
361 public String toString() {
362 return "InterfaceStateUpdateWorker{key=" + key + ", fcNodeConnectorOld=" + fcNodeConnectorOld
363 + ", fcNodeConnectorNew=" + fcNodeConnectorNew + ", interfaceName='" + interfaceName + '\'' + '}';
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;
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;
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);
393 public String toString() {
394 return "InterfaceStateRemoveWorker{nodeConnectorIdNew=" + nodeConnectorIdNew + ", nodeConnectorIdOld="
395 + nodeConnectorIdOld + ", fcNodeConnectorOld=" + fcNodeConnectorOld + ", interfaceName='"
396 + interfaceName + '\'' + '}';