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 com.google.common.util.concurrent.MoreExecutors;
15 import java.util.ArrayList;
16 import java.util.Collections;
17 import java.util.List;
18 import javax.annotation.PostConstruct;
19 import javax.inject.Inject;
20 import javax.inject.Singleton;
21 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
22 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
23 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
24 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
25 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
26 import org.opendaylight.genius.infra.Datastore;
27 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
28 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
29 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
30 import org.opendaylight.genius.utils.SystemPropertyReader;
31 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
32 import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
33 import org.opendaylight.netvirt.fibmanager.api.IFibManager;
34 import org.opendaylight.netvirt.vpnmanager.api.VpnExtraRouteHelper;
35 import org.opendaylight.yang.gen.v1.urn.ericsson.params.xml.ns.yang.ebgp.rev150901.AddressFamily;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.L3nexthop;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.VpnNexthops;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.VpnNexthopsKey;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.VpnInstanceOpData;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.VpnIds;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpntargets.VpnTarget;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroutes.Vpn;
45 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
50 public class VpnOpStatusListener extends AsyncDataTreeChangeListenerBase<VpnInstanceOpDataEntry, VpnOpStatusListener> {
51 private static final Logger LOG = LoggerFactory.getLogger(VpnOpStatusListener.class);
52 private final DataBroker dataBroker;
53 private final ManagedNewTransactionRunner txRunner;
54 private final IBgpManager bgpManager;
55 private final IdManagerService idManager;
56 private final IFibManager fibManager;
57 private final IMdsalApiManager mdsalManager;
58 private final VpnFootprintService vpnFootprintService;
59 private final JobCoordinator jobCoordinator;
60 private final VpnUtil vpnUtil;
63 public VpnOpStatusListener(final DataBroker dataBroker, final IBgpManager bgpManager,
64 final IdManagerService idManager, final IFibManager fibManager,
65 final IMdsalApiManager mdsalManager, final VpnFootprintService vpnFootprintService,
66 final JobCoordinator jobCoordinator, VpnUtil vpnUtil) {
67 super(VpnInstanceOpDataEntry.class, VpnOpStatusListener.class);
68 this.dataBroker = dataBroker;
69 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
70 this.bgpManager = bgpManager;
71 this.idManager = idManager;
72 this.fibManager = fibManager;
73 this.mdsalManager = mdsalManager;
74 this.vpnFootprintService = vpnFootprintService;
75 this.jobCoordinator = jobCoordinator;
76 this.vpnUtil = vpnUtil;
81 LOG.info("{} start", getClass().getSimpleName());
82 registerListener(LogicalDatastoreType.OPERATIONAL, dataBroker);
86 protected InstanceIdentifier<VpnInstanceOpDataEntry> getWildCardPath() {
87 return InstanceIdentifier.create(VpnInstanceOpData.class).child(VpnInstanceOpDataEntry.class);
91 protected VpnOpStatusListener getDataTreeChangeListener() {
92 return VpnOpStatusListener.this;
96 protected void remove(InstanceIdentifier<VpnInstanceOpDataEntry> identifier, VpnInstanceOpDataEntry value) {
97 LOG.info("remove: Ignoring vpn Op {} with rd {}", value.getVpnInstanceName(), value.getVrfId());
101 @SuppressWarnings("checkstyle:IllegalCatch")
102 protected void update(InstanceIdentifier<VpnInstanceOpDataEntry> identifier,
103 VpnInstanceOpDataEntry original, VpnInstanceOpDataEntry update) {
104 LOG.info("update: Processing update for vpn {} with rd {}", update.getVpnInstanceName(), update.getVrfId());
105 if (update.getVpnState() == VpnInstanceOpDataEntry.VpnState.PendingDelete
106 && vpnFootprintService.isVpnFootPrintCleared(update)) {
108 final String vpnName = update.getVpnInstanceName();
109 final List<String> rds = update.getRd();
110 String primaryRd = update.getVrfId();
111 final long vpnId = vpnUtil.getVpnId(vpnName);
112 jobCoordinator.enqueueJob("VPN-" + update.getVpnInstanceName(), () -> {
113 // Two transactions are used, one for operational, one for config; we only submit the config
114 // transaction if the operational transaction succeeds
115 ListenableFuture<Void> operationalFuture = txRunner.callWithNewWriteOnlyTransactionAndSubmit(
116 Datastore.OPERATIONAL, operTx -> {
117 // Clean up VPNExtraRoutes Operational DS
118 if (VpnUtil.isBgpVpn(vpnName, primaryRd)) {
119 if (update.getType() == VpnInstanceOpDataEntry.Type.L2) {
120 rds.parallelStream().forEach(rd -> bgpManager.deleteVrf(
121 rd, false, AddressFamily.L2VPN));
123 if (update.isIpv4Configured()) {
124 rds.parallelStream().forEach(rd -> bgpManager.deleteVrf(
125 rd, false, AddressFamily.IPV4));
127 if (update.isIpv6Configured()) {
128 rds.parallelStream().forEach(rd -> bgpManager.deleteVrf(
129 rd, false, AddressFamily.IPV6));
132 InstanceIdentifier<Vpn> vpnToExtraroute =
133 VpnExtraRouteHelper.getVpnToExtrarouteVpnIdentifier(vpnName);
134 Optional<Vpn> optVpnToExtraroute = Optional.absent();
136 optVpnToExtraroute = SingleTransactionDataBroker.syncReadOptional(dataBroker,
137 LogicalDatastoreType.OPERATIONAL, vpnToExtraroute);
138 } catch (ReadFailedException e) {
139 LOG.error("update: Failed to read VpnToExtraRoute for vpn {}", vpnName);
141 if (optVpnToExtraroute.isPresent()) {
142 VpnUtil.removeVpnExtraRouteForVpn(vpnName, operTx);
144 if (VpnUtil.isL3VpnOverVxLan(update.getL3vni())) {
145 vpnUtil.removeExternalTunnelDemuxFlows(vpnName);
147 // Clean up PrefixToInterface Operational DS
148 Optional<VpnIds> optPrefixToIntf = Optional.absent();
150 optPrefixToIntf = SingleTransactionDataBroker.syncReadOptional(dataBroker,
151 LogicalDatastoreType.OPERATIONAL, VpnUtil.getPrefixToInterfaceIdentifier(vpnId));
152 } catch (ReadFailedException e) {
153 LOG.error("update: Failed to read PrefixToInterface for vpn {}", vpnName);
155 if (optPrefixToIntf.isPresent()) {
156 VpnUtil.removePrefixToInterfaceForVpnId(vpnId, operTx);
158 // Clean up L3NextHop Operational DS
159 InstanceIdentifier<VpnNexthops> vpnNextHops = InstanceIdentifier.builder(L3nexthop.class).child(
160 VpnNexthops.class, new VpnNexthopsKey(vpnId)).build();
161 Optional<VpnNexthops> optL3nexthopForVpnId = Optional.absent();
163 optL3nexthopForVpnId = SingleTransactionDataBroker.syncReadOptional(dataBroker,
164 LogicalDatastoreType.OPERATIONAL, vpnNextHops);
165 } catch (ReadFailedException e) {
166 LOG.error("update: Failed to read VpnNextHops for vpn {}", vpnName);
168 if (optL3nexthopForVpnId.isPresent()) {
169 VpnUtil.removeL3nexthopForVpnId(vpnId, operTx);
172 // Clean up VPNInstanceOpDataEntry
173 VpnUtil.removeVpnOpInstance(primaryRd, operTx);
176 Futures.addCallback(operationalFuture, new FutureCallback<Void>() {
178 public void onSuccess(Void result) {
179 Futures.addCallback(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
180 Datastore.CONFIGURATION, confTx -> {
181 // Clean up VpnInstanceToVpnId from Config DS
182 VpnUtil.removeVpnIdToVpnInstance(vpnId, confTx);
183 VpnUtil.removeVpnInstanceToVpnId(vpnName, confTx);
184 LOG.trace("Removed vpnIdentifier for rd{} vpnname {}", primaryRd, vpnName);
186 // Clean up FIB Entries Config DS
187 synchronized (vpnName.intern()) {
188 fibManager.removeVrfTable(primaryRd, confTx);
190 }), new VpnOpStatusListener.PostDeleteVpnInstanceWorker(vpnName),
191 MoreExecutors.directExecutor());
192 // Note: Release the of VpnId will happen in PostDeleteVpnInstancWorker only if
193 // operationalTxn/Config succeeds.
197 public void onFailure(Throwable throwable) {
198 LOG.error("Error deleting VPN {}", vpnName, throwable);
200 }, MoreExecutors.directExecutor());
202 LOG.info("Removed vpn data for vpnname {}", vpnName);
203 return Collections.singletonList(operationalFuture);
204 }, SystemPropertyReader.getDataStoreJobCoordinatorMaxRetries());
205 } else if (update.getVpnState() == VpnInstanceOpDataEntry.VpnState.Created) {
206 final String vpnName = update.getVpnInstanceName();
207 final List<String> rds = update.getRd();
208 String primaryRd = update.getVrfId();
209 if (!VpnUtil.isBgpVpn(vpnName, primaryRd)) {
212 if (original == null) {
213 LOG.error("VpnOpStatusListener.update: vpn {} with RD {}. add() handler already called",
217 if (update.getVpnTargets() == null) {
218 LOG.error("VpnOpStatusListener.update: vpn {} with RD {} vpnTargets not ready",
222 List<VpnTarget> vpnTargetList = update.getVpnTargets().getVpnTarget();
223 List<String> ertList = new ArrayList<>();
224 List<String> irtList = new ArrayList<>();
225 if (vpnTargetList != null) {
226 for (VpnTarget vpnTarget : vpnTargetList) {
227 if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.ExportExtcommunity) {
228 ertList.add(vpnTarget.getVrfRTValue());
230 if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.ImportExtcommunity) {
231 irtList.add(vpnTarget.getVrfRTValue());
233 if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.Both) {
234 ertList.add(vpnTarget.getVrfRTValue());
235 irtList.add(vpnTarget.getVrfRTValue());
239 LOG.error("VpnOpStatusListener.update: vpn target list is empty, cannot add BGP"
240 + " VPN {} RD {}", vpnName, primaryRd);
243 jobCoordinator.enqueueJob("VPN-" + update.getVpnInstanceName(), () -> {
244 rds.parallelStream().forEach(rd -> {
246 LOG.info("VpnOpStatusListener.update: updating BGPVPN for vpn {} with RD {}"
247 + " Type is {}, IPv4 is {}, IPv6 is {}", vpnName, primaryRd, update.getType(),
248 update.isIpv4Configured(), update.isIpv6Configured());
249 List<String> importRTList = rd.equals(primaryRd) ? irtList : Collections.emptyList();
250 if (update.getType() == VpnInstanceOpDataEntry.Type.L2) {
251 bgpManager.addVrf(rd, importRTList, ertList, AddressFamily.L2VPN);
253 if (!original.isIpv4Configured() && update.isIpv4Configured()) {
254 bgpManager.addVrf(rd, importRTList, ertList, AddressFamily.IPV4);
255 } else if (original.isIpv4Configured() && !update.isIpv4Configured()) {
256 bgpManager.deleteVrf(rd, false, AddressFamily.IPV4);
258 if (!original.isIpv6Configured() && update.isIpv6Configured()) {
259 bgpManager.addVrf(rd, importRTList, ertList, AddressFamily.IPV6);
260 } else if (original.isIpv6Configured() && !update.isIpv6Configured()) {
261 bgpManager.deleteVrf(rd, false, AddressFamily.IPV6);
263 } catch (Exception e) {
264 LOG.error("VpnOpStatusListener.update: Exception when updating VRF to BGP"
265 + " for vpn {} rd {}", vpnName, rd);
268 return Collections.emptyList();
274 protected void add(final InstanceIdentifier<VpnInstanceOpDataEntry> identifier,
275 final VpnInstanceOpDataEntry value) {
276 LOG.debug("add: Ignoring vpn Op {} with rd {}", value.getVpnInstanceName(), value.getVrfId());
279 private class PostDeleteVpnInstanceWorker implements FutureCallback<Void> {
280 private final Logger log = LoggerFactory.getLogger(VpnOpStatusListener.PostDeleteVpnInstanceWorker.class);
283 PostDeleteVpnInstanceWorker(String vpnName) {
284 this.vpnName = vpnName;
288 * This implies that all the future instances have returned success.
289 * Release the ID used for VPN back to IdManager
292 public void onSuccess(Void ignored) {
293 vpnUtil.releaseId(VpnConstants.VPN_IDPOOL_NAME, vpnName);
294 log.info("onSuccess: VpnId for VpnName {} is released to IdManager successfully.", vpnName);
298 * This method is used to handle failure callbacks.
301 public void onFailure(Throwable throwable) {
302 log.error("onFailure: Job for vpnInstance: {} failed with exception:",
303 vpnName , throwable);