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.CheckedFuture;
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 java.util.ArrayList;
16 import java.util.Collections;
17 import java.util.List;
18 import java.util.concurrent.ExecutionException;
19 import javax.annotation.PostConstruct;
20 import javax.inject.Inject;
21 import javax.inject.Singleton;
22 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
23 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
24 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
25 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
26 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
27 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
28 import org.opendaylight.genius.utils.SystemPropertyReader;
29 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
30 import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
31 import org.opendaylight.netvirt.fibmanager.api.IFibManager;
32 import org.opendaylight.netvirt.vpnmanager.api.VpnExtraRouteHelper;
33 import org.opendaylight.yang.gen.v1.urn.ericsson.params.xml.ns.yang.ebgp.rev150901.AddressFamily;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.L3nexthop;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.VpnNexthops;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.VpnNexthopsKey;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.VpnInstanceOpData;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.VpnIds;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpntargets.VpnTarget;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroutes.Vpn;
43 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
48 public class VpnOpStatusListener extends AsyncDataTreeChangeListenerBase<VpnInstanceOpDataEntry, VpnOpStatusListener> {
49 private static final Logger LOG = LoggerFactory.getLogger(VpnOpStatusListener.class);
50 private final DataBroker dataBroker;
51 private final IBgpManager bgpManager;
52 private final IdManagerService idManager;
53 private final IFibManager fibManager;
54 private final IMdsalApiManager mdsalManager;
55 private final VpnFootprintService vpnFootprintService;
56 private final JobCoordinator jobCoordinator;
59 public VpnOpStatusListener(final DataBroker dataBroker, final IBgpManager bgpManager,
60 final IdManagerService idManager, final IFibManager fibManager,
61 final IMdsalApiManager mdsalManager, final VpnFootprintService vpnFootprintService,
62 final JobCoordinator jobCoordinator) {
63 super(VpnInstanceOpDataEntry.class, VpnOpStatusListener.class);
64 this.dataBroker = dataBroker;
65 this.bgpManager = bgpManager;
66 this.idManager = idManager;
67 this.fibManager = fibManager;
68 this.mdsalManager = mdsalManager;
69 this.vpnFootprintService = vpnFootprintService;
70 this.jobCoordinator = jobCoordinator;
75 LOG.info("{} start", getClass().getSimpleName());
76 registerListener(LogicalDatastoreType.OPERATIONAL, dataBroker);
80 protected InstanceIdentifier<VpnInstanceOpDataEntry> getWildCardPath() {
81 return InstanceIdentifier.create(VpnInstanceOpData.class).child(VpnInstanceOpDataEntry.class);
85 protected VpnOpStatusListener getDataTreeChangeListener() {
86 return VpnOpStatusListener.this;
90 protected void remove(InstanceIdentifier<VpnInstanceOpDataEntry> identifier, VpnInstanceOpDataEntry value) {
91 LOG.info("remove: Ignoring vpn Op {} with rd {}", value.getVpnInstanceName(), value.getVrfId());
95 @SuppressWarnings("checkstyle:IllegalCatch")
96 protected void update(InstanceIdentifier<VpnInstanceOpDataEntry> identifier,
97 VpnInstanceOpDataEntry original, VpnInstanceOpDataEntry update) {
98 LOG.info("update: Processing update for vpn {} with rd {}", update.getVpnInstanceName(), update.getVrfId());
99 if (update.getVpnState() == VpnInstanceOpDataEntry.VpnState.PendingDelete
100 && vpnFootprintService.isVpnFootPrintCleared(update)) {
102 final String vpnName = update.getVpnInstanceName();
103 final List<String> rds = update.getRd();
104 String primaryRd = update.getVrfId();
105 final long vpnId = VpnUtil.getVpnId(dataBroker, vpnName);
106 jobCoordinator.enqueueJob("VPN-" + update.getVpnInstanceName(), () -> {
107 WriteTransaction writeConfigTxn = dataBroker.newWriteOnlyTransaction();
108 WriteTransaction writeOperTxn = dataBroker.newWriteOnlyTransaction();
109 // Clean up VpnInstanceToVpnId from Config DS
110 VpnUtil.removeVpnIdToVpnInstance(dataBroker, vpnId, writeConfigTxn);
111 VpnUtil.removeVpnInstanceToVpnId(dataBroker, vpnName, writeConfigTxn);
112 LOG.trace("Removed vpnIdentifier for rd{} vpnname {}", primaryRd, vpnName);
114 // Clean up FIB Entries Config DS
115 synchronized (vpnName.intern()) {
116 fibManager.removeVrfTable(primaryRd, null);
119 // Clean up VPNExtraRoutes Operational DS
120 if (VpnUtil.isBgpVpn(vpnName, primaryRd)) {
121 if (update.getType() == VpnInstanceOpDataEntry.Type.L2) {
122 rds.parallelStream().forEach(rd -> bgpManager.deleteVrf(rd, false, AddressFamily.L2VPN));
124 if (update.isIpv4Configured()) {
125 rds.parallelStream().forEach(rd -> bgpManager.deleteVrf(rd, false, AddressFamily.IPV4));
127 if (update.isIpv6Configured()) {
128 rds.parallelStream().forEach(rd -> bgpManager.deleteVrf(rd, false, AddressFamily.IPV6));
131 InstanceIdentifier<Vpn> vpnToExtraroute = VpnExtraRouteHelper.getVpnToExtrarouteVpnIdentifier(vpnName);
132 Optional<Vpn> optVpnToExtraroute = VpnUtil.read(dataBroker,
133 LogicalDatastoreType.OPERATIONAL, vpnToExtraroute);
134 if (optVpnToExtraroute.isPresent()) {
135 VpnUtil.removeVpnExtraRouteForVpn(dataBroker, vpnName, writeOperTxn);
138 if (VpnUtil.isL3VpnOverVxLan(update.getL3vni())) {
139 VpnUtil.removeExternalTunnelDemuxFlows(vpnName, dataBroker, mdsalManager);
142 // Clean up PrefixToInterface Operational DS
143 Optional<VpnIds> optPrefixToIntf = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL,
144 VpnUtil.getPrefixToInterfaceIdentifier(vpnId));
145 if (optPrefixToIntf.isPresent()) {
146 VpnUtil.removePrefixToInterfaceForVpnId(dataBroker, vpnId, writeOperTxn);
149 // Clean up L3NextHop Operational DS
150 InstanceIdentifier<VpnNexthops> vpnNextHops = InstanceIdentifier.builder(L3nexthop.class).child(
151 VpnNexthops.class, new VpnNexthopsKey(vpnId)).build();
152 Optional<VpnNexthops> optL3nexthopForVpnId = VpnUtil.read(dataBroker,
153 LogicalDatastoreType.OPERATIONAL,
155 if (optL3nexthopForVpnId.isPresent()) {
156 VpnUtil.removeL3nexthopForVpnId(dataBroker, vpnId, writeOperTxn);
159 // Clean up VPNInstanceOpDataEntry
160 VpnUtil.removeVpnOpInstance(dataBroker, primaryRd, writeOperTxn);
162 // Note: Release the of VpnId will happen in PostDeleteVpnInstancWorker only if
163 // operationalTxn/Config succeeds.
165 CheckedFuture<Void, TransactionCommitFailedException> checkFutures = writeOperTxn.submit();
168 } catch (InterruptedException | ExecutionException e) {
169 LOG.error("Error deleting vpn {} ", vpnName);
170 writeConfigTxn.cancel();
171 throw new RuntimeException(e);
173 List<ListenableFuture<Void>> futures = new ArrayList<>();
174 futures.add(writeConfigTxn.submit());
175 ListenableFuture<List<Void>> listenableFuture = Futures.allAsList(futures);
176 Futures.addCallback(listenableFuture, new VpnOpStatusListener.PostDeleteVpnInstanceWorker(vpnName));
177 LOG.info("Removed vpn data for vpnname {}", vpnName);
179 }, SystemPropertyReader.getDataStoreJobCoordinatorMaxRetries());
180 } else if (update.getVpnState() == VpnInstanceOpDataEntry.VpnState.Created) {
181 final String vpnName = update.getVpnInstanceName();
182 final List<String> rds = update.getRd();
183 String primaryRd = update.getVrfId();
184 if (!VpnUtil.isBgpVpn(vpnName, primaryRd)) {
187 if (original == null) {
188 LOG.error("VpnOpStatusListener.update: vpn {} with RD {}. add() handler already called",
192 if (update.getVpnTargets() == null) {
193 LOG.error("VpnOpStatusListener.update: vpn {} with RD {} vpnTargets not ready",
197 List<VpnTarget> vpnTargetList = update.getVpnTargets().getVpnTarget();
198 List<String> ertList = new ArrayList<>();
199 List<String> irtList = new ArrayList<>();
200 if (vpnTargetList != null) {
201 for (VpnTarget vpnTarget : vpnTargetList) {
202 if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.ExportExtcommunity) {
203 ertList.add(vpnTarget.getVrfRTValue());
205 if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.ImportExtcommunity) {
206 irtList.add(vpnTarget.getVrfRTValue());
208 if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.Both) {
209 ertList.add(vpnTarget.getVrfRTValue());
210 irtList.add(vpnTarget.getVrfRTValue());
214 LOG.error("VpnOpStatusListener.update: vpn target list is empty, cannot add BGP"
215 + " VPN {} RD {}", vpnName, primaryRd);
218 jobCoordinator.enqueueJob("VPN-" + update.getVpnInstanceName(), () -> {
219 WriteTransaction writeTxn = dataBroker.newWriteOnlyTransaction();
220 long primaryRdAddFailed = rds.parallelStream().filter(rd -> {
222 LOG.info("VpnOpStatusListener.update: updating BGPVPN for vpn {} with RD {}"
223 + " Type is {}, IPv4 is {}, IPv6 is {}", vpnName, primaryRd, update.getType(),
224 update.isIpv4Configured(), update.isIpv6Configured());
225 if (update.getType() == VpnInstanceOpDataEntry.Type.L2) {
226 bgpManager.addVrf(rd, irtList, ertList, AddressFamily.L2VPN);
228 bgpManager.deleteVrf(rd, false, AddressFamily.L2VPN);
230 if (!original.isIpv4Configured() && update.isIpv4Configured()) {
231 bgpManager.addVrf(rd, irtList, ertList, AddressFamily.IPV4);
232 } else if (original.isIpv4Configured() && !update.isIpv4Configured()) {
233 bgpManager.deleteVrf(rd, false, AddressFamily.IPV4);
235 if (!original.isIpv6Configured() && update.isIpv6Configured()) {
236 bgpManager.addVrf(rd, irtList, ertList, AddressFamily.IPV6);
237 } else if (original.isIpv6Configured() && !update.isIpv6Configured()) {
238 bgpManager.deleteVrf(rd, false, AddressFamily.IPV6);
240 } catch (Exception e) {
241 LOG.error("VpnOpStatusListener.update: Exception when updating VRF to BGP"
242 + " for vpn {} rd {}", vpnName, rd);
247 return Collections.emptyList();
253 protected void add(final InstanceIdentifier<VpnInstanceOpDataEntry> identifier,
254 final VpnInstanceOpDataEntry value) {
255 LOG.debug("add: Ignoring vpn Op {} with rd {}", value.getVpnInstanceName(), value.getVrfId());
258 private class PostDeleteVpnInstanceWorker implements FutureCallback<List<Void>> {
259 private final Logger log = LoggerFactory.getLogger(VpnOpStatusListener.PostDeleteVpnInstanceWorker.class);
262 PostDeleteVpnInstanceWorker(String vpnName) {
263 this.vpnName = vpnName;
267 * This implies that all the future instances have returned success.
268 * Release the ID used for VPN back to IdManager
271 public void onSuccess(List<Void> voids) {
272 VpnUtil.releaseId(idManager, VpnConstants.VPN_IDPOOL_NAME, vpnName);
273 log.info("onSuccess: VpnId for VpnName {} is released to IdManager successfully.", vpnName);
277 * This method is used to handle failure callbacks.
280 public void onFailure(Throwable throwable) {
281 log.error("onFailure: Job for vpnInstance: {} failed with exception:",
282 vpnName , throwable);