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