Skip to content

Commit

Permalink
kvm: ref-count secondary storage pool usage (#9498)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
rp- authored Nov 13, 2024
1 parent adbf370 commit dfe4a67
Show file tree
Hide file tree
Showing 10 changed files with 62 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ public KVMStoragePool createStoragePool(String name, String host, int port, Stri
//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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;


Expand All @@ -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;
Expand Down Expand Up @@ -637,8 +639,44 @@ public KVMPhysicalDisk getPhysicalDisk(String volumeUuid, KVMStoragePool pool) {
}
}

/**
* 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;
Expand Down Expand Up @@ -744,6 +782,12 @@ public KVMStoragePool createStoragePool(String name, String host, int port, Stri
}

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);
Expand All @@ -755,6 +799,7 @@ public KVMStoragePool createStoragePool(String name, String host, int port, Stri

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, " +
Expand Down Expand Up @@ -805,6 +850,13 @@ private boolean destroyStoragePoolHandleException(Connect conn, String uuid)
@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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public ManagedNfsStorageAdaptor(StorageLayer storagelayer) {
}

@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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ private KVMPhysicalDisk getPhysicalDisk(AddressInfo address, KVMStoragePool pool
}

@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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ public KVMPhysicalDisk getPhysicalDisk(String volumePath, KVMStoragePool pool) {
}

@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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,6 @@ public void testCreateStoragePoolWithNFSMountOpts() throws Exception {

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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ public KVMPhysicalDisk getPhysicalDisk(String name, KVMStoragePool pool)

@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));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public static void SP_LOG(String fmt, Object... args) {
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);
Expand Down

0 comments on commit dfe4a67

Please sign in to comment.