"neutronvpn:createL3VPN" fails to create L3VPN for IPv6 use case.
[netvirt.git] / vpnmanager / impl / src / main / java / org / opendaylight / netvirt / vpnmanager / SubnetmapChangeListener.java
index 16611421c0cd7dde7ebd0e4bac60d0cae115ef06..5ff2973befbbf450144229fef4707730aced1ee0 100644 (file)
@@ -11,16 +11,24 @@ package org.opendaylight.netvirt.vpnmanager;
 import com.google.common.base.Optional;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.locks.ReentrantLock;
 import javax.annotation.PostConstruct;
 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.controller.md.sal.common.api.data.ReadFailedException;
 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
+import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
+import org.opendaylight.genius.utils.JvmGlobalLocks;
+import org.opendaylight.netvirt.vpnmanager.api.IVpnManager;
 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;
@@ -34,12 +42,17 @@ public class SubnetmapChangeListener extends AsyncDataTreeChangeListenerBase<Sub
     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;
 
     @Inject
-    public SubnetmapChangeListener(final DataBroker dataBroker, final VpnSubnetRouteHandler vpnSubnetRouteHandler) {
+    public SubnetmapChangeListener(final DataBroker dataBroker, final VpnSubnetRouteHandler vpnSubnetRouteHandler,
+                                   VpnUtil vpnUtil, IVpnManager vpnManager) {
         super(Subnetmap.class, SubnetmapChangeListener.class);
         this.dataBroker = dataBroker;
         this.vpnSubnetRouteHandler = vpnSubnetRouteHandler;
+        this.vpnUtil = vpnUtil;
+        this.vpnManager = vpnManager;
     }
 
     @PostConstruct
@@ -66,60 +79,56 @@ public class SubnetmapChangeListener extends AsyncDataTreeChangeListenerBase<Sub
 
     @Override
     protected void add(InstanceIdentifier<Subnetmap> identifier, Subnetmap subnetmap) {
-        LOG.trace("add:SubnetmapChangeListener add subnetmap method - key: {}, value: {}", identifier, subnetmap);
+        LOG.debug("SubnetmapChangeListener 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("SubnetMapChangeListener: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("SubnetMapChangeListener: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("SubnetMapChangeListener: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);
+        LOG.trace("SubnetmapListener:remove: subnetmap method - key: {}, value: {}", identifier, subnetmap);
     }
 
     @Override
@@ -127,49 +136,46 @@ public class SubnetmapChangeListener extends AsyncDataTreeChangeListenerBase<Sub
     @SuppressWarnings("checkstyle:IllegalCatch")
     protected 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("SubnetMapChangeListener 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("SubnetMapChangeListener: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("SubnetMapChangeListener: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("SubnetMapChangeListener: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("SubnetMapChangeListener: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("SubnetMapChangeListener: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 +183,7 @@ public class SubnetmapChangeListener extends AsyncDataTreeChangeListenerBase<Sub
         if (newPortList.size() == oldPortList.size()) {
             return;
         }
+        LOG.info("SubnetMapChangeListener:update: update port list for subnet {}", subnetmapUpdate.getId().getValue());
         if (newPortList.size() > oldPortList.size()) {
             for (Uuid portId : newPortList) {
                 if (! oldPortList.contains(portId)) {
@@ -194,40 +201,42 @@ 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;
+    }
+
+    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());
+            }
+        }
     }
 
     @Override
@@ -235,18 +244,16 @@ public class SubnetmapChangeListener extends AsyncDataTreeChangeListenerBase<Sub
         return this;
     }
 
-    // TODO Clean up the exception handling
-    @SuppressWarnings("checkstyle:IllegalCatch")
     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);
+            Optional<ElanInstance> elanInstance = SingleTransactionDataBroker.syncReadOptional(dataBroker,
+                    LogicalDatastoreType.CONFIGURATION, elanIdentifierId);
             if (elanInstance.isPresent()) {
                 if (elanInstance.get().getElanTag() != null) {
-                    elanTag = elanInstance.get().getElanTag();
+                    elanTag = elanInstance.get().getElanTag().toJava();
                 } else {
                     LOG.error("Notification failed because of failure in fetching elanTag for ElanInstance {}",
                             elanInstanceName);
@@ -254,7 +261,7 @@ public class SubnetmapChangeListener extends AsyncDataTreeChangeListenerBase<Sub
             } else {
                 LOG.error("Notification failed because of failure in reading ELANInstance {}", elanInstanceName);
             }
-        } catch (Exception e) {
+        } catch (ReadFailedException e) {
             LOG.error("Notification failed because of failure in fetching elanTag for ElanInstance {}",
                 elanInstanceName, e);
         }