diff --git a/core/src/main/java/com/cloud/agent/api/ModifyStoragePoolAnswer.java b/core/src/main/java/com/cloud/agent/api/ModifyStoragePoolAnswer.java index 552ffb85aaf6..a3401d6faed7 100644 --- a/core/src/main/java/com/cloud/agent/api/ModifyStoragePoolAnswer.java +++ b/core/src/main/java/com/cloud/agent/api/ModifyStoragePoolAnswer.java @@ -46,6 +46,10 @@ public ModifyStoragePoolAnswer(ModifyStoragePoolCommand cmd, long capacityBytes, templateInfo = tInfo; } + public ModifyStoragePoolAnswer(ModifyStoragePoolCommand cmd, boolean success, String details) { + super(cmd, success, details); + } + public void setPoolInfo(StoragePoolInfo poolInfo) { this.poolInfo = poolInfo; } diff --git a/core/src/main/java/com/cloud/agent/api/UnprepareStorageClientCommand.java b/core/src/main/java/com/cloud/agent/api/UnprepareStorageClientCommand.java index bebd30ca519d..b98ce6fa345e 100644 --- a/core/src/main/java/com/cloud/agent/api/UnprepareStorageClientCommand.java +++ b/core/src/main/java/com/cloud/agent/api/UnprepareStorageClientCommand.java @@ -19,18 +19,22 @@ package com.cloud.agent.api; +import java.util.Map; + import com.cloud.storage.Storage.StoragePoolType; public class UnprepareStorageClientCommand extends Command { private StoragePoolType poolType; private String poolUuid; + private Map details; public UnprepareStorageClientCommand() { } - public UnprepareStorageClientCommand(StoragePoolType poolType, String poolUuid) { + public UnprepareStorageClientCommand(StoragePoolType poolType, String poolUuid, Map details) { this.poolType = poolType; this.poolUuid = poolUuid; + this.details = details; } @Override @@ -45,4 +49,8 @@ public StoragePoolType getPoolType() { public String getPoolUuid() { return poolUuid; } + + public Map getDetails() { + return details; + } } diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreDriver.java b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreDriver.java index b197afad863a..af3b38b368c3 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreDriver.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreDriver.java @@ -45,4 +45,8 @@ public interface DataStoreDriver { boolean canCopy(DataObject srcData, DataObject destData); void resize(DataObject data, AsyncCompletionCallback callback); + + default boolean canDisplayDetails() { + return true; + } } diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreDriver.java b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreDriver.java index 2011b1f08fbf..7561c2286002 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreDriver.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreDriver.java @@ -137,6 +137,14 @@ default boolean canHostPrepareStoragePoolAccess(Host host, StoragePool pool) { return false; } + /** + * intended for managed storage + * returns true if the host can be disconnected from storage pool + */ + default boolean canDisconnectHostFromStoragePool(Host host, StoragePool pool) { + return true; + } + /** * Used by storage pools which want to keep VMs' information * @return true if additional VM info is needed (intended for storage pools). diff --git a/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java b/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java index b51536688990..2da6d81296d5 100644 --- a/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java +++ b/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java @@ -308,6 +308,8 @@ static Boolean getFullCloneConfiguration(Long storeId) { boolean canHostPrepareStoragePoolAccess(Host host, StoragePool pool); + boolean canDisconnectHostFromStoragePool(Host host, StoragePool pool); + Host getHost(long hostId); Host updateSecondaryStorage(long secStorageId, String newUrl); diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDao.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDao.java index f0c235e842c1..d205379cdb24 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDao.java @@ -60,6 +60,8 @@ public interface PrimaryDataStoreDao extends GenericDao { StoragePoolVO persist(StoragePoolVO pool, Map details, List tags, Boolean isTagARule); + StoragePoolVO persist(StoragePoolVO pool, Map details, List tags, Boolean isTagARule, boolean displayDetails); + /** * Find pool by name. * @@ -103,6 +105,8 @@ public interface PrimaryDataStoreDao extends GenericDao { void updateDetails(long poolId, Map details); + void removeDetails(long poolId); + Map getDetails(long poolId); List searchForStoragePoolTags(long poolId); diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java index 1658fe0a537e..f0769ae0b479 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java @@ -296,14 +296,19 @@ public StoragePoolVO listById(Integer id) { } @Override - @DB public StoragePoolVO persist(StoragePoolVO pool, Map details, List tags, Boolean isTagARule) { + return persist(pool, details, tags, isTagARule, true); + } + + @Override + @DB + public StoragePoolVO persist(StoragePoolVO pool, Map details, List tags, Boolean isTagARule, boolean displayDetails) { TransactionLegacy txn = TransactionLegacy.currentTxn(); txn.start(); pool = super.persist(pool); if (details != null) { for (Map.Entry detail : details.entrySet()) { - StoragePoolDetailVO vo = new StoragePoolDetailVO(pool.getId(), detail.getKey(), detail.getValue(), true); + StoragePoolDetailVO vo = new StoragePoolDetailVO(pool.getId(), detail.getKey(), detail.getValue(), displayDetails); _detailsDao.persist(vo); } } @@ -570,6 +575,11 @@ public void updateDetails(long poolId, Map details) { } } + @Override + public void removeDetails(long poolId) { + _detailsDao.removeDetails(poolId); + } + @Override public Map getDetails(long poolId) { return _detailsDao.listDetailsKeyPairs(poolId); diff --git a/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/vmsnapshot/ScaleIOVMSnapshotStrategy.java b/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/vmsnapshot/ScaleIOVMSnapshotStrategy.java index d27beecfddac..b95c1e1d0a80 100644 --- a/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/vmsnapshot/ScaleIOVMSnapshotStrategy.java +++ b/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/vmsnapshot/ScaleIOVMSnapshotStrategy.java @@ -33,6 +33,7 @@ import org.apache.cloudstack.storage.datastore.client.ScaleIOGatewayClient; import org.apache.cloudstack.storage.datastore.client.ScaleIOGatewayClientConnectionPool; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO; import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.datastore.util.ScaleIOUtil; @@ -300,7 +301,11 @@ public boolean revertVMSnapshot(VMSnapshot vmSnapshot) { srcSnapshotDestVolumeMap.put(srcSnapshotVolumeId, destVolumeId); } - String systemId = storagePoolDetailsDao.findDetail(storagePoolId, ScaleIOGatewayClient.STORAGE_POOL_SYSTEM_ID).getValue(); + String systemId = null; + StoragePoolDetailVO systemIdDetail = storagePoolDetailsDao.findDetail(storagePoolId, ScaleIOGatewayClient.STORAGE_POOL_SYSTEM_ID); + if (systemIdDetail != null) { + systemId = systemIdDetail.getValue(); + } if (systemId == null) { throw new CloudRuntimeException("Failed to get the system id for PowerFlex storage pool for reverting VM snapshot: " + vmSnapshot.getName()); } @@ -379,7 +384,11 @@ public boolean deleteVMSnapshot(VMSnapshot vmSnapshot) { try { List volumeTOs = vmSnapshotHelper.getVolumeTOList(vmSnapshot.getVmId()); Long storagePoolId = vmSnapshotHelper.getStoragePoolForVM(userVm.getId()); - String systemId = storagePoolDetailsDao.findDetail(storagePoolId, ScaleIOGatewayClient.STORAGE_POOL_SYSTEM_ID).getValue(); + String systemId = null; + StoragePoolDetailVO systemIdDetail = storagePoolDetailsDao.findDetail(storagePoolId, ScaleIOGatewayClient.STORAGE_POOL_SYSTEM_ID); + if (systemIdDetail != null) { + systemId = systemIdDetail.getValue(); + } if (systemId == null) { throw new CloudRuntimeException("Failed to get the system id for PowerFlex storage pool for deleting VM snapshot: " + vmSnapshot.getName()); } diff --git a/engine/storage/src/main/java/org/apache/cloudstack/storage/volume/datastore/PrimaryDataStoreHelper.java b/engine/storage/src/main/java/org/apache/cloudstack/storage/volume/datastore/PrimaryDataStoreHelper.java index e4c269326198..e4aa808b80b9 100644 --- a/engine/storage/src/main/java/org/apache/cloudstack/storage/volume/datastore/PrimaryDataStoreHelper.java +++ b/engine/storage/src/main/java/org/apache/cloudstack/storage/volume/datastore/PrimaryDataStoreHelper.java @@ -28,13 +28,16 @@ import org.apache.cloudstack.annotation.AnnotationService; import org.apache.cloudstack.annotation.dao.AnnotationDao; -import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.springframework.stereotype.Component; import org.apache.cloudstack.engine.subsystem.api.storage.ClusterScope; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreDriver; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProvider; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProviderManager; import org.apache.cloudstack.engine.subsystem.api.storage.HostScope; import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreParameters; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; @@ -75,6 +78,8 @@ public class PrimaryDataStoreHelper { protected StoragePoolHostDao storagePoolHostDao; @Inject private AnnotationDao annotationDao; + @Inject + DataStoreProviderManager dataStoreProviderMgr; public DataStore createPrimaryDataStore(PrimaryDataStoreParameters params) { if(params == null) @@ -141,7 +146,17 @@ public DataStore createPrimaryDataStore(PrimaryDataStoreParameters params) { storageTags.add(tag); } } - dataStoreVO = dataStoreDao.persist(dataStoreVO, details, storageTags, params.isTagARule()); + + boolean displayDetails = true; + DataStoreProvider storeProvider = dataStoreProviderMgr.getDataStoreProvider(params.getProviderName()); + if (storeProvider != null) { + DataStoreDriver storeDriver = storeProvider.getDataStoreDriver(); + if (storeDriver != null) { + displayDetails = storeDriver.canDisplayDetails(); + } + } + + dataStoreVO = dataStoreDao.persist(dataStoreVO, details, storageTags, params.isTagARule(), displayDetails); return dataStoreMgr.getDataStore(dataStoreVO.getId(), DataStoreRole.Primary); } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtModifyStoragePoolCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtModifyStoragePoolCommandWrapper.java index 7dcedb12340d..06883c708d96 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtModifyStoragePoolCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtModifyStoragePoolCommandWrapper.java @@ -38,6 +38,17 @@ public final class LibvirtModifyStoragePoolCommandWrapper extends CommandWrapper @Override public Answer execute(final ModifyStoragePoolCommand command, final LibvirtComputingResource libvirtComputingResource) { final KVMStoragePoolManager storagePoolMgr = libvirtComputingResource.getStoragePoolMgr(); + if (!command.getAdd()) { + boolean status = storagePoolMgr.deleteStoragePool(command.getPool().getType(), command.getPool().getUuid(), command.getDetails()); + if (status) { + final ModifyStoragePoolAnswer answer = new ModifyStoragePoolAnswer(command, true, null); + return answer; + } + + final ModifyStoragePoolAnswer answer = new ModifyStoragePoolAnswer(command, false, "Failed to delete storage pool"); + return answer; + } + final KVMStoragePool storagepool = storagePoolMgr.createStoragePool(command.getPool().getUuid(), command.getPool().getHost(), command.getPool().getPort(), command.getPool().getPath(), command.getPool() .getUserInfo(), command.getPool().getType(), command.getDetails()); @@ -47,7 +58,6 @@ public Answer execute(final ModifyStoragePoolCommand command, final LibvirtCompu final Map tInfo = new HashMap(); final ModifyStoragePoolAnswer answer = new ModifyStoragePoolAnswer(command, storagepool.getCapacity(), storagepool.getAvailable(), tInfo, storagepool.getDetails()); - return answer; } } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtUnprepareStorageClientCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtUnprepareStorageClientCommandWrapper.java index 2f23a934003f..178f68367c07 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtUnprepareStorageClientCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtUnprepareStorageClientCommandWrapper.java @@ -34,7 +34,7 @@ public class LibvirtUnprepareStorageClientCommandWrapper extends CommandWrapper< @Override public Answer execute(UnprepareStorageClientCommand cmd, LibvirtComputingResource libvirtComputingResource) { final KVMStoragePoolManager storagePoolMgr = libvirtComputingResource.getStoragePoolMgr(); - Pair unprepareStorageClientResult = storagePoolMgr.unprepareStorageClient(cmd.getPoolType(), cmd.getPoolUuid()); + Pair unprepareStorageClientResult = storagePoolMgr.unprepareStorageClient(cmd.getPoolType(), cmd.getPoolUuid(), cmd.getDetails()); if (!unprepareStorageClientResult.first()) { String msg = unprepareStorageClientResult.second(); logger.debug("Couldn't unprepare storage client, due to: " + msg); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java index 4e76030d06f6..a59effdeb2ba 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java @@ -410,12 +410,26 @@ public boolean disconnectPhysicalDisk(StoragePoolType type, String poolUuid, Str public boolean deleteStoragePool(StoragePoolType type, String uuid) { StorageAdaptor adaptor = getStorageAdaptor(type); - _haMonitor.removeStoragePool(uuid); - adaptor.deleteStoragePool(uuid); + if (type == StoragePoolType.NetworkFilesystem) { + _haMonitor.removeStoragePool(uuid); + } + boolean deleteStatus = adaptor.deleteStoragePool(uuid);; + synchronized (_storagePools) { + _storagePools.remove(uuid); + } + return deleteStatus; + } + + public boolean deleteStoragePool(StoragePoolType type, String uuid, Map details) { + StorageAdaptor adaptor = getStorageAdaptor(type); + if (type == StoragePoolType.NetworkFilesystem) { + _haMonitor.removeStoragePool(uuid); + } + boolean deleteStatus = adaptor.deleteStoragePool(uuid, details); synchronized (_storagePools) { _storagePools.remove(uuid); } - return true; + return deleteStatus; } public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, String name, Storage.ProvisioningType provisioningType, @@ -479,11 +493,11 @@ public KVMPhysicalDisk createPhysicalDiskFromDirectDownloadTemplate(String templ public Ternary, String> prepareStorageClient(StoragePoolType type, String uuid, Map details) { StorageAdaptor adaptor = getStorageAdaptor(type); - return adaptor.prepareStorageClient(type, uuid, details); + return adaptor.prepareStorageClient(uuid, details); } - public Pair unprepareStorageClient(StoragePoolType type, String uuid) { + public Pair unprepareStorageClient(StoragePoolType type, String uuid, Map details) { StorageAdaptor adaptor = getStorageAdaptor(type); - return adaptor.unprepareStorageClient(type, uuid); + return adaptor.unprepareStorageClient(uuid, details); } } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ScaleIOStorageAdaptor.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ScaleIOStorageAdaptor.java index 7477d768e9a4..06146830fa08 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ScaleIOStorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ScaleIOStorageAdaptor.java @@ -28,6 +28,7 @@ import java.util.UUID; import org.apache.cloudstack.storage.datastore.client.ScaleIOGatewayClient; +import org.apache.cloudstack.storage.datastore.manager.ScaleIOSDCManager; import org.apache.cloudstack.storage.datastore.util.ScaleIOUtil; import org.apache.cloudstack.utils.cryptsetup.CryptSetup; import org.apache.cloudstack.utils.cryptsetup.CryptSetupException; @@ -148,12 +149,37 @@ 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 details, boolean isPrimaryStorage) { ScaleIOStoragePool storagePool = new ScaleIOStoragePool(uuid, host, port, path, type, details, this); + if (details != null && details.containsKey(ScaleIOSDCManager.ConnectOnDemand.key())) { + String connectOnDemand = details.get(ScaleIOSDCManager.ConnectOnDemand.key()); + if (connectOnDemand != null && !Boolean.parseBoolean(connectOnDemand)) { + Ternary, String> prepareStorageClientStatus = prepareStorageClient(uuid, details); + if (prepareStorageClientStatus.first()) { + details.putAll(prepareStorageClientStatus.second()); + } + } + } MapStorageUuidToStoragePool.put(uuid, storagePool); return storagePool; } @Override public boolean deleteStoragePool(String uuid) { + ScaleIOStoragePool storagePool = (ScaleIOStoragePool) MapStorageUuidToStoragePool.get(uuid); + if (storagePool != null) { + unprepareStorageClient(uuid, storagePool.getDetails()); + } + return MapStorageUuidToStoragePool.remove(uuid) != null; + } + + @Override + public boolean deleteStoragePool(String uuid, Map details) { + if (details != null && details.containsKey(ScaleIOSDCManager.ConnectOnDemand.key())) { + String connectOnDemand = details.get(ScaleIOSDCManager.ConnectOnDemand.key()); + if (connectOnDemand != null && !Boolean.parseBoolean(connectOnDemand)) { + Pair unprepareStorageClientStatus = unprepareStorageClient(uuid, details); + return MapStorageUuidToStoragePool.remove(uuid) != null && unprepareStorageClientStatus.first(); + } + } return MapStorageUuidToStoragePool.remove(uuid) != null; } @@ -567,7 +593,7 @@ public void resizeQcow2ToVolume(String volumePath, QemuImageOptions options, Lis qemu.resize(options, objects, usableSizeBytes); } - public Ternary, String> prepareStorageClient(Storage.StoragePoolType type, String uuid, Map details) { + public Ternary, String> prepareStorageClient(String uuid, Map details) { if (!ScaleIOUtil.isSDCServiceInstalled()) { logger.debug("SDC service not installed on host, preparing the SDC client not possible"); return new Ternary<>(false, null, "SDC service not installed on host"); @@ -584,14 +610,28 @@ public Ternary, String> prepareStorageClient(Storag if (!ScaleIOUtil.startSDCService()) { return new Ternary<>(false, null, "Couldn't start SDC service on host"); } - } else if (!ScaleIOUtil.restartSDCService()) { - return new Ternary<>(false, null, "Couldn't restart SDC service on host"); + } + + if (details != null && details.containsKey(ScaleIOGatewayClient.STORAGE_POOL_MDMS)) { + // Assuming SDC service is started, add mdms + String mdms = details.get(ScaleIOGatewayClient.STORAGE_POOL_MDMS); + String[] mdmAddresses = mdms.split(","); + if (mdmAddresses.length > 0) { + if (ScaleIOUtil.mdmAdded(mdmAddresses[0])) { + return new Ternary<>(true, getSDCDetails(details), "MDM added, no need to prepare the SDC client"); + } + + ScaleIOUtil.addMdms(Arrays.asList(mdmAddresses)); + if (!ScaleIOUtil.mdmAdded(mdmAddresses[0])) { + return new Ternary<>(false, null, "Failed to add MDMs"); + } + } } return new Ternary<>( true, getSDCDetails(details), "Prepared client successfully"); } - public Pair unprepareStorageClient(Storage.StoragePoolType type, String uuid) { + public Pair unprepareStorageClient(String uuid, Map details) { if (!ScaleIOUtil.isSDCServiceInstalled()) { logger.debug("SDC service not installed on host, no need to unprepare the SDC client"); return new Pair<>(true, "SDC service not installed on host, no need to unprepare the SDC client"); @@ -602,8 +642,19 @@ public Pair unprepareStorageClient(Storage.StoragePoolType type return new Pair<>(true, "SDC service not enabled on host, no need to unprepare the SDC client"); } - if (!ScaleIOUtil.stopSDCService()) { - return new Pair<>(false, "Couldn't stop SDC service on host"); + if (details != null && details.containsKey(ScaleIOGatewayClient.STORAGE_POOL_MDMS)) { + String mdms = details.get(ScaleIOGatewayClient.STORAGE_POOL_MDMS); + String[] mdmAddresses = mdms.split(","); + if (mdmAddresses.length > 0) { + if (!ScaleIOUtil.mdmAdded(mdmAddresses[0])) { + return new Pair<>(true, "MDM not added, no need to unprepare the SDC client"); + } + + ScaleIOUtil.removeMdms(Arrays.asList(mdmAddresses)); + if (ScaleIOUtil.mdmAdded(mdmAddresses[0])) { + return new Pair<>(false, "Failed to remove MDMs, unable to unprepare the SDC client"); + } + } } return new Pair<>(true, "Unprepared SDC client successfully"); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/StorageAdaptor.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/StorageAdaptor.java index 15b03a55a3e0..126b1216db0f 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/StorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/StorageAdaptor.java @@ -44,6 +44,10 @@ public interface StorageAdaptor { public boolean deleteStoragePool(String uuid); + public default boolean deleteStoragePool(String uuid, Map details) { + return true; + } + public default KVMPhysicalDisk createPhysicalDisk(String name, KVMStoragePool pool, PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size, Long usableSize, byte[] passphrase) { return createPhysicalDisk(name, pool, format, provisioningType, size, passphrase); @@ -120,22 +124,21 @@ default boolean supportsPhysicalDiskCopy(StoragePoolType type) { /** * Prepares the storage client. - * @param type type of the storage pool * @param uuid uuid of the storage pool * @param details any details of the storage pool that are required for client preparation * @return status, client details, & message in case failed */ - default Ternary, String> prepareStorageClient(StoragePoolType type, String uuid, Map details) { + default Ternary, String> prepareStorageClient(String uuid, Map details) { return new Ternary<>(true, new HashMap<>(), ""); } /** * Unprepares the storage client. - * @param type type of the storage pool * @param uuid uuid of the storage pool + * @param details any details of the storage pool that are required for client unpreparation * @return status, & message in case failed */ - default Pair unprepareStorageClient(StoragePoolType type, String uuid) { + default Pair unprepareStorageClient(String uuid, Map details) { return new Pair<>(true, ""); } } diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtUnprepareStorageClientCommandWrapperTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtUnprepareStorageClientCommandWrapperTest.java index 7409b286f32e..942a6eb18379 100644 --- a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtUnprepareStorageClientCommandWrapperTest.java +++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtUnprepareStorageClientCommandWrapperTest.java @@ -14,6 +14,10 @@ package com.cloud.hypervisor.kvm.resource.wrapper; +import java.util.HashMap; +import java.util.Map; + +import org.apache.cloudstack.storage.datastore.client.ScaleIOGatewayClient; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @@ -45,10 +49,13 @@ public void testUnprepareStorageClientSuccess() { UnprepareStorageClientCommand cmd = Mockito.mock(UnprepareStorageClientCommand.class); Mockito.when(cmd.getPoolType()).thenReturn(Storage.StoragePoolType.PowerFlex); Mockito.when(cmd.getPoolUuid()).thenReturn(poolUuid); + Map details = new HashMap<>(); + details.put(ScaleIOGatewayClient.STORAGE_POOL_MDMS, "1.1.1.1,2.2.2.2"); + Mockito.when(cmd.getDetails()).thenReturn(details); KVMStoragePoolManager storagePoolMgr = Mockito.mock(KVMStoragePoolManager.class); Mockito.when(libvirtComputingResourceMock.getStoragePoolMgr()).thenReturn(storagePoolMgr); - Mockito.when(storagePoolMgr.unprepareStorageClient(cmd.getPoolType(), cmd.getPoolUuid())).thenReturn(new Pair<>(true, "")); + Mockito.when(storagePoolMgr.unprepareStorageClient(cmd.getPoolType(), cmd.getPoolUuid(), cmd.getDetails())).thenReturn(new Pair<>(true, "")); UnprepareStorageClientAnswer result = (UnprepareStorageClientAnswer) libvirtUnprepareStorageClientCommandWrapperSpy.execute(cmd, libvirtComputingResourceMock); @@ -60,10 +67,13 @@ public void testUnprepareStorageClientFailure() { UnprepareStorageClientCommand cmd = Mockito.mock(UnprepareStorageClientCommand.class); Mockito.when(cmd.getPoolType()).thenReturn(Storage.StoragePoolType.PowerFlex); Mockito.when(cmd.getPoolUuid()).thenReturn(poolUuid); + Map details = new HashMap<>(); + details.put(ScaleIOGatewayClient.STORAGE_POOL_MDMS, "1.1.1.1,2.2.2.2"); + Mockito.when(cmd.getDetails()).thenReturn(details); KVMStoragePoolManager storagePoolMgr = Mockito.mock(KVMStoragePoolManager.class); Mockito.when(libvirtComputingResourceMock.getStoragePoolMgr()).thenReturn(storagePoolMgr); - Mockito.when(storagePoolMgr.unprepareStorageClient(cmd.getPoolType(), cmd.getPoolUuid())).thenReturn(new Pair<>(false, "Unprepare storage client failed")); + Mockito.when(storagePoolMgr.unprepareStorageClient(cmd.getPoolType(), cmd.getPoolUuid(), cmd.getDetails())).thenReturn(new Pair<>(false, "Unprepare storage client failed")); UnprepareStorageClientAnswer result = (UnprepareStorageClientAnswer) libvirtUnprepareStorageClientCommandWrapperSpy.execute(cmd, libvirtComputingResourceMock); diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/storage/ScaleIOStorageAdaptorTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/storage/ScaleIOStorageAdaptorTest.java index 07aea0cfbee5..421fee096341 100644 --- a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/storage/ScaleIOStorageAdaptorTest.java +++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/storage/ScaleIOStorageAdaptorTest.java @@ -34,7 +34,6 @@ import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; -import com.cloud.storage.Storage; import com.cloud.storage.StorageLayer; import com.cloud.utils.Pair; import com.cloud.utils.Ternary; @@ -73,7 +72,7 @@ public void getUsableBytesFromRawBytesTest() { public void testPrepareStorageClient_SDCServiceNotInstalled() { when(Script.runSimpleBashScriptForExitValue(Mockito.eq("systemctl status scini"))).thenReturn(4); - Ternary, String> result = scaleIOStorageAdaptor.prepareStorageClient(Storage.StoragePoolType.PowerFlex, poolUuid, new HashMap<>()); + Ternary, String> result = scaleIOStorageAdaptor.prepareStorageClient(poolUuid, new HashMap<>()); Assert.assertFalse(result.first()); Assert.assertNull(result.second()); @@ -86,41 +85,13 @@ public void testPrepareStorageClient_SDCServiceNotEnabled() { when(Script.runSimpleBashScriptForExitValue(Mockito.eq("systemctl is-enabled scini"))).thenReturn(1); when(Script.runSimpleBashScriptForExitValue(Mockito.eq("systemctl enable scini"))).thenReturn(1); - Ternary, String> result = scaleIOStorageAdaptor.prepareStorageClient(Storage.StoragePoolType.PowerFlex, poolUuid, new HashMap<>()); + Ternary, String> result = scaleIOStorageAdaptor.prepareStorageClient(poolUuid, new HashMap<>()); Assert.assertFalse(result.first()); Assert.assertNull(result.second()); Assert.assertEquals("SDC service not enabled on host", result.third()); } - @Test - public void testPrepareStorageClient_SDCServiceNotRestarted() { - when(Script.runSimpleBashScriptForExitValue(Mockito.eq("systemctl status scini"))).thenReturn(3); - when(Script.runSimpleBashScriptForExitValue(Mockito.eq("systemctl is-enabled scini"))).thenReturn(0); - when(Script.runSimpleBashScriptForExitValue(Mockito.eq("systemctl is-active scini"))).thenReturn(0); - when(Script.runSimpleBashScriptForExitValue(Mockito.eq("systemctl restart scini"))).thenReturn(1); - - Ternary, String> result = scaleIOStorageAdaptor.prepareStorageClient(Storage.StoragePoolType.PowerFlex, poolUuid, new HashMap<>()); - - Assert.assertFalse(result.first()); - Assert.assertNull(result.second()); - Assert.assertEquals("Couldn't restart SDC service on host", result.third()); - } - - @Test - public void testPrepareStorageClient_SDCServiceRestarted() { - when(Script.runSimpleBashScriptForExitValue(Mockito.eq("systemctl status scini"))).thenReturn(3); - when(Script.runSimpleBashScriptForExitValue(Mockito.eq("systemctl is-enabled scini"))).thenReturn(0); - when(Script.runSimpleBashScriptForExitValue(Mockito.eq("systemctl is-active scini"))).thenReturn(0); - when(Script.runSimpleBashScriptForExitValue(Mockito.eq("systemctl restart scini"))).thenReturn(0); - - Ternary, String> result = scaleIOStorageAdaptor.prepareStorageClient(Storage.StoragePoolType.PowerFlex, poolUuid, new HashMap<>()); - - Assert.assertTrue(result.first()); - Assert.assertNotNull(result.second()); - Assert.assertTrue(result.second().isEmpty()); - } - @Test public void testPrepareStorageClient_SDCServiceNotStarted() { when(Script.runSimpleBashScriptForExitValue(Mockito.eq("systemctl status scini"))).thenReturn(3); @@ -128,7 +99,7 @@ public void testPrepareStorageClient_SDCServiceNotStarted() { when(Script.runSimpleBashScriptForExitValue(Mockito.eq("systemctl is-active scini"))).thenReturn(1); when(Script.runSimpleBashScriptForExitValue(Mockito.eq("systemctl start scini"))).thenReturn(1); - Ternary, String> result = scaleIOStorageAdaptor.prepareStorageClient(Storage.StoragePoolType.PowerFlex, poolUuid, new HashMap<>()); + Ternary, String> result = scaleIOStorageAdaptor.prepareStorageClient(poolUuid, new HashMap<>()); Assert.assertFalse(result.first()); Assert.assertNull(result.second()); @@ -149,7 +120,7 @@ public void testPrepareStorageClient_SDCServiceStartedReturnSDCId() { String sdcId = "301b852c00000003"; when(ScaleIOUtil.getSdcId(systemId)).thenReturn(sdcId); - Ternary, String> result = scaleIOStorageAdaptor.prepareStorageClient(Storage.StoragePoolType.PowerFlex, poolUuid, details); + Ternary, String> result = scaleIOStorageAdaptor.prepareStorageClient(poolUuid, details); Assert.assertTrue(result.first()); Assert.assertNotNull(result.second()); @@ -172,7 +143,8 @@ public void testPrepareStorageClient_SDCServiceStartedReturnSDCGuid() { when(ScaleIOUtil.getSdcId(systemId)).thenReturn(null); when(ScaleIOUtil.getSdcGuid()).thenReturn(sdcGuid); - Ternary, String> result = scaleIOStorageAdaptor.prepareStorageClient(Storage.StoragePoolType.PowerFlex, poolUuid, details); + Ternary, String> result = scaleIOStorageAdaptor.prepareStorageClient(poolUuid, details); + Assert.assertTrue(result.first()); Assert.assertNotNull(result.second()); Assert.assertEquals(sdcGuid, result.second().get(ScaleIOGatewayClient.SDC_GUID)); @@ -181,9 +153,10 @@ public void testPrepareStorageClient_SDCServiceStartedReturnSDCGuid() { @Test public void testUnprepareStorageClient_SDCServiceNotInstalled() { + Map details = new HashMap<>(); when(Script.runSimpleBashScriptForExitValue(Mockito.eq("systemctl status scini"))).thenReturn(4); - Pair result = scaleIOStorageAdaptor.unprepareStorageClient(Storage.StoragePoolType.PowerFlex, poolUuid); + Pair result = scaleIOStorageAdaptor.unprepareStorageClient(poolUuid, details); Assert.assertTrue(result.first()); Assert.assertEquals("SDC service not installed on host, no need to unprepare the SDC client", result.second()); @@ -191,35 +164,42 @@ public void testUnprepareStorageClient_SDCServiceNotInstalled() { @Test public void testUnprepareStorageClient_SDCServiceNotEnabled() { + Map details = new HashMap<>(); when(Script.runSimpleBashScriptForExitValue(Mockito.eq("systemctl status scini"))).thenReturn(3); when(Script.runSimpleBashScriptForExitValue(Mockito.eq("systemctl is-enabled scini"))).thenReturn(1); - Pair result = scaleIOStorageAdaptor.unprepareStorageClient(Storage.StoragePoolType.PowerFlex, poolUuid); + Pair result = scaleIOStorageAdaptor.unprepareStorageClient(poolUuid, details); Assert.assertTrue(result.first()); Assert.assertEquals("SDC service not enabled on host, no need to unprepare the SDC client", result.second()); } @Test - public void testUnprepareStorageClient_SDCServiceNotStopped() { + public void testUnprepareStorageClient_MDMNotAdded() { + Map details = new HashMap<>(); + details.put(ScaleIOGatewayClient.STORAGE_POOL_MDMS, "1.1.1.1,2.2.2.2"); when(Script.runSimpleBashScriptForExitValue(Mockito.eq("systemctl status scini"))).thenReturn(3); when(Script.runSimpleBashScriptForExitValue(Mockito.eq("systemctl is-enabled scini"))).thenReturn(0); - when(Script.runSimpleBashScriptForExitValue(Mockito.eq("systemctl stop scini"))).thenReturn(1); + when(Script.runSimpleBashScript(Mockito.eq("/opt/emc/scaleio/sdc/bin/drv_cfg --query_mdms|grep 1.1.1.1"))).thenReturn("MDM-ID 71fd458f0775010f SDC ID 4421a91a00000000 INSTALLATION ID 204930df2cbcaf8e IPs [0]-3.3.3.3 [1]-4.4.4.4"); - Pair result = scaleIOStorageAdaptor.unprepareStorageClient(Storage.StoragePoolType.PowerFlex, poolUuid); + Pair result = scaleIOStorageAdaptor.unprepareStorageClient(poolUuid, details); - Assert.assertFalse(result.first()); - Assert.assertEquals("Couldn't stop SDC service on host", result.second()); + Assert.assertTrue(result.first()); + Assert.assertEquals("MDM not added, no need to unprepare the SDC client", result.second()); } @Test - public void testUnprepareStorageClient_SDCServiceStopped() { + public void testUnprepareStorageClient_RemoveMDMFailed() { + Map details = new HashMap<>(); + details.put(ScaleIOGatewayClient.STORAGE_POOL_MDMS, "1.1.1.1,2.2.2.2"); when(Script.runSimpleBashScriptForExitValue(Mockito.eq("systemctl status scini"))).thenReturn(3); when(Script.runSimpleBashScriptForExitValue(Mockito.eq("systemctl is-enabled scini"))).thenReturn(0); - when(Script.runSimpleBashScriptForExitValue(Mockito.eq("systemctl stop scini"))).thenReturn(0); + when(Script.runSimpleBashScriptForExitValue(Mockito.eq("systemctl restart scini"))).thenReturn(0); + when(Script.runSimpleBashScript(Mockito.eq("/opt/emc/scaleio/sdc/bin/drv_cfg --query_mdms|grep 1.1.1.1"))).thenReturn("MDM-ID 71fd458f0775010f SDC ID 4421a91a00000000 INSTALLATION ID 204930df2cbcaf8e IPs [0]-1.1.1.1 [1]-2.2.2.2"); - Pair result = scaleIOStorageAdaptor.unprepareStorageClient(Storage.StoragePoolType.PowerFlex, poolUuid); + Pair result = scaleIOStorageAdaptor.unprepareStorageClient(poolUuid, details); - Assert.assertTrue(result.first()); + Assert.assertFalse(result.first()); + Assert.assertEquals("Failed to remove MDMs, unable to unprepare the SDC client", result.second()); } } diff --git a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/api/StorageConfiguration.java b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/api/StorageConfiguration.java new file mode 100644 index 000000000000..a485d5dab937 --- /dev/null +++ b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/api/StorageConfiguration.java @@ -0,0 +1,48 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.storage.datastore.api; + +public class StorageConfiguration { + String systemId; + Long mdmPort; + String[] mdmAddresses; + + public String getSystemId() { + return systemId; + } + + public void setSystemId(String systemId) { + this.systemId = systemId; + } + + public Long getMdmPort() { + return mdmPort; + } + + public void setMdmPort(Long mdmPort) { + this.mdmPort = mdmPort; + } + + public String[] getMdmAddresses() { + return mdmAddresses; + } + + public void setMdmAddresses(String[] mdmAddresses) { + this.mdmAddresses = mdmAddresses; + } +} diff --git a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/api/StoragePool.java b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/api/StoragePool.java index df903bb67f7c..36375ac2f441 100644 --- a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/api/StoragePool.java +++ b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/api/StoragePool.java @@ -17,12 +17,15 @@ package org.apache.cloudstack.storage.datastore.api; +import java.util.List; + public class StoragePool { String id; String name; String mediaType; String protectionDomainId; String systemId; + List mdmAddresses; StoragePoolStatistics statistics; public String getId() { @@ -65,6 +68,14 @@ public void setSystemId(String systemId) { this.systemId = systemId; } + public List getMdmAddresses() { + return mdmAddresses; + } + + public void setMdmAddresses(List mdmAddresses) { + this.mdmAddresses = mdmAddresses; + } + public StoragePoolStatistics getStatistics() { return statistics; } diff --git a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClient.java b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClient.java index fd2b93bc674b..2dc5acffcbc1 100644 --- a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClient.java +++ b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClient.java @@ -38,6 +38,7 @@ public interface ScaleIOGatewayClient { String GATEWAY_API_PASSWORD = "powerflex.gw.password"; String STORAGE_POOL_NAME = "powerflex.storagepool.name"; String STORAGE_POOL_SYSTEM_ID = "powerflex.storagepool.system.id"; + String STORAGE_POOL_MDMS = "powerflex.storagepool.mdms"; String SDC_ID = "powerflex.sdc.id"; String SDC_GUID = "powerflex.sdc.guid"; @@ -91,4 +92,6 @@ Volume createVolume(final String name, final String storagePoolId, boolean haveConnectedSdcs(); boolean isSdcConnected(String sdcId); boolean isSdcConnectedByIP(String ipAddress); + + List getMdmAddresses(); } diff --git a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientConnectionPool.java b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientConnectionPool.java index a9dc8b42cd5a..99c41b862293 100644 --- a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientConnectionPool.java +++ b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientConnectionPool.java @@ -22,6 +22,7 @@ import java.security.NoSuchAlgorithmException; import java.util.concurrent.ConcurrentHashMap; +import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO; import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; @@ -57,11 +58,23 @@ public ScaleIOGatewayClient getClient(Long storagePoolId, StoragePoolDetailsDao synchronized (gatewayClients) { client = gatewayClients.get(storagePoolId); if (client == null) { - final String url = storagePoolDetailsDao.findDetail(storagePoolId, ScaleIOGatewayClient.GATEWAY_API_ENDPOINT).getValue(); - final String encryptedUsername = storagePoolDetailsDao.findDetail(storagePoolId, ScaleIOGatewayClient.GATEWAY_API_USERNAME).getValue(); - final String username = DBEncryptionUtil.decrypt(encryptedUsername); - final String encryptedPassword = storagePoolDetailsDao.findDetail(storagePoolId, ScaleIOGatewayClient.GATEWAY_API_PASSWORD).getValue(); - final String password = DBEncryptionUtil.decrypt(encryptedPassword); + String url = null; + StoragePoolDetailVO urlDetail = storagePoolDetailsDao.findDetail(storagePoolId, ScaleIOGatewayClient.GATEWAY_API_ENDPOINT); + if (urlDetail != null) { + url = urlDetail.getValue(); + } + String username = null; + StoragePoolDetailVO encryptedUsernameDetail = storagePoolDetailsDao.findDetail(storagePoolId, ScaleIOGatewayClient.GATEWAY_API_USERNAME); + if (encryptedUsernameDetail != null) { + final String encryptedUsername = encryptedUsernameDetail.getValue(); + username = DBEncryptionUtil.decrypt(encryptedUsername); + } + String password = null; + StoragePoolDetailVO encryptedPasswordDetail = storagePoolDetailsDao.findDetail(storagePoolId, ScaleIOGatewayClient.GATEWAY_API_PASSWORD); + if (encryptedPasswordDetail != null) { + final String encryptedPassword = encryptedPasswordDetail.getValue(); + password = DBEncryptionUtil.decrypt(encryptedPassword); + } final int clientTimeout = StorageManager.STORAGE_POOL_CLIENT_TIMEOUT.valueIn(storagePoolId); final int clientMaxConnections = StorageManager.STORAGE_POOL_CLIENT_MAX_CONNECTIONS.valueIn(storagePoolId); diff --git a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientImpl.java b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientImpl.java index 62581ca066e4..55e30bf29711 100644 --- a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientImpl.java +++ b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientImpl.java @@ -34,6 +34,7 @@ import javax.net.ssl.SSLContext; import javax.net.ssl.X509TrustManager; +import org.apache.cloudstack.storage.datastore.api.StorageConfiguration; import org.apache.commons.lang3.StringUtils; import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.ServerApiException; @@ -1126,6 +1127,15 @@ public boolean isSdcConnectedByIP(String ipAddress) { return false; } + @Override + public List getMdmAddresses() { + StorageConfiguration storageConfiguration = get("/Configuration", StorageConfiguration.class); + if (storageConfiguration != null && storageConfiguration.getMdmAddresses().length > 0) { + return Arrays.asList(storageConfiguration.getMdmAddresses()); + } + return new ArrayList<>(); + } + private String getConnectionManagerStats() { StringBuilder sb = new StringBuilder(); sb.append("\n").append("Client Connection Manager Stats => "); diff --git a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/driver/ScaleIOPrimaryDataStoreDriver.java b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/driver/ScaleIOPrimaryDataStoreDriver.java index 8044e787bd2d..8fc0b7395127 100644 --- a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/driver/ScaleIOPrimaryDataStoreDriver.java +++ b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/driver/ScaleIOPrimaryDataStoreDriver.java @@ -256,7 +256,7 @@ public void revokeAccess(DataObject dataObject, Host host, DataStore dataStore) } if (client.listVolumesMappedToSdc(sdcId).isEmpty()) { sdcManager = ComponentContext.inject(sdcManager); - sdcManager.stopSDC(host, dataStore); + sdcManager.unprepareSDC(host, dataStore); } } catch (Exception e) { logger.warn("Failed to revoke access due to: " + e.getMessage(), e); @@ -282,7 +282,7 @@ public void revokeVolumeAccess(String volumePath, Host host, DataStore dataStore client.unmapVolumeFromSdc(ScaleIOUtil.getVolumePath(volumePath), sdcId); if (client.listVolumesMappedToSdc(sdcId).isEmpty()) { sdcManager = ComponentContext.inject(sdcManager); - sdcManager.stopSDC(host, dataStore); + sdcManager.unprepareSDC(host, dataStore); } } catch (Exception e) { logger.warn("Failed to revoke access due to: " + e.getMessage(), e); @@ -1054,7 +1054,11 @@ private Map getVolumeDetails(VolumeInfo volumeInfo, DataStore da volumeDetails.put(DiskTO.CHAP_TARGET_SECRET, chapInfo.getTargetSecret()); } - String systemId = storagePoolDetailsDao.findDetail(storagePoolId, ScaleIOGatewayClient.STORAGE_POOL_SYSTEM_ID).getValue(); + String systemId = null; + StoragePoolDetailVO systemIdDetail = storagePoolDetailsDao.findDetail(storagePoolId, ScaleIOGatewayClient.STORAGE_POOL_SYSTEM_ID); + if (systemIdDetail != null) { + systemId = systemIdDetail.getValue(); + } volumeDetails.put(ScaleIOGatewayClient.STORAGE_POOL_SYSTEM_ID, systemId); return volumeDetails; @@ -1465,6 +1469,31 @@ public boolean canHostPrepareStoragePoolAccess(Host host, StoragePool pool) { return sdcManager.areSDCConnectionsWithinLimit(pool.getId()); } + @Override + public boolean canDisconnectHostFromStoragePool(Host host, StoragePool pool) { + if (host == null || pool == null) { + return false; + } + + StoragePoolHostVO poolHostVO = storagePoolHostDao.findByPoolHost(pool.getId(), host.getId()); + if (poolHostVO == null) { + return false; + } + + final String sdcId = poolHostVO.getLocalPath(); + if (StringUtils.isBlank(sdcId)) { + return false; + } + + try { + final ScaleIOGatewayClient client = getScaleIOClient(pool.getId()); + return client.listVolumesMappedToSdc(sdcId).isEmpty(); + } catch (Exception e) { + logger.warn("Unable to check whether the host: " + host.getId() + " can be disconnected from storage pool: " + pool.getId() + ", due to " + e.getMessage(), e); + return false; + } + } + private void alertHostSdcDisconnection(Host host) { if (host == null) { return; @@ -1534,4 +1563,9 @@ public boolean volumesRequireGrantAccessWhenUsed() { public boolean zoneWideVolumesAvailableWithoutClusterMotion() { return true; } + + @Override + public boolean canDisplayDetails() { + return false; + } } diff --git a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/ScaleIOPrimaryDataStoreLifeCycle.java b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/ScaleIOPrimaryDataStoreLifeCycle.java index 7bbe0331c071..2612042d2a3b 100644 --- a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/ScaleIOPrimaryDataStoreLifeCycle.java +++ b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/ScaleIOPrimaryDataStoreLifeCycle.java @@ -25,6 +25,7 @@ import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; @@ -32,7 +33,10 @@ import javax.inject.Inject; import org.apache.cloudstack.storage.datastore.client.ScaleIOGatewayClientConnectionPool; +import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO; import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; +import org.apache.cloudstack.storage.datastore.manager.ScaleIOSDCManager; +import org.apache.cloudstack.storage.datastore.manager.ScaleIOSDCManagerImpl; import org.apache.cloudstack.storage.datastore.util.ScaleIOUtil; import org.apache.commons.collections.CollectionUtils; import org.apache.cloudstack.engine.subsystem.api.storage.ClusterScope; @@ -47,6 +51,7 @@ import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.volume.datastore.PrimaryDataStoreHelper; +import org.apache.commons.lang3.StringUtils; import com.cloud.agent.AgentManager; import com.cloud.agent.api.Answer; @@ -70,6 +75,7 @@ import com.cloud.storage.dao.StoragePoolHostDao; import com.cloud.template.TemplateManager; import com.cloud.utils.UriUtils; +import com.cloud.utils.component.ComponentContext; import com.cloud.utils.crypt.DBEncryptionUtil; import com.cloud.utils.exception.CloudRuntimeException; @@ -96,8 +102,10 @@ public class ScaleIOPrimaryDataStoreLifeCycle extends BasePrimaryDataStoreLifeCy private TemplateManager templateMgr; @Inject private AgentManager agentMgr; + private ScaleIOSDCManager sdcManager; public ScaleIOPrimaryDataStoreLifeCycle() { + sdcManager = new ScaleIOSDCManagerImpl(); } private org.apache.cloudstack.storage.datastore.api.StoragePool findStoragePool(String url, String username, String password, String storagePoolName) { @@ -114,6 +122,8 @@ private org.apache.cloudstack.storage.datastore.api.StoragePool findStoragePool( String systemId = client.getSystemId(pool.getProtectionDomainId()); pool.setSystemId(systemId); + List mdmAddresses = client.getMdmAddresses(); + pool.setMdmAddresses(mdmAddresses); return pool; } } @@ -245,6 +255,7 @@ public DataStore initialize(Map dsInfos) { details.put(ScaleIOGatewayClient.GATEWAY_API_PASSWORD, DBEncryptionUtil.encrypt(gatewayPassword)); details.put(ScaleIOGatewayClient.STORAGE_POOL_NAME, storagePoolName); details.put(ScaleIOGatewayClient.STORAGE_POOL_SYSTEM_ID, scaleIOPool.getSystemId()); + details.put(ScaleIOGatewayClient.STORAGE_POOL_MDMS, StringUtils.join(scaleIOPool.getMdmAddresses(), ",")); parameters.setDetails(details); return dataStoreHelper.createPrimaryDataStore(parameters); @@ -318,15 +329,43 @@ public boolean attachZone(DataStore dataStore, ZoneScope scope, Hypervisor.Hyper @Override public boolean maintain(DataStore store) { - storagePoolAutomation.maintain(store); + Map details = new HashMap<>(); + StoragePoolDetailVO systemIdDetail = storagePoolDetailsDao.findDetail(store.getId(), ScaleIOGatewayClient.STORAGE_POOL_SYSTEM_ID); + if (systemIdDetail != null) { + details.put(ScaleIOGatewayClient.STORAGE_POOL_SYSTEM_ID, systemIdDetail.getValue()); + StoragePoolDetailVO mdmsDetail = storagePoolDetailsDao.findDetail(store.getId(), ScaleIOGatewayClient.STORAGE_POOL_MDMS); + if (mdmsDetail != null) { + details.put(ScaleIOGatewayClient.STORAGE_POOL_MDMS, mdmsDetail.getValue()); + details.put(ScaleIOSDCManager.ConnectOnDemand.key(), "false"); + } + } + + storagePoolAutomation.maintain(store, details); dataStoreHelper.maintain(store); return true; } @Override public boolean cancelMaintain(DataStore store) { + Map details = new HashMap<>(); + StoragePoolDetailVO systemIdDetail = storagePoolDetailsDao.findDetail(store.getId(), ScaleIOGatewayClient.STORAGE_POOL_SYSTEM_ID); + if (systemIdDetail != null) { + details.put(ScaleIOGatewayClient.STORAGE_POOL_SYSTEM_ID, systemIdDetail.getValue()); + sdcManager = ComponentContext.inject(sdcManager); + if (sdcManager.areSDCConnectionsWithinLimit(store.getId())) { + StoragePoolVO storagePoolVO = primaryDataStoreDao.findById(store.getId()); + if (storagePoolVO != null) { + details.put(ScaleIOSDCManager.ConnectOnDemand.key(), String.valueOf(ScaleIOSDCManager.ConnectOnDemand.valueIn(storagePoolVO.getDataCenterId()))); + StoragePoolDetailVO mdmsDetail = storagePoolDetailsDao.findDetail(store.getId(), ScaleIOGatewayClient.STORAGE_POOL_MDMS); + if (mdmsDetail != null) { + details.put(ScaleIOGatewayClient.STORAGE_POOL_MDMS, mdmsDetail.getValue()); + } + } + } + } + dataStoreHelper.cancelMaintain(store); - storagePoolAutomation.cancelMaintain(store); + storagePoolAutomation.cancelMaintain(store, details); return true; } @@ -372,7 +411,11 @@ public boolean deleteDataStore(DataStore dataStore) { ScaleIOGatewayClientConnectionPool.getInstance().removeClient(dataStore.getId()); - return dataStoreHelper.deletePrimaryDataStore(dataStore); + boolean isDeleted = dataStoreHelper.deletePrimaryDataStore(dataStore); + if (isDeleted) { + primaryDataStoreDao.removeDetails(dataStore.getId()); + } + return isDeleted; } @Override diff --git a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/manager/ScaleIOSDCManager.java b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/manager/ScaleIOSDCManager.java index 696643cb17a1..e73fa1e79ff0 100644 --- a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/manager/ScaleIOSDCManager.java +++ b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/manager/ScaleIOSDCManager.java @@ -18,10 +18,20 @@ package org.apache.cloudstack.storage.datastore.manager; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; +import org.apache.cloudstack.framework.config.ConfigKey; import com.cloud.host.Host; public interface ScaleIOSDCManager { + ConfigKey ConnectOnDemand = new ConfigKey<>("Storage", + Boolean.class, + "powerflex.connect.on.demand", + Boolean.FALSE.toString(), + "When true, connects PowerFlex client on Host when first Volume is mapped to SDC & client connections configured 'storage.pool.connected.clients.limit' are within the limit and disconnects when last Volume is unmapped from SDC; " + + "and When false, connects PowerFlex client on Host when host connects to storage pool & client connections configured 'storage.pool.connected.clients.limit' are within the limit and disconnects when host disconnects from storage pool & no volumes mapped to SDC.", + Boolean.TRUE, + ConfigKey.Scope.Zone); + /** * Checks SDC connections limit. * @param storagePoolId the storage pool id @@ -30,18 +40,57 @@ public interface ScaleIOSDCManager { boolean areSDCConnectionsWithinLimit(Long storagePoolId); /** - * Prepares/starts the SDC on the host. + * Returns connected SDC Id. * @param host the host * @param dataStore the datastore * @return SDC Id of the host */ + String getConnectedSdc(Host host, DataStore dataStore); + + /** + * Prepares the SDC on the host (adds the MDM IPs to SDC, starts scini service if required). + * @param host the host + * @param dataStore the datastore + * @return SDC Id of the host if SDC is successfully prepared-ed on the host + */ String prepareSDC(Host host, DataStore dataStore); /** - * Stops the SDC on the host. + * Unprepares the SDC on the host (removes the MDM IPs from SDC, restarts scini service). + * @param host the host + * @param dataStore the datastore + * @return true if SDC is successfully unprepared-ed on the host + */ + boolean unprepareSDC(Host host, DataStore dataStore); + + /** + * Checks if the SDC can be unprepared on the host (don't remove MDM IPs from SDC if any volumes mapped to SDC). * @param host the host * @param dataStore the datastore - * @return true if SDC stopped on the host + * @return true if SDC can be unprepared on the host + */ + boolean canUnprepareSDC(Host host, DataStore dataStore); + + /** + * Returns the SDC Id of the host for the pool. + * @param sdcGuid the SDC GUID + * @param poolId the pool id + * @return SDC Id of the host for the pool + */ + String getHostSdcId(String sdcGuid, long poolId); + + /** + * Returns the connection status of host SDC of the pool. + * @param sdcId the SDC id + * @param poolId the pool id + * @return true if Host SDC is connected to the pool + */ + boolean isHostSdcConnected(String sdcId, long poolId, int waitTimeInSecs); + + /** + * Returns the comma-separated list of MDM IPs of the pool. + * @param poolId the pool id + * @return Comma-separated list of MDM IPs of the pool */ - boolean stopSDC(Host host, DataStore dataStore); + String getMdms(long poolId); } diff --git a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/manager/ScaleIOSDCManagerImpl.java b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/manager/ScaleIOSDCManagerImpl.java index 4d3a78f6875f..78b0a29bd56f 100644 --- a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/manager/ScaleIOSDCManagerImpl.java +++ b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/manager/ScaleIOSDCManagerImpl.java @@ -18,6 +18,7 @@ package org.apache.cloudstack.storage.datastore.manager; import java.util.HashMap; +import java.util.List; import java.util.Map; import javax.inject.Inject; @@ -29,7 +30,9 @@ import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.storage.datastore.client.ScaleIOGatewayClient; import org.apache.cloudstack.storage.datastore.client.ScaleIOGatewayClientConnectionPool; +import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO; import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; +import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; @@ -56,15 +59,6 @@ public class ScaleIOSDCManagerImpl implements ScaleIOSDCManager, Configurable { private Logger logger = LogManager.getLogger(getClass()); - static ConfigKey ConnectOnDemand = new ConfigKey<>("Storage", - Boolean.class, - "powerflex.connect.on.demand", - Boolean.FALSE.toString(), - "Connect PowerFlex client on Host when first Volume is mapped to SDC and disconnect when last Volume is unmapped from SDC," + - " otherwise no action (that is connection remains in the same state whichever it is, connected or disconnected).", - Boolean.TRUE, - ConfigKey.Scope.Zone); - @Inject AgentManager agentManager; @Inject @@ -86,6 +80,7 @@ public boolean areSDCConnectionsWithinLimit(Long storagePoolId) { try { int connectedClientsLimit = StorageManager.STORAGE_POOL_CONNECTED_CLIENTS_LIMIT.valueIn(storagePoolId); if (connectedClientsLimit <= 0) { + logger.debug(String.format("SDC connections limit (unlimited) on PowerFlex Storage with pool id: %d", storagePoolId)); return true; } @@ -110,7 +105,11 @@ public String prepareSDC(Host host, DataStore dataStore) { return getConnectedSdc(host, dataStore); } - String systemId = storagePoolDetailsDao.findDetail(dataStore.getId(), ScaleIOGatewayClient.STORAGE_POOL_SYSTEM_ID).getValue(); + String systemId = null; + StoragePoolDetailVO systemIdDetail = storagePoolDetailsDao.findDetail(dataStore.getId(), ScaleIOGatewayClient.STORAGE_POOL_SYSTEM_ID); + if (systemIdDetail != null) { + systemId = systemIdDetail.getValue(); + } if (systemId == null) { throw new CloudRuntimeException("Unable to prepare SDC, failed to get the system id for PowerFlex storage pool: " + dataStore.getName()); } @@ -156,7 +155,8 @@ public String prepareSDC(Host host, DataStore dataStore) { throw new CloudRuntimeException(errorMsg); } - sdcId = prepareSDCOnHost(host, dataStore, systemId); + String mdms = getMdms(dataStore.getId()); + sdcId = prepareSDCOnHost(host, dataStore, systemId, mdms); StoragePoolHostVO storagePoolHost = storagePoolHostDao.findByPoolHost(poolId, hostId); if (StringUtils.isBlank(sdcId)) { @@ -174,7 +174,7 @@ public String prepareSDC(Host host, DataStore dataStore) { } int waitTimeInSecs = 15; // Wait for 15 secs (usual tests with SDC service start took 10-15 secs) - if (hostSdcConnected(sdcId, poolId, waitTimeInSecs)) { + if (isHostSdcConnected(sdcId, poolId, waitTimeInSecs)) { return sdcId; } return null; @@ -190,10 +190,11 @@ public String prepareSDC(Host host, DataStore dataStore) { } } - private String prepareSDCOnHost(Host host, DataStore dataStore, String systemId) { + private String prepareSDCOnHost(Host host, DataStore dataStore, String systemId, String mdms) { logger.debug(String.format("Preparing SDC on the host %s (%s)", host.getId(), host.getName())); Map details = new HashMap<>(); details.put(ScaleIOGatewayClient.STORAGE_POOL_SYSTEM_ID, systemId); + details.put(ScaleIOGatewayClient.STORAGE_POOL_MDMS, mdms); PrepareStorageClientCommand cmd = new PrepareStorageClientCommand(((PrimaryDataStore) dataStore).getPoolType(), dataStore.getUuid(), details); int timeoutSeconds = 60; cmd.setWait(timeoutSeconds); @@ -242,13 +243,17 @@ private String prepareSDCOnHost(Host host, DataStore dataStore, String systemId) } @Override - public boolean stopSDC(Host host, DataStore dataStore) { + public boolean unprepareSDC(Host host, DataStore dataStore) { if (Boolean.FALSE.equals(ConnectOnDemand.valueIn(host.getDataCenterId()))) { logger.debug(String.format("On-demand connect/disconnect config %s disabled in the zone %d, no need to unprepare SDC", ConnectOnDemand.key(), host.getDataCenterId())); return true; } - String systemId = storagePoolDetailsDao.findDetail(dataStore.getId(), ScaleIOGatewayClient.STORAGE_POOL_SYSTEM_ID).getValue(); + String systemId = null; + StoragePoolDetailVO systemIdDetail = storagePoolDetailsDao.findDetail(dataStore.getId(), ScaleIOGatewayClient.STORAGE_POOL_SYSTEM_ID); + if (systemIdDetail != null) { + systemId = systemIdDetail.getValue(); + } if (systemId == null) { throw new CloudRuntimeException("Unable to unprepare SDC, failed to get the system id for PowerFlex storage pool: " + dataStore.getName()); } @@ -272,10 +277,28 @@ public boolean stopSDC(Host host, DataStore dataStore) { String sdcId = getConnectedSdc(host, dataStore); if (StringUtils.isBlank(sdcId)) { logger.debug("SDC not connected, no need to unprepare it"); + StoragePoolHostVO storagePoolHost = storagePoolHostDao.findByPoolHost(dataStore.getId(), host.getId()); + if (storagePoolHost != null) { + storagePoolHostDao.deleteStoragePoolHostDetails(host.getId(), dataStore.getId()); + } return true; } - return unprepareSDCOnHost(host, dataStore); + if (!canUnprepareSDC(host, dataStore)) { + logger.debug("Cannot unprepare SDC, there might be some volumes mapped to the SDC that belongs to the storage pools of PowerFlex storage cluster"); + return false; + } + + String mdms = getMdms(dataStore.getId());; + boolean unprepareSDCStatus = unprepareSDCOnHost(host, dataStore, mdms); + if (unprepareSDCStatus) { + StoragePoolHostVO storagePoolHost = storagePoolHostDao.findByPoolHost(dataStore.getId(), host.getId()); + if (storagePoolHost != null) { + storagePoolHostDao.deleteStoragePoolHostDetails(host.getId(), dataStore.getId()); + } + } + + return unprepareSDCStatus; } finally { if (lock != null) { lock.unlock(); @@ -284,9 +307,11 @@ public boolean stopSDC(Host host, DataStore dataStore) { } } - private boolean unprepareSDCOnHost(Host host, DataStore dataStore) { + private boolean unprepareSDCOnHost(Host host, DataStore dataStore, String mdms) { logger.debug(String.format("Unpreparing SDC on the host %s (%s)", host.getId(), host.getName())); - UnprepareStorageClientCommand cmd = new UnprepareStorageClientCommand(((PrimaryDataStore) dataStore).getPoolType(), dataStore.getUuid()); + Map details = new HashMap<>(); + details.put(ScaleIOGatewayClient.STORAGE_POOL_MDMS, mdms); + UnprepareStorageClientCommand cmd = new UnprepareStorageClientCommand(((PrimaryDataStore) dataStore).getPoolType(), dataStore.getUuid(), details); int timeoutSeconds = 60; cmd.setWait(timeoutSeconds); @@ -307,7 +332,33 @@ private boolean unprepareSDCOnHost(Host host, DataStore dataStore) { return true; } - private String getHostSdcId(String sdcGuid, long poolId) { + @Override + public boolean canUnprepareSDC(Host host, DataStore dataStore) { + if (host == null || dataStore == null) { + return false; + } + + StoragePoolHostVO poolHostVO = storagePoolHostDao.findByPoolHost(dataStore.getId(), host.getId()); + if (poolHostVO == null) { + return false; + } + + final String sdcId = poolHostVO.getLocalPath(); + if (StringUtils.isBlank(sdcId)) { + return false; + } + + try { + final ScaleIOGatewayClient client = getScaleIOClient(dataStore.getId()); + return client.listVolumesMappedToSdc(sdcId).isEmpty(); + } catch (Exception e) { + logger.warn("Unable to check whether the SDC of the pool: " + dataStore.getId() + " can be unprepared on the host: " + host.getId() + ", due to " + e.getMessage(), e); + return false; + } + } + + @Override + public String getHostSdcId(String sdcGuid, long poolId) { try { logger.debug(String.format("Try to get host SDC Id for pool: %s, with SDC guid %s", poolId, sdcGuid)); ScaleIOGatewayClient client = getScaleIOClient(poolId); @@ -318,7 +369,8 @@ private String getHostSdcId(String sdcGuid, long poolId) { } } - private String getConnectedSdc(Host host, DataStore dataStore) { + @Override + public String getConnectedSdc(Host host, DataStore dataStore) { long poolId = dataStore.getId(); long hostId = host.getId(); @@ -339,7 +391,8 @@ private String getConnectedSdc(Host host, DataStore dataStore) { return null; } - private boolean hostSdcConnected(String sdcId, long poolId, int waitTimeInSecs) { + @Override + public boolean isHostSdcConnected(String sdcId, long poolId, int waitTimeInSecs) { logger.debug(String.format("Waiting (for %d secs) for the SDC %s of the pool id: %d to connect", waitTimeInSecs, sdcId, poolId)); int timeBetweenTries = 1000; // Try more frequently (every sec) and return early if connected while (waitTimeInSecs > 0) { @@ -355,6 +408,32 @@ private boolean hostSdcConnected(String sdcId, long poolId, int waitTimeInSecs) return isHostSdcConnected(sdcId, poolId); } + @Override + public String getMdms(long poolId) { + String mdms = null; + StoragePoolDetailVO mdmsDetail = storagePoolDetailsDao.findDetail(poolId, ScaleIOGatewayClient.STORAGE_POOL_MDMS); + if (mdmsDetail != null) { + mdms = mdmsDetail.getValue(); + } + if (StringUtils.isNotBlank(mdms)) { + return mdms; + } + + try { + final ScaleIOGatewayClient client = getScaleIOClient(poolId); + List mdmAddresses = client.getMdmAddresses(); + if (CollectionUtils.isNotEmpty(mdmAddresses)) { + mdms = StringUtils.join(mdmAddresses, ","); + StoragePoolDetailVO storagePoolDetailVO = new StoragePoolDetailVO(poolId, ScaleIOGatewayClient.STORAGE_POOL_MDMS, mdms, false); + storagePoolDetailsDao.persist(storagePoolDetailVO); + } + return mdms; + } catch (Exception e) { + logger.error("Failed to get MDMs", e); + throw new CloudRuntimeException("Failed to fetch PowerFlex MDM details"); + } + } + private boolean isHostSdcConnected(String sdcId, long poolId) { try { final ScaleIOGatewayClient client = getScaleIOClient(poolId); diff --git a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/provider/ScaleIOHostListener.java b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/provider/ScaleIOHostListener.java index 737cc818be87..19e76af3fff1 100644 --- a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/provider/ScaleIOHostListener.java +++ b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/provider/ScaleIOHostListener.java @@ -18,20 +18,20 @@ */ package org.apache.cloudstack.storage.datastore.provider; -import java.net.URISyntaxException; -import java.security.KeyManagementException; -import java.security.NoSuchAlgorithmException; import java.util.HashMap; import java.util.Map; import javax.inject.Inject; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; import org.apache.cloudstack.engine.subsystem.api.storage.HypervisorHostListener; import org.apache.cloudstack.storage.datastore.client.ScaleIOGatewayClient; -import org.apache.cloudstack.storage.datastore.client.ScaleIOGatewayClientConnectionPool; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO; import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; +import org.apache.cloudstack.storage.datastore.manager.ScaleIOSDCManager; +import org.apache.cloudstack.storage.datastore.manager.ScaleIOSDCManagerImpl; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; @@ -48,6 +48,7 @@ import com.cloud.storage.StoragePool; import com.cloud.storage.StoragePoolHostVO; import com.cloud.storage.dao.StoragePoolHostDao; +import com.cloud.utils.component.ComponentContext; import com.cloud.utils.exception.CloudRuntimeException; public class ScaleIOHostListener implements HypervisorHostListener { @@ -60,6 +61,7 @@ public class ScaleIOHostListener implements HypervisorHostListener { @Inject private StoragePoolHostDao _storagePoolHostDao; @Inject private PrimaryDataStoreDao _primaryDataStoreDao; @Inject private StoragePoolDetailsDao _storagePoolDetailsDao; + private ScaleIOSDCManager _sdcManager = new ScaleIOSDCManagerImpl(); @Override public boolean hostAdded(long hostId) { @@ -74,7 +76,8 @@ public boolean hostConnect(long hostId, long poolId) { return false; } - StoragePool storagePool = (StoragePool)_dataStoreMgr.getDataStore(poolId, DataStoreRole.Primary); + DataStore dataStore = _dataStoreMgr.getDataStore(poolId, DataStoreRole.Primary); + StoragePool storagePool = (StoragePool) dataStore; StoragePoolHostVO storagePoolHost = _storagePoolHostDao.findByPoolHost(poolId, hostId); String sdcId = getSdcIdOfHost(host, storagePool); if (StringUtils.isBlank(sdcId)) { @@ -97,12 +100,22 @@ public boolean hostConnect(long hostId, long poolId) { private String getSdcIdOfHost(HostVO host, StoragePool storagePool) { long hostId = host.getId(); long poolId = storagePool.getId(); - String systemId = _storagePoolDetailsDao.findDetail(poolId, ScaleIOGatewayClient.STORAGE_POOL_SYSTEM_ID).getValue(); + String systemId = null; + StoragePoolDetailVO systemIdDetail = _storagePoolDetailsDao.findDetail(poolId, ScaleIOGatewayClient.STORAGE_POOL_SYSTEM_ID); + if (systemIdDetail != null) { + systemId = systemIdDetail.getValue(); + } if (systemId == null) { throw new CloudRuntimeException("Failed to get the system id for PowerFlex storage pool " + storagePool.getName()); } Map details = new HashMap<>(); details.put(ScaleIOGatewayClient.STORAGE_POOL_SYSTEM_ID, systemId); + _sdcManager = ComponentContext.inject(_sdcManager); + if (_sdcManager.areSDCConnectionsWithinLimit(poolId)) { + details.put(ScaleIOSDCManager.ConnectOnDemand.key(), String.valueOf(ScaleIOSDCManager.ConnectOnDemand.valueIn(host.getDataCenterId()))); + String mdms = _sdcManager.getMdms(poolId); + details.put(ScaleIOGatewayClient.STORAGE_POOL_MDMS, mdms); + } ModifyStoragePoolCommand cmd = new ModifyStoragePoolCommand(true, storagePool, storagePool.getPath(), details); ModifyStoragePoolAnswer answer = sendModifyStoragePoolCommand(cmd, storagePool, hostId); @@ -110,7 +123,7 @@ private String getSdcIdOfHost(HostVO host, StoragePool storagePool) { if (MapUtils.isEmpty(poolDetails)) { String msg = "PowerFlex storage SDC details not found on the host: " + hostId + ", (re)install SDC and restart agent"; logger.warn(msg); - _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_HOST, host.getDataCenterId(), host.getPodId(), "SDC not found on host: " + host.getUuid(), msg); + _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_HOST, host.getDataCenterId(), host.getPodId(), "SDC details not found on host: " + host.getUuid(), msg); return null; } @@ -119,7 +132,7 @@ private String getSdcIdOfHost(HostVO host, StoragePool storagePool) { sdcId = poolDetails.get(ScaleIOGatewayClient.SDC_ID); } else if (poolDetails.containsKey(ScaleIOGatewayClient.SDC_GUID)) { String sdcGuid = poolDetails.get(ScaleIOGatewayClient.SDC_GUID); - sdcId = getHostSdcId(sdcGuid, poolId); + sdcId = _sdcManager.getHostSdcId(sdcGuid, poolId); } if (StringUtils.isBlank(sdcId)) { @@ -129,34 +142,39 @@ private String getSdcIdOfHost(HostVO host, StoragePool storagePool) { return null; } - return sdcId; - } - - private String getHostSdcId(String sdcGuid, long poolId) { - try { - logger.debug(String.format("Try to get host SDC Id for pool: %s, with SDC guid %s", poolId, sdcGuid)); - ScaleIOGatewayClient client = ScaleIOGatewayClientConnectionPool.getInstance().getClient(poolId, _storagePoolDetailsDao); - return client.getSdcIdByGuid(sdcGuid); - } catch (NoSuchAlgorithmException | KeyManagementException | URISyntaxException e) { - logger.error(String.format("Failed to get host SDC Id for pool: %s", poolId), e); - throw new CloudRuntimeException(String.format("Failed to establish connection with PowerFlex Gateway to get host SDC Id for pool: %s", poolId)); + if (details.containsKey(ScaleIOSDCManager.ConnectOnDemand.key())) { + String connectOnDemand = details.get(ScaleIOSDCManager.ConnectOnDemand.key()); + if (connectOnDemand != null && !Boolean.parseBoolean(connectOnDemand) && !_sdcManager.isHostSdcConnected(sdcId, poolId, 15)) { + logger.warn("SDC not connected on the host: " + hostId); + String msg = "SDC not connected on the host: " + hostId + ", reconnect the SDC to MDM and restart agent"; + _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_HOST, host.getDataCenterId(), host.getPodId(), "SDC not connected on host: " + host.getUuid(), msg); + return null; + } } + + return sdcId; } private ModifyStoragePoolAnswer sendModifyStoragePoolCommand(ModifyStoragePoolCommand cmd, StoragePool storagePool, long hostId) { Answer answer = _agentMgr.easySend(hostId, cmd); if (answer == null) { - throw new CloudRuntimeException("Unable to get an answer to the modify storage pool command (" + storagePool.getId() + ")"); + throw new CloudRuntimeException(String.format("Unable to get an answer to the modify storage pool command (add: %s) for PowerFlex storage pool %s, sent to host %d", + cmd.getAdd(), getStoragePoolDetails(storagePool), hostId)); } if (!answer.getResult()) { - String msg = "Unable to attach PowerFlex storage pool " + storagePool.getId() + " to host " + hostId; + if (cmd.getAdd()) { + String msg = "Unable to attach PowerFlex storage pool " + getStoragePoolDetails(storagePool) + " to the host " + hostId; - _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_HOST, storagePool.getDataCenterId(), storagePool.getPodId(), msg, msg); + _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_HOST, storagePool.getDataCenterId(), storagePool.getPodId(), msg, msg); - throw new CloudRuntimeException("Unable to establish a connection from agent to PowerFlex storage pool " + storagePool.getId() + " due to " + answer.getDetails() + - " (" + storagePool.getId() + ")"); + throw new CloudRuntimeException("Unable to connect to PowerFlex storage pool " + getStoragePoolDetails(storagePool) + " due to " + answer.getDetails()); + } else { + String msg = "Unable to detach PowerFlex storage pool " + getStoragePoolDetails(storagePool) + " from the host " + hostId; + + _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_HOST, storagePool.getDataCenterId(), storagePool.getPodId(), msg, msg); + } } assert (answer instanceof ModifyStoragePoolAnswer) : "ModifyStoragePoolAnswer expected ; PowerFlex Storage Pool = " + storagePool.getId() + " Host = " + hostId; @@ -166,7 +184,43 @@ private ModifyStoragePoolAnswer sendModifyStoragePoolCommand(ModifyStoragePoolCo @Override public boolean hostDisconnected(long hostId, long poolId) { - // SDC ID is getting updated upon host connect, no need to delete the storage_pool_host_ref entry + HostVO host = _hostDao.findById(hostId); + if (host == null) { + logger.error("Failed to disconnect host by HostListener as host was not found with id : " + hostId); + return false; + } + + DataStore dataStore = _dataStoreMgr.getDataStore(poolId, DataStoreRole.Primary); + StoragePool storagePool = (StoragePool) dataStore; + String systemId = null; + StoragePoolDetailVO systemIdDetail = _storagePoolDetailsDao.findDetail(poolId, ScaleIOGatewayClient.STORAGE_POOL_SYSTEM_ID); + if (systemIdDetail != null) { + systemId = systemIdDetail.getValue(); + } + if (systemId == null) { + throw new CloudRuntimeException("Failed to get the system id for PowerFlex storage pool " + storagePool.getName()); + } + Map details = new HashMap<>(); + details.put(ScaleIOGatewayClient.STORAGE_POOL_SYSTEM_ID, systemId); + _sdcManager = ComponentContext.inject(_sdcManager); + if (_sdcManager.canUnprepareSDC(host, dataStore)) { + details.put(ScaleIOSDCManager.ConnectOnDemand.key(), String.valueOf(ScaleIOSDCManager.ConnectOnDemand.valueIn(host.getDataCenterId()))); + String mdms = _sdcManager.getMdms(poolId); + details.put(ScaleIOGatewayClient.STORAGE_POOL_MDMS, mdms); + } + + ModifyStoragePoolCommand cmd = new ModifyStoragePoolCommand(false, storagePool, storagePool.getPath(), details); + ModifyStoragePoolAnswer answer = sendModifyStoragePoolCommand(cmd, storagePool, hostId); + if (!answer.getResult()) { + logger.error("Failed to disconnect storage pool: " + storagePool + " and host: " + hostId); + return false; + } + + StoragePoolHostVO storagePoolHost = _storagePoolHostDao.findByPoolHost(poolId, hostId); + if (storagePoolHost != null) { + _storagePoolHostDao.deleteStoragePoolHostDetails(hostId, poolId); + } + logger.info("Connection removed between storage pool: " + storagePool + " and host: " + hostId); return true; } @@ -184,4 +238,12 @@ public boolean hostRemoved(long hostId, long clusterId) { public boolean hostEnabled(long hostId) { return true; } + + private String getStoragePoolDetails(StoragePool storagePool) { + String poolDetails = ""; + if (storagePool != null) { + poolDetails = String.format("%s (id: %d, uuid: %s)", storagePool.getName(), storagePool.getId(), storagePool.getUuid()); + } + return poolDetails; + } } diff --git a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/util/ScaleIOUtil.java b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/util/ScaleIOUtil.java index 4bb8df9b60db..a8c02a90bf9e 100644 --- a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/util/ScaleIOUtil.java +++ b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/util/ScaleIOUtil.java @@ -17,6 +17,9 @@ package org.apache.cloudstack.storage.datastore.util; +import java.util.List; + +import org.apache.commons.collections.CollectionUtils; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; @@ -85,6 +88,54 @@ public class ScaleIOUtil { */ private static final String QUERY_MDMS_CMD = "drv_cfg --query_mdms"; + private static final String ADD_MDMS_CMD = "drv_cfg --add_mdm"; + private static final String DRV_CFG_FILE = "/etc/emc/scaleio/drv_cfg.txt"; + + public static void addMdms(List mdmAddresses) { + if (CollectionUtils.isEmpty(mdmAddresses)) { + return; + } + // Sample Cmd - /opt/emc/scaleio/sdc/bin/drv_cfg --add_mdm --ip x.x.x.x,x.x.x.x --file /etc/emc/scaleio/drv_cfg.txt + String addMdmsCmd = ScaleIOUtil.SDC_HOME_PATH + "/bin/" + ScaleIOUtil.ADD_MDMS_CMD; + addMdmsCmd += " --ip " + String.join(",", mdmAddresses); + addMdmsCmd += " --file " + DRV_CFG_FILE; + String result = Script.runSimpleBashScript(addMdmsCmd); + if (result == null) { + LOGGER.warn("Failed to add mdms"); + } + } + + public static void removeMdms(List mdmAddresses) { + if (CollectionUtils.isEmpty(mdmAddresses)) { + return; + } + // (i) Remove MDMs from config file (ii) Restart scini + // Sample Cmd - sed -i '/x.x.x.x\,/d' /etc/emc/scaleio/drv_cfg.txt + boolean restartSDC = false; + String removeMdmsCmdFormat = "sed -i '/%s\\,/d' %s"; + for (String mdmAddress : mdmAddresses) { + if (mdmAdded(mdmAddress)) { + restartSDC = true; + } + String removeMdmsCmd = String.format(removeMdmsCmdFormat, mdmAddress, DRV_CFG_FILE); + Script.runSimpleBashScript(removeMdmsCmd); + } + if (restartSDC) { + restartSDCService(); + } + } + + public static boolean mdmAdded(String mdmAddress) { + //query_mdms outputs "MDM-ID SDC ID INSTALLATION ID IPs [0]-x.x.x.x [1]-x.x.x.x" for a MDM with ID: + String queryMdmsCmd = ScaleIOUtil.SDC_HOME_PATH + "/bin/" + ScaleIOUtil.QUERY_MDMS_CMD; + queryMdmsCmd += "|grep " + mdmAddress; + String result = Script.runSimpleBashScript(queryMdmsCmd); + if (StringUtils.isNotBlank(result) && result.contains(mdmAddress)) { + return true; + } + return false; + } + public static String getSdcHomePath() { String sdcHomePath = DEFAULT_SDC_HOME_PATH; String sdcHomePropertyCmdFormat = "sed -n '/%s/p' '%s' 2>/dev/null | sed 's/%s=//g' 2>/dev/null"; diff --git a/plugins/storage/volume/scaleio/src/test/java/org/apache/cloudstack/storage/datastore/lifecycle/ScaleIOPrimaryDataStoreLifeCycleTest.java b/plugins/storage/volume/scaleio/src/test/java/org/apache/cloudstack/storage/datastore/lifecycle/ScaleIOPrimaryDataStoreLifeCycleTest.java index 52dcad519421..bd8ef0945213 100644 --- a/plugins/storage/volume/scaleio/src/test/java/org/apache/cloudstack/storage/datastore/lifecycle/ScaleIOPrimaryDataStoreLifeCycleTest.java +++ b/plugins/storage/volume/scaleio/src/test/java/org/apache/cloudstack/storage/datastore/lifecycle/ScaleIOPrimaryDataStoreLifeCycleTest.java @@ -22,6 +22,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyMap; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mock; @@ -176,7 +177,7 @@ public void testAttachZone_UnsupportedHypervisor() throws Exception { @Test public void testMaintain() { final DataStore store = mock(DataStore.class); - when(storagePoolAutomation.maintain(any(DataStore.class))).thenReturn(true); + when(storagePoolAutomation.maintain(any(DataStore.class), anyMap())).thenReturn(true); when(dataStoreHelper.maintain(any(DataStore.class))).thenReturn(true); final boolean result = scaleIOPrimaryDataStoreLifeCycleTest.maintain(store); assertThat(result).isTrue(); @@ -186,7 +187,7 @@ public void testMaintain() { public void testCancelMaintain() { final DataStore store = mock(DataStore.class); when(dataStoreHelper.cancelMaintain(any(DataStore.class))).thenReturn(true); - when(storagePoolAutomation.cancelMaintain(any(DataStore.class))).thenReturn(true); + when(storagePoolAutomation.cancelMaintain(any(DataStore.class), anyMap())).thenReturn(true); final boolean result = scaleIOPrimaryDataStoreLifeCycleTest.cancelMaintain(store); assertThat(result).isTrue(); } diff --git a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java index f2e03cddb7cd..8665b37b9476 100644 --- a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java +++ b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java @@ -2826,6 +2826,17 @@ public boolean canHostPrepareStoragePoolAccess(Host host, StoragePool pool) { return storeDriver instanceof PrimaryDataStoreDriver && ((PrimaryDataStoreDriver)storeDriver).canHostPrepareStoragePoolAccess(host, pool); } + @Override + public boolean canDisconnectHostFromStoragePool(Host host, StoragePool pool) { + if (pool == null || !pool.isManaged()) { + return true; + } + + DataStoreProvider storeProvider = _dataStoreProviderMgr.getDataStoreProvider(pool.getStorageProviderName()); + DataStoreDriver storeDriver = storeProvider.getDataStoreDriver(); + return storeDriver instanceof PrimaryDataStoreDriver && ((PrimaryDataStoreDriver)storeDriver).canDisconnectHostFromStoragePool(host, pool); + } + @Override @DB public Host getHost(long hostId) { diff --git a/server/src/main/java/com/cloud/storage/StoragePoolAutomation.java b/server/src/main/java/com/cloud/storage/StoragePoolAutomation.java index 1acd574c2cc6..ea5619182ccb 100644 --- a/server/src/main/java/com/cloud/storage/StoragePoolAutomation.java +++ b/server/src/main/java/com/cloud/storage/StoragePoolAutomation.java @@ -18,10 +18,16 @@ */ package com.cloud.storage; +import java.util.Map; + import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; public interface StoragePoolAutomation { - public boolean maintain(DataStore store); + boolean maintain(DataStore store); + + boolean maintain(DataStore store, Map details); + + boolean cancelMaintain(DataStore store); - public boolean cancelMaintain(DataStore store); + boolean cancelMaintain(DataStore store, Map details); } diff --git a/server/src/main/java/com/cloud/storage/StoragePoolAutomationImpl.java b/server/src/main/java/com/cloud/storage/StoragePoolAutomationImpl.java index f1c7c38b8dc6..f814146bbc2a 100644 --- a/server/src/main/java/com/cloud/storage/StoragePoolAutomationImpl.java +++ b/server/src/main/java/com/cloud/storage/StoragePoolAutomationImpl.java @@ -31,14 +31,15 @@ import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; -import org.apache.logging.log4j.Logger; +import org.apache.commons.collections.MapUtils; import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.springframework.stereotype.Component; import com.cloud.agent.AgentManager; import com.cloud.agent.api.Answer; -import com.cloud.agent.api.ModifyStoragePoolCommand; import com.cloud.agent.api.ModifyStoragePoolAnswer; +import com.cloud.agent.api.ModifyStoragePoolCommand; import com.cloud.alert.AlertManager; import com.cloud.host.HostVO; import com.cloud.host.Status; @@ -112,6 +113,11 @@ public class StoragePoolAutomationImpl implements StoragePoolAutomation { @Override public boolean maintain(DataStore store) { + return maintain(store, null); + } + + @Override + public boolean maintain(DataStore store, Map details) { Long userId = CallContext.current().getCallingUserId(); User user = _userDao.findById(userId); Account account = CallContext.current().getCallingAccount(); @@ -162,6 +168,9 @@ public boolean maintain(DataStore store) { // remove heartbeat for (HostVO host : hosts) { ModifyStoragePoolCommand cmd = new ModifyStoragePoolCommand(false, storagePool); + if (MapUtils.isNotEmpty(details) && storageManager.canDisconnectHostFromStoragePool(host, storagePool)) { + cmd.setDetails(details); + } final Answer answer = agentMgr.easySend(host.getId(), cmd); if (answer == null || !answer.getResult()) { if (logger.isDebugEnabled()) { @@ -300,6 +309,11 @@ public boolean maintain(DataStore store) { @Override public boolean cancelMaintain(DataStore store) { + return cancelMaintain(store, null); + } + + @Override + public boolean cancelMaintain(DataStore store, Map details) { // Change the storage state back to up Long userId = CallContext.current().getCallingUserId(); User user = _userDao.findById(userId); @@ -328,7 +342,10 @@ public boolean cancelMaintain(DataStore store) { Pair, Boolean> nfsMountOpts = storageManager.getStoragePoolNFSMountOpts(pool, null); // add heartbeat for (HostVO host : hosts) { - ModifyStoragePoolCommand msPoolCmd = new ModifyStoragePoolCommand(true, pool, nfsMountOpts.first()); + ModifyStoragePoolCommand msPoolCmd = new ModifyStoragePoolCommand(true, pool); + if (MapUtils.isNotEmpty(details)) { + msPoolCmd.setDetails(details); + } final Answer answer = agentMgr.easySend(host.getId(), msPoolCmd); if (answer == null || !answer.getResult()) { if (logger.isDebugEnabled()) {