Freeze upstream versions
[netvirt.git] / vpnmanager / impl / src / main / java / org / opendaylight / netvirt / vpnmanager / VpnInstanceListener.java
1 /*
2  * Copyright (c) 2015 - 2017 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.netvirt.vpnmanager;
9
10 import static org.opendaylight.mdsal.binding.util.Datastore.CONFIGURATION;
11 import static org.opendaylight.mdsal.binding.util.Datastore.OPERATIONAL;
12
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;
23 import java.util.Map;
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.mdsalutil.FlowEntity;
35 import org.opendaylight.genius.mdsalutil.InstructionInfo;
36 import org.opendaylight.genius.mdsalutil.MDSALUtil;
37 import org.opendaylight.genius.mdsalutil.MatchInfo;
38 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
39 import org.opendaylight.genius.mdsalutil.NWUtil;
40 import org.opendaylight.genius.mdsalutil.NwConstants;
41 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
42 import org.opendaylight.genius.mdsalutil.instructions.InstructionWriteMetadata;
43 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
44 import org.opendaylight.genius.mdsalutil.matches.MatchTunnelId;
45 import org.opendaylight.genius.utils.JvmGlobalLocks;
46 import org.opendaylight.genius.utils.SystemPropertyReader;
47 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
48 import org.opendaylight.infrautils.utils.concurrent.Executors;
49 import org.opendaylight.infrautils.utils.concurrent.LoggingFutures;
50 import org.opendaylight.mdsal.binding.api.DataBroker;
51 import org.opendaylight.mdsal.binding.api.WriteTransaction;
52 import org.opendaylight.mdsal.binding.util.Datastore.Configuration;
53 import org.opendaylight.mdsal.binding.util.Datastore.Operational;
54 import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunner;
55 import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunnerImpl;
56 import org.opendaylight.mdsal.binding.util.TypedWriteTransaction;
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;
85
86 @Singleton
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;
102
103     @Inject
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;
121         start();
122     }
123
124     public void start() {
125         LOG.info("{} start", getClass().getSimpleName());
126     }
127
128     @Override
129     @PreDestroy
130     public void close() {
131         super.close();
132         Executors.shutdownAndAwaitTermination(getExecutorService());
133     }
134
135
136     @Override
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);
144             return;
145         }
146
147         //TODO(vpnteam): Entire code would need refactoring to listen only on the parent object - VPNInstance
148         try {
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);
154             return;
155         }
156
157         if (!vpnOpValue.isPresent()) {
158             LOG.error("{}, failed to remove VPN: Unable to retrieve VpnInstanceOpDataEntry for VPN {}. ",
159                 LOGGING_PREFIX_DELETE, vpnName);
160             return;
161         } else {
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);
170                     } else {
171                         builder = new VpnInstanceOpDataEntryBuilder().setVrfId(vpnName)
172                                 .setVpnState(VpnInstanceOpDataEntry.VpnState.PendingDelete);
173                         id = VpnUtil.getVpnInstanceOpDataIdentifier(vpnName);
174                     }
175                     tx.merge(id, builder.build());
176
177                     LOG.info("{} call: Operational status set to PENDING_DELETE for vpn {} with rd {}",
178                             LOGGING_PREFIX_DELETE, vpnName, primaryRd);
179                 })), SystemPropertyReader.getDataStoreJobCoordinatorMaxRetries());
180         }
181     }
182
183     @Override
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)) {
188             return;
189         }
190         jobCoordinator.enqueueJob("VPN-" + original.getVpnInstanceName(),
191                 new UpdateVpnInstanceWorker(dataBroker, identifier, original, update));
192     }
193
194     private class UpdateVpnInstanceWorker implements Callable<List<? extends ListenableFuture<?>>> {
195         private final Logger log = LoggerFactory.getLogger(VpnInstanceListener.UpdateVpnInstanceWorker.class);
196         VpnInstance original;
197         VpnInstance update;
198         InstanceIdentifier<VpnInstance> vpnIdentifier;
199         DataBroker broker;
200         String vpnName;
201
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();
211         }
212
213         @Override
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);
221                 return futures;
222             }
223             updateVpnInstance(writeOperTxn, primaryRd);
224             try {
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));
230                 return futures;
231             }
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;
237             }
238             Futures.addCallback(listenableFuture,
239                     new PostVpnInstanceChangeWorker(update , isIpAddressFamilyUpdated, primaryRd),
240                     MoreExecutors.directExecutor());
241             return futures;
242         }
243
244         private class PostVpnInstanceChangeWorker implements FutureCallback<List<Void>> {
245             private final Logger log = LoggerFactory.getLogger(PostVpnInstanceChangeWorker.class);
246             VpnInstance vpnInstance;
247             String vpnName;
248             boolean isIpAddressFamilyUpdated;
249             String primaryRd;
250
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;
256             }
257
258             /**
259              * This implies that all the future instances have returned success. -- TODO: Confirm this
260              */
261             @Override
262             public void onSuccess(List<Void> voids) {
263                 if (!VpnUtil.isBgpVpn(vpnName, primaryRd)) {
264                     // plain router
265                     notifyTask();
266                     vpnInterfaceManager.vpnInstanceIsReady(vpnName);
267                     return;
268                 }
269                 if (isIpAddressFamilyUpdated) {
270                     //bgpvpn
271                     notifyTask();
272                     vpnInterfaceManager.vpnInstanceIsReady(vpnName);
273                 }
274             }
275
276             /**
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.
281              */
282
283             @Override
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);
288             }
289
290             private void notifyTask() {
291                 vpnOpDataNotifier.notifyVpnOpDataReady(VpnOpDataSyncer.VpnOpDataType.vpnInstanceToId,
292                         vpnInstance.getVpnInstanceName());
293                 vpnOpDataNotifier.notifyVpnOpDataReady(VpnOpDataSyncer.VpnOpDataType.vpnOpData,
294                         vpnInstance.getVpnInstanceName());
295             }
296         }
297
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;
302             try {
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);
308                 return;
309             }
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);
315                 return;
316             }
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(),
328                         writeOperTxn);
329             }
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);
335             }
336             //Handle BGP-VPN Instance RD Update
337             if ((update.getBgpvpnType() != VpnInstance.BgpvpnType.InternalVPN)) {
338                 if (originalIpAddrFamilyValue < updateIpAddrFamilyValue) {
339                     isBgpVrfTableUpdateRequired = true;
340                 }
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
347                      */
348                     vpnInstanceUpdatedRdList = update.getRouteDistinguisher() != null
349                             ? new ArrayList<>(update.getRouteDistinguisher()) : new ArrayList<>();
350                     vpnInstanceUpdatedRdList.removeAll(original.getRouteDistinguisher());
351                     isBgpVrfTableUpdateRequired = true;
352                     isVpnInstanceRdUpdated = true;
353                 }
354             }
355             //update Bgp VrfTable
356             if (isBgpVrfTableUpdateRequired) {
357                 addBgpVrfTableForVpn(update, vpnName, vpnInstanceUpdatedRdList, isVpnInstanceRdUpdated);
358             }
359         }
360     }
361
362     @Override
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());
368     }
369
370     private class AddVpnInstanceWorker implements Callable<List<? extends ListenableFuture<?>>> {
371         private final Logger log = LoggerFactory.getLogger(AddVpnInstanceWorker.class);
372         VpnInstance vpnInstance;
373         DataBroker broker;
374
375         AddVpnInstanceWorker(DataBroker broker,
376                 VpnInstance value) {
377             this.broker = broker;
378             this.vpnInstance = value;
379         }
380
381         @Override
382         public List<? extends ListenableFuture<?>> 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<?>> futures = new ArrayList<>(2);
386             futures.add(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, confTx -> {
387                 ListenableFuture<?> 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());
392                 futures.add(future);
393             }));
394             Futures.addCallback(Futures.allAsList(futures),
395                                 new PostAddVpnInstanceWorker(vpnInstance , vpnInstance.getVpnInstanceName()),
396                                 MoreExecutors.directExecutor());
397             return futures;
398         }
399     }
400
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);
408             return;
409         }
410         if (writeOperTxn == null) {
411             LoggingFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL, tx ->
412                 addVpnInstance(value, writeConfigTxn, tx)), LOG, "Error adding VPN instance {}", value);
413             return;
414         }
415         String vpnInstanceName = value.getVpnInstanceName();
416
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());
421             return;
422         }
423         LOG.info("{} addVpnInstance: VPN Id {} generated for VpnInstanceName {}", LOGGING_PREFIX_ADD, vpnId,
424                 vpnInstanceName);
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);
428
429         writeConfigTxn.mergeParentStructurePut(VpnOperDsUtils.getVpnInstanceToVpnIdIdentifier(vpnInstanceName),
430             vpnInstanceToVpnId);
431
432         VpnIds vpnIdToVpnInstance = VpnUtil.getVpnIdToVpnInstance(vpnId, value.getVpnInstanceName(),
433             primaryRd, VpnUtil.isBgpVpn(vpnInstanceName, primaryRd));
434
435         writeConfigTxn.mergeParentStructurePut(VpnUtil.getVpnIdToVpnInstanceIdentifier(vpnId), vpnIdToVpnInstance);
436
437         try {
438             String cachedTransType = fibManager.getConfTransType();
439             if (cachedTransType.equals("Invalid")) {
440                 try {
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);
445                 }
446             } else {
447                 LOG.debug("{} addVpnInstance: Configured tunnel transport type for L3VPN {} as {}", LOGGING_PREFIX_ADD,
448                         vpnInstanceName, cachedTransType);
449             }
450         } catch (Exception e) {
451             LOG.error("{} addVpnInstance: Error when trying to retrieve tunnel transport type for L3VPN {}",
452                     LOGGING_PREFIX_ADD, vpnInstanceName, e);
453         }
454
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());
464             }
465             if (value.isL2vpn()) {
466                 builder.setType(VpnInstanceOpDataEntry.Type.L2);
467             }
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());
482                     }
483                 }
484             }
485             VpnTargetsBuilder vpnTargetsBuilder = new VpnTargetsBuilder().setVpnTarget(opVpnTargetList);
486             builder.setVpnTargets(vpnTargetsBuilder.build());
487
488             List<String> rds = value.getRouteDistinguisher();
489             builder.setRd(rds);
490         }
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);
496     }
497
498     private class PostAddVpnInstanceWorker implements FutureCallback<List<?>> {
499         private final Logger log = LoggerFactory.getLogger(PostAddVpnInstanceWorker.class);
500         VpnInstance vpnInstance;
501         String vpnName;
502
503         PostAddVpnInstanceWorker(VpnInstance vpnInstance, String vpnName) {
504             this.vpnInstance = vpnInstance;
505             this.vpnName = vpnName;
506         }
507
508         /**
509          * This implies that all the future instances have returned success. -- TODO: Confirm this
510          */
511         @Override
512         public void onSuccess(List<?> voids) {
513             /*
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
516              */
517             List<String> rd = vpnInstance.getRouteDistinguisher();
518             if (rd == null || addBgpVrf()) {
519                 notifyTask();
520                 vpnInterfaceManager.vpnInstanceIsReady(vpnName);
521             }
522             log.info("{} onSuccess: Vpn Instance Op Data addition for {} successful.", LOGGING_PREFIX_ADD, vpnName);
523             VpnInstanceOpDataEntry vpnInstanceOpDataEntry = vpnUtil.getVpnInstanceOpData(
524                     VpnUtil.getPrimaryRd(vpnInstance));
525
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*/);
532                 }
533
534                 // install flow
535                 List<MatchInfo> mkMatches = new ArrayList<>();
536                 mkMatches.add(new MatchTunnelId(Uint64.valueOf(vpnInstance.getL3vni())));
537
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));
542                 try {
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);
551                     }
552                 } catch (ExecutionException | InterruptedException e) {
553                     LOG.error("PostAddVpnInstanceWorker: Exception while getting the list of Operative DPNs for Vpn {}",
554                             vpnName, e);
555                 }
556
557                 ///////////////////////
558             }
559         }
560
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();
568
569             if (vpnTargetList == null) {
570                 log.error("{} addBgpVrf: vpn target list is empty for vpn {} RD {}", LOGGING_PREFIX_ADD,
571                         this.vpnName, primaryRd);
572                 return false;
573             }
574             // FIXME: separate out to somehow?
575             final ReentrantLock lock = JvmGlobalLocks.getLockForString(vpnName);
576             lock.lock();
577             try {
578                 fibManager.addVrfTable(primaryRd, null);
579             } finally {
580                 lock.unlock();
581             }
582             vpnInterfaceManager.handleVpnsExportingRoutes(this.vpnName, primaryRd);
583             return true;
584         }
585
586         private void notifyTask() {
587             vpnOpDataNotifier.notifyVpnOpDataReady(VpnOpDataSyncer.VpnOpDataType.vpnInstanceToId, vpnName);
588             vpnOpDataNotifier.notifyVpnOpDataReady(VpnOpDataSyncer.VpnOpDataType.vpnOpData, vpnName);
589         }
590
591         /**
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.
596          */
597
598         @Override
599         public void onFailure(Throwable throwable) {
600             log.error("{} onFailure: Job for vpnInstance: {} failed with exception:", LOGGING_PREFIX_ADD, vpnName,
601                     throwable);
602             vpnInterfaceManager.vpnInstanceFailed(vpnName);
603         }
604     }
605
606     @Nullable
607     protected VpnInstanceOpDataEntry getVpnInstanceOpData(String rd) {
608         InstanceIdentifier<VpnInstanceOpDataEntry> id = VpnUtil.getVpnInstanceOpDataIdentifier(rd);
609         try {
610             return SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.OPERATIONAL,
611                     id).orElse(null);
612         } catch (InterruptedException | ExecutionException e) {
613             throw new RuntimeException("Error reading VPN instance data for " + rd, e);
614         }
615     }
616
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<>();
621         try {
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;
629             }
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());
641                 }
642                 List<String> dcGatewayIpList = new ArrayList<>();
643                 for (DcGatewayIp dcGatewayIp: keyDcGatewayIpMap.values()) {
644                     dcGatewayIpList.add(dcGatewayIp.getIpAddress().getIpv4Address().toString());
645                 }
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);
652                         }
653                     }
654                 }
655                 for (String externalTunnelIpsInDcGatewayIp: externalTunnelIpsInDcGatewayIpList) {
656                     for (ExternalTunnel externalTunnel: keyExternalTunnelMap.values()) {
657                         if (externalTunnel.getDestinationDevice().contentEquals(externalTunnelIpsInDcGatewayIp)) {
658                             tunnelInterfaceNameList.add(externalTunnel.getTunnelInterfaceName());
659                         }
660                     }
661                 }
662
663             }
664         } catch (InterruptedException | ExecutionException e) {
665             LOG.error("getDcGatewayTunnelInterfaceNameList: Failed to read data store");
666         }
667         return tunnelInterfaceNameList;
668     }
669
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;
675     }
676
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());
692                 }
693                 if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.ImportExtcommunity) {
694                     importRTList.add(vpnTarget.getVrfRTValue());
695                 }
696                 if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.Both) {
697                     exportRTList.add(vpnTarget.getVrfRTValue());
698                     importRTList.add(vpnTarget.getVrfRTValue());
699                 }
700             }
701         }
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;
707             } else {
708                 rds = vpnInstance.getRouteDistinguisher() != null
709                         ? new ArrayList<>(vpnInstance.getRouteDistinguisher()) : new ArrayList<>();
710             }
711             for (String rd : rds) {
712                 List<String> irtList = rd.equals(primaryRd) ? importRTList : Collections.emptyList();
713                 int ipAddrFamilyConfigured = vpnInstance.getIpAddressFamilyConfigured().getIntValue();
714                 switch (ipAddrFamilyConfigured) {
715                     case 10:
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);
720                         break;
721                     case 6:
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);
725                         break;
726                     case 4:
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);
730                         break;
731                     default:
732                         break;
733                 }
734                 //L2VPN Use case
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);
739                 }
740             }
741         }
742     }
743 }