2 * Copyright (c) 2015, 2017 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.netvirt.vpnmanager;
10 import com.google.common.base.Optional;
11 import com.google.common.util.concurrent.FutureCallback;
12 import com.google.common.util.concurrent.Futures;
13 import com.google.common.util.concurrent.ListenableFuture;
14 import java.math.BigInteger;
15 import java.util.ArrayList;
16 import java.util.List;
17 import javax.annotation.PostConstruct;
18 import javax.inject.Inject;
19 import javax.inject.Singleton;
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.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
23 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
24 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
25 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
26 import org.opendaylight.netvirt.vpnmanager.api.InterfaceUtils;
27 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
28 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.vpn._interface.VpnInstanceNames;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.L2vlan;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesState;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
32 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.OperStatus;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn._interface.op.data.VpnInterfaceOpDataEntry;
34 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
39 public class InterfaceStateChangeListener
40 extends AsyncDataTreeChangeListenerBase<Interface, InterfaceStateChangeListener> {
42 private static final Logger LOG = LoggerFactory.getLogger(InterfaceStateChangeListener.class);
43 private static final short DJC_MAX_RETRIES = 3;
45 private final DataBroker dataBroker;
46 private final ManagedNewTransactionRunner txRunner;
47 private final VpnInterfaceManager vpnInterfaceManager;
48 private final JobCoordinator jobCoordinator;
51 public InterfaceStateChangeListener(final DataBroker dataBroker, final VpnInterfaceManager vpnInterfaceManager,
52 final JobCoordinator jobCoordinator) {
53 super(Interface.class, InterfaceStateChangeListener.class);
54 this.dataBroker = dataBroker;
55 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
56 this.vpnInterfaceManager = vpnInterfaceManager;
57 this.jobCoordinator = jobCoordinator;
62 LOG.info("{} start", getClass().getSimpleName());
63 registerListener(LogicalDatastoreType.OPERATIONAL, dataBroker);
68 protected InstanceIdentifier<Interface> getWildCardPath() {
69 return InstanceIdentifier.create(InterfacesState.class).child(Interface.class);
73 protected InterfaceStateChangeListener getDataTreeChangeListener() {
74 return InterfaceStateChangeListener.this;
79 // TODO Clean up the exception handling
80 @SuppressWarnings("checkstyle:IllegalCatch")
81 protected void add(InstanceIdentifier<Interface> identifier, Interface intrf) {
83 if (L2vlan.class.equals(intrf.getType())) {
84 LOG.info("VPN Interface add event - intfName {} from InterfaceStateChangeListener",
86 jobCoordinator.enqueueJob("VPNINTERFACE-" + intrf.getName(), () -> {
87 List<ListenableFuture<Void>> futures = new ArrayList<>(3);
88 futures.add(txRunner.callWithNewWriteOnlyTransactionAndSubmit(writeInvTxn -> {
89 ListenableFuture<Void> configFuture
90 = txRunner.callWithNewWriteOnlyTransactionAndSubmit(writeConfigTxn -> {
91 ListenableFuture<Void> operFuture
92 = txRunner.callWithNewWriteOnlyTransactionAndSubmit(writeOperTxn -> {
93 final String interfaceName = intrf.getName();
94 LOG.info("Detected interface add event for interface {}", interfaceName);
95 final VpnInterface vpnIf =
96 VpnUtil.getConfiguredVpnInterface(dataBroker, interfaceName);
98 for (VpnInstanceNames vpnInterfaceVpnInstance :
99 vpnIf.getVpnInstanceNames()) {
100 String vpnName = vpnInterfaceVpnInstance.getVpnName();
101 String primaryRd = VpnUtil.getPrimaryRd(dataBroker, vpnName);
102 if (!vpnInterfaceManager.isVpnInstanceReady(vpnName)) {
103 LOG.info("VPN Interface add event - intfName {} onto vpnName {} "
104 + "running oper-driven, VpnInstance not ready, holding"
105 + " on", vpnIf.getName(), vpnName);
106 } else if (VpnUtil.isVpnPendingDelete(dataBroker, primaryRd)) {
107 LOG.error("add: Ignoring addition of vpnInterface {}, as"
108 + " vpnInstance {} with primaryRd {} is already marked for"
109 + " deletion", interfaceName, vpnName, primaryRd);
111 BigInteger intfDpnId = BigInteger.ZERO;
113 intfDpnId = InterfaceUtils.getDpIdFromInterface(intrf);
114 } catch (Exception e) {
115 LOG.error("Unable to retrieve dpnId for interface {}. "
116 + "Process vpn interface add failed",intrf.getName(),
120 final BigInteger dpnId = intfDpnId;
121 final int ifIndex = intrf.getIfIndex();
122 LOG.info("VPN Interface add event - intfName {} onto vpnName {}"
123 + " running oper-driven", vpnIf.getName(), vpnName);
124 vpnInterfaceManager.processVpnInterfaceUp(dpnId, vpnIf, primaryRd,
125 ifIndex, false, writeConfigTxn, writeOperTxn, writeInvTxn,
133 futures.add(operFuture);
134 operFuture.get(); //Synchronous submit of operTxn
136 futures.add(configFuture);
137 //TODO: Allow immediateFailedFuture from writeCfgTxn to cancel writeInvTxn as well.
138 Futures.addCallback(configFuture, new PostVpnInterfaceThreadWorker(intrf.getName(), true,
144 } catch (Exception e) {
145 LOG.error("Exception caught in Interface {} Operational State Up event", intrf.getName(), e);
150 // TODO Clean up the exception handling
151 @SuppressWarnings("checkstyle:IllegalCatch")
152 protected void remove(InstanceIdentifier<Interface> identifier, Interface intrf) {
153 final String ifName = intrf.getName();
154 BigInteger dpId = BigInteger.ZERO;
156 if (L2vlan.class.equals(intrf.getType())) {
157 LOG.info("VPN Interface remove event - intfName {} from InterfaceStateChangeListener",
160 dpId = InterfaceUtils.getDpIdFromInterface(intrf);
161 } catch (Exception e) {
162 LOG.error("Unable to retrieve dpnId from interface operational data store for interface"
163 + " {}. Fetching from vpn interface op data store. ", ifName, e);
165 final BigInteger inputDpId = dpId;
166 jobCoordinator.enqueueJob("VPNINTERFACE-" + ifName, () -> {
167 List<ListenableFuture<Void>> futures = new ArrayList<>(3);
168 ListenableFuture<Void> configFuture = txRunner
169 .callWithNewWriteOnlyTransactionAndSubmit(writeConfigTxn -> {
170 futures.add(txRunner.callWithNewWriteOnlyTransactionAndSubmit(writeOperTxn -> {
171 futures.add(txRunner.callWithNewWriteOnlyTransactionAndSubmit(writeInvTxn -> {
172 VpnInterface cfgVpnInterface =
173 VpnUtil.getConfiguredVpnInterface(dataBroker, ifName);
174 if (cfgVpnInterface == null) {
175 LOG.debug("Interface {} is not a vpninterface, ignoring.", ifName);
178 for (VpnInstanceNames vpnInterfaceVpnInstance :
179 cfgVpnInterface.getVpnInstanceNames()) {
180 String vpnName = vpnInterfaceVpnInstance.getVpnName();
181 Optional<VpnInterfaceOpDataEntry> optVpnInterface =
182 VpnUtil.getVpnInterfaceOpDataEntry(dataBroker, ifName, vpnName);
183 if (!optVpnInterface.isPresent()) {
184 LOG.debug("Interface {} vpn {} is not a vpninterface, or deletion"
185 + " triggered by northbound agent. ignoring.", ifName, vpnName);
188 final VpnInterfaceOpDataEntry vpnInterface = optVpnInterface.get();
189 String gwMac = intrf.getPhysAddress() != null ? intrf.getPhysAddress()
190 .getValue() : vpnInterface.getGatewayMacAddress();
191 BigInteger dpnId = inputDpId;
192 if (dpnId == null || dpnId.equals(BigInteger.ZERO)) {
193 dpnId = vpnInterface.getDpnId();
195 final int ifIndex = intrf.getIfIndex();
196 LOG.info("VPN Interface remove event - intfName {} onto vpnName {}"
197 + " running oper-driver", vpnInterface.getName(), vpnName);
198 vpnInterfaceManager.processVpnInterfaceDown(dpnId, ifName, ifIndex, gwMac,
199 vpnInterface, false, writeConfigTxn, writeOperTxn, writeInvTxn);
204 futures.add(configFuture);
205 Futures.addCallback(configFuture, new PostVpnInterfaceThreadWorker(intrf.getName(), false,
210 } catch (Exception e) {
211 LOG.error("Exception observed in handling deletion of VPN Interface {}. ", ifName, e);
215 // TODO Clean up the exception handling
216 @SuppressWarnings("checkstyle:IllegalCatch")
218 protected void update(InstanceIdentifier<Interface> identifier,
219 Interface original, Interface update) {
220 final String ifName = update.getName();
222 OperStatus originalOperStatus = original.getOperStatus();
223 OperStatus updateOperStatus = update.getOperStatus();
224 if (originalOperStatus.equals(Interface.OperStatus.Unknown)
225 || updateOperStatus.equals(Interface.OperStatus.Unknown)) {
226 LOG.debug("Interface {} state change is from/to null/UNKNOWN. Ignoring the update event.",
231 if (update.getIfIndex() == null) {
234 if (L2vlan.class.equals(update.getType())) {
235 LOG.info("VPN Interface update event - intfName {} from InterfaceStateChangeListener",
237 jobCoordinator.enqueueJob("VPNINTERFACE-" + ifName, () -> {
238 List<ListenableFuture<Void>> futures = new ArrayList<>(3);
239 futures.add(txRunner.callWithNewWriteOnlyTransactionAndSubmit(writeOperTxn -> {
240 futures.add(txRunner.callWithNewWriteOnlyTransactionAndSubmit(writeConfigTxn -> {
241 futures.add(txRunner.callWithNewWriteOnlyTransactionAndSubmit(writeInvTxn -> {
242 final VpnInterface vpnIf =
243 VpnUtil.getConfiguredVpnInterface(dataBroker, ifName);
245 final int ifIndex = update.getIfIndex();
246 BigInteger dpnId = BigInteger.ZERO;
248 dpnId = InterfaceUtils.getDpIdFromInterface(update);
249 } catch (Exception e) {
250 LOG.error("remove: Unable to retrieve dpnId for interface {}", ifName, e);
253 if (update.getOperStatus().equals(Interface.OperStatus.Up)) {
254 for (VpnInstanceNames vpnInterfaceVpnInstance : vpnIf.getVpnInstanceNames()) {
255 String vpnName = vpnInterfaceVpnInstance.getVpnName();
256 String primaryRd = VpnUtil.getPrimaryRd(dataBroker, vpnName);
257 if (!vpnInterfaceManager.isVpnInstanceReady(vpnName)) {
258 LOG.error("VPN Interface update event - intfName {} onto vpnName {} "
259 + "running oper-driven UP, VpnInstance not ready,"
260 + " holding on", vpnIf.getName(), vpnName);
261 } else if (VpnUtil.isVpnPendingDelete(dataBroker, primaryRd)) {
262 LOG.error("update: Ignoring UP event for vpnInterface {}, as "
263 + "vpnInstance {} with primaryRd {} is already marked for"
264 + " deletion", vpnIf.getName(), vpnName, primaryRd);
266 vpnInterfaceManager.processVpnInterfaceUp(dpnId, vpnIf, primaryRd,
267 ifIndex, true, writeConfigTxn, writeOperTxn, writeInvTxn,
271 } else if (update.getOperStatus().equals(Interface.OperStatus.Down)) {
272 for (VpnInstanceNames vpnInterfaceVpnInstance : vpnIf.getVpnInstanceNames()) {
273 String vpnName = vpnInterfaceVpnInstance.getVpnName();
274 LOG.info("VPN Interface update event - intfName {} onto vpnName {}"
275 + " running oper-driven DOWN", vpnIf.getName(), vpnName);
276 Optional<VpnInterfaceOpDataEntry> optVpnInterface =
277 VpnUtil.getVpnInterfaceOpDataEntry(dataBroker,
278 vpnIf.getName(), vpnName);
279 if (optVpnInterface.isPresent()) {
280 VpnInterfaceOpDataEntry vpnOpInterface = optVpnInterface.get();
281 vpnInterfaceManager.processVpnInterfaceDown(dpnId, vpnIf.getName(),
282 ifIndex, update.getPhysAddress().getValue(), vpnOpInterface,
283 true, writeConfigTxn, writeOperTxn, writeInvTxn);
285 LOG.error("InterfaceStateChangeListener Update DOWN - vpnInterface {}"
286 + " not available, ignoring event", vpnIf.getName());
292 LOG.debug("Interface {} is not a vpninterface, ignoring.", ifName);
300 } catch (Exception e) {
301 LOG.error("Exception observed in handling updation of VPN Interface {}. ", update.getName(), e);
305 private class PostVpnInterfaceThreadWorker implements FutureCallback<Void> {
306 private final String interfaceName;
307 private final boolean add;
308 private final String txnDestination;
310 PostVpnInterfaceThreadWorker(String interfaceName, boolean add, String transactionDest) {
311 this.interfaceName = interfaceName;
313 this.txnDestination = transactionDest;
317 public void onSuccess(Void voidObj) {
319 LOG.debug("InterfaceStateChangeListener: VrfEntries for {} stored into destination {} successfully",
320 interfaceName, txnDestination);
322 LOG.debug("InterfaceStateChangeListener: VrfEntries for {} removed successfully", interfaceName);
327 public void onFailure(Throwable throwable) {
329 LOG.error("InterfaceStateChangeListener: VrfEntries for {} failed to store into destination {}"
330 + " with exception: {}", interfaceName, txnDestination, throwable);
332 LOG.error("InterfaceStateChangeListener: VrfEntries for {} removal failed with exception: {}",
333 interfaceName, throwable);
334 VpnUtil.unsetScheduledToRemoveForVpnInterface(dataBroker, interfaceName);