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 org.opendaylight.genius.infra.Datastore.CONFIGURATION;
11 import static org.opendaylight.genius.infra.Datastore.OPERATIONAL;
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 edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
18 import java.util.ArrayList;
19 import java.util.Arrays;
20 import java.util.Collection;
21 import java.util.Collections;
22 import java.util.List;
24 import java.util.Objects;
25 import java.util.Optional;
26 import java.util.concurrent.Callable;
27 import java.util.concurrent.ExecutionException;
28 import java.util.concurrent.locks.ReentrantLock;
29 import javax.annotation.PreDestroy;
30 import javax.inject.Inject;
31 import javax.inject.Singleton;
32 import org.eclipse.jdt.annotation.Nullable;
33 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
34 import org.opendaylight.genius.infra.Datastore.Configuration;
35 import org.opendaylight.genius.infra.Datastore.Operational;
36 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
37 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
38 import org.opendaylight.genius.infra.TypedWriteTransaction;
39 import org.opendaylight.genius.mdsalutil.FlowEntity;
40 import org.opendaylight.genius.mdsalutil.InstructionInfo;
41 import org.opendaylight.genius.mdsalutil.MDSALUtil;
42 import org.opendaylight.genius.mdsalutil.MatchInfo;
43 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
44 import org.opendaylight.genius.mdsalutil.NWUtil;
45 import org.opendaylight.genius.mdsalutil.NwConstants;
46 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
47 import org.opendaylight.genius.mdsalutil.instructions.InstructionWriteMetadata;
48 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
49 import org.opendaylight.genius.mdsalutil.matches.MatchTunnelId;
50 import org.opendaylight.genius.utils.JvmGlobalLocks;
51 import org.opendaylight.genius.utils.SystemPropertyReader;
52 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
53 import org.opendaylight.infrautils.utils.concurrent.Executors;
54 import org.opendaylight.infrautils.utils.concurrent.LoggingFutures;
55 import org.opendaylight.mdsal.binding.api.DataBroker;
56 import org.opendaylight.mdsal.binding.api.WriteTransaction;
57 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
58 import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
59 import org.opendaylight.netvirt.fibmanager.api.IFibManager;
60 import org.opendaylight.serviceutils.tools.listener.AbstractAsyncDataTreeChangeListener;
61 import org.opendaylight.yang.gen.v1.urn.ericsson.params.xml.ns.yang.ebgp.rev150901.AddressFamily;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.ExternalTunnelList;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.external.tunnel.list.ExternalTunnel;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.external.tunnel.list.ExternalTunnelKey;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.DcGatewayIpList;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.dc.gateway.ip.list.DcGatewayIp;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.dc.gateway.ip.list.DcGatewayIpKey;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTables;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.id.to.vpn.instance.VpnIds;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntryBuilder;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnTargetsBuilder;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpntargets.VpnTargetBuilder;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpntargets.VpnTargetKey;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.l3vpn.rev200204.VpnInstances;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.l3vpn.rev200204.vpn.instances.VpnInstance;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.l3vpn.rev200204.vpn.instances.vpn.instance.VpnTargets;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.l3vpn.rev200204.vpn.instances.vpn.instance.vpntargets.VpnTarget;
80 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
81 import org.opendaylight.yangtools.yang.common.Uint32;
82 import org.opendaylight.yangtools.yang.common.Uint64;
83 import org.slf4j.Logger;
84 import org.slf4j.LoggerFactory;
87 public class VpnInstanceListener extends AbstractAsyncDataTreeChangeListener<VpnInstance> {
88 private static final Logger LOG = LoggerFactory.getLogger(VpnInstanceListener.class);
89 private static final String LOGGING_PREFIX_ADD = "VPN-ADD:";
90 private static final String LOGGING_PREFIX_UPDATE = "VPN-UPDATE:";
91 private static final String LOGGING_PREFIX_DELETE = "VPN-REMOVE:";
92 private final DataBroker dataBroker;
93 private final ManagedNewTransactionRunner txRunner;
94 private final IdManagerService idManager;
95 private final VpnInterfaceManager vpnInterfaceManager;
96 private final IFibManager fibManager;
97 private final IBgpManager bgpManager;
98 private final VpnOpDataSyncer vpnOpDataNotifier;
99 private final IMdsalApiManager mdsalManager;
100 private final JobCoordinator jobCoordinator;
101 private final VpnUtil vpnUtil;
104 public VpnInstanceListener(final DataBroker dataBroker, final IdManagerService idManager,
105 final VpnInterfaceManager vpnInterfaceManager, final IFibManager fibManager,
106 final IBgpManager bgpManager, final VpnOpDataSyncer vpnOpDataSyncer, final IMdsalApiManager mdsalManager,
107 final JobCoordinator jobCoordinator, VpnUtil vpnUtil) {
108 super(dataBroker, LogicalDatastoreType.CONFIGURATION,
109 InstanceIdentifier.create(VpnInstances.class).child(VpnInstance.class),
110 Executors.newListeningSingleThreadExecutor("VpnInstanceListener", LOG));
111 this.dataBroker = dataBroker;
112 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
113 this.idManager = idManager;
114 this.vpnInterfaceManager = vpnInterfaceManager;
115 this.fibManager = fibManager;
116 this.bgpManager = bgpManager;
117 this.vpnOpDataNotifier = vpnOpDataSyncer;
118 this.mdsalManager = mdsalManager;
119 this.jobCoordinator = jobCoordinator;
120 this.vpnUtil = vpnUtil;
124 public void start() {
125 LOG.info("{} start", getClass().getSimpleName());
130 public void close() {
132 Executors.shutdownAndAwaitTermination(getExecutorService());
137 public void remove(InstanceIdentifier<VpnInstance> identifier, VpnInstance del) {
138 LOG.trace("{} : VPN event key: {}, value: {}", LOGGING_PREFIX_DELETE, identifier, del);
139 final String vpnName = del.getVpnInstanceName();
140 Optional<VpnInstanceOpDataEntry> vpnOpValue;
141 String primaryRd = vpnUtil.getVpnRd(vpnName);
142 if (primaryRd == null) {
143 LOG.error("{}, failed to remove VPN: primaryRd is null for vpn {}", LOGGING_PREFIX_DELETE, vpnName);
147 //TODO(vpnteam): Entire code would need refactoring to listen only on the parent object - VPNInstance
149 vpnOpValue = SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.OPERATIONAL,
150 VpnUtil.getVpnInstanceOpDataIdentifier(primaryRd));
151 } catch (InterruptedException | ExecutionException e) {
152 LOG.error("{}, failed to remove VPN: Exception while retrieving VpnInstanceOpDataEntry for VPN {}. ",
153 LOGGING_PREFIX_DELETE, vpnName, e);
157 if (!vpnOpValue.isPresent()) {
158 LOG.error("{}, failed to remove VPN: Unable to retrieve VpnInstanceOpDataEntry for VPN {}. ",
159 LOGGING_PREFIX_DELETE, vpnName);
162 jobCoordinator.enqueueJob("VPN-" + vpnName, () ->
163 Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL, tx -> {
164 VpnInstanceOpDataEntryBuilder builder = null;
165 InstanceIdentifier<VpnInstanceOpDataEntry> id = null;
166 if (primaryRd != null) {
167 builder = new VpnInstanceOpDataEntryBuilder().setVrfId(primaryRd)
168 .setVpnState(VpnInstanceOpDataEntry.VpnState.PendingDelete);
169 id = VpnUtil.getVpnInstanceOpDataIdentifier(primaryRd);
171 builder = new VpnInstanceOpDataEntryBuilder().setVrfId(vpnName)
172 .setVpnState(VpnInstanceOpDataEntry.VpnState.PendingDelete);
173 id = VpnUtil.getVpnInstanceOpDataIdentifier(vpnName);
175 tx.merge(id, builder.build());
177 LOG.info("{} call: Operational status set to PENDING_DELETE for vpn {} with rd {}",
178 LOGGING_PREFIX_DELETE, vpnName, primaryRd);
179 })), SystemPropertyReader.getDataStoreJobCoordinatorMaxRetries());
184 public void update(InstanceIdentifier<VpnInstance> identifier,
185 VpnInstance original, VpnInstance update) {
186 LOG.trace("VPN-UPDATE: update: VPN event key: {}, value: {}.", identifier, update);
187 if (Objects.equals(original, update)) {
190 jobCoordinator.enqueueJob("VPN-" + original.getVpnInstanceName(),
191 new UpdateVpnInstanceWorker(dataBroker, identifier, original, update));
194 private class UpdateVpnInstanceWorker implements Callable<List<? extends ListenableFuture<?>>> {
195 private final Logger log = LoggerFactory.getLogger(VpnInstanceListener.UpdateVpnInstanceWorker.class);
196 VpnInstance original;
198 InstanceIdentifier<VpnInstance> vpnIdentifier;
202 UpdateVpnInstanceWorker(DataBroker broker,
203 InstanceIdentifier<VpnInstance> identifier,
204 VpnInstance original,
205 VpnInstance update) {
206 this.broker = broker;
207 this.vpnIdentifier = identifier;
208 this.original = original;
209 this.update = update;
210 this.vpnName = update.getVpnInstanceName();
214 @SuppressWarnings("checkstyle:ForbidCertainMethod")
215 public List<ListenableFuture<Void>> call() {
216 WriteTransaction writeOperTxn = broker.newWriteOnlyTransaction();
217 List<ListenableFuture<Void>> futures = new ArrayList<>();
218 String primaryRd = vpnUtil.getVpnRd(vpnName);
219 if (primaryRd == null) {
220 log.error("{}, failed to update VPN: PrimaryRD is null for vpnName {}", LOGGING_PREFIX_UPDATE, vpnName);
223 updateVpnInstance(writeOperTxn, primaryRd);
225 writeOperTxn.commit().get();
226 } catch (InterruptedException | ExecutionException e) {
227 log.error("{}, failed to update VPN: Exception in updating vpn {} rd {} ", LOGGING_PREFIX_UPDATE,
228 vpnName, update.getRouteDistinguisher(), e);
229 futures.add(Futures.immediateFailedFuture(e));
232 ListenableFuture<List<Void>> listenableFuture = Futures.allAsList(futures);
233 boolean isIpAddressFamilyUpdated = false;
234 if (original.getIpAddressFamilyConfigured() == VpnInstance.IpAddressFamilyConfigured.Undefined
235 && update.getIpAddressFamilyConfigured() != original.getIpAddressFamilyConfigured()) {
236 isIpAddressFamilyUpdated = true;
238 Futures.addCallback(listenableFuture,
239 new PostVpnInstanceChangeWorker(update , isIpAddressFamilyUpdated, primaryRd),
240 MoreExecutors.directExecutor());
244 private class PostVpnInstanceChangeWorker implements FutureCallback<List<Void>> {
245 private final Logger log = LoggerFactory.getLogger(PostVpnInstanceChangeWorker.class);
246 VpnInstance vpnInstance;
248 boolean isIpAddressFamilyUpdated;
251 PostVpnInstanceChangeWorker(VpnInstance vpnInstance, boolean isIpAddressFamilyUpdated, String primaryRd) {
252 this.vpnInstance = vpnInstance;
253 this.vpnName = vpnInstance.getVpnInstanceName();
254 this.isIpAddressFamilyUpdated = isIpAddressFamilyUpdated;
255 this.primaryRd = primaryRd;
259 * This implies that all the future instances have returned success. -- TODO: Confirm this
262 public void onSuccess(List<Void> voids) {
263 if (!VpnUtil.isBgpVpn(vpnName, primaryRd)) {
266 vpnInterfaceManager.vpnInstanceIsReady(vpnName);
269 if (isIpAddressFamilyUpdated) {
272 vpnInterfaceManager.vpnInstanceIsReady(vpnName);
277 * This method is used to handle failure callbacks.
278 * If more retry needed, the retrycount is decremented and mainworker is executed again.
279 * After retries completed, rollbackworker is executed.
280 * If rollbackworker fails, this is a double-fault. Double fault is logged and ignored.
284 public void onFailure(Throwable throwable) {
285 log.error("{} onFailure: Job for vpnInstance: {} with rd {} failed with exception:", LOGGING_PREFIX_ADD,
286 vpnName, primaryRd, throwable);
287 vpnInterfaceManager.vpnInstanceFailed(vpnName);
290 private void notifyTask() {
291 vpnOpDataNotifier.notifyVpnOpDataReady(VpnOpDataSyncer.VpnOpDataType.vpnInstanceToId,
292 vpnInstance.getVpnInstanceName());
293 vpnOpDataNotifier.notifyVpnOpDataReady(VpnOpDataSyncer.VpnOpDataType.vpnOpData,
294 vpnInstance.getVpnInstanceName());
298 public void updateVpnInstance(WriteTransaction writeOperTxn, String primaryRd) {
299 log.trace("updateVpnInstance: VPN event key: {}, value: {}.", vpnIdentifier, update);
300 InstanceIdentifier<VrfTables> id = VpnUtil.buildVrfTableForPrimaryRd(primaryRd);
301 Optional<VrfTables> vrfTable;
303 vrfTable = SingleTransactionDataBroker.syncReadOptional(dataBroker,
304 LogicalDatastoreType.CONFIGURATION, id);
305 } catch (ExecutionException | InterruptedException e) {
306 log.trace("updateVpnInstance: Exception while reading FIB VRF Table for VPN Instance {} with "
307 + "Primary RD {}.", vpnName, primaryRd);
310 //TODO Later if FIB VRF table is available we need to callback the vpnInstanceOpData Update to proceed
311 if (!vrfTable.isPresent()) {
312 log.error("updateVpnInstance: FIB VRF table is not present for the VPN Instance {} "
313 + "with Primary RD {}. Unable to Proceed VpnInstanceOpData Update event {}",
314 vpnName, primaryRd, update);
317 List<String> vpnInstanceUpdatedRdList = Collections.emptyList();
318 boolean isBgpVrfTableUpdateRequired = false;
319 boolean isVpnInstanceRdUpdated = false;
320 //Handle VpnInstance Address Family update
321 int originalIpAddrFamilyValue = original.getIpAddressFamilyConfigured().getIntValue();
322 int updateIpAddrFamilyValue = update.getIpAddressFamilyConfigured().getIntValue();
323 if (originalIpAddrFamilyValue != updateIpAddrFamilyValue) {
324 log.debug("updateVpnInstance: VpnInstance: {} updated with IP address family {} from IP address "
325 + "family {}", vpnName, update.getIpAddressFamilyConfigured().getName(),
326 original.getIpAddressFamilyConfigured().getName());
327 vpnUtil.setVpnInstanceOpDataWithAddressFamily(vpnName, update.getIpAddressFamilyConfigured(),
330 //Update VpnInstanceOpData with BGPVPN to Internet BGPVPN and vice-versa
331 if (original.getBgpvpnType() != update.getBgpvpnType()) {
332 log.debug("updateVpnInstance: VpnInstance: {} updated with BGP-VPN type: {} from BGP-VPN type: {}",
333 vpnName, update.getBgpvpnType(), original.getBgpvpnType());
334 vpnUtil.updateVpnInstanceOpDataWithVpnType(vpnName, update.getBgpvpnType(), writeOperTxn);
336 //Handle BGP-VPN Instance RD Update
337 if ((update.getBgpvpnType() != VpnInstance.BgpvpnType.InternalVPN)) {
338 if (originalIpAddrFamilyValue < updateIpAddrFamilyValue) {
339 isBgpVrfTableUpdateRequired = true;
341 if (original.getRouteDistinguisher().size() != update.getRouteDistinguisher().size()) {
342 log.debug("updateVpnInstance: VpnInstance:{} updated with new RDs: {} from old RDs: {}", vpnName,
343 update.getRouteDistinguisher(), original.getRouteDistinguisher());
344 vpnUtil.updateVpnInstanceOpDataWithRdList(vpnName, update.getRouteDistinguisher(), writeOperTxn);
345 /* Update BGP Vrf entry for newly added RD. VPN Instance does not support for
346 * deleting the existing RDs
348 vpnInstanceUpdatedRdList = update.getRouteDistinguisher() != null
349 ? new ArrayList<>(update.getRouteDistinguisher()) : new ArrayList<>();
350 vpnInstanceUpdatedRdList.removeAll(original.getRouteDistinguisher());
351 isBgpVrfTableUpdateRequired = true;
352 isVpnInstanceRdUpdated = true;
355 //update Bgp VrfTable
356 if (isBgpVrfTableUpdateRequired) {
357 addBgpVrfTableForVpn(update, vpnName, vpnInstanceUpdatedRdList, isVpnInstanceRdUpdated);
363 public void add(final InstanceIdentifier<VpnInstance> identifier, final VpnInstance value) {
364 LOG.trace("{} add: Add VPN event key: {}, value: {}", LOGGING_PREFIX_ADD, identifier, value);
365 final String vpnName = value.getVpnInstanceName();
366 jobCoordinator.enqueueJob("VPN-" + vpnName, new AddVpnInstanceWorker(dataBroker, value),
367 SystemPropertyReader.getDataStoreJobCoordinatorMaxRetries());
370 private class AddVpnInstanceWorker implements Callable<List<? extends ListenableFuture<?>>> {
371 private final Logger log = LoggerFactory.getLogger(AddVpnInstanceWorker.class);
372 VpnInstance vpnInstance;
375 AddVpnInstanceWorker(DataBroker broker,
377 this.broker = broker;
378 this.vpnInstance = value;
382 public List<ListenableFuture<Void>> call() {
383 // If another renderer(for eg : CSS) needs to be supported, check can be performed here
384 // to call the respective helpers.
385 List<ListenableFuture<Void>> futures = new ArrayList<>(2);
386 futures.add(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, confTx -> {
387 ListenableFuture<Void> future = txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL, operTx ->
388 addVpnInstance(vpnInstance, confTx, operTx));
389 LoggingFutures.addErrorLogging(future, LOG, "{} call: error creating VPN {} rd {}",
390 LOGGING_PREFIX_ADD, vpnInstance.getVpnInstanceName(),
391 vpnInstance.getRouteDistinguisher());
394 Futures.addCallback(Futures.allAsList(futures),
395 new PostAddVpnInstanceWorker(vpnInstance , vpnInstance.getVpnInstanceName()),
396 MoreExecutors.directExecutor());
401 // TODO Clean up the exception handling
402 @SuppressWarnings("checkstyle:IllegalCatch")
403 private void addVpnInstance(VpnInstance value, TypedWriteTransaction<Configuration> writeConfigTxn,
404 TypedWriteTransaction<Operational> writeOperTxn) {
405 if (writeConfigTxn == null) {
406 LoggingFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx ->
407 addVpnInstance(value, tx, writeOperTxn)), LOG, "Error adding VPN instance {}", value);
410 if (writeOperTxn == null) {
411 LoggingFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL, tx ->
412 addVpnInstance(value, writeConfigTxn, tx)), LOG, "Error adding VPN instance {}", value);
415 String vpnInstanceName = value.getVpnInstanceName();
417 Uint32 vpnId = vpnUtil.getUniqueId(VpnConstants.VPN_IDPOOL_NAME, vpnInstanceName);
418 if (vpnId.longValue() == 0) {
419 LOG.error("{} addVpnInstance: Unable to fetch label from Id Manager. Bailing out of adding operational"
420 + " data for Vpn Instance {}", LOGGING_PREFIX_ADD, value.getVpnInstanceName());
423 LOG.info("{} addVpnInstance: VPN Id {} generated for VpnInstanceName {}", LOGGING_PREFIX_ADD, vpnId,
425 String primaryRd = VpnUtil.getPrimaryRd(value);
426 org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance
427 vpnInstanceToVpnId = VpnUtil.getVpnInstanceToVpnId(vpnInstanceName, vpnId, primaryRd);
429 writeConfigTxn.mergeParentStructurePut(VpnOperDsUtils.getVpnInstanceToVpnIdIdentifier(vpnInstanceName),
432 VpnIds vpnIdToVpnInstance = VpnUtil.getVpnIdToVpnInstance(vpnId, value.getVpnInstanceName(),
433 primaryRd, VpnUtil.isBgpVpn(vpnInstanceName, primaryRd));
435 writeConfigTxn.mergeParentStructurePut(VpnUtil.getVpnIdToVpnInstanceIdentifier(vpnId), vpnIdToVpnInstance);
438 String cachedTransType = fibManager.getConfTransType();
439 if (cachedTransType.equals("Invalid")) {
441 fibManager.setConfTransType("L3VPN", "VXLAN");
442 } catch (Exception e) {
443 LOG.error("{} addVpnInstance: Exception caught setting the L3VPN tunnel transportType for vpn {}",
444 LOGGING_PREFIX_ADD, vpnInstanceName, e);
447 LOG.debug("{} addVpnInstance: Configured tunnel transport type for L3VPN {} as {}", LOGGING_PREFIX_ADD,
448 vpnInstanceName, cachedTransType);
450 } catch (Exception e) {
451 LOG.error("{} addVpnInstance: Error when trying to retrieve tunnel transport type for L3VPN {}",
452 LOGGING_PREFIX_ADD, vpnInstanceName, e);
455 VpnInstanceOpDataEntryBuilder builder =
456 new VpnInstanceOpDataEntryBuilder().setVrfId(primaryRd).setVpnId(vpnId)
457 .setVpnInstanceName(vpnInstanceName)
458 .setVpnState(VpnInstanceOpDataEntry.VpnState.Created);
459 if (VpnUtil.isBgpVpn(vpnInstanceName, primaryRd)) {
460 List<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn
461 .instance.op.data.entry.vpntargets.VpnTarget> opVpnTargetList = new ArrayList<>();
462 if (value.getL3vni() != null) {
463 builder.setL3vni(value.getL3vni());
465 if (value.isL2vpn()) {
466 builder.setType(VpnInstanceOpDataEntry.Type.L2);
468 VpnTargets vpnTargets = value.getVpnTargets();
469 if (vpnTargets != null) {
470 @Nullable Map<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.l3vpn.rev200204.vpn
471 .instances.vpn.instance.vpntargets.VpnTargetKey, VpnTarget> vpnTargetListMap
472 = vpnTargets.nonnullVpnTarget();
473 if (vpnTargetListMap != null) {
474 for (VpnTarget vpnTarget : vpnTargetListMap.values()) {
475 VpnTargetBuilder vpnTargetBuilder =
476 new VpnTargetBuilder().withKey(new VpnTargetKey(vpnTarget.key().getVrfRTValue()))
477 .setVrfRTType(org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn
478 .instance.op.data.vpn.instance.op.data.entry.vpntargets.VpnTarget.VrfRTType
479 .forValue(vpnTarget.getVrfRTType().getIntValue())).setVrfRTValue(
480 vpnTarget.getVrfRTValue());
481 opVpnTargetList.add(vpnTargetBuilder.build());
485 VpnTargetsBuilder vpnTargetsBuilder = new VpnTargetsBuilder().setVpnTarget(opVpnTargetList);
486 builder.setVpnTargets(vpnTargetsBuilder.build());
488 List<String> rds = value.getRouteDistinguisher();
491 // Get BGP-VPN type configured details from config vpn-instance
492 builder.setBgpvpnType(VpnInstanceOpDataEntry.BgpvpnType.forValue(value.getBgpvpnType().getIntValue()));
493 writeOperTxn.mergeParentStructureMerge(VpnUtil.getVpnInstanceOpDataIdentifier(primaryRd), builder.build());
494 LOG.info("{} addVpnInstance: VpnInstanceOpData populated successfully for vpn {} rd {}", LOGGING_PREFIX_ADD,
495 vpnInstanceName, primaryRd);
498 private class PostAddVpnInstanceWorker implements FutureCallback<List<Void>> {
499 private final Logger log = LoggerFactory.getLogger(PostAddVpnInstanceWorker.class);
500 VpnInstance vpnInstance;
503 PostAddVpnInstanceWorker(VpnInstance vpnInstance, String vpnName) {
504 this.vpnInstance = vpnInstance;
505 this.vpnName = vpnName;
509 * This implies that all the future instances have returned success. -- TODO: Confirm this
512 public void onSuccess(List<Void> voids) {
514 if rd is null, then its either a router vpn instance (or) a vlan external network vpn instance.
515 if rd is non-null, then it is a bgpvpn instance
517 List<String> rd = vpnInstance.getRouteDistinguisher();
518 if (rd == null || addBgpVrf()) {
520 vpnInterfaceManager.vpnInstanceIsReady(vpnName);
522 log.info("{} onSuccess: Vpn Instance Op Data addition for {} successful.", LOGGING_PREFIX_ADD, vpnName);
523 VpnInstanceOpDataEntry vpnInstanceOpDataEntry = vpnUtil.getVpnInstanceOpData(
524 VpnUtil.getPrimaryRd(vpnInstance));
526 // bind service on each tunnel interface
527 //TODO (KIRAN): Add a new listener to handle creation of new DC-GW binding and deletion of existing DC-GW.
528 if (VpnUtil.isL3VpnOverVxLan(vpnInstance.getL3vni())) { //Handled for L3VPN Over VxLAN
529 for (String tunnelInterfaceName: getDcGatewayTunnelInterfaceNameList()) {
530 vpnUtil.bindService(vpnInstance.getVpnInstanceName(), tunnelInterfaceName,
531 true/*isTunnelInterface*/);
535 List<MatchInfo> mkMatches = new ArrayList<>();
536 mkMatches.add(new MatchTunnelId(Uint64.valueOf(vpnInstance.getL3vni())));
538 List<InstructionInfo> instructions =
539 Arrays.asList(new InstructionWriteMetadata(MetaDataUtil.getVpnIdMetadata(vpnInstanceOpDataEntry
540 .getVpnId().toJava()), MetaDataUtil.METADATA_MASK_VRFID),
541 new InstructionGotoTable(NwConstants.L3_GW_MAC_TABLE));
543 for (Uint64 dpnId: NWUtil.getOperativeDPNs(dataBroker)) {
544 String flowRef = getFibFlowRef(dpnId, NwConstants.L3VNI_EXTERNAL_TUNNEL_DEMUX_TABLE,
545 vpnName, VpnConstants.DEFAULT_FLOW_PRIORITY);
546 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpnId,
547 NwConstants.L3VNI_EXTERNAL_TUNNEL_DEMUX_TABLE, flowRef,
548 VpnConstants.DEFAULT_FLOW_PRIORITY, "VxLAN VPN Tunnel Bind Service",
549 0, 0, NwConstants.COOKIE_VM_FIB_TABLE, mkMatches, instructions);
550 mdsalManager.installFlow(dpnId, flowEntity);
552 } catch (ExecutionException | InterruptedException e) {
553 LOG.error("PostAddVpnInstanceWorker: Exception while getting the list of Operative DPNs for Vpn {}",
557 ///////////////////////
561 // TODO Clean up the exception handling
562 @SuppressWarnings("checkstyle:IllegalCatch")
563 private boolean addBgpVrf() {
564 String primaryRd = vpnUtil.getPrimaryRd(vpnName);
565 @Nullable Map<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.l3vpn.rev200204.vpn
566 .instances.vpn.instance.vpntargets.VpnTargetKey, VpnTarget> vpnTargetList
567 = vpnInstance.getVpnTargets().getVpnTarget();
569 if (vpnTargetList == null) {
570 log.error("{} addBgpVrf: vpn target list is empty for vpn {} RD {}", LOGGING_PREFIX_ADD,
571 this.vpnName, primaryRd);
574 // FIXME: separate out to somehow?
575 final ReentrantLock lock = JvmGlobalLocks.getLockForString(vpnName);
578 fibManager.addVrfTable(primaryRd, null);
582 vpnInterfaceManager.handleVpnsExportingRoutes(this.vpnName, primaryRd);
586 private void notifyTask() {
587 vpnOpDataNotifier.notifyVpnOpDataReady(VpnOpDataSyncer.VpnOpDataType.vpnInstanceToId, vpnName);
588 vpnOpDataNotifier.notifyVpnOpDataReady(VpnOpDataSyncer.VpnOpDataType.vpnOpData, vpnName);
592 * This method is used to handle failure callbacks.
593 * If more retry needed, the retrycount is decremented and mainworker is executed again.
594 * After retries completed, rollbackworker is executed.
595 * If rollbackworker fails, this is a double-fault. Double fault is logged and ignored.
599 public void onFailure(Throwable throwable) {
600 log.error("{} onFailure: Job for vpnInstance: {} failed with exception:", LOGGING_PREFIX_ADD, vpnName,
602 vpnInterfaceManager.vpnInstanceFailed(vpnName);
607 protected VpnInstanceOpDataEntry getVpnInstanceOpData(String rd) {
608 InstanceIdentifier<VpnInstanceOpDataEntry> id = VpnUtil.getVpnInstanceOpDataIdentifier(rd);
610 return SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.OPERATIONAL,
612 } catch (InterruptedException | ExecutionException e) {
613 throw new RuntimeException("Error reading VPN instance data for " + rd, e);
617 @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
618 justification = "https://github.com/spotbugs/spotbugs/issues/811")
619 private List<String> getDcGatewayTunnelInterfaceNameList() {
620 List<String> tunnelInterfaceNameList = new ArrayList<>();
622 InstanceIdentifier<DcGatewayIpList> dcGatewayIpListInstanceIdentifier = InstanceIdentifier
623 .create(DcGatewayIpList.class);
624 Optional<DcGatewayIpList> dcGatewayIpListOptional = SingleTransactionDataBroker.syncReadOptional(
625 dataBroker, LogicalDatastoreType.CONFIGURATION, dcGatewayIpListInstanceIdentifier);
626 if (!dcGatewayIpListOptional.isPresent()) {
627 LOG.info("No DC gateways configured.");
628 return tunnelInterfaceNameList;
630 Map<DcGatewayIpKey, DcGatewayIp> keyDcGatewayIpMap = dcGatewayIpListOptional.get().nonnullDcGatewayIp();
631 InstanceIdentifier<ExternalTunnelList> externalTunnelListId = InstanceIdentifier
632 .create(ExternalTunnelList.class);
633 Optional<ExternalTunnelList> externalTunnelListOptional = SingleTransactionDataBroker.syncReadOptional(
634 dataBroker, LogicalDatastoreType.OPERATIONAL, externalTunnelListId);
635 if (externalTunnelListOptional.isPresent()) {
636 Map<ExternalTunnelKey, ExternalTunnel> keyExternalTunnelMap
637 = externalTunnelListOptional.get().nonnullExternalTunnel();
638 List<String> externalTunnelIpList = new ArrayList<>();
639 for (ExternalTunnel externalTunnel: keyExternalTunnelMap.values()) {
640 externalTunnelIpList.add(externalTunnel.getDestinationDevice());
642 List<String> dcGatewayIpList = new ArrayList<>();
643 for (DcGatewayIp dcGatewayIp: keyDcGatewayIpMap.values()) {
644 dcGatewayIpList.add(dcGatewayIp.getIpAddress().getIpv4Address().toString());
646 // Find all externalTunnelIps present in dcGateWayIpList
647 List<String> externalTunnelIpsInDcGatewayIpList = new ArrayList<>();
648 for (String externalTunnelIp: externalTunnelIpList) {
649 for (String dcGateWayIp: dcGatewayIpList) {
650 if (externalTunnelIp.contentEquals(dcGateWayIp)) {
651 externalTunnelIpsInDcGatewayIpList.add(externalTunnelIp);
655 for (String externalTunnelIpsInDcGatewayIp: externalTunnelIpsInDcGatewayIpList) {
656 for (ExternalTunnel externalTunnel: keyExternalTunnelMap.values()) {
657 if (externalTunnel.getDestinationDevice().contentEquals(externalTunnelIpsInDcGatewayIp)) {
658 tunnelInterfaceNameList.add(externalTunnel.getTunnelInterfaceName());
664 } catch (InterruptedException | ExecutionException e) {
665 LOG.error("getDcGatewayTunnelInterfaceNameList: Failed to read data store");
667 return tunnelInterfaceNameList;
670 @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
671 justification = "https://github.com/spotbugs/spotbugs/issues/811")
672 private String getFibFlowRef(Uint64 dpnId, short tableId, String vpnName, int priority) {
673 return VpnConstants.FLOWID_PREFIX + dpnId + NwConstants.FLOWID_SEPARATOR + tableId
674 + NwConstants.FLOWID_SEPARATOR + vpnName + NwConstants.FLOWID_SEPARATOR + priority;
677 @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
678 justification = "https://github.com/spotbugs/spotbugs/issues/811")
679 private void addBgpVrfTableForVpn(VpnInstance vpnInstance, String vpnName, List<String> vpnInstanceUpdatedRdList,
680 boolean isVpnInstanceRdUpdated) {
681 String primaryRd = vpnUtil.getPrimaryRd(vpnName);
682 Collection<VpnTarget> vpnTargetCollection = (vpnInstance.getVpnTargets() != null)
683 ? vpnInstance.getVpnTargets().getVpnTarget().values() : null;
684 List<VpnTarget> vpnTargetList = new ArrayList<VpnTarget>(vpnTargetCollection != null ? vpnTargetCollection
685 : Collections.emptyList());
686 List<String> exportRTList = new ArrayList<>();
687 List<String> importRTList = new ArrayList<>();
688 if (!vpnTargetList.isEmpty()) {
689 for (VpnTarget vpnTarget : vpnTargetList) {
690 if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.ExportExtcommunity) {
691 exportRTList.add(vpnTarget.getVrfRTValue());
693 if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.ImportExtcommunity) {
694 importRTList.add(vpnTarget.getVrfRTValue());
696 if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.Both) {
697 exportRTList.add(vpnTarget.getVrfRTValue());
698 importRTList.add(vpnTarget.getVrfRTValue());
702 synchronized (vpnName.intern()) {
703 List<String> rds = Collections.emptyList();
704 //Vpn Instance RD Update for ECMP use case
705 if (isVpnInstanceRdUpdated) {
706 rds = vpnInstanceUpdatedRdList;
708 rds = vpnInstance.getRouteDistinguisher() != null
709 ? new ArrayList<>(vpnInstance.getRouteDistinguisher()) : new ArrayList<>();
711 for (String rd : rds) {
712 List<String> irtList = rd.equals(primaryRd) ? importRTList : Collections.emptyList();
713 int ipAddrFamilyConfigured = vpnInstance.getIpAddressFamilyConfigured().getIntValue();
714 switch (ipAddrFamilyConfigured) {
716 bgpManager.addVrf(rd, irtList, exportRTList, AddressFamily.IPV4);
717 bgpManager.addVrf(rd, irtList, exportRTList, AddressFamily.IPV6);
718 LOG.debug("addBgpVrfTableForVpn: ADD BGP VRF table for VPN {} with RD {}, ImportRTList {}, "
719 + "ExportRTList {} for IPv4andIPv6 AddressFamily ", vpnName, rd, irtList, exportRTList);
722 bgpManager.addVrf(rd, irtList, exportRTList, AddressFamily.IPV6);
723 LOG.debug("addBgpVrfTableForVpn: ADD BGP VRF table for VPN {} with RD {}, ImportRTList {}, "
724 + "ExportRTList {} for IPv6 AddressFamily ", vpnName, rd, irtList, exportRTList);
727 bgpManager.addVrf(rd, irtList, exportRTList, AddressFamily.IPV4);
728 LOG.debug("addBgpVrfTableForVpn: ADD BGP VRF table for VPN {} with RD {}, ImportRTList {}, "
729 + "ExportRTList {} for IPv4 AddressFamily ", vpnName, rd, irtList, exportRTList);
735 if (vpnInstance.isL2vpn()) {
736 bgpManager.addVrf(rd, importRTList, exportRTList, AddressFamily.L2VPN);
737 LOG.debug("addBgpVrfTableForVpn: ADD BGP VRF table for VPN {} RD {}, ImportRTList {}, "
738 + "ExportRTList {} for L2VPN AddressFamily ", vpnName, rd, irtList, exportRTList);