NEUTRON-208: Neutronvpn changes for BGPVPN network and router association
[netvirt.git] / neutronvpn / impl / src / main / java / org / opendaylight / netvirt / neutronvpn / NeutronBgpvpnChangeListener.java
index cc004ba201c95fe4eac366ff9be75258e49af756..797be2f0e0e7917856c18a68baaed373f7c2104c 100644 (file)
@@ -7,29 +7,24 @@
  */
 package org.opendaylight.netvirt.neutronvpn;
 
-import java.math.BigInteger;
 import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
 import javax.annotation.PreDestroy;
 import javax.inject.Inject;
 import javax.inject.Singleton;
 import org.opendaylight.infrautils.utils.concurrent.Executors;
+import org.opendaylight.infrautils.utils.concurrent.NamedLocks;
+import org.opendaylight.infrautils.utils.concurrent.NamedSimpleReentrantLock.AcquireResult;
 import org.opendaylight.mdsal.binding.api.DataBroker;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
 import org.opendaylight.netvirt.neutronvpn.api.utils.NeutronConstants;
 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.genius.idmanager.rev160406.CreateIdPoolInput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInputBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolOutput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.vpnmaps.VpnMap;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.bgpvpns.rev150903.BgpvpnTypeBase;
@@ -38,8 +33,6 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.bgpvpns.rev150903.b
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.bgpvpns.rev150903.bgpvpns.attributes.bgpvpns.Bgpvpn;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.common.RpcError;
-import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.FrameworkUtil;
 import org.slf4j.Logger;
