Bug 7298: NPE in vpn manager
[netvirt.git] / vpnservice / vpnmanager / vpnmanager-impl / src / main / java / org / opendaylight / netvirt / vpnmanager / VpnInstanceListener.java
index 1398bd1ef093a5f4d933dbf4acffe25842ac8bcf..92b8eb417ddfee72323109d8a7ee1a9c73d6b143 100644 (file)
@@ -12,20 +12,14 @@ import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.ThreadFactoryBuilder;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.Callable;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ThreadFactory;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
-import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
 import org.opendaylight.genius.datastoreutils.DataStoreJobCoordinator;
@@ -33,69 +27,60 @@ import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
 import org.opendaylight.netvirt.fibmanager.api.IFibManager;
 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnAfConfig;
 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInstances;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.af.config.VpnTargets;
 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.af.config.vpntargets.VpnTarget;
 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstance;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.id.to.vpn.instance.VpnIds;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntryBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnTargetsBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList;
-import org.opendaylight.yangtools.concepts.ListenerRegistration;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpntargets.VpnTargetBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpntargets.VpnTargetKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroute.Vpn;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class VpnInstanceListener extends AbstractDataChangeListener<VpnInstance> implements AutoCloseable {
+public class VpnInstanceListener extends AsyncDataTreeChangeListenerBase<VpnInstance, VpnInstanceListener>
+        implements AutoCloseable {
     private static final Logger LOG = LoggerFactory.getLogger(VpnInstanceListener.class);
-    private ListenerRegistration<DataChangeListener> listenerRegistration;
     private final DataBroker dataBroker;
     private final IBgpManager bgpManager;
     private final IdManagerService idManager;
     private final VpnInterfaceManager vpnInterfaceManager;
     private final IFibManager fibManager;
-    private static final ThreadFactory threadFactory = new ThreadFactoryBuilder()
-            .setNameFormat("NV-VpnMgr-%d").build();
-    private ExecutorService executorService = Executors.newSingleThreadExecutor(threadFactory);
-    private ConcurrentMap<String, Runnable> vpnOpMap = new ConcurrentHashMap<String, Runnable>();
+    private final VpnOpDataSyncer vpnOpDataNotifier;
 
     public VpnInstanceListener(final DataBroker dataBroker, final IBgpManager bgpManager,
                                final IdManagerService idManager,
                                final VpnInterfaceManager vpnInterfaceManager,
-                               final IFibManager fibManager) {
-        super(VpnInstance.class);
+                               final IFibManager fibManager,
+                               final VpnOpDataSyncer vpnOpDataSyncer) {
+        super(VpnInstance.class, VpnInstanceListener.class);
         this.dataBroker = dataBroker;
         this.bgpManager = bgpManager;
         this.idManager = idManager;
         this.vpnInterfaceManager = vpnInterfaceManager;
         this.fibManager = fibManager;
+        this.vpnOpDataNotifier = vpnOpDataSyncer;
     }
 
     public void start() {
         LOG.info("{} start", getClass().getSimpleName());
-        listenerRegistration = dataBroker.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
-                getWildCardPath(), this, AsyncDataBroker.DataChangeScope.SUBTREE);
+        registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
     }
 
-    private InstanceIdentifier<VpnInstance> getWildCardPath() {
+    @Override
+    protected InstanceIdentifier<VpnInstance> getWildCardPath() {
         return InstanceIdentifier.create(VpnInstances.class).child(VpnInstance.class);
     }
 
     @Override
-    public void close() throws Exception {
-        if (listenerRegistration != null) {
-            listenerRegistration.close();
-            listenerRegistration = null;
-        }
-        LOG.info("{} close", getClass().getSimpleName());
-    }
-
-    void notifyTaskIfRequired(String vpnName) {
-        Runnable notifyTask = vpnOpMap.remove(vpnName);
-        if (notifyTask == null) {
-            LOG.trace("VpnInstanceListener update: No Notify Task queued for vpnName {}", vpnName);
-            return;
-        }
-        executorService.execute(notifyTask);
+    protected VpnInstanceListener getDataTreeChangeListener() {
+        return VpnInstanceListener.this;
     }
 
     private void waitForOpRemoval(String rd, String vpnName) {
@@ -104,11 +89,11 @@ public class VpnInstanceListener extends AbstractDataChangeListener<VpnInstance>
         VpnInstanceOpDataEntry vpnOpEntry = null;
         Long intfCount = 0L;
         Long currentIntfCount = 0L;
-        Integer retryCount = 2;
+        Integer retryCount = 3;
         long timeout = VpnConstants.MIN_WAIT_TIME_IN_MILLISECONDS;
         Optional<VpnInstanceOpDataEntry> vpnOpValue = null;
         vpnOpValue = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL,
-                VpnUtil.getVpnInstanceOpDataIdentifier(rd));
+                                  VpnUtil.getVpnInstanceOpDataIdentifier(rd));
 
         if ((vpnOpValue != null) && (vpnOpValue.isPresent())) {
             vpnOpEntry = vpnOpValue.get();
@@ -195,7 +180,6 @@ public class VpnInstanceListener extends AbstractDataChangeListener<VpnInstance>
         LOG.trace("Remove VPN event key: {}, value: {}", identifier, del);
         final String vpnName = del.getVpnInstanceName();
         final String rd = del.getIpv4Family().getRouteDistinguisher();
-        final long vpnId = VpnUtil.getVpnId(dataBroker, vpnName);
         Optional<VpnInstanceOpDataEntry> vpnOpValue = null;
 
         //TODO(vpnteam): Entire code would need refactoring to listen only on the parent object - VPNInstance
@@ -250,9 +234,6 @@ public class VpnInstanceListener extends AbstractDataChangeListener<VpnInstance>
             // Clean up VpnInstanceToVpnId from Config DS
             VpnUtil.removeVpnIdToVpnInstance(broker, vpnId, writeTxn);
             VpnUtil.removeVpnInstanceToVpnId(broker, vpnName, writeTxn);
-
-            List<ListenableFuture<Void>> futures = new ArrayList<>();
-            futures.add(writeTxn.submit());
             LOG.trace("Removed vpnIdentifier for  rd{} vpnname {}", rd, vpnName);
             if (rd != null) {
                 synchronized (vpnName.intern()) {
@@ -265,30 +246,40 @@ public class VpnInstanceListener extends AbstractDataChangeListener<VpnInstance>
                 }
 
                 // Clean up VPNExtraRoutes Operational DS
-                VpnUtil.removeVpnExtraRouteForVpn(broker, rd, null);
+                InstanceIdentifier<Vpn> vpnToExtraroute = VpnUtil.getVpnToExtrarouteIdentifier(rd);
+                Optional<Vpn> optVpnToExtraroute = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL, vpnToExtraroute);
+                if (optVpnToExtraroute.isPresent()) {
+                    VpnUtil.removeVpnExtraRouteForVpn(broker, rd, writeTxn);
+                }
 
                 // Clean up VPNInstanceOpDataEntry
-                VpnUtil.removeVpnOpInstance(broker, rd, null);
+                VpnUtil.removeVpnOpInstance(broker, rd, writeTxn);
             } else {
                 // Clean up FIB Entries Config DS
                 synchronized (vpnName.intern()) {
                     fibManager.removeVrfTable(broker, vpnName, null);
                 }
                 // Clean up VPNExtraRoutes Operational DS
-                VpnUtil.removeVpnExtraRouteForVpn(broker, vpnName, null);
+                InstanceIdentifier<Vpn> vpnToExtraroute = VpnUtil.getVpnToExtrarouteIdentifier(vpnName);
+                Optional<Vpn> optVpnToExtraroute = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL, vpnToExtraroute);
+                if (optVpnToExtraroute.isPresent()) {
+                    VpnUtil.removeVpnExtraRouteForVpn(broker, vpnName, writeTxn);
+                }
 
                 // Clean up VPNInstanceOpDataEntry
-                VpnUtil.removeVpnOpInstance(broker, vpnName, null);
+                VpnUtil.removeVpnOpInstance(broker, vpnName, writeTxn);
             }
             // Clean up PrefixToInterface Operational DS
-            VpnUtil.removePrefixToInterfaceForVpnId(broker, vpnId, null);
+            VpnUtil.removePrefixToInterfaceForVpnId(broker, vpnId, writeTxn);
 
             // Clean up L3NextHop Operational DS
-            VpnUtil.removeL3nexthopForVpnId(broker, vpnId, null);
+            VpnUtil.removeL3nexthopForVpnId(broker, vpnId, writeTxn);
 
             // Release the ID used for this VPN back to IdManager
             VpnUtil.releaseId(idManager, VpnConstants.VPN_IDPOOL_NAME, vpnName);
 
+            List<ListenableFuture<Void>> futures = new ArrayList<>();
+            futures.add(writeTxn.submit());
             return futures;
         }
     }
