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.util.concurrent.FutureCallback;
13 import com.google.common.util.concurrent.Futures;
14 import com.google.common.util.concurrent.ListenableFuture;
15 import com.google.common.util.concurrent.MoreExecutors;
16 import java.util.ArrayList;
17 import java.util.Collections;
18 import java.util.List;
20 import java.util.Optional;
21 import java.util.concurrent.ExecutionException;
22 import java.util.concurrent.locks.ReentrantLock;
23 import javax.annotation.PreDestroy;
24 import javax.inject.Inject;
25 import javax.inject.Singleton;
26 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
27 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
28 import org.opendaylight.genius.utils.JvmGlobalLocks;
29 import org.opendaylight.genius.utils.SystemPropertyReader;
30 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
31 import org.opendaylight.infrautils.utils.concurrent.Executors;
32 import org.opendaylight.mdsal.binding.api.DataBroker;
33 import org.opendaylight.mdsal.binding.util.Datastore;
34 import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunner;
35 import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunnerImpl;
36 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
37 import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
38 import org.opendaylight.netvirt.fibmanager.api.IFibManager;
39 import org.opendaylight.netvirt.vpnmanager.api.VpnExtraRouteHelper;
40 import org.opendaylight.serviceutils.tools.listener.AbstractAsyncDataTreeChangeListener;
41 import org.opendaylight.yang.gen.v1.urn.ericsson.params.xml.ns.yang.ebgp.rev150901.AddressFamily;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.L3nexthop;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.VpnNexthops;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.VpnNexthopsKey;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.VpnInstanceOpData;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.VpnIds;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpntargets.VpnTarget;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpntargets.VpnTargetKey;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroutes.Vpn;
52 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
53 import org.opendaylight.yangtools.yang.common.Uint32;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
58 public class VpnOpStatusListener extends AbstractAsyncDataTreeChangeListener<VpnInstanceOpDataEntry> {
59 private static final Logger LOG = LoggerFactory.getLogger(VpnOpStatusListener.class);
60 private final DataBroker dataBroker;
61 private final ManagedNewTransactionRunner txRunner;
62 private final IBgpManager bgpManager;
63 private final IdManagerService idManager;
64 private final IFibManager fibManager;
65 private final IMdsalApiManager mdsalManager;
66 private final VpnFootprintService vpnFootprintService;
67 private final JobCoordinator jobCoordinator;
68 private final VpnUtil vpnUtil;
71 public VpnOpStatusListener(final DataBroker dataBroker, final IBgpManager bgpManager,
72 final IdManagerService idManager, final IFibManager fibManager,
73 final IMdsalApiManager mdsalManager, final VpnFootprintService vpnFootprintService,
74 final JobCoordinator jobCoordinator, VpnUtil vpnUtil) {
75 super(dataBroker, LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.create(VpnInstanceOpData.class)
76 .child(VpnInstanceOpDataEntry.class), Executors
77 .newListeningSingleThreadExecutor("VpnOpStatusListener", LOG));
78 this.dataBroker = dataBroker;
79 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
80 this.bgpManager = bgpManager;
81 this.idManager = idManager;
82 this.fibManager = fibManager;
83 this.mdsalManager = mdsalManager;
84 this.vpnFootprintService = vpnFootprintService;
85 this.jobCoordinator = jobCoordinator;
86 this.vpnUtil = vpnUtil;
91 LOG.info("{} start", getClass().getSimpleName());
98 Executors.shutdownAndAwaitTermination(getExecutorService());
103 public void remove(InstanceIdentifier<VpnInstanceOpDataEntry> identifier, VpnInstanceOpDataEntry value) {
104 LOG.info("remove: Ignoring vpn Op {} with rd {}", value.getVpnInstanceName(), value.getVrfId());
108 @SuppressWarnings("checkstyle:IllegalCatch")
109 public void update(InstanceIdentifier<VpnInstanceOpDataEntry> identifier,
110 VpnInstanceOpDataEntry original, VpnInstanceOpDataEntry update) {
111 LOG.info("update: Processing update for vpn {} with rd {}", update.getVpnInstanceName(), update.getVrfId());
112 if (update.getVpnState() == VpnInstanceOpDataEntry.VpnState.PendingDelete
113 && vpnFootprintService.isVpnFootPrintCleared(update)) {
115 final String vpnName = update.getVpnInstanceName();
116 final List<String> rds = update.getRd();
117 String primaryRd = update.getVrfId();
118 final Uint32 vpnId = vpnUtil.getVpnId(vpnName);
119 jobCoordinator.enqueueJob("VPN-" + update.getVpnInstanceName(), () -> {
120 // Two transactions are used, one for operational, one for config; we only submit the config
121 // transaction if the operational transaction succeeds
122 ListenableFuture<?> operationalFuture = txRunner.callWithNewWriteOnlyTransactionAndSubmit(
123 Datastore.OPERATIONAL, operTx -> {
124 // Clean up VPNExtraRoutes Operational DS
125 if (rds != null && VpnUtil.isBgpVpn(vpnName, primaryRd)) {
126 if (update.getType() == VpnInstanceOpDataEntry.Type.L2) {
127 rds.parallelStream().forEach(rd -> bgpManager.deleteVrf(
128 rd, false, AddressFamily.L2VPN));
130 if (update.getIpAddressFamilyConfigured()
131 == VpnInstanceOpDataEntry.IpAddressFamilyConfigured.Ipv4) {
132 rds.parallelStream().forEach(rd -> bgpManager.deleteVrf(
133 rd, false, AddressFamily.IPV4));
135 if (update.getIpAddressFamilyConfigured()
136 == VpnInstanceOpDataEntry.IpAddressFamilyConfigured.Ipv6) {
137 rds.parallelStream().forEach(rd -> bgpManager.deleteVrf(
138 rd, false, AddressFamily.IPV6));
140 if (update.getIpAddressFamilyConfigured()
141 == VpnInstanceOpDataEntry.IpAddressFamilyConfigured.Ipv4AndIpv6) {
143 .forEach(rd -> bgpManager.deleteVrf(
144 rd, false, AddressFamily.IPV4));
146 .forEach(rd -> bgpManager.deleteVrf(
147 rd, false, AddressFamily.IPV6));
150 InstanceIdentifier<Vpn> vpnToExtraroute =
151 VpnExtraRouteHelper.getVpnToExtrarouteVpnIdentifier(vpnName);
152 Optional<Vpn> optVpnToExtraroute = Optional.empty();
154 optVpnToExtraroute = SingleTransactionDataBroker.syncReadOptional(dataBroker,
155 LogicalDatastoreType.OPERATIONAL, vpnToExtraroute);
156 } catch (InterruptedException | ExecutionException e) {
157 LOG.error("update: Failed to read VpnToExtraRoute for vpn {}", vpnName);
159 if (optVpnToExtraroute.isPresent()) {
160 VpnUtil.removeVpnExtraRouteForVpn(vpnName, operTx);
162 if (VpnUtil.isL3VpnOverVxLan(update.getL3vni())) {
163 vpnUtil.removeExternalTunnelDemuxFlows(vpnName);
165 // Clean up PrefixToInterface Operational DS
166 Optional<VpnIds> optPrefixToIntf = Optional.empty();
168 optPrefixToIntf = SingleTransactionDataBroker.syncReadOptional(dataBroker,
169 LogicalDatastoreType.OPERATIONAL, VpnUtil.getPrefixToInterfaceIdentifier(vpnId));
170 } catch (InterruptedException | ExecutionException e) {
171 LOG.error("update: Failed to read PrefixToInterface for vpn {}", vpnName);
173 if (optPrefixToIntf.isPresent()) {
174 VpnUtil.removePrefixToInterfaceForVpnId(vpnId, operTx);
176 // Clean up L3NextHop Operational DS
177 InstanceIdentifier<VpnNexthops> vpnNextHops = InstanceIdentifier.builder(L3nexthop.class).child(
178 VpnNexthops.class, new VpnNexthopsKey(vpnId)).build();
179 Optional<VpnNexthops> optL3nexthopForVpnId = Optional.empty();
181 optL3nexthopForVpnId = SingleTransactionDataBroker.syncReadOptional(dataBroker,
182 LogicalDatastoreType.OPERATIONAL, vpnNextHops);
183 } catch (InterruptedException | ExecutionException e) {
184 LOG.error("update: Failed to read VpnNextHops for vpn {}", vpnName);
186 if (optL3nexthopForVpnId.isPresent()) {
187 VpnUtil.removeL3nexthopForVpnId(vpnId, operTx);
190 // Clean up VPNInstanceOpDataEntry
191 VpnUtil.removeVpnOpInstance(primaryRd, operTx);
194 Futures.addCallback(operationalFuture, new FutureCallback<Object>() {
196 public void onSuccess(Object result) {
197 Futures.addCallback(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
198 Datastore.CONFIGURATION, confTx -> {
199 // Clean up VpnInstanceToVpnId from Config DS
200 VpnUtil.removeVpnIdToVpnInstance(vpnId, confTx);
201 VpnUtil.removeVpnInstanceToVpnId(vpnName, confTx);
202 LOG.trace("Removed vpnIdentifier for rd{} vpnname {}", primaryRd, vpnName);
204 // Clean up FIB Entries Config DS
205 // FIXME: separate out to somehow?
206 final ReentrantLock lock = JvmGlobalLocks.getLockForString(vpnName);
209 fibManager.removeVrfTable(primaryRd, confTx);
213 }), new VpnOpStatusListener.PostDeleteVpnInstanceWorker(vpnName),
214 MoreExecutors.directExecutor());
215 // Note: Release the of VpnId will happen in PostDeleteVpnInstancWorker only if
216 // operationalTxn/Config succeeds.
220 public void onFailure(Throwable throwable) {
221 LOG.error("Error deleting VPN {}", vpnName, throwable);
223 }, MoreExecutors.directExecutor());
225 LOG.info("Removed vpn data for vpnname {}", vpnName);
226 return Collections.singletonList(operationalFuture);
227 }, SystemPropertyReader.getDataStoreJobCoordinatorMaxRetries());
228 } else if (update.getVpnState() == VpnInstanceOpDataEntry.VpnState.Created) {
229 final String vpnName = update.getVpnInstanceName();
230 String primaryRd = update.getVrfId();
231 if (!VpnUtil.isBgpVpn(vpnName, primaryRd)) {
234 if (original == null) {
235 LOG.error("VpnOpStatusListener.update: vpn {} with RD {}. add() handler already called",
239 if (update.getVpnTargets() == null) {
240 LOG.error("VpnOpStatusListener.update: vpn {} with RD {} vpnTargets not ready",
244 Map<VpnTargetKey, VpnTarget> vpnTargetMap = update.getVpnTargets().getVpnTarget();
245 List<String> ertList = new ArrayList<>();
246 List<String> irtList = new ArrayList<>();
247 if (vpnTargetMap != null) {
248 for (VpnTarget vpnTarget : vpnTargetMap.values()) {
249 if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.ExportExtcommunity) {
250 ertList.add(vpnTarget.getVrfRTValue());
252 if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.ImportExtcommunity) {
253 irtList.add(vpnTarget.getVrfRTValue());
255 if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.Both) {
256 ertList.add(vpnTarget.getVrfRTValue());
257 irtList.add(vpnTarget.getVrfRTValue());
261 LOG.error("VpnOpStatusListener.update: vpn target list is empty, cannot add BGP"
262 + " VPN {} RD {}", vpnName, primaryRd);
265 jobCoordinator.enqueueJob("VPN-" + update.getVpnInstanceName(), () -> {
266 //RD update case get only updated RD list
267 List<String> rds = update.getRd() != null ? new ArrayList<>(update.getRd()) : new ArrayList<>();
268 if (original.getRd() != null && original.getRd().size() != rds.size()) {
269 rds.removeAll(original.getRd());
271 rds.parallelStream().forEach(rd -> {
273 List<String> importRTList = rd.equals(primaryRd) ? irtList : emptyList();
274 LOG.info("VpnOpStatusListener.update: updating BGPVPN for vpn {} with RD {}"
275 + " Type is {}, IPtype is {}, iRT {}", vpnName, primaryRd, update.getType(),
276 update.getIpAddressFamilyConfigured(), importRTList);
277 int ipValue = VpnUtil.getIpFamilyValueToRemove(original,update);
280 bgpManager.deleteVrf(rd, false, AddressFamily.IPV4);
283 bgpManager.deleteVrf(rd, false, AddressFamily.IPV6);
286 bgpManager.deleteVrf(rd, false, AddressFamily.IPV4);
287 bgpManager.deleteVrf(rd, false, AddressFamily.IPV6);
292 /* Update vrf entry with newly added RD list. VPN does not support for
293 * deleting existing RDs
295 if (original.getRd().size() != update.getRd().size()) {
296 ipValue = VpnUtil.getIpFamilyValueToAdd(original,update);
299 bgpManager.addVrf(rd, importRTList, ertList, AddressFamily.IPV4);
302 bgpManager.addVrf(rd, importRTList, ertList, AddressFamily.IPV6);
305 bgpManager.addVrf(rd, importRTList, ertList, AddressFamily.IPV4);
306 bgpManager.addVrf(rd, importRTList, ertList, AddressFamily.IPV6);
312 } catch (RuntimeException e) {
313 LOG.error("VpnOpStatusListener.update: Exception when updating VRF to BGP for vpn {} rd {}",
323 public void add(final InstanceIdentifier<VpnInstanceOpDataEntry> identifier,
324 final VpnInstanceOpDataEntry value) {
325 LOG.debug("add: Ignoring vpn Op {} with rd {}", value.getVpnInstanceName(), value.getVrfId());
328 private class PostDeleteVpnInstanceWorker implements FutureCallback<Object> {
329 private final Logger log = LoggerFactory.getLogger(VpnOpStatusListener.PostDeleteVpnInstanceWorker.class);
332 PostDeleteVpnInstanceWorker(String vpnName) {
333 this.vpnName = vpnName;
337 * This implies that all the future instances have returned success.
338 * Release the ID used for VPN back to IdManager
341 public void onSuccess(Object ignored) {
342 Integer releaseId = vpnUtil.releaseId(VpnConstants.VPN_IDPOOL_NAME, vpnName);
343 log.info("onSuccess: VpnId for VpnName {} is released to IdManager successfully.", vpnName);
344 if (releaseId == VpnConstants.INVALID_IDMAN_ID) {
345 LOG.error("VpnOpStatusListener: onSuccess: Failed to release ID for vpn {}, vpn id pool {}", vpnName,
346 VpnConstants.VPN_IDPOOL_NAME);
351 * This method is used to handle failure callbacks.
354 public void onFailure(Throwable throwable) {
355 log.error("onFailure: Job for vpnInstance: {} failed with exception:",
356 vpnName , throwable);