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.cache.UnprocessedNodeConnectorEndPointCache;
32 import org.opendaylight.genius.itm.globals.ITMConstants;
33 import org.opendaylight.genius.itm.impl.ItmUtils;
34 import org.opendaylight.genius.itm.itmdirecttunnels.renderer.ovs.utilities.DirectTunnelUtils;
35 import org.opendaylight.genius.itm.utils.DpnTepInterfaceInfo;
36 import org.opendaylight.genius.itm.utils.NodeConnectorInfo;
37 import org.opendaylight.genius.itm.utils.NodeConnectorInfoBuilder;
38 import org.opendaylight.genius.utils.clustering.EntityOwnershipUtils;
39 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
40 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
41 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.PortReason;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.tunnels_state.StateTunnelList;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.tunnels_state.StateTunnelListBuilder;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.tunnels_state.StateTunnelListKey;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
51 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
56 * This Class is a Data Change Listener for FlowCapableNodeConnector updates.
57 * This creates an entry in the tunnels-state OperDS for every node-connector used.
59 public class TunnelInventoryStateListener extends AbstractTunnelListenerBase<FlowCapableNodeConnector> {
61 private static final Logger LOG = LoggerFactory.getLogger(TunnelInventoryStateListener.class);
63 private final JobCoordinator coordinator;
64 private final ManagedNewTransactionRunner txRunner;
65 private final TunnelStateCache tunnelStateCache;
67 public TunnelInventoryStateListener(final DataBroker dataBroker,
68 final JobCoordinator coordinator,
69 final EntityOwnershipUtils entityOwnershipUtils,
70 final TunnelStateCache tunnelStateCache,
71 final DpnTepStateCache dpnTepStateCache,
72 final DPNTEPsInfoCache dpntePsInfoCache,
73 final UnprocessedNodeConnectorCache unprocessedNCCache,
74 final UnprocessedNodeConnectorEndPointCache
75 unprocessedNodeConnectorEndPointCache,
76 final DirectTunnelUtils directTunnelUtils) {
77 super(dataBroker, LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.create(Nodes.class).child(Node.class)
78 .child(NodeConnector.class).augmentation(FlowCapableNodeConnector.class), dpnTepStateCache,
79 dpntePsInfoCache, unprocessedNCCache,
80 unprocessedNodeConnectorEndPointCache, entityOwnershipUtils, directTunnelUtils);
81 this.coordinator = coordinator;
82 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
83 this.tunnelStateCache = tunnelStateCache;
88 public void remove(@Nonnull InstanceIdentifier<FlowCapableNodeConnector> key,
89 @Nonnull FlowCapableNodeConnector flowCapableNodeConnector) {
90 String portName = flowCapableNodeConnector.getName();
91 LOG.debug("InterfaceInventoryState Remove for {}", portName);
92 // ITM Direct Tunnels Return if its not tunnel port and if its not Internal
93 if (!DirectTunnelUtils.TUNNEL_PORT_PREDICATE.test(portName)) {
94 LOG.debug("Node Connector Remove - {} Interface is not a tunnel I/f, so no-op", portName);
98 if (!tunnelStateCache.isInternalBasedOnState(portName)) {
99 LOG.debug("Node Connector Remove {} Interface is not a internal tunnel I/f, so no-op", portName);
102 } catch (ReadFailedException e) {
103 LOG.error("Tunnel {} is not present in operational DS ", portName);
107 if (!entityOwner()) {
110 LOG.debug("Received NodeConnector Remove Event: {}, {}", key, flowCapableNodeConnector);
111 NodeConnectorId nodeConnectorId = InstanceIdentifier.keyOf(key.firstIdentifierOf(NodeConnector.class)).getId();
112 remove(nodeConnectorId, flowCapableNodeConnector, portName);
115 private void remove(NodeConnectorId nodeConnectorId,
116 FlowCapableNodeConnector fcNodeConnectorNew, String portName) {
117 LOG.debug("InterfaceInventoryState REMOVE for {}", portName);
118 TunnelInterfaceStateRemoveWorker portStateRemoveWorker = new TunnelInterfaceStateRemoveWorker(nodeConnectorId,
119 fcNodeConnectorNew, portName);
120 coordinator.enqueueJob(portName, portStateRemoveWorker, ITMConstants.JOB_MAX_RETRIES);
124 public void update(@Nonnull InstanceIdentifier<FlowCapableNodeConnector> key,
125 @Nonnull FlowCapableNodeConnector fcNodeConnectorOld,
126 @Nonnull FlowCapableNodeConnector fcNodeConnectorNew) {
127 String portName = fcNodeConnectorNew.getName();
128 if (DirectTunnelUtils.TUNNEL_PORT_PREDICATE.test(portName)) {
129 LOG.debug("Node Connector Update - {} Interface is not a tunnel I/f, so no-op", portName);
131 } else if (!dpnTepStateCache.isInternal(portName)) {
132 LOG.debug("Node Connector Update {} Interface is not a internal tunnel I/f, so no-op", portName);
135 if (fcNodeConnectorNew.getReason() == PortReason.Delete || !entityOwner()) {
138 LOG.debug("Received NodeConnector Update Event: {}, {}, {}", key, fcNodeConnectorOld, fcNodeConnectorNew);
140 TunnelInterfaceStateUpdateWorker portStateUpdateWorker =
141 new TunnelInterfaceStateUpdateWorker(key, fcNodeConnectorOld, fcNodeConnectorNew, portName);
142 coordinator.enqueueJob(portName, portStateUpdateWorker, ITMConstants.JOB_MAX_RETRIES);
146 public void add(@Nonnull InstanceIdentifier<FlowCapableNodeConnector> key,
147 @Nonnull FlowCapableNodeConnector fcNodeConnectorNew) {
148 String portName = fcNodeConnectorNew.getName();
149 LOG.debug("InterfaceInventoryState ADD for {}", portName);
150 // Return if its not tunnel port and if its not Internal
151 if (!DirectTunnelUtils.TUNNEL_PORT_PREDICATE.test(portName)) {
152 LOG.debug("Node Connector Add {} Interface is not a tunnel I/f, so no-op", portName);
155 if (!dpnTepStateCache.isConfigAvailable(portName)) {
156 // Park the notification
157 LOG.debug("Unable to process the NodeConnector ADD event for {} as Config not available."
158 + "Hence parking it", portName);
159 NodeConnectorInfo nodeConnectorInfo = new NodeConnectorInfoBuilder().setNodeConnectorId(key)
160 .setNodeConnector(fcNodeConnectorNew).build();
161 unprocessedNCCache.add(portName, nodeConnectorInfo);
163 } else if (!dpnTepStateCache.isInternal(portName)) {
164 LOG.debug("{} Interface is not a internal tunnel I/f, so no-op", portName);
168 LOG.debug("Received NodeConnector Add Event: {}, {}", key, fcNodeConnectorNew);
169 // NodeConnectorId nodeConnectorId =
170 // InstanceIdentifier.keyOf(key.firstIdentifierOf(NodeConnector.class)).getId();
171 TunnelInterfaceStateAddWorker ifStateAddWorker =
172 new TunnelInterfaceStateAddWorker(key, fcNodeConnectorNew, portName);
173 coordinator.enqueueJob(portName, ifStateAddWorker, ITMConstants.JOB_MAX_RETRIES);
176 private List<ListenableFuture<Void>> updateState(String interfaceName,
177 FlowCapableNodeConnector flowCapableNodeConnectorNew,
178 FlowCapableNodeConnector flowCapableNodeConnectorOld) {
179 LOG.debug("Updating interface state for port: {}", interfaceName);
181 // Hardware updates can be ignored
182 Interface.OperStatus operStatusNew = getOpState(flowCapableNodeConnectorNew);
183 MacAddress macAddressNew = flowCapableNodeConnectorNew.getHardwareAddress();
185 Interface.OperStatus operStatusOld = getOpState(flowCapableNodeConnectorOld);
186 MacAddress macAddressOld = flowCapableNodeConnectorOld.getHardwareAddress();
188 boolean opstateModified = false;
189 boolean hardwareAddressModified = false;
190 if (!operStatusNew.equals(operStatusOld)) {
191 opstateModified = true;
193 if (!macAddressNew.equals(macAddressOld)) {
194 hardwareAddressModified = true;
197 if (!opstateModified && !hardwareAddressModified) {
198 LOG.debug("If State entry for port: {} Not Modified.", interfaceName);
199 return Collections.emptyList();
202 DpnTepInterfaceInfo dpnTepInfo = dpnTepStateCache.getTunnelFromCache(interfaceName);
204 // For monitoring enabled tunnels, skip opstate updation
205 if (!modifyTunnelOpState(dpnTepInfo, opstateModified)) {
206 LOG.debug("skipping Tunnel-state update for monitoring enabled tunnel interface {}", interfaceName);
207 opstateModified = false;
210 if (!opstateModified && !hardwareAddressModified) {
211 LOG.debug("If State entry for port: {} Not Modified.", interfaceName);
212 return Collections.emptyList();
214 if (opstateModified) {
215 return Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL, tx -> {
216 // modify the attributes in interface operational DS
217 handleInterfaceStateUpdates(tx, dpnTepInfo, true, interfaceName, flowCapableNodeConnectorNew.getName(),
222 return Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL, tx -> {
223 // modify the attributes in interface operational DS
224 handleInterfaceStateUpdates(tx, dpnTepInfo, false, interfaceName,
225 flowCapableNodeConnectorNew.getName(), operStatusNew);
231 private void updateInterfaceStateOnNodeRemove(TypedWriteTransaction<Operational> tx, String interfaceName,
232 FlowCapableNodeConnector flowCapableNodeConnector) {
233 LOG.debug("Updating interface oper-status to UNKNOWN for : {}", interfaceName);
234 DpnTepInterfaceInfo dpnTepInfo = dpnTepStateCache.getTunnelFromCache(interfaceName);
236 handleInterfaceStateUpdates(tx, dpnTepInfo, 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(TypedWriteTransaction<Operational> tx,
247 DpnTepInterfaceInfo dpnTepInfo,
248 boolean opStateModified, String interfaceName, String portName,
249 Interface.OperStatus opState) {
250 if (dpnTepInfo == null && !interfaceName.equals(portName)) {
253 LOG.debug("updating interface state entry for {}", interfaceName);
254 InstanceIdentifier<StateTunnelList> tnlStateId = ItmUtils.buildStateTunnelListId(
255 new StateTunnelListKey(interfaceName));
256 StateTunnelListBuilder stateTnlBuilder = new StateTunnelListBuilder();
257 stateTnlBuilder.withKey(new StateTunnelListKey(interfaceName));
258 if (modifyOpState(dpnTepInfo, opStateModified)) {
259 LOG.debug("updating interface oper status as {} for {}", opState.name(), interfaceName);
260 boolean tunnelState = opState.equals(Interface.OperStatus.Up);
261 stateTnlBuilder.setTunnelState(tunnelState);
262 stateTnlBuilder.setOperState(DirectTunnelUtils.convertInterfaceToTunnelOperState(opState));
264 tx.merge(tnlStateId, stateTnlBuilder.build());
267 private boolean modifyOpState(DpnTepInterfaceInfo dpnTepInterfaceInfo, boolean opStateModified) {
268 return opStateModified && (dpnTepInterfaceInfo != null);
271 private boolean modifyTunnelOpState(DpnTepInterfaceInfo dpnTepInterfaceInfo, boolean opStateModified) {
272 return !dpnTepInterfaceInfo.isMonitoringEnabled() && modifyOpState(dpnTepInterfaceInfo, opStateModified);
275 private List<ListenableFuture<Void>> removeInterfaceStateConfiguration(NodeConnectorId nodeConnectorId,
276 String interfaceName,
277 FlowCapableNodeConnector
278 flowCapableNodeConnector) {
279 List<ListenableFuture<Void>> futures = new ArrayList<>();
281 BigInteger dpId = DirectTunnelUtils.getDpnFromNodeConnectorId(nodeConnectorId);
282 // In a genuine port delete scenario, the reason will be there in the incoming event, for all remaining
283 // cases treat the event as DPN disconnect, if old and new ports are same. Else, this is a VM migration
284 // scenario, and should be treated as port removal.
285 if (flowCapableNodeConnector.getReason() != PortReason.Delete) {
286 //Remove event is because of connection lost between controller and switch, or switch shutdown.
287 // Hence, dont remove the interface but set the status as "unknown"
288 futures.add(txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL,
289 tx -> updateInterfaceStateOnNodeRemove(tx, interfaceName, flowCapableNodeConnector)));
291 LOG.debug("removing interface state for interface: {}", interfaceName);
292 directTunnelUtils.deleteTunnelStateEntry(interfaceName);
293 DpnTepInterfaceInfo dpnTepInfo = dpnTepStateCache.getTunnelFromCache(interfaceName);
294 if (dpnTepInfo != null) {
295 futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
296 //SF 419 This will only be tunnel interface
297 directTunnelUtils.removeLportTagInterfaceMap(interfaceName);
298 directTunnelUtils.removeTunnelIngressFlow(tx, dpId, interfaceName);
301 LOG.error("DPNTEPInfo is null for Tunnel Interface {}", interfaceName);
307 private class TunnelInterfaceStateAddWorker implements Callable {
308 private final InstanceIdentifier<FlowCapableNodeConnector> key;
309 private final FlowCapableNodeConnector fcNodeConnectorNew;
310 private final String interfaceName;
312 TunnelInterfaceStateAddWorker(InstanceIdentifier<FlowCapableNodeConnector> key,
313 FlowCapableNodeConnector fcNodeConnectorNew, String portName) {
315 this.fcNodeConnectorNew = fcNodeConnectorNew;
316 this.interfaceName = portName;
320 public Object call() throws Exception {
321 // If another renderer(for eg : OVS) needs to be supported, check can be performed here
322 // to call the respective helpers.
323 return addState(key, interfaceName, fcNodeConnectorNew);
327 public String toString() {
328 return "TunnelInterfaceStateAddWorker{fcNodeConnectorIdentifier=" + key + ", fcNodeConnectorNew="
329 + fcNodeConnectorNew + ", interfaceName='" + interfaceName + '\'' + '}';
333 private class TunnelInterfaceStateUpdateWorker implements Callable {
334 private final InstanceIdentifier<FlowCapableNodeConnector> key;
335 private final FlowCapableNodeConnector fcNodeConnectorOld;
336 private final FlowCapableNodeConnector fcNodeConnectorNew;
337 private final String interfaceName;
339 TunnelInterfaceStateUpdateWorker(InstanceIdentifier<FlowCapableNodeConnector> key,
340 FlowCapableNodeConnector fcNodeConnectorOld,
341 FlowCapableNodeConnector fcNodeConnectorNew, String portName) {
343 this.fcNodeConnectorOld = fcNodeConnectorOld;
344 this.fcNodeConnectorNew = fcNodeConnectorNew;
345 this.interfaceName = portName;
349 public Object call() {
350 // If another renderer(for eg : OVS) needs to be supported, check can be performed here
351 // to call the respective helpers.
352 return updateState(interfaceName, fcNodeConnectorNew, fcNodeConnectorOld);
356 public String toString() {
357 return "TunnelInterfaceStateUpdateWorker{key=" + key + ", fcNodeConnectorOld=" + fcNodeConnectorOld
358 + ", fcNodeConnectorNew=" + fcNodeConnectorNew + ", interfaceName='" + interfaceName + '\'' + '}';
362 private class TunnelInterfaceStateRemoveWorker implements Callable {
363 private final NodeConnectorId nodeConnectorId;
364 private final FlowCapableNodeConnector flowCapableNodeConnector;
365 private final String interfaceName;
367 TunnelInterfaceStateRemoveWorker(NodeConnectorId nodeConnectorId,
368 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 "TunnelInterfaceStateRemoveWorker{nodeConnectorId=" + nodeConnectorId + ", fcNodeConnector"
385 + flowCapableNodeConnector + ", interfaceName='" + interfaceName + '\'' + '}';