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