diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/UpdateStoragePoolCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/UpdateStoragePoolCmd.java index 4b0a6ba00b2..9529da84df7 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/UpdateStoragePoolCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/UpdateStoragePoolCmd.java @@ -73,7 +73,12 @@ public class UpdateStoragePoolCmd extends BaseCmd { @Parameter(name = ApiConstants.URL, type = CommandType.STRING, required = false, - description = "the URL of the storage pool", + description = "the URL of the storage pool. Supported only for NFS and Gluster pool type." + + "The pool must be in Maintenance mode before changing the URL. WARNING: use this parameter" + + "with caution. It is intended for failover scenarios where the storage content is already " + + "fully mirrored at the new location. Pointing to a new location without ensuring complete " + + "data parity will result in data loss or corruption. After the URL is updated, the new mount" + + "is applied to all connected hosts or restart the Management server", since = "4.19.0") private String url; diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java index a03daeb197b..5e00c24e45c 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java @@ -766,6 +766,39 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { path = path.substring(0, path.length() - 1); } + // For NFS and Gluster pools, if the existing pool's source host or path has changed + // (e.g. after a storage URL update), destroy and undefine it so it gets recreated + // with the new source below. Restricted to NFS/Gluster only. + if (sp != null && (type == StoragePoolType.NetworkFilesystem || type == StoragePoolType.Gluster)) { + try { + LibvirtStoragePoolDef existingDef = getStoragePoolDef(conn, sp); + if (existingDef != null) { + String existingSourceHost = existingDef.getSourceHost(); + String existingSourceDir = existingDef.getSourceDir(); + boolean hostChanged = host != null && existingSourceHost != null && !existingSourceHost.equals(host); + boolean pathChanged = path != null && existingSourceDir != null && !existingSourceDir.equals(path); + if (hostChanged || pathChanged) { + logger.info("Storage pool {} source has changed (host: {} -> {}, path: {} -> {}); destroying and redefining.", + name, existingSourceHost, host, existingSourceDir, path); + try { + if (sp.isPersistent() == 1) { + sp.destroy(); + sp.undefine(); + } else { + sp.destroy(); + } + sp.free(); + sp = null; + } catch (LibvirtException destroyEx) { + logger.error("Failed to destroy storage pool {} with changed source; will attempt to reuse existing pool: {}", name, destroyEx.getMessage()); + } + } + } + } catch (LibvirtException e) { + logger.warn("Failed to check existing storage pool {} source path, will attempt to reuse it: {}", name, e.getMessage()); + } + } + if (sp == null) { // see if any existing pool by another name is using our storage path. // if anyone is, undefine the pool so we can define it as requested. diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptorTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptorTest.java index 88346abd017..259cb603aeb 100644 --- a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptorTest.java +++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptorTest.java @@ -42,9 +42,13 @@ import org.mockito.junit.MockitoJUnitRunner; import com.cloud.hypervisor.kvm.resource.LibvirtConnection; import com.cloud.hypervisor.kvm.resource.LibvirtStoragePoolDef; import com.cloud.storage.Storage; +import com.cloud.storage.StorageLayer; import com.cloud.utils.Pair; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.script.Script; +import org.libvirt.LibvirtException; + +import org.springframework.test.util.ReflectionTestUtils; @RunWith(MockitoJUnitRunner.class) public class LibvirtStorageAdaptorTest { @@ -56,6 +60,9 @@ public class LibvirtStorageAdaptorTest { @Mock LibvirtStoragePool mockPool; + @Mock + StorageLayer storageLayer; + MockedStatic