@@ -302,8 +293,6 @@ public class VpnInstanceListener extends AbstractDataChangeListener<VpnInstance>
     @Override
     protected void add(final InstanceIdentifier<VpnInstance> identifier, final VpnInstance value) {
         LOG.trace("Add VPN event key: {}, value: {}", identifier, value);
-        final VpnAfConfig config = value.getIpv4Family();
-        final String rd = config.getRouteDistinguisher();
         final String vpnName = value.getVpnInstanceName();
 
         DataStoreJobCoordinator dataStoreCoordinator = DataStoreJobCoordinator.getInstance();
@@ -332,7 +321,6 @@ public class VpnInstanceListener extends AbstractDataChangeListener<VpnInstance>
             // If another renderer(for eg : CSS) needs to be supported, check can be performed here
             // to call the respective helpers.
             final VpnAfConfig config = vpnInstance.getIpv4Family();
-            final String rd = config.getRouteDistinguisher();
             WriteTransaction writeConfigTxn = broker.newWriteOnlyTransaction();
             WriteTransaction writeOperTxn = broker.newWriteOnlyTransaction();
             addVpnInstance(vpnInstance, writeConfigTxn, writeOperTxn);
@@ -346,10 +334,8 @@ public class VpnInstanceListener extends AbstractDataChangeListener<VpnInstance>
             List<ListenableFuture<Void>> futures = new ArrayList<>();
             futures.add(writeConfigTxn.submit());
             ListenableFuture<List<Void>> listenableFuture = Futures.allAsList(futures);
-            if (rd != null) {
-                Futures.addCallback(listenableFuture,
-                        new AddBgpVrfWorker(config , vpnInstance.getVpnInstanceName()));
-            }
+            Futures.addCallback(listenableFuture,
+                    new PostAddVpnInstanceWorker(config , vpnInstance.getVpnInstanceName()));
             return futures;
         }
     }
