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 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);
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, flowCapableNodeConnectorOld);
107 NodeConnectorId nodeConnectorId = InstanceIdentifier.keyOf(key.firstIdentifierOf(NodeConnector.class)).getId();
108 remove(nodeConnectorId, null, flowCapableNodeConnectorOld, portName);
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);
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 nodeConnectorIdNew,
277 NodeConnectorId nodeConnectorIdOld, String interfaceName, FlowCapableNodeConnector fcNodeConnectorOld) {
278 List<ListenableFuture<Void>> futures = new ArrayList<>();
280 NodeConnectorId nodeConnectorId = (nodeConnectorIdOld != null && !nodeConnectorIdNew.equals(nodeConnectorIdOld))
281 ? nodeConnectorIdOld : nodeConnectorIdNew;
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)));
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);
303 LOG.error("DPNTEPInfo is null for Tunnel Interface {}", interfaceName);
309 private class InterfaceStateAddWorker implements Callable {
310 private final InstanceIdentifier<FlowCapableNodeConnector> key;
311 private final FlowCapableNodeConnector fcNodeConnectorNew;
312 private final String interfaceName;
314 InterfaceStateAddWorker(InstanceIdentifier<FlowCapableNodeConnector> key,
315 FlowCapableNodeConnector fcNodeConnectorNew, String portName) {
317 this.fcNodeConnectorNew = fcNodeConnectorNew;
318 this.interfaceName = portName;
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);
329 public String toString() {
330 return "InterfaceStateAddWorker{fcNodeConnectorIdentifier=" + key + ", fcNodeConnectorNew="
331 + fcNodeConnectorNew + ", interfaceName='" + interfaceName + '\'' + '}';
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;
341 InterfaceStateUpdateWorker(InstanceIdentifier<FlowCapableNodeConnector> key,
342 FlowCapableNodeConnector fcNodeConnectorOld,
343 FlowCapableNodeConnector fcNodeConnectorNew, String portName) {
345 this.fcNodeConnectorOld = fcNodeConnectorOld;
346 this.fcNodeConnectorNew = fcNodeConnectorNew;
347 this.interfaceName = portName;
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);
358 public String toString() {
359 return "InterfaceStateUpdateWorker{key=" + key + ", fcNodeConnectorOld=" + fcNodeConnectorOld
360 + ", fcNodeConnectorNew=" + fcNodeConnectorNew + ", interfaceName='" + interfaceName + '\'' + '}';
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;
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;
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,
388 public String toString() {
389 return "InterfaceStateRemoveWorker{nodeConnectorIdNew=" + nodeConnectorIdNew + ", nodeConnectorIdOld="
390 + nodeConnectorIdOld + ", fcNodeConnectorOld=" + fcNodeConnectorOld + ", interfaceName='"
391 + interfaceName + '\'' + '}';