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