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