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 static java.util.Collections.emptyList;
12 import com.google.common.base.Optional;
13 import com.google.common.util.concurrent.FutureCallback;
14 import com.google.common.util.concurrent.Futures;
15 import com.google.common.util.concurrent.ListenableFuture;
16 import com.google.common.util.concurrent.MoreExecutors;
17 import java.util.ArrayList;
18 import java.util.Collections;
19 import java.util.List;
20 import java.util.concurrent.locks.ReentrantLock;
21 import javax.annotation.PostConstruct;
22 import javax.inject.Inject;
23 import javax.inject.Singleton;
24 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
25 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
26 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
27 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
28 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
29 import org.opendaylight.genius.infra.Datastore;
30 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
31 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
32 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
33 import org.opendaylight.genius.utils.JvmGlobalLocks;
34 import org.opendaylight.genius.utils.SystemPropertyReader;
35 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
36 import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
37 import org.opendaylight.netvirt.fibmanager.api.IFibManager;
38 import org.opendaylight.netvirt.vpnmanager.api.VpnExtraRouteHelper;
39 import org.opendaylight.yang.gen.v1.urn.ericsson.params.xml.ns.yang.ebgp.rev150901.AddressFamily;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.L3nexthop;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.VpnNexthops;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.VpnNexthopsKey;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.VpnInstanceOpData;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.VpnIds;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpntargets.VpnTarget;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroutes.Vpn;
49 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
50 import org.opendaylight.yangtools.yang.common.Uint32;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
55 public class VpnOpStatusListener extends AsyncDataTreeChangeListenerBase<VpnInstanceOpDataEntry, VpnOpStatusListener> {
56 private static final Logger LOG = LoggerFactory.getLogger(VpnOpStatusListener.class);
57 private final DataBroker dataBroker;
58 private final ManagedNewTransactionRunner txRunner;
59 private final IBgpManager bgpManager;
60 private final IdManagerService idManager;
61 private final IFibManager fibManager;
62 private final IMdsalApiManager mdsalManager;
63 private final VpnFootprintService vpnFootprintService;
64 private final JobCoordinator jobCoordinator;
65 private final VpnUtil vpnUtil;
68 public VpnOpStatusListener(final DataBroker dataBroker, final IBgpManager bgpManager,
69 final IdManagerService idManager, final IFibManager fibManager,
70 final IMdsalApiManager mdsalManager, final VpnFootprintService vpnFootprintService,
71 final JobCoordinator jobCoordinator, VpnUtil vpnUtil) {
72 super(VpnInstanceOpDataEntry.class, VpnOpStatusListener.class);
73 this.dataBroker = dataBroker;
74 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
75 this.bgpManager = bgpManager;
76 this.idManager = idManager;
77 this.fibManager = fibManager;
78 this.mdsalManager = mdsalManager;
79 this.vpnFootprintService = vpnFootprintService;
80 this.jobCoordinator = jobCoordinator;
81 this.vpnUtil = vpnUtil;
86 LOG.info("{} start", getClass().getSimpleName());
87 registerListener(LogicalDatastoreType.OPERATIONAL, dataBroker);
91 protected InstanceIdentifier<VpnInstanceOpDataEntry> getWildCardPath() {
92 return InstanceIdentifier.create(VpnInstanceOpData.class).child(VpnInstanceOpDataEntry.class);
96 protected VpnOpStatusListener getDataTreeChangeListener() {
97 return VpnOpStatusListener.this;
101 protected void remove(InstanceIdentifier<VpnInstanceOpDataEntry> identifier, VpnInstanceOpDataEntry value) {
102 LOG.info("remove: Ignoring vpn Op {} with rd {}", value.getVpnInstanceName(), value.getVrfId());
106 @SuppressWarnings("checkstyle:IllegalCatch")
107 protected void update(InstanceIdentifier<VpnInstanceOpDataEntry> identifier,
108 VpnInstanceOpDataEntry original, VpnInstanceOpDataEntry update) {
109 LOG.info("update: Processing update for vpn {} with rd {}", update.getVpnInstanceName(), update.getVrfId());
110 if (update.getVpnState() == VpnInstanceOpDataEntry.VpnState.PendingDelete
111 && vpnFootprintService.isVpnFootPrintCleared(update)) {
113 final String vpnName = update.getVpnInstanceName();
114 final List<String> rds = update.getRd();
115 String primaryRd = update.getVrfId();
116 final Uint32 vpnId = vpnUtil.getVpnId(vpnName);
117 jobCoordinator.enqueueJob("VPN-" + update.getVpnInstanceName(), () -> {
118 // Two transactions are used, one for operational, one for config; we only submit the config
119 // transaction if the operational transaction succeeds
120 ListenableFuture<Void> operationalFuture = txRunner.callWithNewWriteOnlyTransactionAndSubmit(
121 Datastore.OPERATIONAL, operTx -> {
122 // Clean up VPNExtraRoutes Operational DS
123 if (rds != null && VpnUtil.isBgpVpn(vpnName, primaryRd)) {
124 if (update.getType() == VpnInstanceOpDataEntry.Type.L2) {
125 rds.parallelStream().forEach(rd -> bgpManager.deleteVrf(
126 rd, false, AddressFamily.L2VPN));
128 if (update.getIpAddressFamilyConfigured()
129 == VpnInstanceOpDataEntry.IpAddressFamilyConfigured.Ipv4) {
130 rds.parallelStream().forEach(rd -> bgpManager.deleteVrf(
131 rd, false, AddressFamily.IPV4));
133 if (update.getIpAddressFamilyConfigured()
134 == VpnInstanceOpDataEntry.IpAddressFamilyConfigured.Ipv6) {
135 rds.parallelStream().forEach(rd -> bgpManager.deleteVrf(
136 rd, false, AddressFamily.IPV6));
138 if (update.getIpAddressFamilyConfigured()
139 == VpnInstanceOpDataEntry.IpAddressFamilyConfigured.Ipv4AndIpv6) {
141 .forEach(rd -> bgpManager.deleteVrf(
142 rd, false, AddressFamily.IPV4));
144 .forEach(rd -> bgpManager.deleteVrf(
145 rd, false, AddressFamily.IPV6));
148 InstanceIdentifier<Vpn> vpnToExtraroute =
149 VpnExtraRouteHelper.getVpnToExtrarouteVpnIdentifier(vpnName);
150 Optional<Vpn> optVpnToExtraroute = Optional.absent();
152 optVpnToExtraroute = SingleTransactionDataBroker.syncReadOptional(dataBroker,
153 LogicalDatastoreType.OPERATIONAL, vpnToExtraroute);
154 } catch (ReadFailedException e) {
155 LOG.error("update: Failed to read VpnToExtraRoute for vpn {}", vpnName);
157 if (optVpnToExtraroute.isPresent()) {
158 VpnUtil.removeVpnExtraRouteForVpn(vpnName, operTx);
160 if (VpnUtil.isL3VpnOverVxLan(update.getL3vni())) {
161 vpnUtil.removeExternalTunnelDemuxFlows(vpnName);
163 // Clean up PrefixToInterface Operational DS
164 Optional<VpnIds> optPrefixToIntf = Optional.absent();
166 optPrefixToIntf = SingleTransactionDataBroker.syncReadOptional(dataBroker,
167 LogicalDatastoreType.OPERATIONAL, VpnUtil.getPrefixToInterfaceIdentifier(vpnId));
168 } catch (ReadFailedException e) {
169 LOG.error("update: Failed to read PrefixToInterface for vpn {}", vpnName);
171 if (optPrefixToIntf.isPresent()) {
172 VpnUtil.removePrefixToInterfaceForVpnId(vpnId, operTx);
174 // Clean up L3NextHop Operational DS
175 InstanceIdentifier<VpnNexthops> vpnNextHops = InstanceIdentifier.builder(L3nexthop.class).child(
176 VpnNexthops.class, new VpnNexthopsKey(vpnId)).build();
177 Optional<VpnNexthops> optL3nexthopForVpnId = Optional.absent();
179 optL3nexthopForVpnId = SingleTransactionDataBroker.syncReadOptional(dataBroker,
180 LogicalDatastoreType.OPERATIONAL, vpnNextHops);
181 } catch (ReadFailedException e) {
182 LOG.error("update: Failed to read VpnNextHops for vpn {}", vpnName);
184 if (optL3nexthopForVpnId.isPresent()) {
185 VpnUtil.removeL3nexthopForVpnId(vpnId, operTx);
188 // Clean up VPNInstanceOpDataEntry
189 VpnUtil.removeVpnOpInstance(primaryRd, operTx);
192 Futures.addCallback(operationalFuture, new FutureCallback<Void>() {
194 public void onSuccess(Void result) {
195 Futures.addCallback(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
196 Datastore.CONFIGURATION, confTx -> {
197 // Clean up VpnInstanceToVpnId from Config DS
198 VpnUtil.removeVpnIdToVpnInstance(vpnId, confTx);
199 VpnUtil.removeVpnInstanceToVpnId(vpnName, confTx);
200 LOG.trace("Removed vpnIdentifier for rd{} vpnname {}", primaryRd, vpnName);
202 // Clean up FIB Entries Config DS
203 // FIXME: separate out to somehow?
204 final ReentrantLock lock = JvmGlobalLocks.getLockForString(vpnName);
207 fibManager.removeVrfTable(primaryRd, confTx);
211 }), new VpnOpStatusListener.PostDeleteVpnInstanceWorker(vpnName),
212 MoreExecutors.directExecutor());
213 // Note: Release the of VpnId will happen in PostDeleteVpnInstancWorker only if
214 // operationalTxn/Config succeeds.
218 public void onFailure(Throwable throwable) {
219 LOG.error("Error deleting VPN {}", vpnName, throwable);
221 }, MoreExecutors.directExecutor());
223 LOG.info("Removed vpn data for vpnname {}", vpnName);
224 return Collections.singletonList(operationalFuture);
225 }, SystemPropertyReader.getDataStoreJobCoordinatorMaxRetries());
226 } else if (update.getVpnState() == VpnInstanceOpDataEntry.VpnState.Created) {
227 final String vpnName = update.getVpnInstanceName();
228 String primaryRd = update.getVrfId();
229 if (!VpnUtil.isBgpVpn(vpnName, primaryRd)) {
232 if (original == null) {
233 LOG.error("VpnOpStatusListener.update: vpn {} with RD {}. add() handler already called",
237 if (update.getVpnTargets() == null) {
238 LOG.error("VpnOpStatusListener.update: vpn {} with RD {} vpnTargets not ready",
242 List<VpnTarget> vpnTargetList = update.getVpnTargets().getVpnTarget();
243 List<String> ertList = new ArrayList<>();
244 List<String> irtList = new ArrayList<>();
245 if (vpnTargetList != null) {
246 for (VpnTarget vpnTarget : vpnTargetList) {
247 if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.ExportExtcommunity) {
248 ertList.add(vpnTarget.getVrfRTValue());
250 if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.ImportExtcommunity) {
251 irtList.add(vpnTarget.getVrfRTValue());
253 if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.Both) {
254 ertList.add(vpnTarget.getVrfRTValue());
255 irtList.add(vpnTarget.getVrfRTValue());
259 LOG.error("VpnOpStatusListener.update: vpn target list is empty, cannot add BGP"
260 + " VPN {} RD {}", vpnName, primaryRd);
263 jobCoordinator.enqueueJob("VPN-" + update.getVpnInstanceName(), () -> {
264 //RD update case get only updated RD list
265 List<String> rds = update.getRd() != null ? new ArrayList<>(update.getRd()) : new ArrayList<>();
266 if (original.getRd() != null && original.getRd().size() != rds.size()) {
267 rds.removeAll(original.getRd());
269 rds.parallelStream().forEach(rd -> {
271 List<String> importRTList = rd.equals(primaryRd) ? irtList : emptyList();
272 LOG.info("VpnOpStatusListener.update: updating BGPVPN for vpn {} with RD {}"
273 + " Type is {}, IPtype is {}, iRT {}", vpnName, primaryRd, update.getType(),
274 update.getIpAddressFamilyConfigured(), importRTList);
275 int ipValue = VpnUtil.getIpFamilyValueToRemove(original,update);
278 bgpManager.deleteVrf(rd, false, AddressFamily.IPV4);
281 bgpManager.deleteVrf(rd, false, AddressFamily.IPV6);
284 bgpManager.deleteVrf(rd, false, AddressFamily.IPV4);
285 bgpManager.deleteVrf(rd, false, AddressFamily.IPV6);
290 /* Update vrf entry with newly added RD list. VPN does not support for
291 * deleting existing RDs
293 if (original.getRd().size() != update.getRd().size()) {
294 ipValue = VpnUtil.getIpFamilyValueToAdd(original,update);
297 bgpManager.addVrf(rd, importRTList, ertList, AddressFamily.IPV4);
300 bgpManager.addVrf(rd, importRTList, ertList, AddressFamily.IPV6);
303 bgpManager.addVrf(rd, importRTList, ertList, AddressFamily.IPV4);
304 bgpManager.addVrf(rd, importRTList, ertList, AddressFamily.IPV6);
310 } catch (RuntimeException e) {
311 LOG.error("VpnOpStatusListener.update: Exception when updating VRF to BGP for vpn {} rd {}",
321 protected void add(final InstanceIdentifier<VpnInstanceOpDataEntry> identifier,
322 final VpnInstanceOpDataEntry value) {
323 LOG.debug("add: Ignoring vpn Op {} with rd {}", value.getVpnInstanceName(), value.getVrfId());
326 private class PostDeleteVpnInstanceWorker implements FutureCallback<Void> {
327 private final Logger log = LoggerFactory.getLogger(VpnOpStatusListener.PostDeleteVpnInstanceWorker.class);
330 PostDeleteVpnInstanceWorker(String vpnName) {
331 this.vpnName = vpnName;
335 * This implies that all the future instances have returned success.
336 * Release the ID used for VPN back to IdManager
339 public void onSuccess(Void ignored) {
340 vpnUtil.releaseId(VpnConstants.VPN_IDPOOL_NAME, vpnName);
341 log.info("onSuccess: VpnId for VpnName {} is released to IdManager successfully.", vpnName);
345 * This method is used to handle failure callbacks.
348 public void onFailure(Throwable throwable) {
349 log.error("onFailure: Job for vpnInstance: {} failed with exception:",
350 vpnName , throwable);