Enforce datastore-contrained transactions
[netvirt.git] / vpnmanager / impl / src / main / java / org / opendaylight / netvirt / vpnmanager / VpnFootprintService.java
1 /*
2  * Copyright (c) 2016, 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
9 package org.opendaylight.netvirt.vpnmanager;
10
11 import com.google.common.base.Optional;
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 edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
17 import java.util.ArrayList;
18 import java.util.List;
19 import java.util.concurrent.ExecutionException;
20 import java.util.concurrent.atomic.AtomicBoolean;
21 import java.util.concurrent.locks.ReentrantLock;
22 import javax.inject.Inject;
23 import javax.inject.Singleton;
24 import org.apache.commons.lang3.tuple.ImmutablePair;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
27 import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;
28 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
29 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
30 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
31 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
32 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
33 import org.opendaylight.genius.utils.JvmGlobalLocks;
34 import org.opendaylight.netvirt.fibmanager.api.IFibManager;
35 import org.opendaylight.netvirt.vpnmanager.api.IVpnFootprintService;
36 import org.opendaylight.netvirt.vpnmanager.api.VpnHelper;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.AddDpnEvent;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.AddDpnEventBuilder;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.AddInterfaceToDpnOnVpnEvent;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.AddInterfaceToDpnOnVpnEventBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.RemoveDpnEvent;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.RemoveDpnEventBuilder;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.RemoveInterfaceFromDpnOnVpnEvent;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.RemoveInterfaceFromDpnOnVpnEventBuilder;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.add._interface.to.dpn.on.vpn.event.AddInterfaceEventData;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.add._interface.to.dpn.on.vpn.event.AddInterfaceEventDataBuilder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.add.dpn.event.AddEventData;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.add.dpn.event.AddEventDataBuilder;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.remove._interface.from.dpn.on.vpn.event.RemoveInterfaceEventData;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.remove._interface.from.dpn.on.vpn.event.RemoveInterfaceEventDataBuilder;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.remove.dpn.event.RemoveEventData;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.remove.dpn.event.RemoveEventDataBuilder;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnListBuilder;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.IpAddresses;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.IpAddressesBuilder;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.IpAddressesKey;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.VpnInterfaces;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.VpnInterfacesBuilder;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.VpnInterfacesKey;
62 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
63 import org.opendaylight.yangtools.yang.common.Uint32;
64 import org.opendaylight.yangtools.yang.common.Uint64;
65 import org.slf4j.Logger;
66 import org.slf4j.LoggerFactory;
67
68 @Singleton
69 public class VpnFootprintService implements IVpnFootprintService {
70
71     private static final Logger LOG = LoggerFactory.getLogger(VpnFootprintService.class);
72
73     private final DataBroker dataBroker;
74     private final ManagedNewTransactionRunner txRunner;
75     private final IFibManager fibManager;
76     private final VpnOpDataSyncer vpnOpDataSyncer;
77     private final NotificationPublishService notificationPublishService;
78     private final IInterfaceManager interfaceManager;
79     private final VpnUtil vpnUtil;
80
81     @Inject
82     public VpnFootprintService(final DataBroker dataBroker, final IFibManager fibManager,
83             final NotificationPublishService notificationPublishService, final VpnOpDataSyncer vpnOpDataSyncer,
84             final IInterfaceManager interfaceManager, VpnUtil vpnUtil) {
85         this.dataBroker = dataBroker;
86         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
87         this.fibManager = fibManager;
88         this.vpnOpDataSyncer = vpnOpDataSyncer;
89         this.notificationPublishService = notificationPublishService;
90         this.interfaceManager = interfaceManager;
91         this.vpnUtil = vpnUtil;
92     }
93
94     @Override
95     public void updateVpnToDpnMapping(Uint64 dpId, String vpnName, String primaryRd, @Nullable String interfaceName,
96                                       @Nullable ImmutablePair<IpAddresses.IpAddressSource,
97                                               String> ipAddressSourceValuePair, boolean add) {
98         Uint32 vpnId = vpnUtil.getVpnId(vpnName);
99         if (!dpId.equals(Uint64.ZERO)) {
100             if (add) {
101                 // Considering the possibility of VpnInstanceOpData not being ready yet cause
102                 // the VPN is
103                 // still in its creation process
104                 if (VpnConstants.INVALID_ID.equals(vpnId)) {
105                     LOG.error("updateVpnToDpnMapping: Operational data  for vpn not ready. Waiting to update vpn"
106                             + " footprint for vpn {} on dpn {} interface {}", vpnName, dpId, interfaceName);
107                     vpnOpDataSyncer.waitForVpnDataReady(VpnOpDataSyncer.VpnOpDataType.vpnInstanceToId, vpnName,
108                             VpnConstants.PER_VPN_INSTANCE_OPDATA_MAX_WAIT_TIME_IN_MILLISECONDS);
109                     vpnId = vpnUtil.getVpnId(vpnName);
110                 }
111                 if (interfaceName != null) {
112                     createOrUpdateVpnToDpnListForInterfaceName(vpnId, primaryRd, dpId, interfaceName, vpnName);
113                     publishInterfaceAddedToVpnNotification(interfaceName, dpId, vpnName, vpnId);
114                 } else {
115                     createOrUpdateVpnToDpnListForIPAddress(vpnId, primaryRd, dpId, ipAddressSourceValuePair, vpnName);
116                 }
117             } else {
118                 if (interfaceName != null) {
119                     removeOrUpdateVpnToDpnListForInterfaceName(vpnId, primaryRd, dpId, interfaceName, vpnName);
120                     publishInterfaceRemovedFromVpnNotification(interfaceName, dpId, vpnName, vpnId);
121                 } else {
122                     removeOrUpdateVpnToDpnListForIpAddress(vpnId, primaryRd, dpId, ipAddressSourceValuePair, vpnName);
123                 }
124             }
125         }
126     }
127
128     // Allow deprecated TransactionRunner calls for now
129     @SuppressWarnings("ForbidCertainMethod")
130     private void createOrUpdateVpnToDpnListForInterfaceName(Uint32 vpnId, String primaryRd, Uint64 dpnId,
131             String intfName, String vpnName) {
132         AtomicBoolean newDpnOnVpn = new AtomicBoolean(false);
133         /* Starts synchronized block. This ensures only one reader/writer get access to vpn-dpn-list
134          * The future.get ensures that the write to the datastore is complete before leaving the synchronized block.
135          */
136         // FIXME: separate this out somehow?
137         final ReentrantLock lock = JvmGlobalLocks.getLockForString(vpnName);
138         lock.lock();
139         try {
140             ListenableFuture<Void> future = txRunner.callWithNewReadWriteTransactionAndSubmit(tx -> {
141                 InstanceIdentifier<VpnToDpnList> id = VpnHelper.getVpnToDpnListIdentifier(primaryRd, dpnId);
142                 VpnInterfaces vpnInterface = new VpnInterfacesBuilder().setInterfaceName(intfName).build();
143                 Optional<VpnToDpnList> dpnInVpn = tx.read(LogicalDatastoreType.OPERATIONAL, id).checkedGet();
144                 if (dpnInVpn.isPresent()) {
145                     VpnToDpnList vpnToDpnList = dpnInVpn.get();
146                     List<VpnInterfaces> vpnInterfaces = new ArrayList<>(vpnToDpnList.nonnullVpnInterfaces());
147                     vpnInterfaces.add(vpnInterface);
148                     VpnToDpnListBuilder vpnToDpnListBuilder = new VpnToDpnListBuilder(vpnToDpnList);
149                     vpnToDpnListBuilder.setDpnState(VpnToDpnList.DpnState.Active).setVpnInterfaces(vpnInterfaces);
150
151                     tx.put(LogicalDatastoreType.OPERATIONAL, id, vpnToDpnListBuilder.build(),
152                         WriteTransaction.CREATE_MISSING_PARENTS);
153                     /*
154                      * If earlier state was inactive, it is considered new DPN coming back to the
155                      * same VPN
156                      */
157                     if (vpnToDpnList.getDpnState() == VpnToDpnList.DpnState.Inactive) {
158                         newDpnOnVpn.set(true);
159                     }
160                     LOG.debug("createOrUpdateVpnToDpnList: Updating vpn footprint for vpn {} vpnId {} interface {}"
161                             + " on dpn {}", vpnName, vpnId, intfName, dpnId);
162                 } else {
163                     List<VpnInterfaces> vpnInterfaces = new ArrayList<>();
164                     vpnInterfaces.add(vpnInterface);
165                     VpnToDpnListBuilder vpnToDpnListBuilder = new VpnToDpnListBuilder().setDpnId(dpnId);
166                     vpnToDpnListBuilder.setDpnState(VpnToDpnList.DpnState.Active).setVpnInterfaces(vpnInterfaces);
167
168                     tx.put(LogicalDatastoreType.OPERATIONAL, id, vpnToDpnListBuilder.build(),
169                         WriteTransaction.CREATE_MISSING_PARENTS);
170                     newDpnOnVpn.set(true);
171                     LOG.debug("createOrUpdateVpnToDpnList: Creating vpn footprint for vpn {} vpnId {} interface {}"
172                             + " on dpn {}", vpnName, vpnId, intfName, dpnId);
173                 }
174             });
175             future.get();
176         } catch (InterruptedException | ExecutionException e) {
177             LOG.error("createOrUpdateVpnToDpnList: Error adding to dpnToVpnList for vpn {} vpnId {} interface {}"
178                     + " dpn {}", vpnName, vpnId, intfName, dpnId, e);
179             throw new RuntimeException(e.getMessage(), e);
180         } finally {
181             lock.unlock();
182         }
183         LOG.info("createOrUpdateVpnToDpnList: Created/Updated vpn footprint for vpn {} vpnId {} interfacName{}"
184                 + " on dpn {}", vpnName, vpnId, intfName, dpnId);
185         /*
186          * Informing the FIB only after writeTxn is submitted successfully.
187          */
188         if (newDpnOnVpn.get()) {
189             if (vpnUtil.isVlan(intfName)) {
190                 if (!vpnUtil.shouldPopulateFibForVlan(vpnName, null, dpnId)) {
191                     return;
192                 }
193             }
194             fibManager.populateFibOnNewDpn(dpnId, vpnId, primaryRd,
195                     new DpnEnterExitVpnWorker(dpnId, vpnName, primaryRd, true /* entered */));
196             LOG.info("createOrUpdateVpnToDpnList: Sent populateFib event for new dpn {} in VPN {} for interface {}",
197                     dpnId, vpnName, intfName);
198         }
199     }
200
201     // Allow deprecated TransactionRunner calls for now
202     @SuppressWarnings("ForbidCertainMethod")
203     private void createOrUpdateVpnToDpnListForIPAddress(Uint32 vpnId, String primaryRd, Uint64 dpnId,
204             ImmutablePair<IpAddresses.IpAddressSource, String> ipAddressSourceValuePair, String vpnName) {
205         AtomicBoolean newDpnOnVpn = new AtomicBoolean(false);
206         /* Starts synchronized block. This ensures only one reader/writer get access to vpn-dpn-list
207          * The future.get ensures that the write to the datastore is complete before leaving the synchronized block.
208          */
209         // FIXME: separate this out somehow?
210         final ReentrantLock lock = JvmGlobalLocks.getLockForString(vpnName);
211         lock.lock();
212         try {
213             ListenableFuture<Void> future = txRunner.callWithNewReadWriteTransactionAndSubmit(tx -> {
214                 InstanceIdentifier<VpnToDpnList> id = VpnHelper.getVpnToDpnListIdentifier(primaryRd, dpnId);
215                 IpAddressesBuilder ipAddressesBldr = new IpAddressesBuilder()
216                         .setIpAddressSource(ipAddressSourceValuePair.getKey());
217                 ipAddressesBldr.withKey(new IpAddressesKey(ipAddressSourceValuePair.getValue()));
218                 ipAddressesBldr.setIpAddress(ipAddressSourceValuePair.getValue());
219                 Optional<VpnToDpnList> dpnInVpn = tx.read(LogicalDatastoreType.OPERATIONAL, id).checkedGet();
220                 if (dpnInVpn.isPresent()) {
221                     VpnToDpnList vpnToDpnList = dpnInVpn.get();
222                     List<IpAddresses> ipAddresses = new ArrayList<>(vpnToDpnList.nonnullIpAddresses());
223                     ipAddresses.add(ipAddressesBldr.build());
224                     VpnToDpnListBuilder vpnToDpnListBuilder = new VpnToDpnListBuilder(vpnToDpnList);
225                     vpnToDpnListBuilder.setDpnState(VpnToDpnList.DpnState.Active).setIpAddresses(ipAddresses);
226
227                     tx.put(LogicalDatastoreType.OPERATIONAL, id, vpnToDpnListBuilder.build(), true);
228                     /*
229                      * If earlier state was inactive, it is considered new DPN coming back to the
230                      * same VPN
231                      */
232                     if (vpnToDpnList.getDpnState() == VpnToDpnList.DpnState.Inactive) {
233                         newDpnOnVpn.set(true);
234                     }
235                 } else {
236                     List<IpAddresses> ipAddresses = new ArrayList<>();
237                     ipAddresses.add(ipAddressesBldr.build());
238                     VpnToDpnListBuilder vpnToDpnListBuilder = new VpnToDpnListBuilder().setDpnId(dpnId);
239                     vpnToDpnListBuilder.setDpnState(VpnToDpnList.DpnState.Active).setIpAddresses(ipAddresses);
240                     tx.put(LogicalDatastoreType.OPERATIONAL, id, vpnToDpnListBuilder.build(), true);
241                     newDpnOnVpn.set(true);
242                 }
243
244             });
245             future.get();
246         } catch (InterruptedException | ExecutionException e) {
247             LOG.error("createOrUpdateVpnToDpnListForIPAddress: Error adding to dpnToVpnList for vpn {}"
248                     + " ipAddresses {} dpn {}", vpnName, ipAddressSourceValuePair.getValue(), dpnId, e);
249             throw new RuntimeException(e.getMessage(), e); //TODO: Avoid this
250         } finally {
251             lock.unlock();
252         }
253         /*
254          * Informing the Fib only after writeTxn is submitted successfuly.
255          */
256         if (newDpnOnVpn.get()) {
257             LOG.debug("Sending populateFib event for new dpn {} in VPN {}", dpnId, vpnName);
258             fibManager.populateFibOnNewDpn(dpnId, vpnId, primaryRd,
259                     new DpnEnterExitVpnWorker(dpnId, vpnName, primaryRd, true /* entered */));
260         }
261     }
262
263     // Allow deprecated TransactionRunner calls for now
264     @SuppressWarnings("ForbidCertainMethod")
265     private void removeOrUpdateVpnToDpnListForInterfaceName(Uint32 vpnId, String rd, Uint64 dpnId, String intfName,
266             String vpnName) {
267         AtomicBoolean lastDpnOnVpn = new AtomicBoolean(false);
268         /* Starts synchronized block. This ensures only one reader/writer get access to vpn-dpn-list
269          * The future.get ensures that the write to the datastore is complete before leaving the synchronized block.
270          */
271         // FIXME: separate this out somehow?
272         final ReentrantLock lock = JvmGlobalLocks.getLockForString(vpnName);
273         lock.lock();
274         try {
275             try {
276                 ListenableFuture<Void> future = txRunner.callWithNewReadWriteTransactionAndSubmit(tx -> {
277                     InstanceIdentifier<VpnToDpnList> id = VpnHelper.getVpnToDpnListIdentifier(rd, dpnId);
278                     Optional<VpnToDpnList> dpnInVpnOpt = tx.read(LogicalDatastoreType.OPERATIONAL, id)
279                             .checkedGet();
280                     if (!dpnInVpnOpt.isPresent()) {
281                         LOG.error("removeOrUpdateVpnToDpnList: Could not find DpnToVpn map for VPN=[name={}"
282                                 + " rd={} id={}] and dpnId={}", vpnName, rd, id, dpnId);
283                         return;
284                     }
285                     VpnToDpnList dpnInVpn = dpnInVpnOpt.get();
286                     List<VpnInterfaces> vpnInterfaces = new ArrayList<>(dpnInVpn.nonnullVpnInterfaces());
287                     if (vpnInterfaces == null) {
288                         LOG.error("Could not find vpnInterfaces for DpnInVpn map for VPN=[name={} rd={} id={}] and "
289                                 + "dpnId={}", vpnName, rd, id, dpnId);
290                         return;
291                     }
292                     VpnInterfaces currVpnInterface = new VpnInterfacesBuilder().setInterfaceName(intfName).build();
293                     if (vpnInterfaces.remove(currVpnInterface)) {
294                         if (vpnInterfaces.isEmpty()) {
295                             List<IpAddresses> ipAddresses = dpnInVpn.getIpAddresses();
296                             VpnToDpnListBuilder dpnInVpnBuilder =
297                                     new VpnToDpnListBuilder(dpnInVpn).setVpnInterfaces(null);
298                             if (ipAddresses == null || ipAddresses.isEmpty()) {
299                                 dpnInVpnBuilder.setDpnState(VpnToDpnList.DpnState.Inactive);
300                                 lastDpnOnVpn.set(true);
301                             } else {
302                                 LOG.error("removeOrUpdateVpnToDpnList: vpn interfaces are empty but ip addresses"
303                                         + " are present for the vpn {} in dpn {} interface {}", vpnName, dpnId,
304                                         intfName);
305                             }
306                             LOG.debug("removeOrUpdateVpnToDpnList: Removing vpn footprint for vpn {} vpnId {} "
307                                     + "interface {}, on dpn {}", vpnName, vpnName, intfName, dpnId);
308                             tx.put(LogicalDatastoreType.OPERATIONAL, id, dpnInVpnBuilder.build(),
309                                     WriteTransaction.CREATE_MISSING_PARENTS);
310
311                         } else {
312                             tx.delete(LogicalDatastoreType.OPERATIONAL,
313                                     id.child(VpnInterfaces.class, new VpnInterfacesKey(intfName)));
314                             LOG.debug("removeOrUpdateVpnToDpnList: Updating vpn footprint for vpn {} vpnId {} "
315                                     + "interface {}, on dpn {}", vpnName, vpnName, intfName, dpnId);
316                         }
317                     }
318                 });
319                 future.get();
320             } catch (InterruptedException | ExecutionException e) {
321                 LOG.error("removeOrUpdateVpnToDpnList: Error removing from dpnToVpnList for vpn {} vpnId {}"
322                         + " interface {} dpn {}", vpnName, vpnId, intfName, dpnId, e);
323                 throw new RuntimeException(e.getMessage(), e);
324             }
325             // Ends synchronized block
326             LOG.info("removeOrUpdateVpnToDpnList: Updated/Removed vpn footprint for vpn {} vpnId {} interface {},"
327                     + " on dpn {}", vpnName, vpnName, intfName, dpnId);
328
329             if (lastDpnOnVpn.get()) {
330                 fibManager.cleanUpDpnForVpn(dpnId, vpnId, rd,
331                         new DpnEnterExitVpnWorker(dpnId, vpnName, rd, false /* exited */));
332                 LOG.info("removeOrUpdateVpnToDpnList: Sent cleanup event for dpn {} in VPN {} vpnId {} interface {}",
333                         dpnId, vpnName, vpnId, intfName);
334             }
335         } finally {
336             lock.unlock();
337         }
338     }
339
340     // Allow deprecated TransactionRunner calls for now
341     @SuppressWarnings("ForbidCertainMethod")
342     private void removeOrUpdateVpnToDpnListForIpAddress(Uint32 vpnId, String rd, Uint64 dpnId,
343             ImmutablePair<IpAddresses.IpAddressSource, String> ipAddressSourceValuePair, String vpnName) {
344         AtomicBoolean lastDpnOnVpn = new AtomicBoolean(false);
345         /* Starts synchronized block. This ensures only one reader/writer get access to vpn-dpn-list
346          * The future.get ensures that the write to the datastore is complete before leaving the synchronized block.
347          */
348         // FIXME: separate this out somehow?
349         final ReentrantLock lock = JvmGlobalLocks.getLockForString(vpnName);
350         lock.lock();
351         try {
352             ListenableFuture<Void> future = txRunner.callWithNewReadWriteTransactionAndSubmit(tx -> {
353                 InstanceIdentifier<VpnToDpnList> id = VpnHelper.getVpnToDpnListIdentifier(rd, dpnId);
354
355                 Optional<VpnToDpnList> dpnInVpnOpt = tx.read(LogicalDatastoreType.OPERATIONAL, id)
356                         .checkedGet();
357                 if (!dpnInVpnOpt.isPresent()) {
358                     LOG.error("removeOrUpdateVpnToDpnList: Could not find DpnToVpn map for VPN=[name={} "
359                             + "rd={} id={}] and dpnId={}", vpnName, rd, id, dpnId);
360                     return;
361                 }
362                 VpnToDpnList dpnInVpn = dpnInVpnOpt.get();
363                 List<IpAddresses> ipAddresses = new ArrayList<>(dpnInVpn.nonnullIpAddresses());
364                 if (ipAddresses == null) {
365                     LOG.info("Could not find ipAddresses for DpnInVpn map for VPN=[name={} rd={} id={}] "
366                             + "and dpnId={}", vpnName, rd, id, dpnId);
367                     return;
368                 }
369
370                 IpAddresses currIpAddress = new IpAddressesBuilder()
371                         .withKey(new IpAddressesKey(ipAddressSourceValuePair.getValue()))
372                         .setIpAddressSource(ipAddressSourceValuePair.getKey()).build();
373                 if (ipAddresses.remove(currIpAddress)) {
374                     if (ipAddresses.isEmpty()) {
375                         List<VpnInterfaces> vpnInterfaces = dpnInVpn.getVpnInterfaces();
376                         VpnToDpnListBuilder dpnInVpnBuilder =
377                                 new VpnToDpnListBuilder(dpnInVpn).setIpAddresses(null);
378                         if (vpnInterfaces == null || vpnInterfaces.isEmpty()) {
379                             dpnInVpnBuilder.setDpnState(VpnToDpnList.DpnState.Inactive);
380                             lastDpnOnVpn.set(true);
381                         } else {
382                             LOG.warn("ip addresses are empty but vpn interfaces are present for the vpn {} in "
383                                     + "dpn {}", vpnName, dpnId);
384                         }
385                         tx.put(LogicalDatastoreType.OPERATIONAL, id, dpnInVpnBuilder.build(), true);
386
387                     } else {
388                         tx.delete(LogicalDatastoreType.OPERATIONAL, id.child(IpAddresses.class,
389                             new IpAddressesKey(ipAddressSourceValuePair.getValue())));
390                     }
391                 }
392
393             });
394             future.get();
395         } catch (InterruptedException | ExecutionException e) {
396             LOG.error("Error removing from dpnToVpnList for vpn {} Ipaddress {} dpn {}", vpnName,
397                 ipAddressSourceValuePair.getValue(), dpnId, e);
398             throw new RuntimeException(e.getMessage(), e); //TODO: Avoid this
399         } finally {
400             lock.unlock();
401         }
402
403         if (lastDpnOnVpn.get()) {
404             LOG.debug("Sending cleanup event for dpn {} in VPN {}", dpnId, vpnName);
405             fibManager.cleanUpDpnForVpn(dpnId, vpnId, rd,
406                     new DpnEnterExitVpnWorker(dpnId, vpnName, rd, false /* exited */));
407         }
408     }
409
410     @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
411             justification = "https://github.com/spotbugs/spotbugs/issues/811")
412     private void publishAddNotification(final Uint64 dpnId, final String vpnName, final String rd) {
413         LOG.debug("publishAddNotification: Sending notification for add dpn {} in vpn {} rd {} event ", dpnId, vpnName,
414                 rd);
415         AddEventData data = new AddEventDataBuilder().setVpnName(vpnName).setRd(rd).setDpnId(dpnId).build();
416         AddDpnEvent event = new AddDpnEventBuilder().setAddEventData(data).build();
417         final ListenableFuture<?> eventFuture = notificationPublishService.offerNotification(event);
418         Futures.addCallback(eventFuture, new FutureCallback<Object>() {
419             @Override
420             public void onFailure(Throwable error) {
421                 LOG.error("publishAddNotification: Error in notifying listeners for add dpn {} in vpn {} rd {} event ",
422                         dpnId, vpnName, rd, error);
423             }
424
425             @Override
426             public void onSuccess(Object arg) {
427                 LOG.info("publishAddNotification: Successful in notifying listeners for add dpn {} in vpn {} rd {}"
428                         + " event ", dpnId, vpnName, rd);
429             }
430         }, MoreExecutors.directExecutor());
431     }
432
433     @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
434                justification = "https://github.com/spotbugs/spotbugs/issues/811")
435     private void publishRemoveNotification(final Uint64 dpnId, final String vpnName, final String rd) {
436         LOG.debug("publishRemoveNotification: Sending notification for remove dpn {} in vpn {} rd {} event ", dpnId,
437                 vpnName, rd);
438         RemoveEventData data = new RemoveEventDataBuilder().setVpnName(vpnName).setRd(rd).setDpnId(dpnId).build();
439         RemoveDpnEvent event = new RemoveDpnEventBuilder().setRemoveEventData(data).build();
440         final ListenableFuture<?> eventFuture = notificationPublishService.offerNotification(event);
441         Futures.addCallback(eventFuture, new FutureCallback<Object>() {
442             @Override
443             public void onFailure(Throwable error) {
444                 LOG.error("publishRemoveNotification: Error in notifying listeners for remove dpn {} in vpn {} rd {}"
445                         + " event ", dpnId, vpnName, rd, error);
446             }
447
448             @Override
449             public void onSuccess(Object arg) {
450                 LOG.info("publishRemoveNotification: Successful in notifying listeners for remove dpn {} in vpn {}"
451                         + " rd {} event ", dpnId, vpnName, rd);
452             }
453         }, MoreExecutors.directExecutor());
454     }
455
456     private void publishInterfaceAddedToVpnNotification(String interfaceName, Uint64 dpnId, String vpnName,
457             Uint32 vpnId) {
458         LOG.debug("publishInterfaceAddedToVpnNotification: Sending notification for addition of interface {} on dpn {}"
459                 + " for vpn {}", interfaceName, dpnId, vpnName);
460         AddInterfaceEventData data = new AddInterfaceEventDataBuilder().setInterfaceName(interfaceName).setVpnId(vpnId)
461                 .setDpnId(dpnId).build();
462         AddInterfaceToDpnOnVpnEvent event = new AddInterfaceToDpnOnVpnEventBuilder().setAddInterfaceEventData(data)
463                 .build();
464         final ListenableFuture<?> eventFuture = notificationPublishService.offerNotification(event);
465         Futures.addCallback(eventFuture, new FutureCallback<Object>() {
466             @Override
467             public void onFailure(Throwable error) {
468                 LOG.warn("publishInterfaceAddedToVpnNotification: Error in notifying listeners for add interface {}"
469                         + " on dpn {} in vpn {} event ", interfaceName, dpnId, vpnName, error);
470             }
471
472             @Override
473             public void onSuccess(Object arg) {
474                 LOG.trace("publishInterfaceAddedToVpnNotification: Successful in notifying listeners for add"
475                         + " interface {} on dpn {} in vpn {} event ", interfaceName, dpnId, vpnName);
476             }
477         }, MoreExecutors.directExecutor());
478     }
479
480     private void publishInterfaceRemovedFromVpnNotification(String interfaceName, Uint64 dpnId, String vpnName,
481             Uint32 vpnId) {
482         LOG.debug("publishInterfaceAddedToVpnNotification: Sending notification for removal of interface {}"
483                 + " from dpn {} for vpn {}", interfaceName, dpnId, vpnName);
484         RemoveInterfaceEventData data = new RemoveInterfaceEventDataBuilder().setInterfaceName(interfaceName)
485                 .setVpnId(vpnId).setDpnId(dpnId).build();
486         RemoveInterfaceFromDpnOnVpnEvent event = new RemoveInterfaceFromDpnOnVpnEventBuilder()
487                 .setRemoveInterfaceEventData(data).build();
488         final ListenableFuture<?> eventFuture = notificationPublishService.offerNotification(event);
489         Futures.addCallback(eventFuture, new FutureCallback<Object>() {
490             @Override
491             public void onFailure(Throwable error) {
492                 LOG.warn(
493                         "publishInterfaceAddedToVpnNotification: Error in notifying listeners"
494                                 + " for removing interface {} from dpn {} in vpn {} event ",
495                         interfaceName, dpnId, vpnName, error);
496             }
497
498             @Override
499             public void onSuccess(Object arg) {
500                 LOG.trace("publishInterfaceAddedToVpnNotification: Successful in notifying listeners for removing"
501                         + " interface {} from dpn {} in vpn {} event ", interfaceName, dpnId, vpnName);
502             }
503         }, MoreExecutors.directExecutor());
504     }
505
506     /**
507      * JobCallback class is used as a future callback for main and rollback workers
508      * to handle success and failure.
509      */
510     private class DpnEnterExitVpnWorker implements FutureCallback<List<Void>> {
511         private final Logger log = LoggerFactory.getLogger(DpnEnterExitVpnWorker.class);
512         Uint64 dpnId;
513         String vpnName;
514         String rd;
515         boolean entered;
516
517         DpnEnterExitVpnWorker(Uint64 dpnId, String vpnName, String rd, boolean entered) {
518             this.entered = entered;
519             this.dpnId = dpnId;
520             this.vpnName = vpnName;
521             this.rd = rd;
522         }
523
524         /**
525          * This implies that all the future instances have returned success. -- TODO:
526          * Confirm this
527          */
528         @Override
529         public void onSuccess(List<Void> voids) {
530             if (entered) {
531                 publishAddNotification(dpnId, vpnName, rd);
532                 log.info("onSuccess: FootPrint established for vpn {} rd {} on dpn {}", vpnName, rd, dpnId);
533             } else {
534                 publishRemoveNotification(dpnId, vpnName, rd);
535                 log.info("onSuccess: FootPrint cleared for vpn {} rd {} on dpn {}", vpnName, rd, dpnId);
536             }
537         }
538
539         /**
540          * This method is used to handle failure callbacks. If more retry needed, the
541          * retrycount is decremented and mainworker is executed again. After retries
542          * completed, rollbackworker is executed. If rollbackworker fails, this is a
543          * double-fault. Double fault is logged and ignored.
544          */
545         @Override
546         public void onFailure(Throwable throwable) {
547             log.info("onFailure: Failed to establish/clear footprint for vpn {} rd {} on dpn {} ", vpnName, rd, dpnId,
548                     throwable);
549         }
550     }
551
552     boolean isVpnFootPrintCleared(VpnInstanceOpDataEntry vpnInstanceOpData) {
553         return vpnInstanceOpData.getVpnToDpnList() == null || vpnInstanceOpData.getVpnToDpnList().isEmpty();
554     }
555 }