NETVIRT-1630 migrate to md-sal APIs
[netvirt.git] / vpnmanager / impl / src / main / java / org / opendaylight / netvirt / vpnmanager / SubnetmapChangeListener.java
index 16611421c0cd7dde7ebd0e4bac60d0cae115ef06..e2d1c7e663922e2746111a32b91d8ab1387499b7 100644 (file)
@@ -8,19 +8,28 @@
 
 package org.opendaylight.netvirt.vpnmanager;
 
-import com.google.common.base.Optional;
 import java.util.ArrayList;
 import java.util.List;
-import javax.annotation.PostConstruct;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.locks.ReentrantLock;
+import javax.annotation.PreDestroy;
 import javax.inject.Inject;
 import javax.inject.Singleton;
-import org.opendaylight.controller.md.sal.binding.api.DataBroker;
-import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
-import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
+import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
+import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
+import org.opendaylight.genius.utils.JvmGlobalLocks;
+import org.opendaylight.infrautils.utils.concurrent.Executors;
+import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
+import org.opendaylight.mdsal.binding.api.DataBroker;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.netvirt.vpnmanager.api.IVpnManager;
+import org.opendaylight.serviceutils.tools.listener.AbstractAsyncDataTreeChangeListener;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInstances;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstanceKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.l3vpn.rev200204.vpn.instances.vpn.instance.vpntargets.VpnTarget;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.NetworkAttributes.NetworkType;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.Subnetmaps;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.Subnetmap;
@@ -30,146 +39,139 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 @Singleton
-public class SubnetmapChangeListener extends AsyncDataTreeChangeListenerBase<Subnetmap, SubnetmapChangeListener> {
+public class SubnetmapChangeListener extends AbstractAsyncDataTreeChangeListener<Subnetmap> {
     private static final Logger LOG = LoggerFactory.getLogger(SubnetmapChangeListener.class);
     private final DataBroker dataBroker;
     private final VpnSubnetRouteHandler vpnSubnetRouteHandler;
+    private final VpnUtil vpnUtil;
+    private final IVpnManager vpnManager;
+    private final ManagedNewTransactionRunner txRunner;
 
     @Inject
-    public SubnetmapChangeListener(final DataBroker dataBroker, final VpnSubnetRouteHandler vpnSubnetRouteHandler) {
-        super(Subnetmap.class, SubnetmapChangeListener.class);
+    public SubnetmapChangeListener(final DataBroker dataBroker, final VpnSubnetRouteHandler vpnSubnetRouteHandler,
+                                   VpnUtil vpnUtil, IVpnManager vpnManager) {
+        super(dataBroker, LogicalDatastoreType.CONFIGURATION,
+                InstanceIdentifier.create(Subnetmaps.class).child(Subnetmap.class),
+                Executors.newListeningSingleThreadExecutor("SubnetmapChangeListener", LOG));
         this.dataBroker = dataBroker;
         this.vpnSubnetRouteHandler = vpnSubnetRouteHandler;
+        this.vpnUtil = vpnUtil;
+        this.vpnManager = vpnManager;
+        this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
+        start();
     }
 
-    @PostConstruct
     public void start() {
         LOG.info("{} start", getClass().getSimpleName());
-        registerListener(dataBroker);
     }
 
     @Override
-    protected InstanceIdentifier<Subnetmap> getWildCardPath() {
-        return InstanceIdentifier.create(Subnetmaps.class).child(Subnetmap.class);
+    @PreDestroy
+    public void close() {
+        super.close();
+        Executors.shutdownAndAwaitTermination(getExecutorService());
     }
 
-    // TODO Clean up the exception handling
-    @SuppressWarnings("checkstyle:IllegalCatch")
-    private void registerListener(final DataBroker db) {
-        try {
-            registerListener(LogicalDatastoreType.CONFIGURATION, db);
-        } catch (final Exception e) {
-            LOG.error("VPNManager subnetMap config DataChange listener registration fail!", e);
-            throw new IllegalStateException("VPNManager subnetMap config DataChange listener registration failed.", e);
-        }
-    }
 
     @Override
-    protected void add(InstanceIdentifier<Subnetmap> identifier, Subnetmap subnetmap) {
-        LOG.trace("add:SubnetmapChangeListener add subnetmap method - key: {}, value: {}", identifier, subnetmap);
+    public void add(InstanceIdentifier<Subnetmap> identifier, Subnetmap subnetmap) {
+        LOG.debug("add: subnetmap method - key: {}, value: {}", identifier, subnetmap);
         Uuid subnetId = subnetmap.getId();
-        Uuid vpnId = subnetmap.getVpnId();
-
+        Network network = vpnUtil.getNeutronNetwork(subnetmap.getNetworkId());
+        if (network == null) {
+            LOG.error("add: network was not found for subnetId {}", subnetId.getValue());
+            return;
+        }
         if (subnetmap.getVpnId() != null) {
-            // SubnetRoute for ExternalSubnets is handled in ExternalSubnetVpnInstanceListener.
-            // Here we must handle only InternalVpnSubnetRoute and BGPVPNBasedSubnetRoute
-            Network network = VpnUtil.getNeutronNetwork(dataBroker, subnetmap.getNetworkId());
-            if (network == null) {
-                LOG.info("update: vpnId {}, networkId: {}, subnetId: {}: network was not found",
-                    vpnId, subnetmap.getNetworkId(), subnetId);
-                return;
-            }
-            boolean isExtNetwork = VpnUtil.getIsExternal(VpnUtil.getNeutronNetwork(dataBroker,
-                                           subnetmap.getNetworkId()));
-            // SubnetRoute for ExternalNetwork is not impacting for internetvpn
-            if (isExtNetwork) {
-                return;
-            }
-            boolean isBgpVpn = !vpnId.equals(subnetmap.getRouterId());
-            String elanInstanceName = subnetmap.getNetworkId().getValue();
-            Long elanTag = getElanTag(elanInstanceName);
-            if (elanTag.equals(0L)) {
-                LOG.error("add:Unable to fetch elantag from ElanInstance {} and hence not proceeding with "
-                        + "subnetmapListener add for subnet {}", elanInstanceName, subnetId.getValue());
-                return;
-            }
-            if (subnetmap.getNetworkType().equals(NetworkType.VLAN)) {
-                VpnUtil.addRouterPortToElanDpnListForVlaninAllDpn(vpnId.getValue(), dataBroker);
+            if (NetworkType.VLAN.equals(subnetmap.getNetworkType())) {
+                vpnUtil.addRouterPortToElanDpnListForVlaninAllDpn(subnetmap.getVpnId().getValue());
             }
-            // subnet added to VPN case upon config DS replay after reboot
-            // ports added to subnet upon config DS replay after reboot are handled implicitly by subnetAddedToVpn
-            // in SubnetRouteHandler
-            vpnSubnetRouteHandler.onSubnetAddedToVpn(subnetmap, isBgpVpn , elanTag);
         }
-        if (subnetmap.getInternetVpnId() != null) {
-            boolean isBgpVpn = !subnetmap.getInternetVpnId().equals(subnetmap.getRouterId());
-            String elanInstanceName = subnetmap.getNetworkId().getValue();
-            Long elanTag = getElanTag(elanInstanceName);
-            if (elanTag.equals(0L)) {
-                LOG.error("add:Unable to fetch elantag from ElanInstance {} and hence not proceeding with "
-                        + "subnetmapListener add for subnet {}", elanInstanceName, subnetId);
-                return;
+        if (VpnUtil.getIsExternal(network)) {
+            LOG.debug("SubnetmapListener:add: provider subnetwork {} is handling in "
+                      + "ExternalSubnetVpnInstanceListener", subnetId.getValue());
+            return;
+        }
+        String elanInstanceName = subnetmap.getNetworkId().getValue();
+        long elanTag = getElanTag(elanInstanceName);
+        if (elanTag == 0L) {
+            LOG.error("add: unable to fetch elantag from ElanInstance {} for subnet {}",
+                      elanInstanceName, subnetId.getValue());
+            return;
+        }
+        Uuid vpnId = subnetmap.getVpnId();
+        if (vpnId != null) {
+            boolean isBgpVpn = !vpnId.equals(subnetmap.getRouterId());
+            LOG.info("add: subnetmap {} with elanTag {} to VPN {}", subnetmap, elanTag,
+                     vpnId);
+            vpnSubnetRouteHandler.onSubnetAddedToVpn(subnetmap, isBgpVpn, elanTag);
+            if (isBgpVpn && subnetmap.getRouterId() == null) {
+                Set<VpnTarget> routeTargets = vpnManager.getRtListForVpn(vpnId.getValue());
+                if (!routeTargets.isEmpty()) {
+                    // FIXME: separate this out somehow?
+                    final ReentrantLock lock = JvmGlobalLocks.getLockForString(subnetmap.getSubnetIp());
+                    lock.lock();
+                    try {
+                        vpnManager.updateRouteTargetsToSubnetAssociation(routeTargets, subnetmap.getSubnetIp(),
+                                vpnId.getValue());
+                    } finally {
+                        lock.unlock();
+                    }
+                }
             }
-            // subnet added to VPN case upon config DS replay after reboot
-            // ports added to subnet upon config DS replay after reboot are handled implicitly by subnetAddedToVpn
-            // in SubnetRouteHandler
-            vpnSubnetRouteHandler.onSubnetAddedToVpn(subnetmap, isBgpVpn , elanTag);
         }
     }
 
     @Override
-    protected void remove(InstanceIdentifier<Subnetmap> identifier, Subnetmap subnetmap) {
-        LOG.trace("remove:SubnetmapListener remove subnetmap method - key: {}, value: {}", identifier, subnetmap);
+    public void remove(InstanceIdentifier<Subnetmap> identifier, Subnetmap subnetmap) {
+        LOG.trace("remove: subnetmap method - key: {}, value: {}", identifier, subnetmap);
     }
 
     @Override
     // TODO Clean up the exception handling
     @SuppressWarnings("checkstyle:IllegalCatch")
-    protected void update(InstanceIdentifier<Subnetmap> identifier, Subnetmap subnetmapOriginal, Subnetmap
+    public void update(InstanceIdentifier<Subnetmap> identifier, Subnetmap subnetmapOriginal, Subnetmap
             subnetmapUpdate) {
-        LOG.trace("update:SubnetmapListener update subnetmap method - key {}, original {}, update {}", identifier,
-                subnetmapOriginal, subnetmapUpdate);
-        Uuid vpnIdNew = subnetmapUpdate.getVpnId();
-        Uuid vpnIdOld = subnetmapOriginal.getVpnId();
+        LOG.debug("update: method - key {}, original {}, update {}", identifier,
+                  subnetmapOriginal, subnetmapUpdate);
         Uuid subnetId = subnetmapUpdate.getId();
-        if ((subnetmapUpdate.getNetworkId() == null)
-            && subnetmapOriginal.getNetworkId() == null) {
-            // transition: subnetmap is removed with syncwrite.
-            // wait next write to do the update
-            LOG.error("subnetmap has no network for subnetmap {}", subnetmapUpdate);
+        Network network = vpnUtil.getNeutronNetwork(subnetmapUpdate.getNetworkId());
+        if (network == null) {
+            LOG.error("update: network was not found for subnetId {}", subnetId.getValue());
             return;
         }
-        boolean updateCapableForCreation = false;
-        String elanInstanceName = null;
-        Long elanTag = Long.valueOf(0L);
-        if (subnetmapUpdate.getNetworkId() != null) {
-            updateCapableForCreation = true;
-            elanInstanceName = subnetmapUpdate.getNetworkId().getValue();
-            elanTag = getElanTag(elanInstanceName);
-            if (elanTag.equals(0L)) {
-                LOG.error("update:Unable to fetch elantag from ElanInstance {} and "
-                    + "hence not proceeding with "
-                    + "subnetmapListener update for subnet {}", elanInstanceName, subnetId.getValue());
-                return;
-            }
-
-            // SubnetRoute for ExternalSubnets is handled in ExternalSubnetVpnInstanceListener.
-            // Here we must handle only InternalVpnSubnetRoute and BGPVPNBasedSubnetRoute
-            Network network = VpnUtil.getNeutronNetwork(dataBroker, subnetmapUpdate.getNetworkId());
-            if (VpnUtil.getIsExternal(network)) {
-                return;
-            }
+        String elanInstanceName = subnetmapUpdate.getNetworkId().getValue();
+        long elanTag = getElanTag(elanInstanceName);
+        if (elanTag == 0L) {
+            LOG.error("update: unable to fetch elantag from ElanInstance {} for subnetId {}",
+                      elanInstanceName, subnetId);
+            return;
         }
-        Uuid vpnIdInternetNew = subnetmapUpdate.getInternetVpnId();
-        Uuid vpnIdInternetOld = subnetmapOriginal.getInternetVpnId();
-        boolean returnValue1 = updateSubnetmapOpDataEntry(vpnIdInternetOld, vpnIdInternetNew,
-                           subnetmapUpdate, subnetmapOriginal, elanTag, null, updateCapableForCreation);
-        boolean returnValue2 = updateSubnetmapOpDataEntry(vpnIdOld, vpnIdNew,
-                      subnetmapUpdate, subnetmapOriginal, elanTag, elanInstanceName, updateCapableForCreation);
-        if (!returnValue2 || !returnValue1) {
+        updateVlanDataEntry(subnetmapOriginal.getVpnId(), subnetmapUpdate.getVpnId(), subnetmapUpdate,
+                subnetmapOriginal, elanInstanceName);
+        if (VpnUtil.getIsExternal(network)) {
+            LOG.debug("update: provider subnetwork {} is handling in "
+                      + "ExternalSubnetVpnInstanceListener", subnetId.getValue());
             return;
         }
-        // port added/removed to/from subnet case
+        // update on BGPVPN or InternalVPN change
+        Uuid vpnIdOld = subnetmapOriginal.getVpnId();
+        Uuid vpnIdNew = subnetmapUpdate.getVpnId();
+        if (!Objects.equals(vpnIdOld, vpnIdNew)) {
+            LOG.info("update: update subnetOpDataEntry for subnet {} imported in VPN",
+                     subnetmapUpdate.getId().getValue());
+            updateSubnetmapOpDataEntry(subnetmapOriginal.getVpnId(), subnetmapUpdate.getVpnId(), subnetmapUpdate,
+                                       subnetmapOriginal, elanTag);
+        }
+        // update on Internet VPN Id change
+        Uuid inetVpnIdOld = subnetmapOriginal.getInternetVpnId();
+        Uuid inetVpnIdNew = subnetmapUpdate.getInternetVpnId();
+        if (!Objects.equals(inetVpnIdOld, inetVpnIdNew)) {
+            LOG.info("update: update subnetOpDataEntry for subnet {} imported in InternetVPN",
+                     subnetmapUpdate.getId().getValue());
+            updateSubnetmapOpDataEntry(inetVpnIdOld, inetVpnIdNew, subnetmapUpdate, subnetmapOriginal, elanTag);
+        }
+        // update on PortList change
         List<Uuid> oldPortList;
         List<Uuid> newPortList;
         newPortList = subnetmapUpdate.getPortList() != null ? subnetmapUpdate.getPortList() : new ArrayList<>();
@@ -177,6 +179,7 @@ public class SubnetmapChangeListener extends AsyncDataTreeChangeListenerBase<Sub
         if (newPortList.size() == oldPortList.size()) {
             return;
         }
+        LOG.info("update: update port list for subnet {}", subnetmapUpdate.getId().getValue());
         if (newPortList.size() > oldPortList.size()) {
             for (Uuid portId : newPortList) {
                 if (! oldPortList.contains(portId)) {
@@ -194,59 +197,56 @@ public class SubnetmapChangeListener extends AsyncDataTreeChangeListenerBase<Sub
         }
     }
 
-    private boolean updateSubnetmapOpDataEntry(Uuid vpnIdOld, Uuid vpnIdNew, Subnetmap subnetmapUpdate,
-                                    Subnetmap subnetmapOriginal, Long elanTag, String  elanInstanceName,
-                                    boolean updateCapableForCreation) {
-        // subnet added to VPN case
-        if (vpnIdNew != null && vpnIdOld == null && updateCapableForCreation) {
-            if (elanInstanceName != null && subnetmapUpdate.getNetworkType().equals(NetworkType.VLAN)) {
-                VpnUtil.addRouterPortToElanDpnListForVlaninAllDpn(vpnIdNew.getValue(), dataBroker);
-            }
-            boolean isBgpVpn = !vpnIdNew.equals(subnetmapUpdate.getRouterId());
-            if (!isBgpVpn) {
-                return false;
+    private void updateSubnetmapOpDataEntry(Uuid vpnIdOld, Uuid vpnIdNew, Subnetmap subnetmapUpdate,
+                                    Subnetmap subnetmapOriginal, Long elanTag) {
+
+        // subnet added to VPN
+        if (vpnIdNew != null && vpnIdOld == null) {
+            if (vpnIdNew.equals(subnetmapUpdate.getRouterId())) {
+                return;
             }
             vpnSubnetRouteHandler.onSubnetAddedToVpn(subnetmapUpdate, true, elanTag);
-            return true;
         }
-        // subnet removed from VPN case
+        // subnet removed from VPN
         if (vpnIdOld != null && vpnIdNew == null) {
-            if (subnetmapOriginal.getNetworkType().equals(NetworkType.VLAN)) {
-                VpnUtil.removeRouterPortFromElanDpnListForVlanInAllDpn(elanInstanceName, subnetmapOriginal
-                        .getRouterInterfacePortId().getValue(), vpnIdOld.getValue(), dataBroker);
-            }
-            boolean isBgpVpn = vpnIdOld.equals(subnetmapOriginal.getRouterId()) ? false : true;
-            if (!isBgpVpn) {
-                return false;
+            if (vpnIdOld.equals(subnetmapOriginal.getRouterId())) {
+                return;
             }
             vpnSubnetRouteHandler.onSubnetDeletedFromVpn(subnetmapOriginal, true);
-            return true;
         }
-        // subnet updated in VPN case
-        if (vpnIdOld != null && vpnIdNew != null && (!vpnIdNew.equals(vpnIdOld))) {
+        // subnet updated in VPN
+        if (vpnIdOld != null && vpnIdNew != null && !vpnIdNew.equals(vpnIdOld)) {
             vpnSubnetRouteHandler.onSubnetUpdatedInVpn(subnetmapUpdate, elanTag);
-            return true;
         }
-        return false;
     }
 
-    @Override
-    protected SubnetmapChangeListener getDataTreeChangeListener() {
-        return this;
+    private void updateVlanDataEntry(Uuid vpnIdOld, Uuid vpnIdNew, Subnetmap subnetmapUpdate,
+            Subnetmap subnetmapOriginal, String elanInstanceName) {
+        if (vpnIdNew != null && vpnIdOld == null) {
+            if (elanInstanceName != null && NetworkType.VLAN.equals(subnetmapUpdate.getNetworkType())) {
+                vpnUtil.addRouterPortToElanDpnListForVlaninAllDpn(vpnIdNew.getValue());
+            }
+        }
+        if (vpnIdOld != null && vpnIdNew == null) {
+            if (NetworkType.VLAN.equals(subnetmapOriginal.getNetworkType())) {
+                vpnUtil.removeRouterPortFromElanDpnListForVlanInAllDpn(elanInstanceName, subnetmapOriginal
+                        .getRouterInterfacePortId().getValue(), vpnIdOld.getValue());
+            }
+        }
     }
 
-    // TODO Clean up the exception handling
-    @SuppressWarnings("checkstyle:IllegalCatch")
+    @SuppressWarnings("all")
     protected long getElanTag(String elanInstanceName) {
-        InstanceIdentifier<ElanInstance> elanIdentifierId = InstanceIdentifier.builder(ElanInstances.class)
-                .child(ElanInstance.class, new ElanInstanceKey(elanInstanceName)).build();
-        long elanTag = 0L;
-        try {
-            Optional<ElanInstance> elanInstance = VpnUtil.read(dataBroker, LogicalDatastoreType
-                    .CONFIGURATION, elanIdentifierId);
-            if (elanInstance.isPresent()) {
-                if (elanInstance.get().getElanTag() != null) {
-                    elanTag = elanInstance.get().getElanTag();
+        final long[] elanTag = {0L};
+
+        ListenableFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(tx -> {
+            InstanceIdentifier<ElanInstance> elanIdentifierId = InstanceIdentifier.builder(ElanInstances.class)
+                    .child(ElanInstance.class, new ElanInstanceKey(elanInstanceName)).build();
+            ElanInstance elanInstance = tx.read(LogicalDatastoreType.CONFIGURATION, elanIdentifierId)
+                    .get().orElse(null);
+            if (elanInstance != null) {
+                if (elanInstance.getElanTag() != null) {
+                    elanTag[0] =elanInstance.getElanTag().longValue();
                 } else {
                     LOG.error("Notification failed because of failure in fetching elanTag for ElanInstance {}",
                             elanInstanceName);
@@ -254,10 +254,8 @@ public class SubnetmapChangeListener extends AsyncDataTreeChangeListenerBase<Sub
             } else {
                 LOG.error("Notification failed because of failure in reading ELANInstance {}", elanInstanceName);
             }
-        } catch (Exception e) {
-            LOG.error("Notification failed because of failure in fetching elanTag for ElanInstance {}",
-                elanInstanceName, e);
-        }
-        return elanTag;
+        }), LOG, "Error binding an ELAN tag for elanInstance {}", elanInstanceName);
+
+        return elanTag[0];
     }
 }