d6223b631a4f87da92b010a4437ac0ffd42fa8b1
[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 (vpnId.longValue() == VpnConstants.INVALID_ID) {
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     private void createOrUpdateVpnToDpnListForInterfaceName(Uint32 vpnId, String primaryRd, Uint64 dpnId,
129             String intfName, String vpnName) {
130         AtomicBoolean newDpnOnVpn = new AtomicBoolean(false);
131         /* Starts synchronized block. This ensures only one reader/writer get access to vpn-dpn-list
132          * The future.get ensures that the write to the datastore is complete before leaving the synchronized block.
133          */
134         // FIXME: separate this out somehow?
135         final ReentrantLock lock = JvmGlobalLocks.getLockForString(vpnName);
136         lock.lock();
137         try {
138             ListenableFuture<Void> future = txRunner.callWithNewReadWriteTransactionAndSubmit(tx -> {
139                 InstanceIdentifier<VpnToDpnList> id = VpnHelper.getVpnToDpnListIdentifier(primaryRd, dpnId);
140                 VpnInterfaces vpnInterface = new VpnInterfacesBuilder().setInterfaceName(intfName).build();
141                 Optional<VpnToDpnList> dpnInVpn = tx.read(LogicalDatastoreType.OPERATIONAL, id).checkedGet();
142                 if (dpnInVpn.isPresent()) {
143                     VpnToDpnList vpnToDpnList = dpnInVpn.get();
144                     List<VpnInterfaces> vpnInterfaces = vpnToDpnList.getVpnInterfaces();
145                     if (vpnInterfaces == null) {
146                         vpnInterfaces = new ArrayList<>();
147                     }
148                     vpnInterfaces.add(vpnInterface);
149                     VpnToDpnListBuilder vpnToDpnListBuilder = new VpnToDpnListBuilder(vpnToDpnList);
150                     vpnToDpnListBuilder.setDpnState(VpnToDpnList.DpnState.Active).setVpnInterfaces(vpnInterfaces);
151
152                     tx.put(LogicalDatastoreType.OPERATIONAL, id, vpnToDpnListBuilder.build(),
153                         WriteTransaction.CREATE_MISSING_PARENTS);
154                     /*
155                      * If earlier state was inactive, it is considered new DPN coming back to the
156                      * same VPN
157                      */
158                     if (vpnToDpnList.getDpnState() == VpnToDpnList.DpnState.Inactive) {
159                         newDpnOnVpn.set(true);
160                     }
161                     LOG.debug("createOrUpdateVpnToDpnList: Updating vpn footprint for vpn {} vpnId {} interface {}"
162                             + " on dpn {}", vpnName, vpnId, intfName, dpnId);
163                 } else {
164                     List<VpnInterfaces> vpnInterfaces = new ArrayList<>();
165                     vpnInterfaces.add(vpnInterface);
166                     VpnToDpnListBuilder vpnToDpnListBuilder = new VpnToDpnListBuilder().setDpnId(dpnId);
167                     vpnToDpnListBuilder.setDpnState(VpnToDpnList.DpnState.Active).setVpnInterfaces(vpnInterfaces);
168
169                     tx.put(LogicalDatastoreType.OPERATIONAL, id, vpnToDpnListBuilder.build(),
170                         WriteTransaction.CREATE_MISSING_PARENTS);
171                     newDpnOnVpn.set(true);
172                     LOG.debug("createOrUpdateVpnToDpnList: Creating vpn footprint for vpn {} vpnId {} interface {}"
173                             + " on dpn {}", vpnName, vpnId, intfName, dpnId);
174                 }
175             });
176             future.get();
177         } catch (InterruptedException | ExecutionException e) {
178             LOG.error("createOrUpdateVpnToDpnList: Error adding to dpnToVpnList for vpn {} vpnId {} interface {}"
179                     + " dpn {}", vpnName, vpnId, intfName, dpnId, e);
180             throw new RuntimeException(e.getMessage(), e);
181         } finally {
182             lock.unlock();
183         }
184         LOG.info("createOrUpdateVpnToDpnList: Created/Updated vpn footprint for vpn {} vpnId {} interfacName{}"
185                 + " on dpn {}", vpnName, vpnId, intfName, dpnId);
186         /*
187          * Informing the FIB only after writeTxn is submitted successfully.
188          */
189         if (newDpnOnVpn.get()) {
190             if (vpnUtil.isVlan(intfName)) {
191                 if (!vpnUtil.shouldPopulateFibForVlan(vpnName, null, dpnId)) {
192                     return;
193                 }
194             }
195             fibManager.populateFibOnNewDpn(dpnId, vpnId, primaryRd,
196                     new DpnEnterExitVpnWorker(dpnId, vpnName, primaryRd, true /* entered */));
197             LOG.info("createOrUpdateVpnToDpnList: Sent populateFib event for new dpn {} in VPN {} for interface {}",
198                     dpnId, vpnName, intfName);
199         }
200     }
201
202     private void createOrUpdateVpnToDpnListForIPAddress(Uint32 vpnId, String primaryRd, Uint64 dpnId,
203             ImmutablePair<IpAddresses.IpAddressSource, String> ipAddressSourceValuePair, String vpnName) {
204         AtomicBoolean newDpnOnVpn = new AtomicBoolean(false);
205         /* Starts synchronized block. This ensures only one reader/writer get access to vpn-dpn-list
206          * The future.get ensures that the write to the datastore is complete before leaving the synchronized block.
207          */
208         // FIXME: separate this out somehow?
209         final ReentrantLock lock = JvmGlobalLocks.getLockForString(vpnName);
210         lock.lock();
211         try {
212             ListenableFuture<Void> future = txRunner.callWithNewReadWriteTransactionAndSubmit(tx -> {
213                 InstanceIdentifier<VpnToDpnList> id = VpnHelper.getVpnToDpnListIdentifier(primaryRd, dpnId);
214                 IpAddressesBuilder ipAddressesBldr = new IpAddressesBuilder()
215                         .setIpAddressSource(ipAddressSourceValuePair.getKey());
216                 ipAddressesBldr.withKey(new IpAddressesKey(ipAddressSourceValuePair.getValue()));
217                 ipAddressesBldr.setIpAddress(ipAddressSourceValuePair.getValue());
218                 Optional<VpnToDpnList> dpnInVpn = tx.read(LogicalDatastoreType.OPERATIONAL, id).checkedGet();
219                 if (dpnInVpn.isPresent()) {
220                     VpnToDpnList vpnToDpnList = dpnInVpn.get();
221                     List<IpAddresses> ipAddresses = vpnToDpnList.getIpAddresses();
222                     if (ipAddresses == null) {
223                         ipAddresses = new ArrayList<>();
224                     }
225                     ipAddresses.add(ipAddressesBldr.build());
226                     VpnToDpnListBuilder vpnToDpnListBuilder = new VpnToDpnListBuilder(vpnToDpnList);
227                     vpnToDpnListBuilder.setDpnState(VpnToDpnList.DpnState.Active).setIpAddresses(ipAddresses);
228
229                     tx.put(LogicalDatastoreType.OPERATIONAL, id, vpnToDpnListBuilder.build(), true);
230                     /*
231                      * If earlier state was inactive, it is considered new DPN coming back to the
232                      * same VPN
233                      */
234                     if (vpnToDpnList.getDpnState() == VpnToDpnList.DpnState.Inactive) {
235                         newDpnOnVpn.set(true);
236                     }
237                 } else {
238                     List<IpAddresses> ipAddresses = new ArrayList<>();
239                     ipAddresses.add(ipAddressesBldr.build());
240                     VpnToDpnListBuilder vpnToDpnListBuilder = new VpnToDpnListBuilder().setDpnId(dpnId);
241                     vpnToDpnListBuilder.setDpnState(VpnToDpnList.DpnState.Active).setIpAddresses(ipAddresses);
242                     tx.put(LogicalDatastoreType.OPERATIONAL, id, vpnToDpnListBuilder.build(), true);
243                     newDpnOnVpn.set(true);
244                 }
245
246             });
247             future.get();
248         } catch (InterruptedException | ExecutionException e) {
249             LOG.error("createOrUpdateVpnToDpnListForIPAddress: Error adding to dpnToVpnList for vpn {}"
250                     + " ipAddresses {} dpn {}", vpnName, ipAddressSourceValuePair.getValue(), dpnId, e);
251             throw new RuntimeException(e.getMessage(), e); //TODO: Avoid this
252         } finally {
253             lock.unlock();
254         }
255         /*
256          * Informing the Fib only after writeTxn is submitted successfuly.
257          */
258         if (newDpnOnVpn.get()) {
259             LOG.debug("Sending populateFib event for new dpn {} in VPN {}", dpnId, vpnName);
260             fibManager.populateFibOnNewDpn(dpnId, vpnId, primaryRd,
261                     new DpnEnterExitVpnWorker(dpnId, vpnName, primaryRd, true /* entered */));
262         }
263     }
264
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 = dpnInVpn.getVpnInterfaces();
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     private void removeOrUpdateVpnToDpnListForIpAddress(Uint32 vpnId, String rd, Uint64 dpnId,
341             ImmutablePair<IpAddresses.IpAddressSource, String> ipAddressSourceValuePair, String vpnName) {
342         AtomicBoolean lastDpnOnVpn = new AtomicBoolean(false);
343         /* Starts synchronized block. This ensures only one reader/writer get access to vpn-dpn-list
344          * The future.get ensures that the write to the datastore is complete before leaving the synchronized block.
345          */
346         // FIXME: separate this out somehow?
347         final ReentrantLock lock = JvmGlobalLocks.getLockForString(vpnName);
348         lock.lock();
349         try {
350             ListenableFuture<Void> future = txRunner.callWithNewReadWriteTransactionAndSubmit(tx -> {
351                 InstanceIdentifier<VpnToDpnList> id = VpnHelper.getVpnToDpnListIdentifier(rd, dpnId);
352
353                 Optional<VpnToDpnList> dpnInVpnOpt = tx.read(LogicalDatastoreType.OPERATIONAL, id)
354                         .checkedGet();
355                 if (!dpnInVpnOpt.isPresent()) {
356                     LOG.error("removeOrUpdateVpnToDpnList: Could not find DpnToVpn map for VPN=[name={} "
357                             + "rd={} id={}] and dpnId={}", vpnName, rd, id, dpnId);
358                     return;
359                 }
360                 VpnToDpnList dpnInVpn = dpnInVpnOpt.get();
361                 List<IpAddresses> ipAddresses = dpnInVpn.getIpAddresses();
362                 if (ipAddresses == null) {
363                     LOG.info("Could not find ipAddresses for DpnInVpn map for VPN=[name={} rd={} id={}] "
364                             + "and dpnId={}", vpnName, rd, id, dpnId);
365                     return;
366                 }
367
368                 IpAddresses currIpAddress = new IpAddressesBuilder()
369                         .withKey(new IpAddressesKey(ipAddressSourceValuePair.getValue()))
370                         .setIpAddressSource(ipAddressSourceValuePair.getKey()).build();
371                 if (ipAddresses.remove(currIpAddress)) {
372                     if (ipAddresses.isEmpty()) {
373                         List<VpnInterfaces> vpnInterfaces = dpnInVpn.getVpnInterfaces();
374                         VpnToDpnListBuilder dpnInVpnBuilder =
375                                 new VpnToDpnListBuilder(dpnInVpn).setIpAddresses(null);
376                         if (vpnInterfaces == null || vpnInterfaces.isEmpty()) {
377                             dpnInVpnBuilder.setDpnState(VpnToDpnList.DpnState.Inactive);
378                             lastDpnOnVpn.set(true);
379                         } else {
380                             LOG.warn("ip addresses are empty but vpn interfaces are present for the vpn {} in "
381                                     + "dpn {}", vpnName, dpnId);
382                         }
383                         tx.put(LogicalDatastoreType.OPERATIONAL, id, dpnInVpnBuilder.build(), true);
384
385                     } else {
386                         tx.delete(LogicalDatastoreType.OPERATIONAL, id.child(IpAddresses.class,
387                             new IpAddressesKey(ipAddressSourceValuePair.getValue())));
388                     }
389                 }
390
391             });
392             future.get();
393         } catch (InterruptedException | ExecutionException e) {
394             LOG.error("Error removing from dpnToVpnList for vpn {} Ipaddress {} dpn {}", vpnName,
395                 ipAddressSourceValuePair.getValue(), dpnId, e);
396             throw new RuntimeException(e.getMessage(), e); //TODO: Avoid this
397         } finally {
398             lock.unlock();
399         }
400
401         if (lastDpnOnVpn.get()) {
402             LOG.debug("Sending cleanup event for dpn {} in VPN {}", dpnId, vpnName);
403             fibManager.cleanUpDpnForVpn(dpnId, vpnId, rd,
404                     new DpnEnterExitVpnWorker(dpnId, vpnName, rd, false /* exited */));
405         }
406     }
407
408     @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
409             justification = "https://github.com/spotbugs/spotbugs/issues/811")
410     private void publishAddNotification(final Uint64 dpnId, final String vpnName, final String rd) {
411         LOG.debug("publishAddNotification: Sending notification for add dpn {} in vpn {} rd {} event ", dpnId, vpnName,
412                 rd);
413         AddEventData data = new AddEventDataBuilder().setVpnName(vpnName).setRd(rd).setDpnId(dpnId).build();
414         AddDpnEvent event = new AddDpnEventBuilder().setAddEventData(data).build();
415         final ListenableFuture<?> eventFuture = notificationPublishService.offerNotification(event);
416         Futures.addCallback(eventFuture, new FutureCallback<Object>() {
417             @Override
418             public void onFailure(Throwable error) {
419                 LOG.error("publishAddNotification: Error in notifying listeners for add dpn {} in vpn {} rd {} event ",
420                         dpnId, vpnName, rd, error);
421             }
422
423             @Override
424             public void onSuccess(Object arg) {
425                 LOG.info("publishAddNotification: Successful in notifying listeners for add dpn {} in vpn {} rd {}"
426                         + " event ", dpnId, vpnName, rd);
427             }
428         }, MoreExecutors.directExecutor());
429     }
430
431     @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
432                justification = "https://github.com/spotbugs/spotbugs/issues/811")
433     private void publishRemoveNotification(final Uint64 dpnId, final String vpnName, final String rd) {
434         LOG.debug("publishRemoveNotification: Sending notification for remove dpn {} in vpn {} rd {} event ", dpnId,
435                 vpnName, rd);
436         RemoveEventData data = new RemoveEventDataBuilder().setVpnName(vpnName).setRd(rd).setDpnId(dpnId).build();
437         RemoveDpnEvent event = new RemoveDpnEventBuilder().setRemoveEventData(data).build();
438         final ListenableFuture<?> eventFuture = notificationPublishService.offerNotification(event);
439         Futures.addCallback(eventFuture, new FutureCallback<Object>() {
440             @Override
441             public void onFailure(Throwable error) {
442                 LOG.error("publishRemoveNotification: Error in notifying listeners for remove dpn {} in vpn {} rd {}"
443                         + " event ", dpnId, vpnName, rd, error);
444             }
445
446             @Override
447             public void onSuccess(Object arg) {
448                 LOG.info("publishRemoveNotification: Successful in notifying listeners for remove dpn {} in vpn {}"
449                         + " rd {} event ", dpnId, vpnName, rd);
450             }
451         }, MoreExecutors.directExecutor());
452     }
453
454     private void publishInterfaceAddedToVpnNotification(String interfaceName, Uint64 dpnId, String vpnName,
455             Uint32 vpnId) {
456         LOG.debug("publishInterfaceAddedToVpnNotification: Sending notification for addition of interface {} on dpn {}"
457                 + " for vpn {}", interfaceName, dpnId, vpnName);
458         AddInterfaceEventData data = new AddInterfaceEventDataBuilder().setInterfaceName(interfaceName).setVpnId(vpnId)
459                 .setDpnId(dpnId).build();
460         AddInterfaceToDpnOnVpnEvent event = new AddInterfaceToDpnOnVpnEventBuilder().setAddInterfaceEventData(data)
461                 .build();
462         final ListenableFuture<?> eventFuture = notificationPublishService.offerNotification(event);
463         Futures.addCallback(eventFuture, new FutureCallback<Object>() {
464             @Override
465             public void onFailure(Throwable error) {
466                 LOG.warn("publishInterfaceAddedToVpnNotification: Error in notifying listeners for add interface {}"
467                         + " on dpn {} in vpn {} event ", interfaceName, dpnId, vpnName, error);
468             }
469
470             @Override
471             public void onSuccess(Object arg) {
472                 LOG.trace("publishInterfaceAddedToVpnNotification: Successful in notifying listeners for add"
473                         + " interface {} on dpn {} in vpn {} event ", interfaceName, dpnId, vpnName);
474             }
475         }, MoreExecutors.directExecutor());
476     }
477
478     private void publishInterfaceRemovedFromVpnNotification(String interfaceName, Uint64 dpnId, String vpnName,
479             Uint32 vpnId) {
480         LOG.debug("publishInterfaceAddedToVpnNotification: Sending notification for removal of interface {}"
481                 + " from dpn {} for vpn {}", interfaceName, dpnId, vpnName);
482         RemoveInterfaceEventData data = new RemoveInterfaceEventDataBuilder().setInterfaceName(interfaceName)
483                 .setVpnId(vpnId).setDpnId(dpnId).build();
484         RemoveInterfaceFromDpnOnVpnEvent event = new RemoveInterfaceFromDpnOnVpnEventBuilder()
485                 .setRemoveInterfaceEventData(data).build();
486         final ListenableFuture<?> eventFuture = notificationPublishService.offerNotification(event);
487         Futures.addCallback(eventFuture, new FutureCallback<Object>() {
488             @Override
489             public void onFailure(Throwable error) {
490                 LOG.warn(
491                         "publishInterfaceAddedToVpnNotification: Error in notifying listeners"
492                                 + " for removing interface {} from dpn {} in vpn {} event ",
493                         interfaceName, dpnId, vpnName, error);
494             }
495
496             @Override
497             public void onSuccess(Object arg) {
498                 LOG.trace("publishInterfaceAddedToVpnNotification: Successful in notifying listeners for removing"
499                         + " interface {} from dpn {} in vpn {} event ", interfaceName, dpnId, vpnName);
500             }
501         }, MoreExecutors.directExecutor());
502     }
503
504     /**
505      * JobCallback class is used as a future callback for main and rollback workers
506      * to handle success and failure.
507      */
508     private class DpnEnterExitVpnWorker implements FutureCallback<List<Void>> {
509         private final Logger log = LoggerFactory.getLogger(DpnEnterExitVpnWorker.class);
510         Uint64 dpnId;
511         String vpnName;
512         String rd;
513         boolean entered;
514
515         DpnEnterExitVpnWorker(Uint64 dpnId, String vpnName, String rd, boolean entered) {
516             this.entered = entered;
517             this.dpnId = dpnId;
518             this.vpnName = vpnName;
519             this.rd = rd;
520         }
521
522         /**
523          * This implies that all the future instances have returned success. -- TODO:
524          * Confirm this
525          */
526         @Override
527         public void onSuccess(List<Void> voids) {
528             if (entered) {
529                 publishAddNotification(dpnId, vpnName, rd);
530                 log.info("onSuccess: FootPrint established for vpn {} rd {} on dpn {}", vpnName, rd, dpnId);
531             } else {
532                 publishRemoveNotification(dpnId, vpnName, rd);
533                 log.info("onSuccess: FootPrint cleared for vpn {} rd {} on dpn {}", vpnName, rd, dpnId);
534             }
535         }
536
537         /**
538          * This method is used to handle failure callbacks. If more retry needed, the
539          * retrycount is decremented and mainworker is executed again. After retries
540          * completed, rollbackworker is executed. If rollbackworker fails, this is a
541          * double-fault. Double fault is logged and ignored.
542          */
543         @Override
544         public void onFailure(Throwable throwable) {
545             log.info("onFailure: Failed to establish/clear footprint for vpn {} rd {} on dpn {} ", vpnName, rd, dpnId,
546                     throwable);
547         }
548     }
549
550     boolean isVpnFootPrintCleared(VpnInstanceOpDataEntry vpnInstanceOpData) {
551         return vpnInstanceOpData.getVpnToDpnList() == null || vpnInstanceOpData.getVpnToDpnList().isEmpty();
552     }
553 }