@@ -361,7 +347,12 @@ public class VpnInstanceListener extends AbstractDataChangeListener<VpnInstance>
         String vpnInstanceName = value.getVpnInstanceName();
 
         long vpnId = VpnUtil.getUniqueId(idManager, VpnConstants.VPN_IDPOOL_NAME, vpnInstanceName);
-        LOG.trace("VPN instance to ID generated.");
+        if (vpnId == 0) {
+            LOG.error("Unable to fetch label from Id Manager. Bailing out of adding operational data for Vpn Instance {}", value.getVpnInstanceName());
+            LOG.error("Unable to fetch label from Id Manager. Bailing out of adding operational data for Vpn Instance {}", value.getVpnInstanceName());
+            return;
+        }
+        LOG.info("VPN Id {} generated for VpnInstanceName {}", vpnId, vpnInstanceName);
         org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance
                 vpnInstanceToVpnId = VpnUtil.getVpnInstanceToVpnId(vpnInstanceName, vpnId, (rd != null) ? rd
                 : vpnInstanceName);
@@ -391,26 +382,23 @@ public class VpnInstanceListener extends AbstractDataChangeListener<VpnInstance>
 
         try {
             String cachedTransType = fibManager.getConfTransType();
-            LOG.trace("Value for confTransportType is " + cachedTransType);
             if (cachedTransType.equals("Invalid")) {
                 try {
                     fibManager.setConfTransType("L3VPN", "VXLAN");
                 } catch (Exception e) {
-                    LOG.trace("Exception caught setting the cached value for transportType");
-                    LOG.error(e.getMessage());
+                    LOG.error("Exception caught setting the L3VPN tunnel transportType", e);
                 }
             } else {
-                LOG.trace(":cached val is neither unset/invalid. NO-op.");
+                LOG.trace("Configured tunnel transport type for L3VPN as {}", cachedTransType);
             }
         } catch (Exception e) {
-            LOG.error(e.getMessage());
+            LOG.error("Error when trying to retrieve tunnel transport type for L3VPN ", e);
         }
 
         if (rd == null) {
             VpnInstanceOpDataEntryBuilder builder =
                     new VpnInstanceOpDataEntryBuilder().setVrfId(vpnInstanceName).setVpnId(vpnId)
-                            .setVpnInstanceName(vpnInstanceName)
-                            .setVpnInterfaceCount(0L);
+                            .setVpnInstanceName(vpnInstanceName);
             if (writeOperTxn != null) {
                 writeOperTxn.merge(LogicalDatastoreType.OPERATIONAL,
                         VpnUtil.getVpnInstanceOpDataIdentifier(vpnInstanceName),
@@ -420,12 +408,26 @@ public class VpnInstanceListener extends AbstractDataChangeListener<VpnInstance>
                          VpnUtil.getVpnInstanceOpDataIdentifier(vpnInstanceName),
                          builder.build(), TransactionUtil.DEFAULT_CALLBACK);
             }
-            synchronized (vpnInstanceName.intern()) {
-                fibManager.addVrfTable(dataBroker, vpnInstanceName, null);
-            }
         } else {
             VpnInstanceOpDataEntryBuilder builder = new VpnInstanceOpDataEntryBuilder()
-                    .setVrfId(rd).setVpnId(vpnId).setVpnInstanceName(vpnInstanceName).setVpnInterfaceCount(0L);
+                    .setVrfId(rd).setVpnId(vpnId).setVpnInstanceName(vpnInstanceName);
+
+            List<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpntargets.VpnTarget> opVpnTargetList = new ArrayList<>();
+            VpnTargets vpnTargets = config.getVpnTargets();
+            if (vpnTargets != null) {
+                List<VpnTarget> vpnTargetList = vpnTargets.getVpnTarget();
+                if (vpnTargetList != null) {
+                    for (VpnTarget vpnTarget : vpnTargetList) {
+                        VpnTargetBuilder vpnTargetBuilder = new VpnTargetBuilder().setKey(new VpnTargetKey(vpnTarget.getKey().getVrfRTValue()))
+                                        .setVrfRTType(org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn
+                                                .instance.op.data.vpn.instance.op.data.entry.vpntargets.VpnTarget.VrfRTType
+                                                .forValue(vpnTarget.getVrfRTType().getIntValue())).setVrfRTValue(vpnTarget.getVrfRTValue());
+                        opVpnTargetList.add(vpnTargetBuilder.build());
+                    }
+                }
+            }
+            VpnTargetsBuilder vpnTargetsBuilder = new VpnTargetsBuilder().setVpnTarget(opVpnTargetList);
+            builder.setVpnTargets(vpnTargetsBuilder.build());
 
             if (writeOperTxn != null) {
                 writeOperTxn.merge(LogicalDatastoreType.OPERATIONAL,
@@ -436,18 +438,16 @@ public class VpnInstanceListener extends AbstractDataChangeListener<VpnInstance>
                         VpnUtil.getVpnInstanceOpDataIdentifier(rd),
                         builder.build(), TransactionUtil.DEFAULT_CALLBACK);
             }
-            synchronized (vpnInstanceName.intern()) {
-                fibManager.addVrfTable(dataBroker, rd, null);
-            }
         }
