Fix CSIT failures
[netvirt.git] / vpnservice / vpnmanager / vpnmanager-impl / src / main / java / org / opendaylight / netvirt / 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.netvirt;
9
10 import java.util.ArrayList;
11 import java.util.List;
12 import java.util.concurrent.*;
13
14 import com.google.common.util.concurrent.CheckedFuture;
15 import org.opendaylight.bgpmanager.api.IBgpManager;
16 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
17 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
18 import org.opendaylight.controller.md.sal.binding.api.NotificationService;
19 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
20 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
21 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
22 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.af.config.vpntargets.VpnTarget;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnInstanceToVpnId;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnInstanceToVpnIdBuilder;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnRouteList;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntryBuilder;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntryKey;
29 import org.opendaylight.yangtools.concepts.ListenerRegistration;
30 import org.opendaylight.yangtools.yang.binding.DataObject;
31 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
32 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
33 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
34 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnAfConfig;
35 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfaces;
36 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstance;
37 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstanceKey;
38 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInstances;
39 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstanceBuilder;
40 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnInstanceOpData;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnInstanceOpDataBuilder;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fibmanager.rev150330.FibEntries;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fibmanager.rev150330.fibentries.VrfTables;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fibmanager.rev150330.fibentries.VrfTablesKey;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fibmanager.rev150330.vrfentries.VrfEntry;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.IdManagerService;
48 import org.opendaylight.fibmanager.api.IFibManager;
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 waitForOpRemoval(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             try {
123                 vpnOpMap.put(id, notifyTask);
124                 synchronized (notifyTask) {
125                     try {
126                         notifyTask.wait(timeout);
127                     } catch (InterruptedException e) {
128                     }
129                 }
130             } finally {
131                 vpnOpMap.remove(id);
132             }
133         }
134
135     }
136
137     @Override
138     protected void remove(InstanceIdentifier<VpnInstance> identifier, VpnInstance del) {
139         LOG.trace("Remove VPN event key: {}, value: {}", identifier, del);
140         String vpnName = del.getVpnInstanceName();
141         String rd = del.getIpv4Family().getRouteDistinguisher();
142         long vpnId = VpnUtil.getVpnId(broker, vpnName);
143
144         //TODO(vpnteam): Entire code would need refactoring to listen only on the parent object - VPNInstance
145         Optional<VpnInstanceOpDataEntry> vpnOpValue = null;
146         if ((rd != null) && (!rd.isEmpty())) {
147             vpnOpValue = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL,
148                     VpnUtil.getVpnInstanceOpDataIdentifier(rd));
149         } else {
150             vpnOpValue = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL,
151                     VpnUtil.getVpnInstanceOpDataIdentifier(vpnName));
152         }
153
154         if ((vpnOpValue != null) && (vpnOpValue.isPresent())) {
155             VpnInstanceOpDataEntry vpnOpEntry = null;
156             long timeout = VpnConstants.MIN_WAIT_TIME_IN_MILLISECONDS;
157             Long intfCount = 0L;
158
159             vpnOpEntry = vpnOpValue.get();
160             intfCount = vpnOpEntry.getVpnInterfaceCount();
161             if (intfCount != null && intfCount > 0) {
162                 // Minimum wait time of 10 seconds for one VPN Interface clearance (inclusive of full trace on)
163                 timeout = intfCount * 10000;
164                 // Maximum wait time of 90 seconds for all VPN Interfaces clearance (inclusive of full trace on)
165                 if (timeout > VpnConstants.MAX_WAIT_TIME_IN_MILLISECONDS) {
166                     timeout = VpnConstants.MAX_WAIT_TIME_IN_MILLISECONDS;
167                 }
168                 LOG.trace("VPNInstance removal count of interface at {} for for rd {}, vpnname {}",
169                         intfCount, rd, vpnName);
170             }
171             LOG.trace("VPNInstance removal thread waiting for {} seconds for rd {}, vpnname {}",
172                     (timeout/1000), rd, vpnName);
173
174             if ((rd != null)  && (!rd.isEmpty())) {
175                 waitForOpRemoval(rd, timeout);
176             } else {
177                 waitForOpRemoval(vpnName, timeout);
178             }
179
180             LOG.trace("Returned out of waiting for  Op Data removal for rd {}, vpnname {}", rd, vpnName);
181         }
182         // Clean up VpnInstanceToVpnId from Config DS
183         VpnUtil.removeVpnInstanceToVpnId(broker, vpnName);
184         LOG.trace("Removed vpnIdentifier for  rd{} vpnname {}", rd, vpnName);
185         if (rd != null) {
186             try {
187                 bgpManager.deleteVrf(rd);
188             } catch (Exception e) {
189                 LOG.error("Exception when removing VRF from BGP for RD {} in VPN {} exception " + e, rd, vpnName);
190             }
191
192             // Clean up VPNExtraRoutes Operational DS
193             VpnUtil.removeVpnExtraRouteForVpn(broker, rd);
194
195             // Clean up VPNInstanceOpDataEntry
196             VpnUtil.removeVpnOpInstance(broker, rd);
197         } else {
198             // Clean up FIB Entries Config DS
199             VpnUtil.removeVrfTableForVpn(broker, vpnName);
200
201             // Clean up VPNExtraRoutes Operational DS
202             VpnUtil.removeVpnExtraRouteForVpn(broker, vpnName);
203
204             // Clean up VPNInstanceOpDataEntry
205             VpnUtil.removeVpnOpInstance(broker, vpnName);
206         }
207
208         // Clean up PrefixToInterface Operational DS
209         VpnUtil.removePrefixToInterfaceForVpnId(broker, vpnId);
210
211         // Clean up L3NextHop Operational DS
212         VpnUtil.removeL3nexthopForVpnId(broker, vpnId);
213
214         // Release the ID used for this VPN back to IdManager
215
216         VpnUtil.releaseId(idManager, VpnConstants.VPN_IDPOOL_NAME, vpnName);
217     }
218
219     @Override
220     protected void update(InstanceIdentifier<VpnInstance> identifier,
221             VpnInstance original, VpnInstance update) {
222         LOG.trace("Update VPN event key: {}, value: {}", identifier, update);
223     }
224
225     @Override
226     protected void add(InstanceIdentifier<VpnInstance> identifier,
227             VpnInstance value) {
228         LOG.trace("Add VPN event key: {}, value: {}", identifier, value);
229         VpnAfConfig config = value.getIpv4Family();
230         String rd = config.getRouteDistinguisher();
231
232         long vpnId = VpnUtil.getUniqueId(idManager, VpnConstants.VPN_IDPOOL_NAME, value.getVpnInstanceName());
233         LOG.trace("VPN instance to ID generated.");
234         org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance
235             vpnInstanceToVpnId = VpnUtil.getVpnInstanceToVpnId(value.getVpnInstanceName(), vpnId,
236                                                                     (rd != null) ? rd : value.getVpnInstanceName());
237
238         syncWrite(LogicalDatastoreType.CONFIGURATION,
239                    VpnUtil.getVpnInstanceToVpnIdIdentifier(value.getVpnInstanceName()),
240                    vpnInstanceToVpnId, DEFAULT_CALLBACK);
241
242         IFibManager fibManager = vpnInterfaceManager.getFibManager();
243         try {
244             String cachedTransType = fibManager.getReqTransType();
245             LOG.trace("Value for confTransportType is " + cachedTransType);
246             if (cachedTransType.equals("Invalid")) {
247                 try {
248                     fibManager.setConfTransType("L3VPN", "VXLAN");
249                     LOG.trace("setting it to vxlan now");
250                 } catch (Exception e) {
251                     LOG.trace("Exception caught setting the cached value for transportType");
252                     LOG.error(e.getMessage());
253                 }
254             } else {
255                 LOG.trace(":cached val is neither unset/invalid. NO-op.");
256             }
257         } catch (Exception e) {
258             System.out.println("Exception caught accessing the cached value for transportType");
259             LOG.error(e.getMessage());
260         }
261
262         if(rd == null) {
263             VpnInstanceOpDataEntryBuilder builder = new VpnInstanceOpDataEntryBuilder();
264             builder.setVrfId(value.getVpnInstanceName()).setVpnId(vpnId);
265             builder.setVpnInterfaceCount(0L);
266             syncWrite(LogicalDatastoreType.OPERATIONAL,
267                     VpnUtil.getVpnInstanceOpDataIdentifier(value.getVpnInstanceName()),
268                     builder.build(), DEFAULT_CALLBACK);
269
270         } else {
271             VpnInstanceOpDataEntryBuilder builder = new VpnInstanceOpDataEntryBuilder();
272             builder.setVrfId(rd).setVpnId(vpnId);
273             builder.setVpnInterfaceCount(0L);
274             syncWrite(LogicalDatastoreType.OPERATIONAL,
275                        VpnUtil.getVpnInstanceOpDataIdentifier(rd),
276                        builder.build(), DEFAULT_CALLBACK);
277
278             List<VpnTarget> vpnTargetList = config.getVpnTargets().getVpnTarget();
279
280             List<String> ertList = new ArrayList<String>();
281             List<String> irtList = new ArrayList<String>();
282
283             for (VpnTarget vpnTarget : vpnTargetList) {
284                 if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.ExportExtcommunity) {
285                     ertList.add(vpnTarget.getVrfRTValue());
286                 }
287                 if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.ImportExtcommunity) {
288                     irtList.add(vpnTarget.getVrfRTValue());
289                 }
290                 if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.Both) {
291                     ertList.add(vpnTarget.getVrfRTValue());
292                     irtList.add(vpnTarget.getVrfRTValue());
293                 }
294             }
295
296             try {
297                 bgpManager.addVrf(rd, irtList, ertList);
298             } catch(Exception e) {
299                 LOG.error("Exception when adding VRF to BGP", e);
300             }
301         }
302         //Try to add up vpn Interfaces if already in Operational Datastore
303         InstanceIdentifier<VpnInterfaces> vpnInterfacesId = InstanceIdentifier.builder(VpnInterfaces.class).build();
304         Optional<VpnInterfaces> optionalVpnInterfaces = read(LogicalDatastoreType.CONFIGURATION, vpnInterfacesId);
305
306         if(optionalVpnInterfaces.isPresent()) {
307             List<VpnInterface> vpnInterfaces = optionalVpnInterfaces.get().getVpnInterface();
308             for(VpnInterface vpnInterface : vpnInterfaces) {
309                 if(vpnInterface.getVpnInstanceName().equals(value.getVpnInstanceName())) {
310                     LOG.debug("VpnInterface {} will be added from VPN {}", vpnInterface.getName(), value.getVpnInstanceName());
311                     vpnInterfaceManager.add(
312                                 VpnUtil.getVpnInterfaceIdentifier(vpnInterface.getName()), vpnInterface);
313
314                 }
315             }
316         }
317     }
318
319     public boolean isVPNConfigured() {
320
321         InstanceIdentifier<VpnInstances> vpnsIdentifier =
322                 InstanceIdentifier.builder(VpnInstances.class).build();
323         Optional<VpnInstances> optionalVpns = read( LogicalDatastoreType.CONFIGURATION,
324                 vpnsIdentifier);
325         if (!optionalVpns.isPresent() ||
326                 optionalVpns.get().getVpnInstance() == null ||
327                 optionalVpns.get().getVpnInstance().isEmpty()) {
328             LOG.trace("No VPNs configured.");
329             return false;
330         }
331         LOG.trace("VPNs are configured on the system.");
332         return true;
333     }
334
335     private InstanceIdentifier<?> getWildCardPath() {
336         return InstanceIdentifier.create(VpnInstances.class).child(VpnInstance.class);
337     }
338
339     private InstanceIdentifier<?> getFibEntryListenerPath() {
340         return InstanceIdentifier.create(FibEntries.class).child(VrfTables.class)
341                 .child(VrfEntry.class);
342     }
343
344     private InstanceIdentifier<?> getVpnInstanceOpListenerPath() {
345         return InstanceIdentifier.create(VpnInstanceOpData.class).child(VpnInstanceOpDataEntry.class);
346
347     }
348
349     @Override
350     public void close() throws Exception {
351         if (listenerRegistration != null) {
352             try {
353                 listenerRegistration.close();
354             } catch (final Exception e) {
355                 LOG.error("Error when cleaning up Vpn DataChangeListener.", e);
356             }
357             listenerRegistration = null;
358         }
359         if (fibListenerRegistration != null) {
360             try {
361                 fibListenerRegistration.close();
362             } catch (final Exception e) {
363                 LOG.error("Error when cleaning up Fib entries DataChangeListener.", e);
364             }
365             fibListenerRegistration = null;
366         }
367         if (opListenerRegistration != null) {
368             try {
369                 opListenerRegistration.close();
370             } catch (final Exception e) {
371                 LOG.error("Error when cleaning up VPN Instance Operational entries DataChangeListener.", e);
372             }
373             opListenerRegistration = null;
374         }
375
376         LOG.trace("VPN Manager Closed");
377     }
378
379     private <T extends DataObject> Optional<T> read(LogicalDatastoreType datastoreType,
380             InstanceIdentifier<T> path) {
381
382         ReadOnlyTransaction tx = broker.newReadOnlyTransaction();
383
384         Optional<T> result = Optional.absent();
385         try {
386             result = tx.read(datastoreType, path).get();
387         } catch (Exception e) {
388             throw new RuntimeException(e);
389         }
390
391         return result;
392     }
393
394     private <T extends DataObject> void asyncWrite(LogicalDatastoreType datastoreType,
395             InstanceIdentifier<T> path, T data, FutureCallback<Void> callback) {
396         WriteTransaction tx = broker.newWriteOnlyTransaction();
397         tx.put(datastoreType, path, data, true);
398         Futures.addCallback(tx.submit(), callback);
399     }
400
401     private <T extends DataObject> void syncWrite(LogicalDatastoreType datastoreType,
402                                                    InstanceIdentifier<T> path, T data, FutureCallback<Void> callback) {
403         WriteTransaction tx = broker.newWriteOnlyTransaction();
404         tx.put(datastoreType, path, data, true);
405         CheckedFuture<Void, TransactionCommitFailedException> futures = tx.submit();
406         try {
407             futures.get();
408         } catch (InterruptedException | ExecutionException e) {
409             LOG.error("Error writing VPN instance to ID info to datastore (path, data) : ({}, {})", path, data);
410             throw new RuntimeException(e.getMessage());
411         }
412     }
413
414     protected VpnInstanceOpDataEntry getVpnInstanceOpData(String rd) {
415         InstanceIdentifier<VpnInstanceOpDataEntry> id = VpnUtil.getVpnInstanceOpDataIdentifier(rd);
416         Optional<VpnInstanceOpDataEntry> vpnInstanceOpData = read(LogicalDatastoreType.OPERATIONAL, id);
417         if(vpnInstanceOpData.isPresent()) {
418             return vpnInstanceOpData.get();
419         }
420         return null;
421     }
422
423     private <T extends DataObject> void delete(LogicalDatastoreType datastoreType, InstanceIdentifier<T> path) {
424         WriteTransaction tx = broker.newWriteOnlyTransaction();
425         tx.delete(datastoreType, path);
426         Futures.addCallback(tx.submit(), DEFAULT_CALLBACK);
427     }
428
429     private class FibEntriesListener extends AbstractDataChangeListener<VrfEntry>  {
430
431         public FibEntriesListener() {
432             super(VrfEntry.class);
433         }
434
435         @Override
436         protected void remove(InstanceIdentifier<VrfEntry> identifier,
437                 VrfEntry del) {
438             LOG.trace("Remove Fib event - Key : {}, value : {} ", identifier, del);
439             final VrfTablesKey key = identifier.firstKeyOf(VrfTables.class, VrfTablesKey.class);
440             String rd = key.getRouteDistinguisher();
441             Long label = del.getLabel();
442             VpnInstanceOpDataEntry vpnInstanceOpData = getVpnInstanceOpData(rd);
443             if(vpnInstanceOpData != null) {
444                 List<Long> routeIds = vpnInstanceOpData.getRouteEntryId();
445                 if(routeIds == null) {
446                     LOG.debug("Fib Route entry is empty.");
447                     return;
448                 }
449                 LOG.debug("Removing label from vpn info - {}", label);
450                 routeIds.remove(label);
451                 asyncWrite(LogicalDatastoreType.OPERATIONAL, VpnUtil.getVpnInstanceOpDataIdentifier(rd),
452                            new VpnInstanceOpDataEntryBuilder(vpnInstanceOpData).setRouteEntryId(routeIds).build(), DEFAULT_CALLBACK);
453             } else {
454                 LOG.warn("No VPN Instance found for RD: {}", rd);
455             }
456         }
457
458         @Override
459         protected void update(InstanceIdentifier<VrfEntry> identifier,
460                 VrfEntry original, VrfEntry update) {
461             // TODO Auto-generated method stub
462
463         }
464
465         @Override
466         protected void add(InstanceIdentifier<VrfEntry> identifier,
467                            VrfEntry add) {
468             LOG.trace("Add Vrf Entry event - Key : {}, value : {}", identifier, add);
469             final VrfTablesKey key = identifier.firstKeyOf(VrfTables.class, VrfTablesKey.class);
470             String rd = key.getRouteDistinguisher();
471             Long label = add.getLabel();
472             VpnInstanceOpDataEntry vpn = getVpnInstanceOpData(rd);
473             if(vpn != null) {
474                 List<Long> routeIds = vpn.getRouteEntryId();
475                 if(routeIds == null) {
476                     routeIds = new ArrayList<>();
477                 }
478                 LOG.debug("Adding label to vpn info - {}", label);
479                 routeIds.add(label);
480                 asyncWrite(LogicalDatastoreType.OPERATIONAL, VpnUtil.getVpnInstanceOpDataIdentifier(rd),
481                            new VpnInstanceOpDataEntryBuilder(vpn).setRouteEntryId(routeIds).build(), DEFAULT_CALLBACK);
482             } else {
483                 LOG.warn("No VPN Instance found for RD: {}", rd);
484             }
485         }
486     }
487
488     class VpnInstanceOpListener extends org.opendaylight.vpnservice.mdsalutil.AbstractDataChangeListener<VpnInstanceOpDataEntry> {
489
490         public VpnInstanceOpListener() {
491             super(VpnInstanceOpDataEntry.class);
492         }
493
494         @Override
495         protected void remove(InstanceIdentifier<VpnInstanceOpDataEntry> identifier, VpnInstanceOpDataEntry del) {
496
497         }
498
499         @Override
500         protected void update(InstanceIdentifier<VpnInstanceOpDataEntry> identifier, VpnInstanceOpDataEntry original, VpnInstanceOpDataEntry update) {
501             final VpnInstanceOpDataEntryKey key = identifier.firstKeyOf(VpnInstanceOpDataEntry.class, VpnInstanceOpDataEntryKey.class);
502             String vpnName = key.getVrfId();
503
504             LOG.trace("VpnInstanceOpListener update: vpn name {} interface count in Old VpnOp Instance {} in New VpnOp Instance {}" ,
505                             vpnName, original.getVpnInterfaceCount(), update.getVpnInterfaceCount() );
506
507             //if((original.getVpnToDpnList().size() != update.getVpnToDpnList().size()) && (update.getVpnToDpnList().size() == 0)) {
508             if((original.getVpnInterfaceCount() != update.getVpnInterfaceCount()) && (update.getVpnInterfaceCount() == 0)) {
509                 notifyTaskIfRequired(vpnName);
510             }
511         }
512
513         private void notifyTaskIfRequired(String vpnName) {
514             Runnable notifyTask = vpnOpMap.remove(vpnName);
515             if (notifyTask == null) {
516                 LOG.trace("VpnInstanceOpListener update: No Notify Task queued for vpnName {}", vpnName);
517                 return;
518             }
519             executorService.execute(notifyTask);
520         }
521
522         @Override
523         protected void add(InstanceIdentifier<VpnInstanceOpDataEntry> identifier, VpnInstanceOpDataEntry add) {
524         }
525     }
526 }