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 static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
11 import static org.opendaylight.genius.infra.Datastore.OPERATIONAL;
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;
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.
58 public class TunnelInventoryStateListener extends AbstractTunnelListenerBase<FlowCapableNodeConnector> {
60 private static final Logger LOG = LoggerFactory.getLogger(TunnelInventoryStateListener.class);
62 private final JobCoordinator coordinator;
63 private final ManagedNewTransactionRunner txRunner;
64 private final TunnelStateCache tunnelStateCache;
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;
84 public void remove(@Nonnull InstanceIdentifier<FlowCapableNodeConnector> key,
85 @Nonnull FlowCapableNodeConnector flowCapableNodeConnector) {
86 String portName = flowCapableNodeConnector.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);
94 if (!tunnelStateCache.isInternalBasedOnState(portName)) {
95 LOG.debug("Node Connector Remove {} Interface is not a internal tunnel I/f, so no-op", portName);
98 } catch (ReadFailedException e) {
99 LOG.error("Tunnel {} is not present in operational DS ", portName);
103 if (!entityOwner()) {
106 LOG.debug("Received NodeConnector Remove Event: {}, {}", key, flowCapableNodeConnector);
107 NodeConnectorId nodeConnectorId = InstanceIdentifier.keyOf(key.firstIdentifierOf(NodeConnector.class)).getId();
108 remove(nodeConnectorId, flowCapableNodeConnector, portName);
111 private void remove(NodeConnectorId nodeConnectorId,
112 FlowCapableNodeConnector fcNodeConnectorNew, String portName) {
113 LOG.debug("InterfaceInventoryState REMOVE for {}", portName);
114 InterfaceStateRemoveWorker portStateRemoveWorker = new InterfaceStateRemoveWorker(nodeConnectorId,
115 fcNodeConnectorNew, portName);
116 coordinator.enqueueJob(portName, portStateRemoveWorker, ITMConstants.JOB_MAX_RETRIES);
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);
127 } else if (!dpnTepStateCache.isInternal(portName)) {
128 LOG.debug("Node Connector Update {} Interface is not a internal tunnel I/f, so no-op", portName);
131 if (fcNodeConnectorNew.getReason() == PortReason.Delete || !entityOwner()) {
134 LOG.debug("Received NodeConnector Update Event: {}, {}, {}", key, fcNodeConnectorOld, fcNodeConnectorNew);
136 InterfaceStateUpdateWorker portStateUpdateWorker = new InterfaceStateUpdateWorker(key, fcNodeConnectorOld,
137 fcNodeConnectorNew, portName);
138 coordinator.enqueueJob(portName, portStateUpdateWorker, ITMConstants.JOB_MAX_RETRIES);
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);
151 if (!entityOwner()) {
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);
162 } else if (!dpnTepStateCache.isInternal(portName)) {
163 LOG.debug("{} Interface is not a internal tunnel I/f, so no-op", portName);
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);
177 private List<ListenableFuture<Void>> updateState(String interfaceName,
178 FlowCapableNodeConnector flowCapableNodeConnectorNew,
179 FlowCapableNodeConnector flowCapableNodeConnectorOld) {
180 LOG.debug("Updating interface state for port: {}", interfaceName);
182 // Hardware updates can be ignored
183 Interface.OperStatus operStatusNew = getOpState(flowCapableNodeConnectorNew);
184 MacAddress macAddressNew = flowCapableNodeConnectorNew.getHardwareAddress();
186 Interface.OperStatus operStatusOld = getOpState(flowCapableNodeConnectorOld);
187 MacAddress macAddressOld = flowCapableNodeConnectorOld.getHardwareAddress();
189 boolean opstateModified = false;
190 boolean hardwareAddressModified = false;
191 if (!operStatusNew.equals(operStatusOld)) {
192 opstateModified = true;
194 if (!macAddressNew.equals(macAddressOld)) {
195 hardwareAddressModified = true;
198 if (!opstateModified && !hardwareAddressModified) {
199 LOG.debug("If State entry for port: {} Not Modified.", interfaceName);
200 return Collections.emptyList();
203 DpnTepInterfaceInfo dpnTepInfo = dpnTepStateCache.getTunnelFromCache(interfaceName);
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;
211 if (!opstateModified && !hardwareAddressModified) {
212 LOG.debug("If State entry for port: {} Not Modified.", interfaceName);
213 return Collections.emptyList();
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(),
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);
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);
237 handleInterfaceStateUpdates(tx, dpnTepInfo, true, interfaceName, flowCapableNodeConnector.getName(),
238 Interface.OperStatus.Unknown);
241 private Interface.OperStatus getOpState(FlowCapableNodeConnector flowCapableNodeConnector) {
242 return flowCapableNodeConnector.getState().isLive()
243 && !flowCapableNodeConnector.getConfiguration().isPORTDOWN()
244 ? Interface.OperStatus.Up : Interface.OperStatus.Down;
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)) {
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));
265 tx.merge(tnlStateId, stateTnlBuilder.build());
268 private boolean modifyOpState(DpnTepInterfaceInfo dpnTepInterfaceInfo, boolean opStateModified) {
269 return opStateModified && (dpnTepInterfaceInfo != null);
272 private boolean modifyTunnelOpState(DpnTepInterfaceInfo dpnTepInterfaceInfo, boolean opStateModified) {
273 return !dpnTepInterfaceInfo.isMonitoringEnabled() && modifyOpState(dpnTepInterfaceInfo, opStateModified);
276 private List<ListenableFuture<Void>> removeInterfaceStateConfiguration(NodeConnectorId nodeConnectorId,
277 String interfaceName,
278 FlowCapableNodeConnector
279 flowCapableNodeConnector) {
280 List<ListenableFuture<Void>> futures = new ArrayList<>();
282 BigInteger dpId = DirectTunnelUtils.getDpnFromNodeConnectorId(nodeConnectorId);
283 // In a genuine port delete scenario, the reason will be there in the incoming event, for all remaining
284 // cases treat the event as DPN disconnect, if old and new ports are same. Else, this is a VM migration
285 // scenario, and should be treated as port removal.
286 if (flowCapableNodeConnector.getReason() != PortReason.Delete) {
287 //Remove event is because of connection lost between controller and switch, or switch shutdown.
288 // Hence, dont remove the interface but set the status as "unknown"
289 futures.add(txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL,
290 tx -> updateInterfaceStateOnNodeRemove(tx, interfaceName, flowCapableNodeConnector)));
292 LOG.debug("removing interface state for interface: {}", interfaceName);
293 directTunnelUtils.deleteTunnelStateEntry(interfaceName);
294 DpnTepInterfaceInfo dpnTepInfo = dpnTepStateCache.getTunnelFromCache(interfaceName);
295 if (dpnTepInfo != null) {
296 futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
297 //SF 419 This will only be tunnel interface
298 directTunnelUtils.removeLportTagInterfaceMap(interfaceName);
299 directTunnelUtils.removeTunnelIngressFlow(tx, dpId, interfaceName);
302 LOG.error("DPNTEPInfo is null for Tunnel Interface {}", interfaceName);
308 private class InterfaceStateAddWorker implements Callable {
309 private final InstanceIdentifier<FlowCapableNodeConnector> key;
310 private final FlowCapableNodeConnector fcNodeConnectorNew;
311 private final String interfaceName;
313 InterfaceStateAddWorker(InstanceIdentifier<FlowCapableNodeConnector> key,
314 FlowCapableNodeConnector fcNodeConnectorNew, String portName) {
316 this.fcNodeConnectorNew = fcNodeConnectorNew;
317 this.interfaceName = portName;
321 public Object call() throws Exception {
322 // If another renderer(for eg : OVS) needs to be supported, check can be performed here
323 // to call the respective helpers.
324 return addState(key, interfaceName, fcNodeConnectorNew);
328 public String toString() {
329 return "InterfaceStateAddWorker{fcNodeConnectorIdentifier=" + key + ", fcNodeConnectorNew="
330 + fcNodeConnectorNew + ", interfaceName='" + interfaceName + '\'' + '}';
334 private class InterfaceStateUpdateWorker implements Callable {
335 private final InstanceIdentifier<FlowCapableNodeConnector> key;
336 private final FlowCapableNodeConnector fcNodeConnectorOld;
337 private final FlowCapableNodeConnector fcNodeConnectorNew;
338 private final String interfaceName;
340 InterfaceStateUpdateWorker(InstanceIdentifier<FlowCapableNodeConnector> key,
341 FlowCapableNodeConnector fcNodeConnectorOld,
342 FlowCapableNodeConnector fcNodeConnectorNew, String portName) {
344 this.fcNodeConnectorOld = fcNodeConnectorOld;
345 this.fcNodeConnectorNew = fcNodeConnectorNew;
346 this.interfaceName = portName;
350 public Object call() {
351 // If another renderer(for eg : OVS) needs to be supported, check can be performed here
352 // to call the respective helpers.
353 return updateState(interfaceName, fcNodeConnectorNew, fcNodeConnectorOld);
357 public String toString() {
358 return "InterfaceStateUpdateWorker{key=" + key + ", fcNodeConnectorOld=" + fcNodeConnectorOld
359 + ", fcNodeConnectorNew=" + fcNodeConnectorNew + ", interfaceName='" + interfaceName + '\'' + '}';
363 private class InterfaceStateRemoveWorker implements Callable {
364 private final NodeConnectorId nodeConnectorId;
365 private final FlowCapableNodeConnector flowCapableNodeConnector;
366 private final String interfaceName;
368 InterfaceStateRemoveWorker(NodeConnectorId nodeConnectorId, FlowCapableNodeConnector flowCapableNodeConnector,
369 String interfaceName) {
370 this.nodeConnectorId = nodeConnectorId;
371 this.flowCapableNodeConnector = flowCapableNodeConnector;
372 this.interfaceName = interfaceName;
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);
383 public String toString() {
384 return "InterfaceStateRemoveWorker{nodeConnectorId=" + nodeConnectorId + ", fcNodeConnector"
385 + flowCapableNodeConnector + ", interfaceName='" + interfaceName + '\'' + '}';