kvm: ref-count secondary storage pool usage (#9498)

If a secondary storage pool is used by e.g.
2 concurrent snapshot->template actions,
if the first action finished it removed the netfs mount
point for the other action.
Now the storage pools are usage ref-counted and will only
deleted if there are no more users.
This commit is contained in:
Rene Peinthor 2024-11-13 14:32:46 +01:00 committed by GitHub
parent adbf370909
commit dfe4a67859
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 62 additions and 10 deletions

View File

@ -43,7 +43,7 @@ public class IscsiAdmStorageAdaptor implements StorageAdaptor {
private static final Map<String, KVMStoragePool> MapStorageUuidToStoragePool = new HashMap<>();
@Override
public KVMStoragePool createStoragePool(String uuid, String host, int port, String path, String userInfo, StoragePoolType storagePoolType, Map<String, String> details) {
public KVMStoragePool createStoragePool(String uuid, String host, int port, String path, String userInfo, StoragePoolType storagePoolType, Map<String, String> details, boolean isPrimaryStorage) {
IscsiAdmStoragePool storagePool = new IscsiAdmStoragePool(uuid, host, port, storagePoolType, this);
MapStorageUuidToStoragePool.put(uuid, storagePool);

View File

@ -361,7 +361,7 @@ public class KVMStoragePoolManager {
//Note: due to bug CLOUDSTACK-4459, createStoragepool can be called in parallel, so need to be synced.
private synchronized KVMStoragePool createStoragePool(String name, String host, int port, String path, String userInfo, StoragePoolType type, Map<String, String> details, boolean primaryStorage) {
StorageAdaptor adaptor = getStorageAdaptor(type);
KVMStoragePool pool = adaptor.createStoragePool(name, host, port, path, userInfo, type, details);
KVMStoragePool pool = adaptor.createStoragePool(name, host, port, path, userInfo, type, details, primaryStorage);
// LibvirtStorageAdaptor-specific statement
if (pool.isPoolSupportHA() && primaryStorage) {

View File

@ -72,6 +72,7 @@ import static com.cloud.utils.NumbersUtil.toHumanReadableSize;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
@ -80,6 +81,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
private StorageLayer _storageLayer;
private String _mountPoint = "/mnt";
private String _manageSnapshotPath;
private static final ConcurrentHashMap<String, Integer> storagePoolRefCounts = new ConcurrentHashMap<>();
private String rbdTemplateSnapName = "cloudstack-base-snap";
private static final int RBD_FEATURE_LAYERING = 1;
@ -637,8 +639,44 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
}
}
/**
* adjust refcount
*/
private int adjustStoragePoolRefCount(String uuid, int adjustment) {
final String mutexKey = storagePoolRefCounts.keySet().stream()
.filter(k -> k.equals(uuid))
.findFirst()
.orElse(uuid);
synchronized (mutexKey) {
// some access on the storagePoolRefCounts.key(mutexKey) element
int refCount = storagePoolRefCounts.computeIfAbsent(mutexKey, k -> 0);
refCount += adjustment;
if (refCount < 1) {
storagePoolRefCounts.remove(mutexKey);
} else {
storagePoolRefCounts.put(mutexKey, refCount);
}
return refCount;
}
}
/**
* Thread-safe increment storage pool usage refcount
* @param uuid UUID of the storage pool to increment the count
*/
private void incStoragePoolRefCount(String uuid) {
adjustStoragePoolRefCount(uuid, 1);
}
/**
* Thread-safe decrement storage pool usage refcount for the given uuid and return if storage pool still in use.
* @param uuid UUID of the storage pool to decrement the count
* @return true if the storage pool is still used, else false.
*/
private boolean decStoragePoolRefCount(String uuid) {
return adjustStoragePoolRefCount(uuid, -1) > 0;
}
@Override
public KVMStoragePool createStoragePool(String name, String host, int port, String path, String userInfo, StoragePoolType type, Map<String, String> details) {
public KVMStoragePool createStoragePool(String name, String host, int port, String path, String userInfo, StoragePoolType type, Map<String, String> details, boolean isPrimaryStorage) {
s_logger.info("Attempting to create storage pool " + name + " (" + type.toString() + ") in libvirt");
StoragePool sp = null;
@ -744,6 +782,12 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
}
try {
if (!isPrimaryStorage) {
// only ref count storage pools for secondary storage, as primary storage is assumed
// to be always mounted, as long the primary storage isn't fully deleted.
incStoragePoolRefCount(name);
}
if (sp.isActive() == 0) {
s_logger.debug("Attempting to activate pool " + name);
sp.create(0);
@ -755,6 +799,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
return getStoragePool(name);
} catch (LibvirtException e) {
decStoragePoolRefCount(name);
String error = e.toString();
if (error.contains("Storage source conflict")) {
throw new CloudRuntimeException("A pool matching this location already exists in libvirt, " +
@ -805,6 +850,13 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
@Override
public boolean deleteStoragePool(String uuid) {
s_logger.info("Attempting to remove storage pool " + uuid + " from libvirt");
// decrement and check if storage pool still in use
if (decStoragePoolRefCount(uuid)) {
s_logger.info(String.format("deleteStoragePool: Storage pool %s still in use", uuid));
return true;
}
Connect conn;
try {
conn = LibvirtConnection.getConnection();

View File

@ -55,7 +55,7 @@ public class ManagedNfsStorageAdaptor implements StorageAdaptor {
}
@Override
public KVMStoragePool createStoragePool(String uuid, String host, int port, String path, String userInfo, StoragePoolType storagePoolType, Map<String, String> details) {
public KVMStoragePool createStoragePool(String uuid, String host, int port, String path, String userInfo, StoragePoolType storagePoolType, Map<String, String> details, boolean isPrimaryStorage) {
LibvirtStoragePool storagePool = new LibvirtStoragePool(uuid, path, StoragePoolType.ManagedNFS, this, null);
storagePool.setSourceHost(host);

View File

@ -168,7 +168,7 @@ public abstract class MultipathSCSIAdapterBase implements StorageAdaptor {
}
@Override
public KVMStoragePool createStoragePool(String uuid, String host, int port, String path, String userInfo, Storage.StoragePoolType type, Map<String, String> details) {
public KVMStoragePool createStoragePool(String uuid, String host, int port, String path, String userInfo, Storage.StoragePoolType type, Map<String, String> details, boolean isPrimaryStorage) {
LOGGER.info(String.format("createStoragePool(uuid,host,port,path,type) called with args (%s, %s, %s, %s, %s)", uuid, host, ""+port, path, type));
MultipathSCSIPool storagePool = new MultipathSCSIPool(uuid, host, port, path, type, details, this);
MapStorageUuidToStoragePool.put(uuid, storagePool);

View File

@ -143,7 +143,7 @@ public class ScaleIOStorageAdaptor implements StorageAdaptor {
}
@Override
public KVMStoragePool createStoragePool(String uuid, String host, int port, String path, String userInfo, Storage.StoragePoolType type, Map<String, String> details) {
public KVMStoragePool createStoragePool(String uuid, String host, int port, String path, String userInfo, Storage.StoragePoolType type, Map<String, String> details, boolean isPrimaryStorage) {
ScaleIOStoragePool storagePool = new ScaleIOStoragePool(uuid, host, port, path, type, details, this);
MapStorageUuidToStoragePool.put(uuid, storagePool);
return storagePool;

View File

@ -38,7 +38,7 @@ public interface StorageAdaptor {
// it with info from local disk, and return it
public KVMPhysicalDisk getPhysicalDisk(String volumeUuid, KVMStoragePool pool);
public KVMStoragePool createStoragePool(String name, String host, int port, String path, String userInfo, StoragePoolType type, Map<String, String> details);
public KVMStoragePool createStoragePool(String name, String host, int port, String path, String userInfo, StoragePoolType type, Map<String, String> details, boolean isPrimaryStorage);
public boolean deleteStoragePool(String uuid);

View File

@ -86,6 +86,6 @@ public class LibvirtStorageAdaptorTest {
Map<String, String> details = new HashMap<>();
details.put("nfsmountopts", "vers=4.1, nconnect=4");
KVMStoragePool pool = libvirtStorageAdaptor.createStoragePool(uuid, null, 0, dir, null, Storage.StoragePoolType.NetworkFilesystem, details);
KVMStoragePool pool = libvirtStorageAdaptor.createStoragePool(uuid, null, 0, dir, null, Storage.StoragePoolType.NetworkFilesystem, details, true);
}
}

View File

@ -149,7 +149,7 @@ public class LinstorStorageAdaptor implements StorageAdaptor {
@Override
public KVMStoragePool createStoragePool(String name, String host, int port, String path, String userInfo,
Storage.StoragePoolType type, Map<String, String> details)
Storage.StoragePoolType type, Map<String, String> details, boolean isPrimaryStorage)
{
s_logger.debug(String.format(
"Linstor createStoragePool: name: '%s', host: '%s', path: %s, userinfo: %s", name, host, path, userInfo));

View File

@ -57,7 +57,7 @@ public class StorPoolStorageAdaptor implements StorageAdaptor {
private static final Map<String, KVMStoragePool> storageUuidToStoragePool = new HashMap<String, KVMStoragePool>();
@Override
public KVMStoragePool createStoragePool(String uuid, String host, int port, String path, String userInfo, StoragePoolType storagePoolType, Map<String, String> details) {
public KVMStoragePool createStoragePool(String uuid, String host, int port, String path, String userInfo, StoragePoolType storagePoolType, Map<String, String> details, boolean isPrimaryStorage) {
SP_LOG("StorPoolStorageAdaptor.createStoragePool: uuid=%s, host=%s:%d, path=%s, userInfo=%s, type=%s", uuid, host, port, path, userInfo, storagePoolType);
StorPoolStoragePool storagePool = new StorPoolStoragePool(uuid, host, port, storagePoolType, this);