TR: Local routes not appeared on deletion of VPN
[vpnservice.git] / vpnmanager / vpnmanager-impl / src / main / java / org / opendaylight / vpnservice / VpnManager.java
1 /*
2  * Copyright (c) 2015 - 2016 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 package org.opendaylight.vpnservice;
9
10 import java.util.ArrayList;
11 import java.util.Arrays;
12 import java.util.List;
13 import java.util.concurrent.*;
14
15 import com.google.common.util.concurrent.CheckedFuture;
16 import org.opendaylight.bgpmanager.api.IBgpManager;
17 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
18 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
19 import org.opendaylight.controller.md.sal.binding.api.NotificationService;
20 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
21 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
22 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
23 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.af.config.vpntargets.VpnTarget;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnInstanceToVpnId;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnInstanceToVpnIdBuilder;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnRouteList;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntryBuilder;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntryKey;
30 import org.opendaylight.yangtools.concepts.ListenerRegistration;
31 import org.opendaylight.yangtools.yang.binding.DataObject;
32 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
33 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
34 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
35 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnAfConfig;
36 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfaces;
37 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstance;
38 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstanceKey;
39 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInstances;
40 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstanceBuilder;
41 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnInstanceOpData;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnInstanceOpDataBuilder;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fibmanager.rev150330.FibEntries;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fibmanager.rev150330.fibentries.VrfTables;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fibmanager.rev150330.fibentries.VrfTablesKey;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fibmanager.rev150330.vrfentries.VrfEntry;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.IdManagerService;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51
52 import com.google.common.base.Optional;
53 import com.google.common.util.concurrent.FutureCallback;
54 import com.google.common.util.concurrent.Futures;
55
56 public class VpnManager extends AbstractDataChangeListener<VpnInstance> implements AutoCloseable {
57     private static final Logger LOG = LoggerFactory.getLogger(VpnManager.class);
58     private ListenerRegistration<DataChangeListener> listenerRegistration, fibListenerRegistration, opListenerRegistration;
59     private ConcurrentMap<String, Runnable> vpnOpMap = new ConcurrentHashMap<String, Runnable>();
60     private ExecutorService executorService = Executors.newSingleThreadExecutor();
61     private final DataBroker broker;
62     private final IBgpManager bgpManager;
63     private IdManagerService idManager;
64     private VpnInterfaceManager vpnInterfaceManager;
65     private final FibEntriesListener fibListener;
66     private final VpnInstanceOpListener vpnInstOpListener;
67     private NotificationService notificationService;
68
69     private static final FutureCallback<Void> DEFAULT_CALLBACK =
70             new FutureCallback<Void>() {
71                 public void onSuccess(Void result) {
72                     LOG.debug("Success in Datastore operation");
73                 }
74
75                 public void onFailure(Throwable error) {
76                     LOG.error("Error in Datastore operation", error);
77                 };
78             };
79
80     /**
81      * Listens for data change related to VPN Instance
82      * Informs the BGP about VRF information
83      *
84      * @param db - dataBroker reference
85      */
86     public VpnManager(final DataBroker db, final IBgpManager bgpManager) {
87         super(VpnInstance.class);
88         broker = db;
89         this.bgpManager = bgpManager;
90         this.fibListener = new FibEntriesListener();
91         this.vpnInstOpListener = new VpnInstanceOpListener();
92         registerListener(db);
93     }
94
95     private void registerListener(final DataBroker db) {
96         try {
97             listenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
98                     getWildCardPath(), VpnManager.this, DataChangeScope.SUBTREE);
99             fibListenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL,
100                     getFibEntryListenerPath(), fibListener, DataChangeScope.BASE);
101             opListenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL,
102                     getVpnInstanceOpListenerPath(), vpnInstOpListener, DataChangeScope.SUBTREE);
103
104         } catch (final Exception e) {
105             LOG.error("VPN Service DataChange listener registration fail !", e);
106             throw new IllegalStateException("VPN Service registration Listener failed.", e);
107         }
108     }
109
110     public void setIdManager(IdManagerService idManager) {
111         this.idManager = idManager;
112     }
113
114     public void setVpnInterfaceManager(VpnInterfaceManager vpnInterfaceManager) {
115         this.vpnInterfaceManager = vpnInterfaceManager;
116     }
117
118     private void waitForOpDataRemoval(String id, long timeout) {
119         //wait till DCN for update on VPN Instance Op Data signals that vpn interfaces linked to this vpn instance is zero
120         Runnable notifyTask = new VpnNotifyTask();
121         synchronized (id.intern()) {
122             vpnOpMap.put(id, notifyTask);
123             synchronized (notifyTask) {
124                 try {
125                     notifyTask.wait(timeout);
126                 } catch (InterruptedException e) {
127                 }
128             }
129         }
130
131     }
132
133     @Override
134     protected void remove(InstanceIdentifier<VpnInstance> identifier, VpnInstance del) {
135         LOG.trace("Remove VPN event key: {}, value: {}", identifier, del);
136         String vpnName = del.getVpnInstanceName();
137
138         String rd = del.getIpv4Family().getRouteDistinguisher();
139
140 //        //Clean up vpn Interface
141 //        InstanceIdentifier<VpnInterfaces> vpnInterfacesId = InstanceIdentifier.builder(VpnInterfaces.class).build();
142 //        Optional<VpnInterfaces> optionalVpnInterfaces = read(LogicalDatastoreType.OPERATIONAL, vpnInterfacesId);
143 //
144 //        if(optionalVpnInterfaces.isPresent()) {
145 //            List<VpnInterface> vpnInterfaces = optionalVpnInterfaces.get().getVpnInterface();
146 //            for(VpnInterface vpnInterface : vpnInterfaces) {
147 //                if(vpnInterface.getVpnInstanceName().equals(vpnName)) {
148 //                    LOG.debug("VpnInterface {} will be removed from VPN {}", vpnInterface.getName(), vpnName);
149 //                    vpnInterfaceManager.remove(
150 //                            VpnUtil.getVpnInterfaceIdentifier(vpnInterface.getName()), vpnInterface);
151 //                }
152 //            }
153 //        }
154
155         //TODO(vpnteam): Entire code would need refactoring to listen only on the parent object - VPNInstance
156         Optional<VpnInstanceOpDataEntry> vpnOpValue = null;
157         if ((rd != null) && (!rd.isEmpty())) {
158             vpnOpValue = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL,
159                     VpnUtil.getVpnInstanceOpDataIdentifier(rd));
160         } else {
161             vpnOpValue = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL,
162                     VpnUtil.getVpnInstanceOpDataIdentifier(vpnName));
163         }
164
165         if ((vpnOpValue != null) && (vpnOpValue.isPresent())) {
166             VpnInstanceOpDataEntry vpnOpEntry = null;
167             long timeout = VpnConstants.MIN_WAIT_TIME_IN_MILLISECONDS;
168             Long intfCount = 0L;
169
170             vpnOpEntry = vpnOpValue.get();
171             intfCount = vpnOpEntry.getVpnInterfaceCount();
172             if (intfCount != null && intfCount > 0) {
173                 // Minimum wait time of 10 seconds for one VPN Interface clearance (inclusive of full trace on)
174                 timeout = intfCount * 10000;
175                 // Maximum wait time of 90 seconds for all VPN Interfaces clearance (inclusive of full trace on)
176                 if (timeout > VpnConstants.MAX_WAIT_TIME_IN_MILLISECONDS) {
177                     timeout = VpnConstants.MAX_WAIT_TIME_IN_MILLISECONDS;
178                 }
179                 LOG.trace("VPNInstance removal interface count at {} for for rd {}, vpnname {}",
180                         intfCount, rd, vpnName);
181             }
182             LOG.trace("VPNInstance removal thread waiting for {} seconds for rd {}, vpnname {}",
183                     (timeout/1000), rd, vpnName);
184
185             if ((rd != null)  && (!rd.isEmpty())) {
186                 waitForOpDataRemoval(rd, timeout);
187             } else {
188                 waitForOpDataRemoval(vpnName, timeout);
189             }
190
191             LOG.trace("Returned out of waiting for  Op Data removal for rd {}, vpnname {}", rd, vpnName);
192         }
193
194         InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance>
195                 vpnIdentifier = VpnUtil.getVpnInstanceToVpnIdIdentifier(vpnName);
196         delete(LogicalDatastoreType.CONFIGURATION, vpnIdentifier);
197         LOG.trace("Removed vpnIdentifier for  rd{} vpnname {}", rd, vpnName);
198         if (rd !=null) {
199
200             try {
201                 bgpManager.deleteVrf(rd);
202             } catch (Exception e) {
203                 LOG.error("Exception when removing VRF from BGP", e);
204             }
205
206             delete(LogicalDatastoreType.OPERATIONAL, VpnUtil.getVpnInstanceOpDataIdentifier(rd));
207         } else {
208
209             delete(LogicalDatastoreType.OPERATIONAL, VpnUtil.getVpnInstanceOpDataIdentifier(vpnName));
210         }
211
212         VpnUtil.releaseId(idManager, VpnConstants.VPN_IDPOOL_NAME, vpnName);
213     }
214
215     @Override
216     protected void update(InstanceIdentifier<VpnInstance> identifier,
217             VpnInstance original, VpnInstance update) {
218         LOG.trace("Update VPN event key: {}, value: {}", identifier, update);
219     }
220
221     @Override
222     protected void add(InstanceIdentifier<VpnInstance> identifier,
223             VpnInstance value) {
224         LOG.trace("Add VPN event key: {}, value: {}", identifier, value);
225         VpnAfConfig config = value.getIpv4Family();
226         String rd = config.getRouteDistinguisher();
227
228         long vpnId = VpnUtil.getUniqueId(idManager, VpnConstants.VPN_IDPOOL_NAME, value.getVpnInstanceName());
229         LOG.trace("VPN instance to ID generated.");
230         org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance
231             vpnInstanceToVpnId = VpnUtil.getVpnInstanceToVpnId(value.getVpnInstanceName(), vpnId,
232                                                                     (rd != null) ? rd : value.getVpnInstanceName());
233
234         syncWrite(LogicalDatastoreType.CONFIGURATION,
235                    VpnUtil.getVpnInstanceToVpnIdIdentifier(value.getVpnInstanceName()),
236                    vpnInstanceToVpnId, DEFAULT_CALLBACK);
237
238
239         if(rd == null) {
240             syncWrite(LogicalDatastoreType.OPERATIONAL,
241                     VpnUtil.getVpnInstanceOpDataIdentifier(value.getVpnInstanceName()),
242                     VpnUtil.getVpnInstanceOpDataBuilder(value.getVpnInstanceName(), vpnId), DEFAULT_CALLBACK);
243
244         } else {
245             syncWrite(LogicalDatastoreType.OPERATIONAL,
246                        VpnUtil.getVpnInstanceOpDataIdentifier(rd),
247                        VpnUtil.getVpnInstanceOpDataBuilder(rd, vpnId), DEFAULT_CALLBACK);
248
249             List<VpnTarget> vpnTargetList = config.getVpnTargets().getVpnTarget();
250
251             List<String> ertList = new ArrayList<String>();
252             List<String> irtList = new ArrayList<String>();
253
254             for (VpnTarget vpnTarget : vpnTargetList) {
255                 if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.ExportExtcommunity) {
256                     ertList.add(vpnTarget.getVrfRTValue());
257                 }
258                 if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.ImportExtcommunity) {
259                     irtList.add(vpnTarget.getVrfRTValue());
260                 }
261                 if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.Both) {
262                     ertList.add(vpnTarget.getVrfRTValue());
263                     irtList.add(vpnTarget.getVrfRTValue());
264                 }
265             }
266
267             try {
268                 bgpManager.addVrf(rd, irtList, ertList);
269             } catch(Exception e) {
270                 LOG.error("Exception when adding VRF to BGP", e);
271             }
272         }
273         //Try to add up vpn Interfaces if already in Operational Datastore
274         InstanceIdentifier<VpnInterfaces> vpnInterfacesId = InstanceIdentifier.builder(VpnInterfaces.class).build();
275         Optional<VpnInterfaces> optionalVpnInterfaces = read(LogicalDatastoreType.CONFIGURATION, vpnInterfacesId);
276
277         if(optionalVpnInterfaces.isPresent()) {
278             List<VpnInterface> vpnInterfaces = optionalVpnInterfaces.get().getVpnInterface();
279             for(VpnInterface vpnInterface : vpnInterfaces) {
280                 if(vpnInterface.getVpnInstanceName().equals(value.getVpnInstanceName())) {
281                     LOG.debug("VpnInterface {} will be added from VPN {}", vpnInterface.getName(), value.getVpnInstanceName());
282                     vpnInterfaceManager.add(
283                                 VpnUtil.getVpnInterfaceIdentifier(vpnInterface.getName()), vpnInterface);
284
285                 }
286             }
287         }
288     }
289
290     private InstanceIdentifier<?> getWildCardPath() {
291         return InstanceIdentifier.create(VpnInstances.class).child(VpnInstance.class);
292     }
293
294     private InstanceIdentifier<?> getFibEntryListenerPath() {
295         return InstanceIdentifier.create(FibEntries.class).child(VrfTables.class)
296                 .child(VrfEntry.class);
297     }
298
299     private InstanceIdentifier<?> getVpnInstanceOpListenerPath() {
300         return InstanceIdentifier.create(VpnInstanceOpData.class).child(VpnInstanceOpDataEntry.class);
301
302     }
303
304     @Override
305     public void close() throws Exception {
306         if (listenerRegistration != null) {
307             try {
308                 listenerRegistration.close();
309             } catch (final Exception e) {
310                 LOG.error("Error when cleaning up Vpn DataChangeListener.", e);
311             }
312             listenerRegistration = null;
313         }
314         if (fibListenerRegistration != null) {
315             try {
316                 fibListenerRegistration.close();
317             } catch (final Exception e) {
318                 LOG.error("Error when cleaning up Fib entries DataChangeListener.", e);
319             }
320             fibListenerRegistration = null;
321         }
322         if (opListenerRegistration != null) {
323             try {
324                 opListenerRegistration.close();
325             } catch (final Exception e) {
326                 LOG.error("Error when cleaning up VPN Instance Operational entries DataChangeListener.", e);
327             }
328             opListenerRegistration = null;
329         }
330
331         LOG.trace("VPN Manager Closed");
332     }
333
334     private <T extends DataObject> Optional<T> read(LogicalDatastoreType datastoreType,
335             InstanceIdentifier<T> path) {
336
337         ReadOnlyTransaction tx = broker.newReadOnlyTransaction();
338
339         Optional<T> result = Optional.absent();
340         try {
341             result = tx.read(datastoreType, path).get();
342         } catch (Exception e) {
343             throw new RuntimeException(e);
344         }
345
346         return result;
347     }
348
349     private <T extends DataObject> void asyncWrite(LogicalDatastoreType datastoreType,
350             InstanceIdentifier<T> path, T data, FutureCallback<Void> callback) {
351         WriteTransaction tx = broker.newWriteOnlyTransaction();
352         tx.put(datastoreType, path, data, true);
353         Futures.addCallback(tx.submit(), callback);
354     }
355
356     private <T extends DataObject> void syncWrite(LogicalDatastoreType datastoreType,
357                                                    InstanceIdentifier<T> path, T data, FutureCallback<Void> callback) {
358         WriteTransaction tx = broker.newWriteOnlyTransaction();
359         tx.put(datastoreType, path, data, true);
360         CheckedFuture<Void, TransactionCommitFailedException> futures = tx.submit();
361         try {
362             futures.get();
363         } catch (InterruptedException | ExecutionException e) {
364             LOG.error("Error writing VPN instance to ID info to datastore (path, data) : ({}, {})", path, data);
365             throw new RuntimeException(e.getMessage());
366         }
367     }
368
369     protected VpnInstanceOpDataEntry getVpnInstanceOpData(String rd) {
370         InstanceIdentifier<VpnInstanceOpDataEntry> id = VpnUtil.getVpnInstanceOpDataIdentifier(rd);
371         Optional<VpnInstanceOpDataEntry> vpnInstanceOpData = read(LogicalDatastoreType.OPERATIONAL, id);
372         if(vpnInstanceOpData.isPresent()) {
373             return vpnInstanceOpData.get();
374         }
375         return null;
376     }
377
378     private <T extends DataObject> void delete(LogicalDatastoreType datastoreType, InstanceIdentifier<T> path) {
379         WriteTransaction tx = broker.newWriteOnlyTransaction();
380         tx.delete(datastoreType, path);
381         Futures.addCallback(tx.submit(), DEFAULT_CALLBACK);
382     }
383
384     private class FibEntriesListener extends AbstractDataChangeListener<VrfEntry>  {
385
386         public FibEntriesListener() {
387             super(VrfEntry.class);
388         }
389
390         @Override
391         protected void remove(InstanceIdentifier<VrfEntry> identifier,
392                 VrfEntry del) {
393             LOG.trace("Remove Fib event - Key : {}, value : {} ", identifier, del);
394             final VrfTablesKey key = identifier.firstKeyOf(VrfTables.class, VrfTablesKey.class);
395             String rd = key.getRouteDistinguisher();
396             Long label = del.getLabel();
397             VpnInstanceOpDataEntry vpnInstanceOpData = getVpnInstanceOpData(rd);
398             if(vpnInstanceOpData != null) {
399                 List<Long> routeIds = vpnInstanceOpData.getRouteEntryId();
400                 if(routeIds == null) {
401                     LOG.debug("Fib Route entry is empty.");
402                     return;
403                 }
404                 LOG.debug("Removing label from vpn info - {}", label);
405                 routeIds.remove(label);
406                 asyncWrite(LogicalDatastoreType.OPERATIONAL, VpnUtil.getVpnInstanceOpDataIdentifier(rd),
407                            new VpnInstanceOpDataEntryBuilder(vpnInstanceOpData).setRouteEntryId(routeIds).build(), DEFAULT_CALLBACK);
408             } else {
409                 LOG.warn("No VPN Instance found for RD: {}", rd);
410             }
411         }
412
413         @Override
414         protected void update(InstanceIdentifier<VrfEntry> identifier,
415                 VrfEntry original, VrfEntry update) {
416             // TODO Auto-generated method stub
417
418         }
419
420         @Override
421         protected void add(InstanceIdentifier<VrfEntry> identifier,
422                            VrfEntry add) {
423             LOG.trace("Add Vrf Entry event - Key : {}, value : {}", identifier, add);
424             final VrfTablesKey key = identifier.firstKeyOf(VrfTables.class, VrfTablesKey.class);
425             String rd = key.getRouteDistinguisher();
426             Long label = add.getLabel();
427             VpnInstanceOpDataEntry vpn = getVpnInstanceOpData(rd);
428             if(vpn != null) {
429                 List<Long> routeIds = vpn.getRouteEntryId();
430                 if(routeIds == null) {
431                     routeIds = new ArrayList<>();
432                 }
433                 LOG.debug("Adding label to vpn info - {}", label);
434                 routeIds.add(label);
435                 asyncWrite(LogicalDatastoreType.OPERATIONAL, VpnUtil.getVpnInstanceOpDataIdentifier(rd),
436                            new VpnInstanceOpDataEntryBuilder(vpn).setRouteEntryId(routeIds).build(), DEFAULT_CALLBACK);
437             } else {
438                 LOG.warn("No VPN Instance found for RD: {}", rd);
439             }
440         }
441     }
442
443     class VpnInstanceOpListener extends org.opendaylight.vpnservice.mdsalutil.AbstractDataChangeListener<VpnInstanceOpDataEntry> {
444
445         public VpnInstanceOpListener() {
446             super(VpnInstanceOpDataEntry.class);
447         }
448
449         @Override
450         protected void remove(InstanceIdentifier<VpnInstanceOpDataEntry> identifier, VpnInstanceOpDataEntry del) {
451
452         }
453
454         @Override
455         protected void update(InstanceIdentifier<VpnInstanceOpDataEntry> identifier, VpnInstanceOpDataEntry original, VpnInstanceOpDataEntry update) {
456             final VpnInstanceOpDataEntryKey key = identifier.firstKeyOf(VpnInstanceOpDataEntry.class, VpnInstanceOpDataEntryKey.class);
457             String vpnName = key.getVrfId();
458
459             LOG.trace("VpnInstanceOpListener update: vpn name {} interface count in Old VpnOp Instance {} in New VpnOp Instance {}" ,
460                             vpnName, original.getVpnInterfaceCount(), update.getVpnInterfaceCount() );
461
462             //if((original.getVpnToDpnList().size() != update.getVpnToDpnList().size()) && (update.getVpnToDpnList().size() == 0)) {
463             if((original.getVpnInterfaceCount() != update.getVpnInterfaceCount()) && (update.getVpnInterfaceCount() == 0)) {
464                 notifyTaskIfRequired(vpnName);
465             }
466         }
467
468         private void notifyTaskIfRequired(String vpnName) {
469             Runnable notifyTask = vpnOpMap.remove(vpnName);
470             if (notifyTask == null) {
471                 return;
472             }
473             executorService.execute(notifyTask);
474         }
475
476         @Override
477         protected void add(InstanceIdentifier<VpnInstanceOpDataEntry> identifier, VpnInstanceOpDataEntry add) {
478         }
479     }
480 }