+        LOG.info("VpnInstanceOpData populated successfully for vpn {} rd {}", vpnInstanceName, rd);
     }
 
 
-    private class AddBgpVrfWorker implements FutureCallback<List<Void>> {
+    private class PostAddVpnInstanceWorker implements FutureCallback<List<Void>> {
         VpnAfConfig config;
         String vpnName;
 
-        public AddBgpVrfWorker(VpnAfConfig config, String vpnName)  {
+        public PostAddVpnInstanceWorker(VpnAfConfig config, String vpnName)  {
             this.config = config;
             this.vpnName = vpnName;
         }
@@ -458,13 +458,24 @@ public class VpnInstanceListener extends AbstractDataChangeListener<VpnInstance>
          */
         @Override
         public void onSuccess(List<Void> voids) {
+            /*
+            if rd is null, then its either a router vpn instance (or) a vlan external network vpn instance.
+            if rd is non-null, then it is a bgpvpn instance
+             */
             String rd = config.getRouteDistinguisher();
-            if (rd != null) {
-                List<VpnTarget> vpnTargetList = config.getVpnTargets().getVpnTarget();
+            if ((rd == null) || addBgpVrf(voids)) {
+                notifyTask();
+            }
+        }
+
+        private boolean addBgpVrf(List<Void> voids) {
+            String rd = config.getRouteDistinguisher();
+            List<VpnTarget> vpnTargetList = config.getVpnTargets().getVpnTarget();
 
-                List<String> ertList = new ArrayList<String>();
-                List<String> irtList = new ArrayList<String>();
+            List<String> ertList = new ArrayList<String>();
+            List<String> irtList = new ArrayList<String>();
 
+            if (vpnTargetList != null) {
                 for (VpnTarget vpnTarget : vpnTargetList) {
                     if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.ExportExtcommunity) {
                         ertList.add(vpnTarget.getVrfRTValue());
@@ -477,15 +488,23 @@ public class VpnInstanceListener extends AbstractDataChangeListener<VpnInstance>
                         irtList.add(vpnTarget.getVrfRTValue());
                     }
                 }
-
-                try {
-                    bgpManager.addVrf(rd, irtList, ertList);
-                } catch (Exception e) {
-                    LOG.error("Exception when adding VRF to BGP", e);
-                    return;
-                }
-                vpnInterfaceManager.handleVpnsExportingRoutes(this.vpnName, rd);
+            } else {
+                LOG.error("vpn target list is empty, cannot add BGP VPN {} VRF {}", this.vpnName, rd);
+                return false;
             }
+            try {
+                bgpManager.addVrf(rd, irtList, ertList);
+            } catch (Exception e) {
+                LOG.error("Exception when adding VRF to BGP", e);
+                return false;
+            }
+            vpnInterfaceManager.handleVpnsExportingRoutes(this.vpnName, rd);
+            return true;
+        }
+
+        private void notifyTask() {
+            vpnOpDataNotifier.notifyVpnOpDataReady(VpnOpDataSyncer.VpnOpDataType.vpnInstanceToId, vpnName);
+            vpnOpDataNotifier.notifyVpnOpDataReady(VpnOpDataSyncer.VpnOpDataType.vpnOpData, vpnName);
         }
         /**
          *
@@ -498,14 +517,13 @@ public class VpnInstanceListener extends AbstractDataChangeListener<VpnInstance>
 
         @Override
         public void onFailure(Throwable throwable) {
-            LOG.warn("Job: failed with exception: {}", throwable.getStackTrace());
+            LOG.warn("Job: failed with exception: ", throwable);
         }
     }
 
     public boolean isVPNConfigured() {
 
-        InstanceIdentifier<VpnInstances> vpnsIdentifier =
-                InstanceIdentifier.builder(VpnInstances.class).build();
+        InstanceIdentifier<VpnInstances> vpnsIdentifier = InstanceIdentifier.builder(VpnInstances.class).build();
         Optional<VpnInstances> optionalVpns = TransactionUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION,
                 vpnsIdentifier);
         if (!optionalVpns.isPresent() ||