@@ -47,23 +40,26 @@ import org.slf4j.LoggerFactory;
 
 @Singleton
 public class NeutronBgpvpnChangeListener extends AbstractAsyncDataTreeChangeListener<Bgpvpn> {
+
     private static final Logger LOG = LoggerFactory.getLogger(NeutronBgpvpnChangeListener.class);
-    private final DataBroker dataBroker;
+
     private final NeutronvpnManager nvpnManager;
     private final IdManagerService idManager;
     private final NeutronvpnUtils neutronvpnUtils;
+    private final NeutronBgpvpnUtils neutronBgpvpnUtils;
     private final String adminRDValue;
 
     @Inject
     public NeutronBgpvpnChangeListener(final DataBroker dataBroker, final NeutronvpnManager neutronvpnManager,
-                                       final IdManagerService idManager, final NeutronvpnUtils neutronvpnUtils) {
-        super(dataBroker, LogicalDatastoreType.CONFIGURATION, InstanceIdentifier.create(Neutron.class)
-                .child(Bgpvpns.class).child(Bgpvpn.class), Executors.newSingleThreadExecutor(
-                        "NeutronBgpvpnChangeListener", LOG));
-        this.dataBroker = dataBroker;
-        nvpnManager = neutronvpnManager;
+            final IdManagerService idManager, final NeutronvpnUtils neutronvpnUtils,
+            final NeutronBgpvpnUtils neutronBgpvpnUtils) {
+        super(dataBroker, LogicalDatastoreType.CONFIGURATION,
+                InstanceIdentifier.create(Neutron.class).child(Bgpvpns.class).child(Bgpvpn.class),
+                Executors.newSingleThreadExecutor("NeutronBgpvpnChangeListener", LOG));
+        this.nvpnManager = neutronvpnManager;
         this.idManager = idManager;
         this.neutronvpnUtils = neutronvpnUtils;
+        this.neutronBgpvpnUtils = neutronBgpvpnUtils;
         BundleContext bundleContext = FrameworkUtil.getBundle(NeutronBgpvpnChangeListener.class).getBundleContext();
         adminRDValue = bundleContext.getProperty(NeutronConstants.RD_PROPERTY_KEY);
         init();
@@ -71,7 +67,6 @@ public class NeutronBgpvpnChangeListener extends AbstractAsyncDataTreeChangeList
 
     public void init() {
         LOG.info("{} init", getClass().getSimpleName());
-        createIdPool();
     }
 
     @Override
@@ -95,8 +90,18 @@ public class NeutronBgpvpnChangeListener extends AbstractAsyncDataTreeChangeList
     @SuppressWarnings("checkstyle:IllegalCatch")
     public void add(InstanceIdentifier<Bgpvpn> identifier, Bgpvpn input) {
         LOG.trace("Adding Bgpvpn : key: {}, value={}", identifier, input);
+
         String vpnName = input.getUuid().getValue();
-        if (isBgpvpnTypeL3(input.getType())) {
+        if (!isBgpvpnTypeL3(input.getType())) {
+            LOG.warn("BGPVPN type for VPN {} is not L3", vpnName);
+            return;
+        }
+        NamedLocks<String> vpnLock = neutronBgpvpnUtils.getVpnLock();
+        try (AcquireResult lock = vpnLock.tryAcquire(vpnName, NeutronConstants.LOCK_WAIT_TIME, TimeUnit.SECONDS)) {
+            if (!lock.wasAcquired()) {
+                LOG.error("Add BGPVPN: add bgpvpn failed for vpn : {} due to failure in acquiring lock", vpnName);
+                return;
+            }
             // handle route-target(s)
             List<String> inputRouteList = input.getRouteTargets();
             List<String> inputImportRouteList = input.getImportTargets();
@@ -114,59 +119,86 @@ public class NeutronBgpvpnChangeListener extends AbstractAsyncDataTreeChangeList
             if (inputExportRouteList != null && !inputExportRouteList.isEmpty()) {
                 inputExportRouteSet.addAll(inputExportRouteList);
             }
-            List<String> importRouteTargets = new ArrayList<>();
-            List<String> exportRouteTargets = new ArrayList<>();
-            importRouteTargets.addAll(inputImportRouteSet);
-            exportRouteTargets.addAll(inputExportRouteSet);
+            List<String> importRouteTargets = new ArrayList<>(inputImportRouteSet);
+            List<String> exportRouteTargets = new ArrayList<>(inputExportRouteSet);
+            boolean rdIrtErtStringsValid;
 
-            List<String> rd = input.getRouteDistinguishers() != null
-                    ? input.getRouteDistinguishers() : new ArrayList<>();
+            List<String> rdList = input.getRouteDistinguishers();
 
-            if (rd == null || rd.isEmpty()) {
-                // generate new RD
-                // TODO - commented out for now to avoid "Dead store to rd" violation.
-                //rd = generateNewRD(input.getUuid());
-            } else {
-                String[] rdParams = rd.get(0).split(":");
+            if (rdList != null && !rdList.isEmpty()) {
+                // get the primary RD for vpn instance, if exist
+                rdIrtErtStringsValid =
+                        !(input.getRouteDistinguishers().stream().anyMatch(rdStr -> rdStr.contains(" ")));
+                rdIrtErtStringsValid =
+                        rdIrtErtStringsValid && !(importRouteTargets.stream().anyMatch(irtStr -> irtStr.contains(" ")));
+                rdIrtErtStringsValid =
+                        rdIrtErtStringsValid && !(exportRouteTargets.stream().anyMatch(ertStr -> ertStr.contains(" ")));
+                if (!rdIrtErtStringsValid) {
+                    LOG.error("Error encountered for BGPVPN {} with RD {} as RD/iRT/eRT contains whitespace "
+                            + "characters", vpnName, input.getRouteDistinguishers());
+                    return;
+                }
+                String primaryRd = neutronvpnUtils.getVpnRd(vpnName);
+                if (primaryRd == null) {
+                    primaryRd = rdList.get(0);
+                }
+
+                String[] rdParams = primaryRd.split(":");
                 if (rdParams[0].trim().equals(adminRDValue)) {
                     LOG.error("AS specific part of RD should not be same as that defined by DC Admin. Error "
-                            + "encountered for BGPVPN {} with RD {}", vpnName, rd.get(0));
+                            + "encountered for BGPVPN {} with RD {}", vpnName, primaryRd);
+                    return;
+                }
+                String vpnWithSameRd = neutronvpnUtils.getVpnForRD(primaryRd);
+                if (vpnWithSameRd != null) {
+                    LOG.error("Creation of L3VPN failed for VPN {} as another VPN {} with the same RD {} "
+                            + "is already configured", vpnName, vpnWithSameRd, primaryRd);
                     return;
                 }
-                List<String> existingRDs = neutronvpnUtils.getExistingRDs();
-                if (!Collections.disjoint(existingRDs, rd)) {
-                    LOG.error("Failed to create VPN {} as another VPN with the same RD {} already exists.", vpnName,
-                            rd);
+                String existingOperationalVpn = neutronvpnUtils.getExistingOperationalVpn(primaryRd);
+                if (existingOperationalVpn != null) {
+                    LOG.error("checkVpnCreation: Creation of L3VPN failed for VPN {} as another VPN {} with the "
+                            + "same RD {} is still available.", vpnName, existingOperationalVpn, primaryRd);
                     return;
                 }
-                List<Uuid> routersList = null;
-                if (input.getRouters() != null && !input.getRouters().isEmpty()) {
-                    // try to take all routers
-                    routersList = input.getRouters();
+                List<Uuid> unpRtrs = neutronBgpvpnUtils.getUnprocessedRoutersForBgpvpn(input.getUuid());
+                List<Uuid> unpNets = neutronBgpvpnUtils.getUnprocessedNetworksForBgpvpn(input.getUuid());
+
+                // TODO: Currently handling routers and networks for backward compatibility. Below logic needs to be
+                // removed once updated to latest BGPVPN API's.
+                List<Uuid> inputRouters = input.getRouters();
+                if (inputRouters != null && !inputRouters.isEmpty()) {
+                    if (unpRtrs != null) {
+                        unpRtrs.addAll(inputRouters);
+                    } else {
+                        unpRtrs = new ArrayList<>(inputRouters);
+                    }
                 }
-                if (routersList != null && routersList.size() > NeutronConstants.MAX_ROUTERS_PER_BGPVPN) {
+                if (unpRtrs != null && unpRtrs.size() > NeutronConstants.MAX_ROUTERS_PER_BGPVPN) {
                     LOG.error("Creation of BGPVPN for rd {} failed: maximum allowed number of associated "
-                             + "routers is {}.", rd, NeutronConstants.MAX_ROUTERS_PER_BGPVPN);
+                            + "routers is {}.", rdList, NeutronConstants.MAX_ROUTERS_PER_BGPVPN);
                     return;
                 }
-                List<Uuid> networkList = null;
-                if (input.getNetworks() != null && !input.getNetworks().isEmpty()) {
-                    networkList = input.getNetworks();
-                }
-                if (!rd.isEmpty()) {
-                    try {
-                        nvpnManager.createVpn(input.getUuid(), input.getName(), input.getTenantId(), rd,
-                                importRouteTargets, exportRouteTargets, routersList, networkList,
-                                false /*isL2Vpn*/, 0 /*l3vni*/);
-                    } catch (Exception e) {
-                        LOG.error("Creation of BGPVPN {} failed", vpnName, e);
+                List<Uuid> inputNetworks = input.getNetworks();
+                if (inputNetworks != null && !inputNetworks.isEmpty()) {
+                    if (unpNets != null) {
+                        unpNets.addAll(inputNetworks);
+                    } else {
+                        unpNets = new ArrayList<>(inputNetworks);
                     }
-                } else {
-                    LOG.error("Create BgpVPN with id {} failed due to missing RD value", vpnName);
                 }
+                try {
+                    nvpnManager.createVpn(input.getUuid(), input.getName(), input.getTenantId(), rdList,
+                            importRouteTargets, exportRouteTargets, unpRtrs, unpNets, false /* isL2Vpn */,
+                            0 /* l3vni */);
+                    neutronBgpvpnUtils.getUnProcessedRoutersMap().remove(input.getUuid());
+                    neutronBgpvpnUtils.getUnProcessedNetworksMap().remove(input.getUuid());
+                } catch (Exception e) {
+                    LOG.error("Creation of BGPVPN {} failed with error ", vpnName, e);
+                }
+            } else {
+                LOG.error("add: RD is absent for BGPVPN {}", vpnName);
             }
-        } else {
-            LOG.warn("BGPVPN type for VPN {} is not L3", vpnName);
         }
     }
 
@@ -174,24 +206,26 @@ public class NeutronBgpvpnChangeListener extends AbstractAsyncDataTreeChangeList
     public void remove(InstanceIdentifier<Bgpvpn> identifier, Bgpvpn input) {
         LOG.trace("Removing Bgpvpn : key: {}, value={}", identifier, input);
         Uuid vpnId = input.getUuid();
-        if (isBgpvpnTypeL3(input.getType())) {
+        String vpnName = vpnId.getValue();
+        if (!isBgpvpnTypeL3(input.getType())) {
+            LOG.warn("BGPVPN type for VPN {} is not L3", vpnName);
+            return;
+        }
+        NamedLocks<String> vpnLock = neutronBgpvpnUtils.getVpnLock();
+        try (AcquireResult lock = vpnLock.tryAcquire(vpnName, NeutronConstants.LOCK_WAIT_TIME, TimeUnit.SECONDS)) {
+            if (!lock.wasAcquired()) {
+                LOG.error("Remove BGPVPN: remove bgpvpn failed for vpn : {} due to failure in acquiring lock", vpnName);
+                return;
+            }
+            neutronBgpvpnUtils.getUnProcessedRoutersMap().remove(input.getUuid());
+            neutronBgpvpnUtils.getUnProcessedNetworksMap().remove(input.getUuid());
             VpnMap vpnMap = neutronvpnUtils.getVpnMap(vpnId);
             if (vpnMap == null) {
                 LOG.error("Failed to handle BGPVPN Remove for VPN {} as that VPN is not configured"
-                        + " yet as a VPN Instance", vpnId.getValue());
+                        + " yet as a VPN Instance", vpnName);
                 return;
             }
             nvpnManager.removeVpn(input.getUuid());
-            // Release RD Id in pool
-            List<String> rd = input.getRouteDistinguishers();
-            if (rd == null || rd.isEmpty()) {
-                int releasedId = neutronvpnUtils.releaseId(NeutronConstants.RD_IDPOOL_NAME, vpnId.getValue());
-                if (releasedId == NeutronConstants.INVALID_ID) {
-                    LOG.error("NeutronBgpvpnChangeListener remove: Unable to release ID for key {}", vpnId.getValue());
-                }
-            }
-        } else {
-            LOG.warn("BGPVPN type for VPN {} is not L3", vpnId.getValue());
         }
     }
 
@@ -201,35 +235,56 @@ public class NeutronBgpvpnChangeListener extends AbstractAsyncDataTreeChangeList
         if (Objects.equals(original, update)) {
             return;
         }
-        Uuid vpnId = update.getUuid();
-        if (isBgpvpnTypeL3(update.getType())) {
-            try {
-                handleVpnInstanceUpdate(original.getUuid().getValue(), original.getRouteDistinguishers(),
-                        update.getRouteDistinguishers());
-            } catch (UnsupportedOperationException e) {
-                LOG.error("Error while processing Update Bgpvpn.", e);
+        String vpnName = update.getUuid().getValue();
+        if (!isBgpvpnTypeL3(update.getType())) {
+            LOG.warn("BGPVPN type for VPN {} is not L3", vpnName);
+            return;
+        }
+        boolean rdIrtErtStringsValid = true;
+        rdIrtErtStringsValid = rdIrtErtStringsValid
+                && !(update.getRouteDistinguishers().stream().anyMatch(rdStr -> rdStr.contains(" ")));
+        rdIrtErtStringsValid =
+                rdIrtErtStringsValid && !(update.getImportTargets().stream().anyMatch(irtStr -> irtStr.contains(" ")));
+        rdIrtErtStringsValid =
+                rdIrtErtStringsValid && !(update.getExportTargets().stream().anyMatch(ertStr -> ertStr.contains(" ")));
+        if (!rdIrtErtStringsValid) {
+            LOG.error("Error encountered for BGPVPN {} with RD {} as RD/iRT/eRT contains whitespace characters",
+                    vpnName, update.getRouteDistinguishers());
+            return;
+        }
+        NamedLocks<String> vpnLock = neutronBgpvpnUtils.getVpnLock();
+        try (AcquireResult lock = vpnLock.tryAcquire(vpnName, NeutronConstants.LOCK_WAIT_TIME, TimeUnit.SECONDS)) {
+            if (!lock.wasAcquired()) {
+                LOG.error("Update VPN: update failed for vpn : {} due to failure in acquiring lock", vpnName);
                 return;
             }
-            List<Uuid> oldNetworks = original.getNetworks();
-            List<Uuid> newNetworks = update.getNetworks();
+            handleVpnInstanceUpdate(original.getUuid().getValue(), original.getRouteDistinguishers(),
+                    update.getRouteDistinguishers());
+
+            // TODO: Currently handling routers and networks for backward compatibility. Below logic needs to be
+            // removed once updated to latest BGPVPN API's.
+            Uuid vpnId = update.getUuid();
+            List<Uuid> oldNetworks = new ArrayList<>(original.getNetworks());
+            List<Uuid> newNetworks = new ArrayList<>(update.getNetworks());
             handleNetworksUpdate(vpnId, oldNetworks, newNetworks);
+
             List<Uuid> oldRouters = original.getRouters();
             List<Uuid> newRouters = update.getRouters();
             handleRoutersUpdate(vpnId, oldRouters, newRouters);
-        } else {
-            LOG.warn("BGPVPN type for VPN {} is not L3", vpnId.getValue());
+        } catch (UnsupportedOperationException e) {
+            LOG.error("Error while processing Update Bgpvpn.", e);
         }
     }
 
-    protected void handleVpnInstanceUpdate(String vpnInstanceName,final List<String> originalRds,
-                                           List<String> updateRDs) throws UnsupportedOperationException {
+    protected void handleVpnInstanceUpdate(String vpnInstanceName, final List<String> originalRds,
+            List<String> updateRDs) throws UnsupportedOperationException {
         if (updateRDs == null || updateRDs.isEmpty()) {
             return;
         }
         int oldRdsCount = originalRds.size();
 
         for (String rd : originalRds) {
-            //If the existing rd is not present in the updateRds list, not allow to process the updateRDs.
+            // If the existing rd is not present in the updateRds list, not allow to process the updateRDs.
             if (!updateRDs.contains(rd)) {
                 LOG.error("The existing RD {} not present in the updatedRDsList:{}", rd, updateRDs);
                 throw new UnsupportedOperationException("The existing RD not present in the updatedRDsList");
@@ -243,7 +298,18 @@ public class NeutronBgpvpnChangeListener extends AbstractAsyncDataTreeChangeList
         nvpnManager.updateVpnInstanceWithRDs(vpnInstanceName, updateRDs);
     }
 
-    protected void handleNetworksUpdate(Uuid vpnId, List<Uuid> oldNetworks, List<Uuid> newNetworks) {
+    /**
+     * Handle networks update.
+     *
+     * @deprecated Retaining method for backward compatibility. Below method needs to be removed once
+     *             updated to latest BGPVPN API's.
+     *
+     * @param vpnId the vpn id
+     * @param oldNetworks the old networks
+     * @param newNetworks the new networks
+     */
+    @Deprecated
+    private void handleNetworksUpdate(Uuid vpnId, List<Uuid> oldNetworks, List<Uuid> newNetworks) {
         if (newNetworks != null && !newNetworks.isEmpty()) {
             if (oldNetworks != null && !oldNetworks.isEmpty()) {
                 if (oldNetworks != newNetworks) {
@@ -294,7 +360,18 @@ public class NeutronBgpvpnChangeListener extends AbstractAsyncDataTreeChangeList
         }
     }
 
-    protected void handleRoutersUpdate(Uuid vpnId, List<Uuid> oldRouters, List<Uuid> newRouters) {
+    /**
+     * Handle routers update.
+     *
+     * @deprecated Retaining method for backward compatibility. Below method needs to be removed once
+     *             updated to latest BGPVPN API's.
+     *
+     * @param vpnId the vpn id
+     * @param oldRouters the old routers
+     * @param newRouters the new routers
+     */
+    @Deprecated
+    private void handleRoutersUpdate(Uuid vpnId, List<Uuid> oldRouters, List<Uuid> newRouters) {
         // for dualstack case we can associate with one VPN instance maximum 2 routers: one with
         // only IPv4 ports and one with only IPv6 ports, or only one router with IPv4/IPv6 ports
         // TODO: check router ports ethertype to follow this restriction
@@ -335,30 +412,6 @@ public class NeutronBgpvpnChangeListener extends AbstractAsyncDataTreeChangeList
         }
     }
 
-    private void createIdPool() {
-        CreateIdPoolInput createPool = new CreateIdPoolInputBuilder().setPoolName(NeutronConstants.RD_IDPOOL_NAME)
-                .setLow(NeutronConstants.RD_IDPOOL_START)
-                .setHigh(new BigInteger(NeutronConstants.RD_IDPOOL_SIZE).longValue()).build();
-        try {
-            Future<RpcResult<CreateIdPoolOutput>> result = idManager.createIdPool(createPool);
-            Collection<RpcError> rpcErrors = null;
-            if (result != null && result.get() != null) {
-                RpcResult<CreateIdPoolOutput> rpcResult = result.get();
-                LOG.info("Created IdPool for Bgpvpn RD");
-                if (rpcResult.isSuccessful()) {
-                    LOG.info("Created IdPool for Bgpvpn RD");
-                    return;
-                }
-                rpcErrors = rpcResult.getErrors();
-                LOG.error("Failed to create ID pool for BGPVPN RD, result future returned {}", result);
-            }
-            LOG.error("createIdPool: Failed to create ID pool for BGPVPN RD, the call returned with RPC errors {}",
-                    rpcErrors != null ? rpcErrors : "RpcResult is null");
-        } catch (InterruptedException | ExecutionException e) {
-            LOG.error("Failed to create idPool for Bgpvpn RD", e);
-        }
-    }
-
     private boolean validateRouteInfo(Uuid routerID) {
         Uuid assocVPNId;
         if ((assocVPNId = neutronvpnUtils.getVpnForRouter(routerID, true)) != null) {
@@ -368,5 +421,4 @@ public class NeutronBgpvpnChangeListener extends AbstractAsyncDataTreeChangeList
         }
         return true;
     }
-
-}
+}
\ No newline at end of file