From 73608dec28b5a524743ac74b776b8778bb540e28 Mon Sep 17 00:00:00 2001 From: Mike Tutkowski Date: Fri, 22 Dec 2017 01:02:42 -0700 Subject: [PATCH] Support multiple volume access groups per compute cluster --- .../StorageSystemDataMotionStrategy.java | 12 +- ... GetSolidFireVolumeAccessGroupIdsCmd.java} | 18 +- ...olidFireVolumeAccessGroupIdsResponse.java} | 12 +- ...piSolidFireIntegrationTestServiceImpl.java | 4 +- .../SolidFireIntegrationTestManager.java | 2 +- .../SolidFireIntegrationTestManagerImpl.java | 44 +- .../resource/VmwareStorageProcessor.java | 173 ++++- .../SolidFirePrimaryDataStoreDriver.java | 72 +- ...idFireSharedPrimaryDataStoreLifeCycle.java | 188 +++--- .../provider/SolidFireHostListener.java | 82 +-- .../provider/SolidFireSharedHostListener.java | 23 +- .../storage/datastore/util/SolidFireUtil.java | 631 ++++++++++++------ .../plugins/solidfire/TestAddRemoveHosts.py | 186 +++++- .../solidfire/TestCapacityManagement.py | 5 +- .../plugins/solidfire/TestSnapshots.py | 10 +- .../plugins/solidfire/TestUploadDownload.py | 2 +- .../solidfire/TestVMMigrationWithStorage.py | 14 +- .../plugins/solidfire/TestVMSnapshots.py | 11 +- .../plugins/solidfire/TestVolumes.py | 12 +- 19 files changed, 977 insertions(+), 524 deletions(-) rename plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/api/command/admin/solidfire/{GetSolidFireVolumeAccessGroupIdCmd.java => GetSolidFireVolumeAccessGroupIdsCmd.java} (79%) rename plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/api/response/solidfire/{ApiSolidFireVolumeAccessGroupIdResponse.java => ApiSolidFireVolumeAccessGroupIdsResponse.java} (71%) diff --git a/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java b/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java index 45e951b59a1..8efaebe1c84 100644 --- a/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java +++ b/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java @@ -1654,18 +1654,18 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy { details.put(ModifyTargetsCommand.STORAGE_HOST, storagePool.getHostAddress()); details.put(ModifyTargetsCommand.STORAGE_PORT, String.valueOf(storagePool.getPort())); - ModifyTargetsCommand modifyTargetsCommand = new ModifyTargetsCommand(); + ModifyTargetsCommand cmd = new ModifyTargetsCommand(); List> targets = new ArrayList<>(); targets.add(details); - modifyTargetsCommand.setTargets(targets); - modifyTargetsCommand.setApplyToAllHostsInCluster(true); - modifyTargetsCommand.setAdd(add); - modifyTargetsCommand.setTargetTypeToRemove(ModifyTargetsCommand.TargetTypeToRemove.DYNAMIC); + cmd.setTargets(targets); + cmd.setApplyToAllHostsInCluster(true); + cmd.setAdd(add); + cmd.setTargetTypeToRemove(ModifyTargetsCommand.TargetTypeToRemove.DYNAMIC); - return modifyTargetsCommand; + return cmd; } private List sendModifyTargetsCommand(ModifyTargetsCommand cmd, long hostId) { diff --git a/plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/api/command/admin/solidfire/GetSolidFireVolumeAccessGroupIdCmd.java b/plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/api/command/admin/solidfire/GetSolidFireVolumeAccessGroupIdsCmd.java similarity index 79% rename from plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/api/command/admin/solidfire/GetSolidFireVolumeAccessGroupIdCmd.java rename to plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/api/command/admin/solidfire/GetSolidFireVolumeAccessGroupIdsCmd.java index 5c15e01a30b..0516b8ebc45 100644 --- a/plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/api/command/admin/solidfire/GetSolidFireVolumeAccessGroupIdCmd.java +++ b/plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/api/command/admin/solidfire/GetSolidFireVolumeAccessGroupIdsCmd.java @@ -26,16 +26,16 @@ import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.Parameter; -import org.apache.cloudstack.api.response.solidfire.ApiSolidFireVolumeAccessGroupIdResponse; +import org.apache.cloudstack.api.response.solidfire.ApiSolidFireVolumeAccessGroupIdsResponse; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.solidfire.SolidFireIntegrationTestManager; import org.apache.cloudstack.util.solidfire.SolidFireIntegrationTestUtil; -@APICommand(name = "getSolidFireVolumeAccessGroupId", responseObject = ApiSolidFireVolumeAccessGroupIdResponse.class, description = "Get the SF Volume Access Group ID", +@APICommand(name = "getSolidFireVolumeAccessGroupIds", responseObject = ApiSolidFireVolumeAccessGroupIdsResponse.class, description = "Get the SF Volume Access Group IDs", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) -public class GetSolidFireVolumeAccessGroupIdCmd extends BaseCmd { - private static final Logger LOGGER = Logger.getLogger(GetSolidFireVolumeAccessGroupIdCmd.class.getName()); - private static final String NAME = "getsolidfirevolumeaccessgroupidresponse"; +public class GetSolidFireVolumeAccessGroupIdsCmd extends BaseCmd { + private static final Logger LOGGER = Logger.getLogger(GetSolidFireVolumeAccessGroupIdsCmd.class.getName()); + private static final String NAME = "getsolidfirevolumeaccessgroupidsresponse"; @Parameter(name = ApiConstants.CLUSTER_ID, type = CommandType.STRING, description = "Cluster UUID", required = true) private String clusterUuid; @@ -67,14 +67,14 @@ public class GetSolidFireVolumeAccessGroupIdCmd extends BaseCmd { @Override public void execute() { - LOGGER.info("'GetSolidFireVolumeAccessGroupIdCmd.execute' method invoked"); + LOGGER.info("'GetSolidFireVolumeAccessGroupIdsCmd.execute' method invoked"); - long sfVagId = manager.getSolidFireVolumeAccessGroupId(clusterUuid, storagePoolUuid); + long[] sfVagIds = manager.getSolidFireVolumeAccessGroupIds(clusterUuid, storagePoolUuid); - ApiSolidFireVolumeAccessGroupIdResponse response = new ApiSolidFireVolumeAccessGroupIdResponse(sfVagId); + ApiSolidFireVolumeAccessGroupIdsResponse response = new ApiSolidFireVolumeAccessGroupIdsResponse(sfVagIds); response.setResponseName(getCommandName()); - response.setObjectName("apisolidfirevolumeaccessgroupid"); + response.setObjectName("apisolidfirevolumeaccessgroupids"); this.setResponseObject(response); } diff --git a/plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/api/response/solidfire/ApiSolidFireVolumeAccessGroupIdResponse.java b/plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/api/response/solidfire/ApiSolidFireVolumeAccessGroupIdsResponse.java similarity index 71% rename from plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/api/response/solidfire/ApiSolidFireVolumeAccessGroupIdResponse.java rename to plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/api/response/solidfire/ApiSolidFireVolumeAccessGroupIdsResponse.java index 202a7e9ebba..a37da406362 100644 --- a/plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/api/response/solidfire/ApiSolidFireVolumeAccessGroupIdResponse.java +++ b/plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/api/response/solidfire/ApiSolidFireVolumeAccessGroupIdsResponse.java @@ -22,12 +22,12 @@ import com.google.gson.annotations.SerializedName; import org.apache.cloudstack.api.BaseResponse; -public class ApiSolidFireVolumeAccessGroupIdResponse extends BaseResponse { - @SerializedName("solidFireVolumeAccessGroupId") - @Param(description = "SolidFire Volume Access Group Id") - private long solidFireVolumeAccessGroupId; +public class ApiSolidFireVolumeAccessGroupIdsResponse extends BaseResponse { + @SerializedName("solidFireVolumeAccessGroupIds") + @Param(description = "SolidFire Volume Access Group Ids") + private long[] solidFireVolumeAccessGroupIds; - public ApiSolidFireVolumeAccessGroupIdResponse(long sfVolumeAccessGroupId) { - solidFireVolumeAccessGroupId = sfVolumeAccessGroupId; + public ApiSolidFireVolumeAccessGroupIdsResponse(long[] sfVolumeAccessGroupIds) { + solidFireVolumeAccessGroupIds = sfVolumeAccessGroupIds; } } \ No newline at end of file diff --git a/plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/api/solidfire/ApiSolidFireIntegrationTestServiceImpl.java b/plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/api/solidfire/ApiSolidFireIntegrationTestServiceImpl.java index 04589038d34..4adcbbe89d8 100644 --- a/plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/api/solidfire/ApiSolidFireIntegrationTestServiceImpl.java +++ b/plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/api/solidfire/ApiSolidFireIntegrationTestServiceImpl.java @@ -22,7 +22,7 @@ import java.util.ArrayList; import org.apache.cloudstack.api.command.admin.solidfire.GetPathForVolumeCmd; // import org.apache.log4j.Logger; import org.apache.cloudstack.api.command.admin.solidfire.GetSolidFireAccountIdCmd; -import org.apache.cloudstack.api.command.admin.solidfire.GetSolidFireVolumeAccessGroupIdCmd; +import org.apache.cloudstack.api.command.admin.solidfire.GetSolidFireVolumeAccessGroupIdsCmd; import org.apache.cloudstack.api.command.admin.solidfire.GetVolumeSnapshotDetailsCmd; import org.apache.cloudstack.api.command.admin.solidfire.GetVolumeiScsiNameCmd; import org.apache.cloudstack.api.command.admin.solidfire.GetSolidFireVolumeSizeCmd; @@ -38,7 +38,7 @@ public class ApiSolidFireIntegrationTestServiceImpl extends AdapterBase implemen cmdList.add(GetPathForVolumeCmd.class); cmdList.add(GetSolidFireAccountIdCmd.class); - cmdList.add(GetSolidFireVolumeAccessGroupIdCmd.class); + cmdList.add(GetSolidFireVolumeAccessGroupIdsCmd.class); cmdList.add(GetVolumeiScsiNameCmd.class); cmdList.add(GetSolidFireVolumeSizeCmd.class); cmdList.add(GetVolumeSnapshotDetailsCmd.class); diff --git a/plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/solidfire/SolidFireIntegrationTestManager.java b/plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/solidfire/SolidFireIntegrationTestManager.java index bdc11807efe..302a034911f 100644 --- a/plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/solidfire/SolidFireIntegrationTestManager.java +++ b/plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/solidfire/SolidFireIntegrationTestManager.java @@ -18,6 +18,6 @@ package org.apache.cloudstack.solidfire; public interface SolidFireIntegrationTestManager { long getSolidFireAccountId(String csAccountUuid, String storagePoolUuid); - long getSolidFireVolumeAccessGroupId(String csClusterUuid, String storagePoolUuid); + long[] getSolidFireVolumeAccessGroupIds(String csClusterUuid, String storagePoolUuid); long getSolidFireVolumeSize(String volumeUuid); } diff --git a/plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/solidfire/SolidFireIntegrationTestManagerImpl.java b/plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/solidfire/SolidFireIntegrationTestManagerImpl.java index 66b92281efa..0339379d116 100644 --- a/plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/solidfire/SolidFireIntegrationTestManagerImpl.java +++ b/plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/solidfire/SolidFireIntegrationTestManagerImpl.java @@ -16,8 +16,8 @@ // under the License. package org.apache.cloudstack.solidfire; -import com.cloud.dc.ClusterDetailsDao; -import com.cloud.dc.ClusterDetailsVO; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; import com.cloud.storage.VolumeDetailVO; import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.VolumeDao; @@ -26,18 +26,21 @@ import com.cloud.user.AccountDetailVO; import com.cloud.user.AccountDetailsDao; import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; import org.apache.cloudstack.storage.datastore.util.SolidFireUtil; import org.apache.cloudstack.util.solidfire.SolidFireIntegrationTestUtil; import org.springframework.stereotype.Component; +import java.util.ArrayList; +import java.util.List; import javax.inject.Inject; @Component public class SolidFireIntegrationTestManagerImpl implements SolidFireIntegrationTestManager { - @Inject private AccountDetailsDao accountDetailsDao; - @Inject private ClusterDetailsDao clusterDetailsDao; + @Inject private HostDao hostDao; @Inject private SolidFireIntegrationTestUtil util; + @Inject private StoragePoolDetailsDao storagePoolDetailsDao; @Inject private VolumeDao volumeDao; @Inject private VolumeDetailsDao volumeDetailsDao; @@ -48,7 +51,7 @@ public class SolidFireIntegrationTestManagerImpl implements SolidFireIntegration AccountDetailVO accountDetail = accountDetailsDao.findDetail(csAccountId, SolidFireUtil.getAccountKey(storagePoolId)); - if (accountDetail == null){ + if (accountDetail == null) { throw new CloudRuntimeException("Unable to find SF account for storage " + storagePoolUuid + " for CS account " + csAccountUuid); } @@ -58,14 +61,35 @@ public class SolidFireIntegrationTestManagerImpl implements SolidFireIntegration } @Override - public long getSolidFireVolumeAccessGroupId(String csClusterUuid, String storagePoolUuid) { - long csClusterId = util.getClusterIdForClusterUuid(csClusterUuid); + public long[] getSolidFireVolumeAccessGroupIds(String csClusterUuid, String storagePoolUuid) { long storagePoolId = util.getStoragePoolIdForStoragePoolUuid(storagePoolUuid); - ClusterDetailsVO clusterDetails = clusterDetailsDao.findDetail(csClusterId, SolidFireUtil.getVagKey(storagePoolId)); - String sfVagId = clusterDetails.getValue(); + SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, storagePoolDetailsDao); - return Long.parseLong(sfVagId); + List sfVags = SolidFireUtil.getAllVags(sfConnection); + + long csClusterId = util.getClusterIdForClusterUuid(csClusterUuid); + List hosts = hostDao.findByClusterId(csClusterId); + + if (hosts == null) { + return new long[0]; + } + + List vagIds = new ArrayList<>(hosts.size()); + + for (HostVO host : hosts) { + String iqn = host.getStorageUrl(); + + SolidFireUtil.SolidFireVag sfVag = SolidFireUtil.getVolumeAccessGroup(iqn, sfVags); + + if (sfVag != null) { + if (!vagIds.contains(sfVag.getId())) { + vagIds.add(sfVag.getId()); + } + } + } + + return vagIds.stream().mapToLong(l -> l).toArray(); } @Override diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareStorageProcessor.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareStorageProcessor.java index 0cea62f18fa..82cd4ca23d3 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareStorageProcessor.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareStorageProcessor.java @@ -145,6 +145,7 @@ public class VmwareStorageProcessor implements StorageProcessor { private static final Logger s_logger = Logger.getLogger(VmwareStorageProcessor.class); private static final int DEFAULT_NFS_PORT = 2049; + private static final int SECONDS_TO_WAIT_FOR_DATASTORE = 120; private final VmwareHostService hostService; private boolean _fullCloneFlag; @@ -2602,8 +2603,7 @@ public class VmwareStorageProcessor implements StorageProcessor { } private void waitForAllHostsToSeeDatastore(List> lstHosts, DatastoreMO dsMO) throws Exception { - long secondsToWait = 120; - long endWaitTime = System.currentTimeMillis() + secondsToWait * 1000; + long endWaitTime = System.currentTimeMillis() + SECONDS_TO_WAIT_FOR_DATASTORE * 1000; boolean isConditionMet = false; @@ -2621,7 +2621,7 @@ public class VmwareStorageProcessor implements StorageProcessor { private boolean verifyAllHostsSeeDatastore(List> lstHosts, DatastoreMO dsMO) throws Exception { int numHostsChecked = 0; - for (Pair host: lstHosts) { + for (Pair host : lstHosts) { ManagedObjectReference morHostToMatch = host.first(); HostMO hostToMatchMO = new HostMO(dsMO.getContext(), morHostToMatch); @@ -2641,8 +2641,7 @@ public class VmwareStorageProcessor implements StorageProcessor { } private void waitForAllHostsToMountDatastore(List> lstHosts, DatastoreMO dsMO) throws Exception { - long secondsToWait = 120; - long endWaitTime = System.currentTimeMillis() + secondsToWait * 1000; + long endWaitTime = System.currentTimeMillis() + SECONDS_TO_WAIT_FOR_DATASTORE * 1000; boolean isConditionMet = false; @@ -2657,13 +2656,39 @@ public class VmwareStorageProcessor implements StorageProcessor { } } - private boolean verifyAllHostsMountedDatastore(List> lstHosts, DatastoreMO dsMO) throws Exception { - int numHostsChecked = 0; + private void waitForAllHostsToMountDatastore2(List lstHosts, DatastoreMO dsMO) throws Exception { + long endWaitTime = System.currentTimeMillis() + SECONDS_TO_WAIT_FOR_DATASTORE * 1000; - for (Pair host: lstHosts) { + boolean isConditionMet = false; + + while (System.currentTimeMillis() < endWaitTime && !isConditionMet) { + Thread.sleep(5000); + + isConditionMet = verifyAllHostsMountedDatastore2(lstHosts, dsMO); + } + + if (!isConditionMet) { + throw new CloudRuntimeException("Not all hosts mounted the datastore"); + } + } + + private boolean verifyAllHostsMountedDatastore(List> lstHosts, DatastoreMO dsMO) throws Exception { + List hostMOs = new ArrayList<>(lstHosts.size()); + + for (Pair host : lstHosts) { ManagedObjectReference morHostToMatch = host.first(); HostMO hostToMatchMO = new HostMO(dsMO.getContext(), morHostToMatch); + hostMOs.add(hostToMatchMO); + } + + return verifyAllHostsMountedDatastore2(hostMOs, dsMO); + } + + private boolean verifyAllHostsMountedDatastore2(List lstHosts, DatastoreMO dsMO) throws Exception { + int numHostsChecked = 0; + + for (HostMO hostToMatchMO : lstHosts) { List datastoreHostMounts = dsMO.getHostMounts(); for (DatastoreHostMount datastoreHostMount : datastoreHostMounts) { @@ -2753,6 +2778,16 @@ public class VmwareStorageProcessor implements StorageProcessor { for (Pair host : hosts) { HostMO hostMO = new HostMO(dsMO.getContext(), host.first()); + List hostMOs = new ArrayList<>(1); + + hostMOs.add(hostMO); + + mountVmfsDatastore2(dsMO, hostMOs); + } + } + + private void mountVmfsDatastore2(DatastoreMO dsMO, List hosts) throws Exception { + for (HostMO hostMO : hosts) { if (!isDatastoreMounted(dsMO, hostMO)) { HostStorageSystemMO hostStorageSystemMO = hostMO.getHostStorageSystemMO(); @@ -2760,11 +2795,15 @@ public class VmwareStorageProcessor implements StorageProcessor { hostStorageSystemMO.mountVmfsVolume(getDatastoreUuid(dsMO, hostMO)); } catch (InvalidStateFaultMsg ex) { - List> currentHosts = new ArrayList<>(1); + s_logger.trace("'" + ex.getClass().getName() + "' exception thrown: " + ex.getMessage()); - currentHosts.add(host); + List currentHosts = new ArrayList<>(1); - waitForAllHostsToMountDatastore(currentHosts, dsMO); + currentHosts.add(hostMO); + + s_logger.trace("Waiting for host " + hostMO.getHostName() + " to mount datastore " + dsMO.getName()); + + waitForAllHostsToMountDatastore2(currentHosts, dsMO); } } } @@ -2772,12 +2811,29 @@ public class VmwareStorageProcessor implements StorageProcessor { private void unmountVmfsDatastore(VmwareContext context, VmwareHypervisorHost hyperHost, String datastoreName, List> hosts) throws Exception { - ManagedObjectReference morDs = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, datastoreName); - DatastoreMO dsMO = new DatastoreMO(context, morDs); - for (Pair host : hosts) { HostMO hostMO = new HostMO(context, host.first()); + List hostMOs = new ArrayList<>(1); + + hostMOs.add(hostMO); + + unmountVmfsDatastore2(context, hyperHost, datastoreName, hostMOs); + } + } + + private void unmountVmfsDatastore2(VmwareContext context, VmwareHypervisorHost hyperHost, String datastoreName, + List hosts) throws Exception { + ManagedObjectReference morDs = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, datastoreName); + DatastoreMO dsMO = new DatastoreMO(context, morDs); + + for (HostMO hostMO : hosts) { + unmountVmfsVolume(dsMO, hostMO); + } + } + + private void unmountVmfsVolume(DatastoreMO dsMO, HostMO hostMO) throws Exception { + if (isDatastoreMounted(dsMO, hostMO)) { HostStorageSystemMO hostStorageSystemMO = hostMO.getHostStorageSystemMO(); hostStorageSystemMO.unmountVmfsVolume(getDatastoreUuid(dsMO, hostMO)); @@ -2902,6 +2958,20 @@ public class VmwareStorageProcessor implements StorageProcessor { if (rescan) { rescanAllHosts(hosts, true, false); + + List targetsToAdd = new ArrayList<>(); + + targetsToAdd.addAll(getTargets(staticTargetsForHost)); + targetsToAdd.addAll(getTargets(dynamicTargetsForHost)); + + for (HostInternetScsiHbaStaticTarget targetToAdd : targetsToAdd) { + HostDatastoreSystemMO hostDatastoreSystemMO = host.getHostDatastoreSystemMO(); + String datastoreName = waitForDatastoreName(hostDatastoreSystemMO, targetToAdd.getIScsiName()); + ManagedObjectReference morDs = hostDatastoreSystemMO.findDatastoreByName(datastoreName); + DatastoreMO datastoreMO = new DatastoreMO(host.getContext(), morDs); + + mountVmfsDatastore2(datastoreMO, hosts); + } } } catch (Exception ex) { @@ -2924,26 +2994,9 @@ public class VmwareStorageProcessor implements StorageProcessor { if (targetsToRemove.size() > 0) { if (isRemoveAsync) { - new Thread(() -> { - try { - addRemoveInternetScsiTargetsToAllHosts(false, targetsToRemove, hosts); - - rescanAllHosts(hosts, true, false); - } catch (Exception ex) { - s_logger.warn(ex.getMessage()); - } - }).start(); + new Thread(() -> handleRemove(targetsToRemove, host, hosts)).start(); } else { - executorService.submit(new Thread(() -> { - try { - addRemoveInternetScsiTargetsToAllHosts(false, targetsToRemove, hosts); - - rescanAllHosts(hosts, true, false); - } - catch (Exception ex) { - s_logger.warn(ex.getMessage()); - } - })); + executorService.submit(new Thread(() -> handleRemove(targetsToRemove, host, hosts))); } } } @@ -2956,6 +3009,60 @@ public class VmwareStorageProcessor implements StorageProcessor { } } + private String waitForDatastoreName(HostDatastoreSystemMO hostDatastoreSystemMO, String iqn) throws Exception { + long endWaitTime = System.currentTimeMillis() + SECONDS_TO_WAIT_FOR_DATASTORE * 1000; + + do { + String datastoreName = getDatastoreName(hostDatastoreSystemMO, iqn); + + if (datastoreName != null) { + return datastoreName; + } + + Thread.sleep(5000); + } + while (System.currentTimeMillis() < endWaitTime); + + throw new CloudRuntimeException("Could not find the datastore name"); + } + + private String getDatastoreName(HostDatastoreSystemMO hostDatastoreSystemMO, String iqn) throws Exception { + String datastoreName = "-" + iqn + "-0"; + + ManagedObjectReference morDs = hostDatastoreSystemMO.findDatastoreByName(datastoreName); + + if (morDs != null) { + return datastoreName; + } + + datastoreName = "_" + iqn + "_0"; + + morDs = hostDatastoreSystemMO.findDatastoreByName(datastoreName); + + if (morDs != null) { + return datastoreName; + } + + return null; + } + + private void handleRemove(List targetsToRemove, HostMO host, List hosts) { + try { + for (HostInternetScsiHbaStaticTarget target : targetsToRemove) { + String datastoreName = waitForDatastoreName(host.getHostDatastoreSystemMO(), target.getIScsiName()); + + unmountVmfsDatastore2(host.getContext(), host, datastoreName, hosts); + } + + addRemoveInternetScsiTargetsToAllHosts(false, targetsToRemove, hosts); + + rescanAllHosts(hosts, true, false); + } + catch (Exception ex) { + s_logger.warn(ex.getMessage()); + } + } + private void addRemoveInternetScsiTargetsToAllHosts(VmwareContext context, final boolean add, final List targets, List> hostPairs) throws Exception { List hosts = new ArrayList<>(); diff --git a/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java b/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java index 3600ea92e61..c95e4c534d9 100644 --- a/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java +++ b/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java @@ -30,8 +30,6 @@ import com.cloud.agent.api.to.DataStoreTO; import com.cloud.agent.api.to.DataTO; import com.cloud.agent.api.to.DiskTO; import com.cloud.dc.ClusterVO; -import com.cloud.dc.ClusterDetailsVO; -import com.cloud.dc.ClusterDetailsDao; import com.cloud.dc.dao.ClusterDao; import com.cloud.host.Host; import com.cloud.host.HostVO; @@ -88,7 +86,6 @@ import org.apache.log4j.Logger; public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { private static final Logger LOGGER = Logger.getLogger(SolidFirePrimaryDataStoreDriver.class); - private static final int LOCK_TIME_IN_SECONDS = 300; private static final int LOWEST_HYPERVISOR_SNAPSHOT_RESERVE = 10; private static final long MIN_IOPS_FOR_TEMPLATE_VOLUME = 100L; private static final long MAX_IOPS_FOR_TEMPLATE_VOLUME = 20000L; @@ -102,7 +99,6 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { @Inject private AccountDao accountDao; @Inject private AccountDetailsDao accountDetailsDao; @Inject private ClusterDao clusterDao; - @Inject private ClusterDetailsDao clusterDetailsDao; @Inject private DataStoreManager dataStoreMgr; @Inject private HostDao hostDao; @Inject private SnapshotDao snapshotDao; @@ -147,14 +143,8 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { return null; } - // get the VAG associated with volumeInfo's cluster, if any (ListVolumeAccessGroups) - // if the VAG exists - // update the VAG to contain all IQNs of the hosts (ModifyVolumeAccessGroup) - // if the ID of volumeInfo in not in the VAG, add it (ModifyVolumeAccessGroup) - // if the VAG doesn't exist, create it with the IQNs of the hosts and the ID of volumeInfo (CreateVolumeAccessGroup) @Override - public boolean grantAccess(DataObject dataObject, Host host, DataStore dataStore) - { + public boolean grantAccess(DataObject dataObject, Host host, DataStore dataStore) { Preconditions.checkArgument(dataObject != null, "'dataObject' should not be 'null'"); Preconditions.checkArgument(host != null, "'host' should not be 'null'"); Preconditions.checkArgument(dataStore != null, "'dataStore' should not be 'null'"); @@ -167,7 +157,7 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { GlobalLock lock = GlobalLock.getInternLock(cluster.getUuid()); - if (!lock.lock(LOCK_TIME_IN_SECONDS)) { + if (!lock.lock(SolidFireUtil.LOCK_TIME_IN_SECONDS)) { String errMsg = "Couldn't lock the DB (in grantAccess) on the following string: " + cluster.getUuid(); LOGGER.warn(errMsg); @@ -176,32 +166,11 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { } try { - ClusterDetailsVO clusterDetail = clusterDetailsDao.findDetail(clusterId, SolidFireUtil.getVagKey(storagePoolId)); - - String vagId = clusterDetail != null ? clusterDetail.getValue() : null; - List hosts = hostDao.findByClusterId(clusterId); - if (!SolidFireUtil.hostsSupport_iScsi(hosts)) { - String errMsg = "Not all hosts in the compute cluster support iSCSI."; - - LOGGER.warn(errMsg); - - throw new CloudRuntimeException(errMsg); - } - SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, storagePoolDetailsDao); - if (vagId != null) { - SolidFireUtil.SolidFireVag sfVag = SolidFireUtil.getVag(sfConnection, Long.parseLong(vagId)); - - long[] volumeIds = SolidFireUtil.getNewVolumeIds(sfVag.getVolumeIds(), sfVolumeId, true); - - SolidFireUtil.modifyVag(sfConnection, sfVag.getId(), sfVag.getInitiators(), volumeIds); - } - else { - SolidFireUtil.placeVolumeInVolumeAccessGroup(sfConnection, sfVolumeId, storagePoolId, cluster.getUuid(), hosts, clusterDetailsDao); - } + SolidFireUtil.placeVolumeInVolumeAccessGroups(sfConnection, sfVolumeId, hosts); return true; } @@ -211,9 +180,6 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { } } - // get the VAG associated with volumeInfo's cluster, if any (ListVolumeAccessGroups) // might not exist if using CHAP - // if the VAG exists - // remove the ID of volumeInfo from the VAG (ModifyVolumeAccessGroup) @Override public void revokeAccess(DataObject dataObject, Host host, DataStore dataStore) { @@ -229,27 +195,23 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { GlobalLock lock = GlobalLock.getInternLock(cluster.getUuid()); - if (!lock.lock(LOCK_TIME_IN_SECONDS)) { + if (!lock.lock(SolidFireUtil.LOCK_TIME_IN_SECONDS)) { String errMsg = "Couldn't lock the DB (in revokeAccess) on the following string: " + cluster.getUuid(); - LOGGER.debug(errMsg); + LOGGER.warn(errMsg); throw new CloudRuntimeException(errMsg); } try { - ClusterDetailsVO clusterDetail = clusterDetailsDao.findDetail(clusterId, SolidFireUtil.getVagKey(storagePoolId)); + SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, storagePoolDetailsDao); - String vagId = clusterDetail != null ? clusterDetail.getValue() : null; + List sfVags = SolidFireUtil.getAllVags(sfConnection); - if (vagId != null) { - SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, storagePoolDetailsDao); - - SolidFireUtil.SolidFireVag sfVag = SolidFireUtil.getVag(sfConnection, Long.parseLong(vagId)); - - long[] volumeIds = SolidFireUtil.getNewVolumeIds(sfVag.getVolumeIds(), sfVolumeId, false); - - SolidFireUtil.modifyVag(sfConnection, sfVag.getId(), sfVag.getInitiators(), volumeIds); + for (SolidFireUtil.SolidFireVag sfVag : sfVags) { + if (SolidFireUtil.sfVagContains(sfVag, sfVolumeId, clusterId, hostDao)) { + SolidFireUtil.removeVolumeIdsFromSolidFireVag(sfConnection, sfVag.getId(), new Long[] { sfVolumeId }); + } } } finally { @@ -735,11 +697,7 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { private boolean getBooleanValueFromVolumeDetails(long volumeId, String name) { VolumeDetailVO volumeDetail = volumeDetailsDao.findDetail(volumeId, name); - if (volumeDetail != null && volumeDetail.getValue() != null) { - return Boolean.parseBoolean(volumeDetail.getValue()); - } - - return false; + return volumeDetail != null && volumeDetail.getValue() != null && Boolean.parseBoolean(volumeDetail.getValue()); } private long getCsIdForCloning(long volumeId, String cloneOf) { @@ -755,11 +713,7 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { private boolean shouldTakeSnapshot(long snapshotId) { SnapshotDetailsVO snapshotDetails = snapshotDetailsDao.findDetail(snapshotId, "takeSnapshot"); - if (snapshotDetails != null && snapshotDetails.getValue() != null) { - return Boolean.parseBoolean(snapshotDetails.getValue()); - } - - return false; + return snapshotDetails != null && snapshotDetails.getValue() != null && Boolean.parseBoolean(snapshotDetails.getValue()); } private SolidFireUtil.SolidFireVolume createClone(SolidFireUtil.SolidFireConnection sfConnection, long dataObjectId, VolumeInfo volumeInfo, long sfAccountId, diff --git a/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/SolidFireSharedPrimaryDataStoreLifeCycle.java b/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/SolidFireSharedPrimaryDataStoreLifeCycle.java index 3172b1af5b4..2ebd69a2d93 100644 --- a/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/SolidFireSharedPrimaryDataStoreLifeCycle.java +++ b/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/SolidFireSharedPrimaryDataStoreLifeCycle.java @@ -49,8 +49,6 @@ import com.cloud.agent.api.CreateStoragePoolCommand; import com.cloud.agent.api.DeleteStoragePoolCommand; import com.cloud.agent.api.ModifyTargetsCommand; import com.cloud.agent.api.StoragePoolInfo; -import com.cloud.dc.ClusterDetailsDao; -import com.cloud.dc.ClusterDetailsVO; import com.cloud.dc.ClusterVO; import com.cloud.dc.dao.ClusterDao; import com.cloud.dc.dao.DataCenterDao; @@ -75,23 +73,22 @@ import com.cloud.utils.db.GlobalLock; import com.cloud.utils.exception.CloudRuntimeException; public class SolidFireSharedPrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeCycle { - private static final Logger s_logger = Logger.getLogger(SolidFireSharedPrimaryDataStoreLifeCycle.class); + private static final Logger LOGGER = Logger.getLogger(SolidFireSharedPrimaryDataStoreLifeCycle.class); - @Inject private AccountDao _accountDao; - @Inject private AccountDetailsDao _accountDetailsDao; - @Inject private AgentManager _agentMgr; - @Inject private ClusterDao _clusterDao; - @Inject private ClusterDetailsDao _clusterDetailsDao; - @Inject private DataCenterDao _zoneDao; - @Inject private HostDao _hostDao; - @Inject private PrimaryDataStoreDao _primaryDataStoreDao; - @Inject private PrimaryDataStoreHelper _primaryDataStoreHelper; - @Inject private ResourceManager _resourceMgr; - @Inject private StorageManager _storageMgr; - @Inject private StoragePoolAutomation _storagePoolAutomation; - @Inject private StoragePoolDetailsDao _storagePoolDetailsDao; - @Inject private StoragePoolHostDao _storagePoolHostDao; - @Inject private TemplateManager _tmpltMgr; + @Inject private AccountDao accountDao; + @Inject private AccountDetailsDao accountDetailsDao; + @Inject private AgentManager agentMgr; + @Inject private ClusterDao clusterDao; + @Inject private DataCenterDao zoneDao; + @Inject private HostDao hostDao; + @Inject private PrimaryDataStoreDao primaryDataStoreDao; + @Inject private PrimaryDataStoreHelper primaryDataStoreHelper; + @Inject private ResourceManager resourceMgr; + @Inject private StorageManager storageMgr; + @Inject private StoragePoolAutomation storagePoolAutomation; + @Inject private StoragePoolDetailsDao storagePoolDetailsDao; + @Inject private StoragePoolHostDao storagePoolHostDao; + @Inject private TemplateManager tmpltMgr; // invoked to add primary storage that is based on the SolidFire plug-in @Override @@ -184,7 +181,7 @@ public class SolidFireSharedPrimaryDataStoreLifeCycle implements PrimaryDataStor lMinIops = Long.parseLong(minIops); } } catch (Exception ex) { - s_logger.info("[ignored] error getting Min IOPS: " + ex.getLocalizedMessage()); + LOGGER.info("[ignored] error getting Min IOPS: " + ex.getLocalizedMessage()); } try { @@ -194,7 +191,7 @@ public class SolidFireSharedPrimaryDataStoreLifeCycle implements PrimaryDataStor lMaxIops = Long.parseLong(maxIops); } } catch (Exception ex) { - s_logger.info("[ignored] error getting Max IOPS: " + ex.getLocalizedMessage()); + LOGGER.info("[ignored] error getting Max IOPS: " + ex.getLocalizedMessage()); } try { @@ -204,7 +201,7 @@ public class SolidFireSharedPrimaryDataStoreLifeCycle implements PrimaryDataStor lBurstIops = Long.parseLong(burstIops); } } catch (Exception ex) { - s_logger.info("[ignored] error getting Burst IOPS: " + ex.getLocalizedMessage()); + LOGGER.info("[ignored] error getting Burst IOPS: " + ex.getLocalizedMessage()); } if (lMinIops > lMaxIops) { @@ -266,14 +263,14 @@ public class SolidFireSharedPrimaryDataStoreLifeCycle implements PrimaryDataStor parameters.setPath(iqn); } - ClusterVO cluster = _clusterDao.findById(clusterId); + ClusterVO cluster = clusterDao.findById(clusterId); GlobalLock lock = GlobalLock.getInternLock(cluster.getUuid()); - if (!lock.lock(SolidFireUtil.s_lockTimeInSeconds)) { + if (!lock.lock(SolidFireUtil.LOCK_TIME_IN_SECONDS)) { String errMsg = "Couldn't lock the DB on the following string: " + cluster.getUuid(); - s_logger.debug(errMsg); + LOGGER.debug(errMsg); throw new CloudRuntimeException(errMsg); } @@ -282,21 +279,21 @@ public class SolidFireSharedPrimaryDataStoreLifeCycle implements PrimaryDataStor try { // this adds a row in the cloud.storage_pool table for this SolidFire volume - dataStore = _primaryDataStoreHelper.createPrimaryDataStore(parameters); + dataStore = primaryDataStoreHelper.createPrimaryDataStore(parameters); // now that we have a DataStore (we need the id from the DataStore instance), we can create a Volume Access Group, if need be, and // place the newly created volume in the Volume Access Group - List hosts = _hostDao.findByClusterId(clusterId); + List hosts = hostDao.findByClusterId(clusterId); - SolidFireUtil.placeVolumeInVolumeAccessGroup(sfConnection, sfVolume.getId(), dataStore.getId(), cluster.getUuid(), hosts, _clusterDetailsDao); + SolidFireUtil.placeVolumeInVolumeAccessGroups(sfConnection, sfVolume.getId(), hosts); SolidFireUtil.SolidFireAccount sfAccount = sfCreateVolume.getAccount(); Account csAccount = CallContext.current().getCallingAccount(); - SolidFireUtil.updateCsDbWithSolidFireAccountInfo(csAccount.getId(), sfAccount, dataStore.getId(), _accountDetailsDao); + SolidFireUtil.updateCsDbWithSolidFireAccountInfo(csAccount.getId(), sfAccount, dataStore.getId(), accountDetailsDao); } catch (Exception ex) { if (dataStore != null) { - _primaryDataStoreDao.expunge(dataStore.getId()); + primaryDataStoreDao.expunge(dataStore.getId()); } throw new CloudRuntimeException(ex.getMessage()); @@ -310,7 +307,7 @@ public class SolidFireSharedPrimaryDataStoreLifeCycle implements PrimaryDataStor } private HypervisorType getHypervisorTypeForCluster(long clusterId) { - ClusterVO cluster = _clusterDao.findById(clusterId); + ClusterVO cluster = clusterDao.findById(clusterId); if (cluster == null) { throw new CloudRuntimeException("Cluster ID '" + clusterId + "' was not found in the database."); @@ -354,7 +351,7 @@ public class SolidFireSharedPrimaryDataStoreLifeCycle implements PrimaryDataStor try { Account csAccount = CallContext.current().getCallingAccount(); long csAccountId = csAccount.getId(); - AccountVO accountVo = _accountDao.findById(csAccountId); + AccountVO accountVo = accountDao.findById(csAccountId); String sfAccountName = SolidFireUtil.getSolidFireAccountName(accountVo.getUuid(), csAccountId); @@ -386,11 +383,11 @@ public class SolidFireSharedPrimaryDataStoreLifeCycle implements PrimaryDataStor PrimaryDataStoreInfo primaryDataStoreInfo = (PrimaryDataStoreInfo)store; // check if there is at least one host up in this cluster - List allHosts = _resourceMgr.listAllUpHosts(Host.Type.Routing, primaryDataStoreInfo.getClusterId(), + List allHosts = resourceMgr.listAllUpHosts(Host.Type.Routing, primaryDataStoreInfo.getClusterId(), primaryDataStoreInfo.getPodId(), primaryDataStoreInfo.getDataCenterId()); if (allHosts.isEmpty()) { - _primaryDataStoreDao.expunge(primaryDataStoreInfo.getId()); + primaryDataStoreDao.expunge(primaryDataStoreInfo.getId()); throw new CloudRuntimeException("No host up to associate a storage pool with in cluster " + primaryDataStoreInfo.getClusterId()); } @@ -413,23 +410,23 @@ public class SolidFireSharedPrimaryDataStoreLifeCycle implements PrimaryDataStor for (HostVO host : allHosts) { try { - _storageMgr.connectHostToSharedPool(host.getId(), primaryDataStoreInfo.getId()); + storageMgr.connectHostToSharedPool(host.getId(), primaryDataStoreInfo.getId()); poolHosts.add(host); } catch (Exception e) { - s_logger.warn("Unable to establish a connection between " + host + " and " + primaryDataStoreInfo, e); + LOGGER.warn("Unable to establish a connection between " + host + " and " + primaryDataStoreInfo, e); } } if (poolHosts.isEmpty()) { - s_logger.warn("No host can access storage pool '" + primaryDataStoreInfo + "' on cluster '" + primaryDataStoreInfo.getClusterId() + "'."); + LOGGER.warn("No host can access storage pool '" + primaryDataStoreInfo + "' on cluster '" + primaryDataStoreInfo.getClusterId() + "'."); - _primaryDataStoreDao.expunge(primaryDataStoreInfo.getId()); + primaryDataStoreDao.expunge(primaryDataStoreInfo.getId()); throw new CloudRuntimeException("Failed to access storage pool"); } - _primaryDataStoreHelper.attachCluster(store); + primaryDataStoreHelper.attachCluster(store); return true; } @@ -444,31 +441,31 @@ public class SolidFireSharedPrimaryDataStoreLifeCycle implements PrimaryDataStor Map details = new HashMap<>(); - StoragePoolDetailVO storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.DATASTORE_NAME); + StoragePoolDetailVO storagePoolDetail = storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.DATASTORE_NAME); details.put(CreateStoragePoolCommand.DATASTORE_NAME, storagePoolDetail.getValue()); - storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.IQN); + storagePoolDetail = storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.IQN); details.put(CreateStoragePoolCommand.IQN, storagePoolDetail.getValue()); - storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.STORAGE_VIP); + storagePoolDetail = storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.STORAGE_VIP); details.put(CreateStoragePoolCommand.STORAGE_HOST, storagePoolDetail.getValue()); - storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.STORAGE_PORT); + storagePoolDetail = storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.STORAGE_PORT); details.put(CreateStoragePoolCommand.STORAGE_PORT, storagePoolDetail.getValue()); cmd.setDetails(details); } - Answer answer = _agentMgr.easySend(hostId, cmd); + Answer answer = agentMgr.easySend(hostId, cmd); if (answer != null && answer.getResult()) { return true; } else { - _primaryDataStoreDao.expunge(storagePool.getId()); + primaryDataStoreDao.expunge(storagePool.getId()); final String msg; @@ -478,7 +475,7 @@ public class SolidFireSharedPrimaryDataStoreLifeCycle implements PrimaryDataStor msg = "Cannot create storage pool through host '" + hostId + "' due to CreateStoragePoolCommand returns null"; } - s_logger.warn(msg); + LOGGER.warn(msg); throw new CloudRuntimeException(msg); } @@ -491,16 +488,16 @@ public class SolidFireSharedPrimaryDataStoreLifeCycle implements PrimaryDataStor @Override public boolean maintain(DataStore dataStore) { - _storagePoolAutomation.maintain(dataStore); - _primaryDataStoreHelper.maintain(dataStore); + storagePoolAutomation.maintain(dataStore); + primaryDataStoreHelper.maintain(dataStore); return true; } @Override public boolean cancelMaintain(DataStore store) { - _primaryDataStoreHelper.cancelMaintain(store); - _storagePoolAutomation.cancelMaintain(store); + primaryDataStoreHelper.cancelMaintain(store); + storagePoolAutomation.cancelMaintain(store); return true; } @@ -508,7 +505,7 @@ public class SolidFireSharedPrimaryDataStoreLifeCycle implements PrimaryDataStor // invoked to delete primary storage that is based on the SolidFire plug-in @Override public boolean deleteDataStore(DataStore dataStore) { - List hostPoolRecords = _storagePoolHostDao.listByPoolId(dataStore.getId()); + List hostPoolRecords = storagePoolHostDao.listByPoolId(dataStore.getId()); HypervisorType hypervisorType = null; @@ -521,11 +518,11 @@ public class SolidFireSharedPrimaryDataStoreLifeCycle implements PrimaryDataStor } StoragePool storagePool = (StoragePool)dataStore; - StoragePoolVO storagePoolVO = _primaryDataStoreDao.findById(storagePool.getId()); - List unusedTemplatesInPool = _tmpltMgr.getUnusedTemplatesInPool(storagePoolVO); + StoragePoolVO storagePoolVO = primaryDataStoreDao.findById(storagePool.getId()); + List unusedTemplatesInPool = tmpltMgr.getUnusedTemplatesInPool(storagePoolVO); for (VMTemplateStoragePoolVO templatePoolVO : unusedTemplatesInPool) { - _tmpltMgr.evictTemplateFromStoragePool(templatePoolVO); + tmpltMgr.evictTemplateFromStoragePool(templatePoolVO); } Long clusterId = null; @@ -539,31 +536,31 @@ public class SolidFireSharedPrimaryDataStoreLifeCycle implements PrimaryDataStor Map details = new HashMap<>(); - StoragePoolDetailVO storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.DATASTORE_NAME); + StoragePoolDetailVO storagePoolDetail = storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.DATASTORE_NAME); details.put(DeleteStoragePoolCommand.DATASTORE_NAME, storagePoolDetail.getValue()); - storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.IQN); + storagePoolDetail = storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.IQN); details.put(DeleteStoragePoolCommand.IQN, storagePoolDetail.getValue()); - storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.STORAGE_VIP); + storagePoolDetail = storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.STORAGE_VIP); details.put(DeleteStoragePoolCommand.STORAGE_HOST, storagePoolDetail.getValue()); - storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.STORAGE_PORT); + storagePoolDetail = storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.STORAGE_PORT); details.put(DeleteStoragePoolCommand.STORAGE_PORT, storagePoolDetail.getValue()); deleteCmd.setDetails(details); } - final Answer answer = _agentMgr.easySend(host.getHostId(), deleteCmd); + final Answer answer = agentMgr.easySend(host.getHostId(), deleteCmd); if (answer != null && answer.getResult()) { - s_logger.info("Successfully deleted storage pool using Host ID " + host.getHostId()); + LOGGER.info("Successfully deleted storage pool using Host ID " + host.getHostId()); - HostVO hostVO = _hostDao.findById(host.getHostId()); + HostVO hostVO = hostDao.findById(host.getHostId()); if (hostVO != null) { clusterId = hostVO.getClusterId(); @@ -574,29 +571,39 @@ public class SolidFireSharedPrimaryDataStoreLifeCycle implements PrimaryDataStor } else { if (answer != null) { - s_logger.error("Failed to delete storage pool using Host ID " + host.getHostId() + ": " + answer.getResult()); + LOGGER.error("Failed to delete storage pool using Host ID " + host.getHostId() + ": " + answer.getResult()); } else { - s_logger.error("Failed to delete storage pool using Host ID " + host.getHostId()); + LOGGER.error("Failed to delete storage pool using Host ID " + host.getHostId()); } } } if (clusterId != null) { - ClusterVO cluster = _clusterDao.findById(clusterId); + ClusterVO cluster = clusterDao.findById(clusterId); GlobalLock lock = GlobalLock.getInternLock(cluster.getUuid()); - if (!lock.lock(SolidFireUtil.s_lockTimeInSeconds)) { + if (!lock.lock(SolidFireUtil.LOCK_TIME_IN_SECONDS)) { String errMsg = "Couldn't lock the DB on the following string: " + cluster.getUuid(); - s_logger.debug(errMsg); + LOGGER.debug(errMsg); throw new CloudRuntimeException(errMsg); } try { - removeVolumeFromVag(storagePool.getId(), clusterId); + long sfVolumeId = getVolumeId(storagePool.getId()); + + SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePool.getId(), storagePoolDetailsDao); + + List sfVags = SolidFireUtil.getAllVags(sfConnection); + + for (SolidFireUtil.SolidFireVag sfVag : sfVags) { + if (SolidFireUtil.sfVagContains(sfVag, sfVolumeId, clusterId, hostDao)) { + SolidFireUtil.removeVolumeIdsFromSolidFireVag(sfConnection, sfVag.getId(), new Long[] { sfVolumeId }); + } + } } finally { lock.unlock(); @@ -610,16 +617,16 @@ public class SolidFireSharedPrimaryDataStoreLifeCycle implements PrimaryDataStor deleteSolidFireVolume(storagePool.getId()); - return _primaryDataStoreHelper.deletePrimaryDataStore(dataStore); + return primaryDataStoreHelper.deletePrimaryDataStore(dataStore); } private void handleTargetsForVMware(long hostId, long storagePoolId) { - HostVO host = _hostDao.findById(hostId); + HostVO host = hostDao.findById(hostId); if (host.getHypervisorType() == HypervisorType.VMware) { - String storageAddress = _storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.STORAGE_VIP).getValue(); - int storagePort = Integer.parseInt(_storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.STORAGE_PORT).getValue()); - String iqn = _storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.IQN).getValue(); + String storageAddress = storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.STORAGE_VIP).getValue(); + int storagePort = Integer.parseInt(storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.STORAGE_PORT).getValue()); + String iqn = storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.IQN).getValue(); ModifyTargetsCommand cmd = new ModifyTargetsCommand(); @@ -644,39 +651,22 @@ public class SolidFireSharedPrimaryDataStoreLifeCycle implements PrimaryDataStor } private void sendModifyTargetsCommand(ModifyTargetsCommand cmd, long hostId) { - Answer answer = _agentMgr.easySend(hostId, cmd); + Answer answer = agentMgr.easySend(hostId, cmd); if (answer == null) { String msg = "Unable to get an answer to the modify targets command"; - s_logger.warn(msg); + LOGGER.warn(msg); } else if (!answer.getResult()) { String msg = "Unable to modify target on the following host: " + hostId; - s_logger.warn(msg); - } - } - - private void removeVolumeFromVag(long storagePoolId, long clusterId) { - long sfVolumeId = getVolumeId(storagePoolId); - ClusterDetailsVO clusterDetail = _clusterDetailsDao.findDetail(clusterId, SolidFireUtil.getVagKey(storagePoolId)); - - String vagId = clusterDetail != null ? clusterDetail.getValue() : null; - - if (vagId != null) { - SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, _storagePoolDetailsDao); - - SolidFireUtil.SolidFireVag sfVag = SolidFireUtil.getVag(sfConnection, Long.parseLong(vagId)); - - long[] volumeIds = SolidFireUtil.getNewVolumeIds(sfVag.getVolumeIds(), sfVolumeId, false); - - SolidFireUtil.modifyVag(sfConnection, sfVag.getId(), sfVag.getInitiators(), volumeIds); + LOGGER.warn(msg); } } private void deleteSolidFireVolume(long storagePoolId) { - SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, _storagePoolDetailsDao); + SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, storagePoolDetailsDao); long sfVolumeId = getVolumeId(storagePoolId); @@ -684,7 +674,7 @@ public class SolidFireSharedPrimaryDataStoreLifeCycle implements PrimaryDataStor } private long getVolumeId(long storagePoolId) { - StoragePoolDetailVO storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.VOLUME_ID); + StoragePoolDetailVO storagePoolDetail = storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.VOLUME_ID); String volumeId = storagePoolDetail.getValue(); @@ -692,7 +682,7 @@ public class SolidFireSharedPrimaryDataStoreLifeCycle implements PrimaryDataStor } private long getIopsValue(long storagePoolId, String iopsKey) { - StoragePoolDetailVO storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePoolId, iopsKey); + StoragePoolDetailVO storagePoolDetail = storagePoolDetailsDao.findDetail(storagePoolId, iopsKey); String iops = storagePoolDetail.getValue(); @@ -704,7 +694,7 @@ public class SolidFireSharedPrimaryDataStoreLifeCycle implements PrimaryDataStor } private HypervisorType getHypervisorType(long hostId) { - HostVO host = _hostDao.findById(hostId); + HostVO host = hostDao.findById(hostId); if (host != null) { return host.getHypervisorType(); @@ -729,7 +719,7 @@ public class SolidFireSharedPrimaryDataStoreLifeCycle implements PrimaryDataStor Long capacityBytes = strCapacityBytes != null ? Long.parseLong(strCapacityBytes) : null; Long capacityIops = strCapacityIops != null ? Long.parseLong(strCapacityIops) : null; - SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePool.getId(), _storagePoolDetailsDao); + SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePool.getId(), storagePoolDetailsDao); long size = capacityBytes != null ? capacityBytes : storagePool.getCapacityBytes(); @@ -764,16 +754,16 @@ public class SolidFireSharedPrimaryDataStoreLifeCycle implements PrimaryDataStor SolidFireUtil.modifyVolume(sfConnection, getVolumeId(storagePool.getId()), size, null, minIops, maxIops, burstIops); - SolidFireUtil.updateCsDbWithSolidFireIopsInfo(storagePool.getId(), _primaryDataStoreDao, _storagePoolDetailsDao, minIops, maxIops, burstIops); + SolidFireUtil.updateCsDbWithSolidFireIopsInfo(storagePool.getId(), primaryDataStoreDao, storagePoolDetailsDao, minIops, maxIops, burstIops); } @Override public void enableStoragePool(DataStore dataStore) { - _primaryDataStoreHelper.enable(dataStore); + primaryDataStoreHelper.enable(dataStore); } @Override public void disableStoragePool(DataStore dataStore) { - _primaryDataStoreHelper.disable(dataStore); + primaryDataStoreHelper.disable(dataStore); } } diff --git a/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/provider/SolidFireHostListener.java b/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/provider/SolidFireHostListener.java index f2a4b79cf78..4fffb702274 100644 --- a/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/provider/SolidFireHostListener.java +++ b/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/provider/SolidFireHostListener.java @@ -40,7 +40,6 @@ import com.cloud.agent.api.ModifyStoragePoolAnswer; import com.cloud.agent.api.ModifyStoragePoolCommand; import com.cloud.agent.api.ModifyTargetsCommand; import com.cloud.alert.AlertManager; -import com.cloud.dc.ClusterDetailsDao; import com.cloud.dc.dao.ClusterDao; import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; @@ -56,31 +55,31 @@ import com.cloud.vm.VMInstanceVO; import com.cloud.vm.dao.VMInstanceDao; public class SolidFireHostListener implements HypervisorHostListener { - private static final Logger s_logger = Logger.getLogger(SolidFireHostListener.class); + private static final Logger LOGGER = Logger.getLogger(SolidFireHostListener.class); - @Inject private AgentManager _agentMgr; - @Inject private AlertManager _alertMgr; - @Inject private ClusterDao _clusterDao; - @Inject private ClusterDetailsDao _clusterDetailsDao; - @Inject private DataStoreManager _dataStoreMgr; - @Inject private HostDao _hostDao; - @Inject private PrimaryDataStoreDao _storagePoolDao; - @Inject private StoragePoolDetailsDao _storagePoolDetailsDao; + @Inject private AgentManager agentMgr; + @Inject private AlertManager alertMgr; + @Inject private ClusterDao clusterDao; + @Inject private DataStoreManager dataStoreMgr; + @Inject private HostDao hostDao; + @Inject private PrimaryDataStoreDao storagePoolDao; + @Inject private StoragePoolDetailsDao storagePoolDetailsDao; @Inject private StoragePoolHostDao storagePoolHostDao; - @Inject private VMInstanceDao _vmDao; - @Inject private VolumeDao _volumeDao; + @Inject private VMInstanceDao vmDao; + @Inject private VolumeDao volumeDao; @Override public boolean hostAdded(long hostId) { - HostVO host = _hostDao.findById(hostId); + HostVO host = hostDao.findById(hostId); if (host == null) { - s_logger.error("Failed to add host by SolidFireHostListener as host was not found with id=" + hostId); + LOGGER.error("Failed to add host by SolidFireHostListener as host was not found with id = " + hostId); + return false; } - SolidFireUtil.hostAddedToOrRemovedFromCluster(hostId, host.getClusterId(), true, SolidFireUtil.PROVIDER_NAME, - _clusterDao, _clusterDetailsDao, _storagePoolDao, _storagePoolDetailsDao, _hostDao); + SolidFireUtil.hostAddedToCluster(hostId, host.getClusterId(), SolidFireUtil.PROVIDER_NAME, + clusterDao, hostDao, storagePoolDao, storagePoolDetailsDao); handleVMware(host, true, ModifyTargetsCommand.TargetTypeToRemove.NEITHER); @@ -89,7 +88,7 @@ public class SolidFireHostListener implements HypervisorHostListener { @Override public boolean hostConnect(long hostId, long storagePoolId) { - HostVO host = _hostDao.findById(hostId); + HostVO host = hostDao.findById(hostId); StoragePoolHostVO storagePoolHost = storagePoolHostDao.findByPoolHost(storagePoolId, hostId); @@ -122,25 +121,25 @@ public class SolidFireHostListener implements HypervisorHostListener { @Override public boolean hostAboutToBeRemoved(long hostId) { - return true; - } + HostVO host = hostDao.findById(hostId); - @Override - public boolean hostRemoved(long hostId, long clusterId) { - SolidFireUtil.hostAddedToOrRemovedFromCluster(hostId, clusterId, false, SolidFireUtil.PROVIDER_NAME, - _clusterDao, _clusterDetailsDao, _storagePoolDao, _storagePoolDetailsDao, _hostDao); - - HostVO host = _hostDao.findById(hostId); + SolidFireUtil.hostRemovedFromCluster(hostId, host.getClusterId(), SolidFireUtil.PROVIDER_NAME, + clusterDao, hostDao, storagePoolDao, storagePoolDetailsDao); handleVMware(host, false, ModifyTargetsCommand.TargetTypeToRemove.BOTH); return true; } + @Override + public boolean hostRemoved(long hostId, long clusterId) { + return true; + } + private void handleXenServer(long clusterId, long hostId, long storagePoolId) { List storagePaths = getStoragePaths(clusterId, storagePoolId); - StoragePool storagePool = (StoragePool)_dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary); + StoragePool storagePool = (StoragePool)dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary); for (String storagePath : storagePaths) { ModifyStoragePoolCommand cmd = new ModifyStoragePoolCommand(true, storagePool); @@ -153,7 +152,7 @@ public class SolidFireHostListener implements HypervisorHostListener { private void handleVMware(HostVO host, boolean add, ModifyTargetsCommand.TargetTypeToRemove targetTypeToRemove) { if (host != null && HypervisorType.VMware.equals(host.getHypervisorType())) { - List storagePools = _storagePoolDao.findPoolsByProvider(SolidFireUtil.PROVIDER_NAME); + List storagePools = storagePoolDao.findPoolsByProvider(SolidFireUtil.PROVIDER_NAME); if (storagePools != null && storagePools.size() > 0) { List> targets = new ArrayList<>(); @@ -169,6 +168,7 @@ public class SolidFireHostListener implements HypervisorHostListener { cmd.setTargets(targets); cmd.setAdd(add); cmd.setTargetTypeToRemove(targetTypeToRemove); + cmd.setRemoveAsync(true); sendModifyTargetsCommand(cmd, host.getId()); } @@ -176,7 +176,7 @@ public class SolidFireHostListener implements HypervisorHostListener { } private void handleKVM(long hostId, long storagePoolId) { - StoragePool storagePool = (StoragePool)_dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary); + StoragePool storagePool = (StoragePool)dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary); ModifyStoragePoolCommand cmd = new ModifyStoragePoolCommand(true, storagePool); @@ -187,19 +187,19 @@ public class SolidFireHostListener implements HypervisorHostListener { List storagePaths = new ArrayList<>(); // If you do not pass in null for the second parameter, you only get back applicable ROOT disks. - List volumes = _volumeDao.findByPoolId(storagePoolId, null); + List volumes = volumeDao.findByPoolId(storagePoolId, null); if (volumes != null) { for (VolumeVO volume : volumes) { Long instanceId = volume.getInstanceId(); if (instanceId != null) { - VMInstanceVO vmInstance = _vmDao.findById(instanceId); + VMInstanceVO vmInstance = vmDao.findById(instanceId); Long hostIdForVm = vmInstance.getHostId() != null ? vmInstance.getHostId() : vmInstance.getLastHostId(); if (hostIdForVm != null) { - HostVO hostForVm = _hostDao.findById(hostIdForVm); + HostVO hostForVm = hostDao.findById(hostIdForVm); if (hostForVm != null && hostForVm.getClusterId().equals(clusterId)) { storagePaths.add(volume.get_iScsiName()); @@ -215,22 +215,22 @@ public class SolidFireHostListener implements HypervisorHostListener { private List> getTargets(long clusterId, long storagePoolId) { List> targets = new ArrayList<>(); - StoragePoolVO storagePool = _storagePoolDao.findById(storagePoolId); + StoragePoolVO storagePool = storagePoolDao.findById(storagePoolId); // If you do not pass in null for the second parameter, you only get back applicable ROOT disks. - List volumes = _volumeDao.findByPoolId(storagePoolId, null); + List volumes = volumeDao.findByPoolId(storagePoolId, null); if (volumes != null) { for (VolumeVO volume : volumes) { Long instanceId = volume.getInstanceId(); if (instanceId != null) { - VMInstanceVO vmInstance = _vmDao.findById(instanceId); + VMInstanceVO vmInstance = vmDao.findById(instanceId); Long hostIdForVm = vmInstance.getHostId() != null ? vmInstance.getHostId() : vmInstance.getLastHostId(); if (hostIdForVm != null) { - HostVO hostForVm = _hostDao.findById(hostIdForVm); + HostVO hostForVm = hostDao.findById(hostIdForVm); if (hostForVm.getClusterId().equals(clusterId)) { Map details = new HashMap<>(); @@ -250,7 +250,7 @@ public class SolidFireHostListener implements HypervisorHostListener { } private void sendModifyTargetsCommand(ModifyTargetsCommand cmd, long hostId) { - Answer answer = _agentMgr.easySend(hostId, cmd); + Answer answer = agentMgr.easySend(hostId, cmd); if (answer == null) { throw new CloudRuntimeException("Unable to get an answer to the modify targets command"); @@ -259,16 +259,16 @@ public class SolidFireHostListener implements HypervisorHostListener { if (!answer.getResult()) { String msg = "Unable to modify targets on the following host: " + hostId; - HostVO host = _hostDao.findById(hostId); + HostVO host = hostDao.findById(hostId); - _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_HOST, host.getDataCenterId(), host.getPodId(), msg, msg); + alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_HOST, host.getDataCenterId(), host.getPodId(), msg, msg); throw new CloudRuntimeException(msg); } } private void sendModifyStoragePoolCommand(ModifyStoragePoolCommand cmd, StoragePool storagePool, long hostId) { - Answer answer = _agentMgr.easySend(hostId, cmd); + 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() + ")"); @@ -277,7 +277,7 @@ public class SolidFireHostListener implements HypervisorHostListener { if (!answer.getResult()) { String msg = "Unable to attach storage pool " + storagePool.getId() + " to 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 storage pool " + storagePool.getId() + " due to " + answer.getDetails() + " (" + storagePool.getId() + ")"); @@ -285,6 +285,6 @@ public class SolidFireHostListener implements HypervisorHostListener { assert (answer instanceof ModifyStoragePoolAnswer) : "ModifyStoragePoolAnswer expected ; Pool = " + storagePool.getId() + " Host = " + hostId; - s_logger.info("Connection established between storage pool " + storagePool + " and host + " + hostId); + LOGGER.info("Connection established between storage pool " + storagePool + " and host + " + hostId); } } diff --git a/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/provider/SolidFireSharedHostListener.java b/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/provider/SolidFireSharedHostListener.java index 66aafacdbfd..575a3020d37 100644 --- a/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/provider/SolidFireSharedHostListener.java +++ b/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/provider/SolidFireSharedHostListener.java @@ -40,7 +40,6 @@ import com.cloud.agent.api.ModifyStoragePoolAnswer; import com.cloud.agent.api.ModifyStoragePoolCommand; import com.cloud.agent.api.ModifyTargetsCommand; import com.cloud.alert.AlertManager; -import com.cloud.dc.ClusterDetailsDao; import com.cloud.dc.dao.ClusterDao; import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; @@ -57,7 +56,6 @@ public class SolidFireSharedHostListener implements HypervisorHostListener { @Inject private AgentManager agentMgr; @Inject private AlertManager alertMgr; @Inject private ClusterDao clusterDao; - @Inject private ClusterDetailsDao clusterDetailsDao; @Inject private DataStoreManager dataStoreMgr; @Inject private HostDao hostDao; @Inject private PrimaryDataStoreDao storagePoolDao; @@ -69,14 +67,15 @@ public class SolidFireSharedHostListener implements HypervisorHostListener { HostVO host = hostDao.findById(hostId); if (host == null) { - LOGGER.error("Failed to add host by SolidFireSharedHostListener as host was not found with id=" + hostId); + LOGGER.error("Failed to add host by SolidFireSharedHostListener as host was not found with id = " + hostId); + return false; } - SolidFireUtil.hostAddedToOrRemovedFromCluster(hostId, host.getClusterId(), true, SolidFireUtil.SHARED_PROVIDER_NAME, - clusterDao, clusterDetailsDao, storagePoolDao, storagePoolDetailsDao, hostDao); + SolidFireUtil.hostAddedToCluster(hostId, host.getClusterId(), SolidFireUtil.SHARED_PROVIDER_NAME, + clusterDao, hostDao, storagePoolDao, storagePoolDetailsDao); - handleVMware(hostId, true, ModifyTargetsCommand.TargetTypeToRemove.NEITHER); + handleVMware(host, true, ModifyTargetsCommand.TargetTypeToRemove.NEITHER); return true; } @@ -123,10 +122,10 @@ public class SolidFireSharedHostListener implements HypervisorHostListener { public boolean hostAboutToBeRemoved(long hostId) { HostVO host = hostDao.findById(hostId); - SolidFireUtil.hostAddedToOrRemovedFromCluster(hostId, host.getClusterId(), false, SolidFireUtil.SHARED_PROVIDER_NAME, - clusterDao, clusterDetailsDao, storagePoolDao, storagePoolDetailsDao, hostDao); + SolidFireUtil.hostRemovedFromCluster(hostId, host.getClusterId(), SolidFireUtil.SHARED_PROVIDER_NAME, + clusterDao, hostDao, storagePoolDao, storagePoolDetailsDao); - handleVMware(hostId, false, ModifyTargetsCommand.TargetTypeToRemove.BOTH); + handleVMware(host, false, ModifyTargetsCommand.TargetTypeToRemove.BOTH); return true; } @@ -136,9 +135,7 @@ public class SolidFireSharedHostListener implements HypervisorHostListener { return true; } - private void handleVMware(long hostId, boolean add, ModifyTargetsCommand.TargetTypeToRemove targetTypeToRemove) { - HostVO host = hostDao.findById(hostId); - + private void handleVMware(HostVO host, boolean add, ModifyTargetsCommand.TargetTypeToRemove targetTypeToRemove) { if (HypervisorType.VMware.equals(host.getHypervisorType())) { List storagePools = storagePoolDao.findPoolsByProvider(SolidFireUtil.SHARED_PROVIDER_NAME); @@ -179,7 +176,7 @@ public class SolidFireSharedHostListener implements HypervisorHostListener { cmd.setTargetTypeToRemove(targetTypeToRemove); cmd.setRemoveAsync(true); - sendModifyTargetsCommand(cmd, hostId); + sendModifyTargetsCommand(cmd, host.getId()); } } } diff --git a/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java b/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java index 81adf4b343e..1f8a2885945 100644 --- a/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java +++ b/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java @@ -17,13 +17,16 @@ package org.apache.cloudstack.storage.datastore.util; import java.util.ArrayList; -import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Random; import java.util.Set; import java.util.StringTokenizer; +import java.util.UUID; +import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; @@ -32,8 +35,6 @@ 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 com.cloud.dc.ClusterDetailsDao; -import com.cloud.dc.ClusterDetailsVO; import com.cloud.dc.ClusterVO; import com.cloud.dc.dao.ClusterDao; import com.cloud.host.Host; @@ -44,11 +45,14 @@ import com.cloud.user.AccountDetailsDao; import com.cloud.utils.db.GlobalLock; import com.cloud.utils.exception.CloudRuntimeException; +import com.google.common.base.Preconditions; import com.google.common.primitives.Longs; import com.solidfire.client.ElementFactory; import com.solidfire.element.api.Account; import com.solidfire.element.api.AddAccountRequest; +import com.solidfire.element.api.AddInitiatorsToVolumeAccessGroupRequest; +import com.solidfire.element.api.AddVolumesToVolumeAccessGroupRequest; import com.solidfire.element.api.CloneVolumeRequest; import com.solidfire.element.api.CloneVolumeResult; import com.solidfire.element.api.CreateSnapshotRequest; @@ -62,9 +66,10 @@ import com.solidfire.element.api.GetAsyncResultRequest; import com.solidfire.element.api.ListSnapshotsRequest; import com.solidfire.element.api.ListVolumeAccessGroupsRequest; import com.solidfire.element.api.ListVolumesRequest; -import com.solidfire.element.api.ModifyVolumeAccessGroupRequest; import com.solidfire.element.api.ModifyVolumeRequest; import com.solidfire.element.api.QoS; +import com.solidfire.element.api.RemoveInitiatorsFromVolumeAccessGroupRequest; +import com.solidfire.element.api.RemoveVolumesFromVolumeAccessGroupRequest; import com.solidfire.element.api.RollbackToSnapshotRequest; import com.solidfire.element.api.Snapshot; import com.solidfire.element.api.SolidFireElement; @@ -75,12 +80,13 @@ import com.solidfire.jsvcgen.javautil.Optional; import static org.apache.commons.lang.ArrayUtils.toPrimitive; public class SolidFireUtil { - private static final Logger s_logger = Logger.getLogger(SolidFireUtil.class); + private static final Logger LOGGER = Logger.getLogger(SolidFireUtil.class); public static final String PROVIDER_NAME = "SolidFire"; public static final String SHARED_PROVIDER_NAME = "SolidFireShared"; - public static final int s_lockTimeInSeconds = 300; + private static final Random RANDOM = new Random(System.nanoTime()); + public static final int LOCK_TIME_IN_SECONDS = 300; public static final String LOG_PREFIX = "SolidFire: "; @@ -127,6 +133,8 @@ public class SolidFireUtil { public static final String DATASTORE_NAME = "datastoreName"; public static final String IQN = "iqn"; + private static final String SF_CS_ACCOUNT_PREFIX = "CloudStack_"; + public static final long MIN_VOLUME_SIZE = 1000000000; public static final long MIN_IOPS_PER_VOLUME = 100; @@ -136,6 +144,9 @@ public class SolidFireUtil { private static final int DEFAULT_MANAGEMENT_PORT = 443; private static final int DEFAULT_STORAGE_PORT = 3260; + private static final int MAX_NUM_VAGS_PER_VOLUME = 4; + private static final int MAX_NUM_INITIATORS_PER_VAG = 64; + public static class SolidFireConnection { private final String _managementVip; private final int _managementPort; @@ -300,7 +311,7 @@ public class SolidFireUtil { } public static String getSolidFireAccountName(String csAccountUuid, long csAccountId) { - return "CloudStack_" + csAccountUuid + "_" + csAccountId; + return SF_CS_ACCOUNT_PREFIX + csAccountUuid + "_" + csAccountId; } public static void updateCsDbWithSolidFireIopsInfo(long storagePoolId, PrimaryDataStoreDao primaryDataStoreDao, @@ -344,17 +355,72 @@ public class SolidFireUtil { } } - public static void hostAddedToOrRemovedFromCluster(long hostId, long clusterId, boolean added, String storageProvider, - ClusterDao clusterDao, ClusterDetailsDao clusterDetailsDao, PrimaryDataStoreDao storagePoolDao, - StoragePoolDetailsDao storagePoolDetailsDao, HostDao hostDao) { + private static boolean isCloudStackOnlyVag(SolidFireConnection sfConnection, SolidFireVag sfVag) { + long[] volumeIds = sfVag.getVolumeIds(); + + if (ArrayUtils.isEmpty(volumeIds)) { + // We count this situation as being "CloudStack only" because the reason we call this method is to determine + // if we can remove a host from a VAG (we only want to allow the host to be removed from the VAG if there are + // no non-CloudStack volumes in it). + return true; + } + + List knownSfAccountsForCs = new ArrayList<>(); + + for (long volumeId : volumeIds) { + SolidFireVolume sfVolume = getVolume(sfConnection, volumeId); + long sfAccountId = sfVolume.getAccountId(); + + if (!knownSfAccountsForCs.contains(sfAccountId)) { + SolidFireAccount sfAccount = getAccountById(sfConnection, sfAccountId); + + if (sfAccount.getName().startsWith(SF_CS_ACCOUNT_PREFIX)) { + knownSfAccountsForCs.add(sfAccountId); + } + else { + return false; + } + } + } + + return true; + } + + private static boolean isStorageApplicableToZoneOrCluster(StoragePoolVO storagePoolVO, long clusterId, ClusterDao clusterDao) { + if (storagePoolVO.getClusterId() != null) { + if (storagePoolVO.getClusterId() == clusterId) { + return true; + } + } + else { + List clustersInZone = clusterDao.listByZoneId(storagePoolVO.getDataCenterId()); + + if (clustersInZone != null) { + for (ClusterVO clusterInZone : clustersInZone) { + if (clusterInZone.getId() == clusterId) { + return true; + } + } + } + } + + return false; + } + + public static void hostRemovedFromCluster(long hostId, long clusterId, String storageProvider, ClusterDao clusterDao, HostDao hostDao, + PrimaryDataStoreDao storagePoolDao, StoragePoolDetailsDao storagePoolDetailsDao) { + HostVO hostVO = hostDao.findByIdIncludingRemoved(hostId); + + Preconditions.checkArgument(hostVO != null, "Could not locate host for ID: " + hostId); + ClusterVO cluster = clusterDao.findById(clusterId); GlobalLock lock = GlobalLock.getInternLock(cluster.getUuid()); - if (!lock.lock(s_lockTimeInSeconds)) { + if (!lock.lock(LOCK_TIME_IN_SECONDS)) { String errMsg = "Couldn't lock the DB on the following string: " + cluster.getUuid(); - s_logger.debug(errMsg); + LOGGER.warn(errMsg); throw new CloudRuntimeException(errMsg); } @@ -366,26 +432,20 @@ public class SolidFireUtil { List sfConnections = new ArrayList<>(); for (StoragePoolVO storagePool : storagePools) { - ClusterDetailsVO clusterDetail = clusterDetailsDao.findDetail(clusterId, SolidFireUtil.getVagKey(storagePool.getId())); + if (!isStorageApplicableToZoneOrCluster(storagePool, clusterId, clusterDao)) { + continue; + } - String vagId = clusterDetail != null ? clusterDetail.getValue() : null; + SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePool.getId(), storagePoolDetailsDao); - if (vagId != null) { - SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePool.getId(), storagePoolDetailsDao); + if (!sfConnections.contains(sfConnection)) { + sfConnections.add(sfConnection); - if (!sfConnections.contains(sfConnection)) { - sfConnections.add(sfConnection); + List sfVags = SolidFireUtil.getAllVags(sfConnection); + SolidFireVag sfVag = getVolumeAccessGroup(hostVO.getStorageUrl(), sfVags); - SolidFireUtil.SolidFireVag sfVag = SolidFireUtil.getVag(sfConnection, Long.parseLong(vagId)); - - List hostsToAddOrRemove = new ArrayList<>(); - HostVO hostToAddOrRemove = hostDao.findByIdIncludingRemoved(hostId); - - hostsToAddOrRemove.add(hostToAddOrRemove); - - String[] hostIqns = SolidFireUtil.getNewHostIqns(sfVag.getInitiators(), SolidFireUtil.getIqnsFromHosts(hostsToAddOrRemove), added); - - SolidFireUtil.modifyVag(sfConnection, sfVag.getId(), hostIqns, sfVag.getVolumeIds()); + if (sfVag != null && isCloudStackOnlyVag(sfConnection, sfVag)) { + removeInitiatorsFromSolidFireVag(sfConnection, sfVag.getId(), new String[] { hostVO.getStorageUrl() }); } } } @@ -397,50 +457,337 @@ public class SolidFireUtil { } } - public static long placeVolumeInVolumeAccessGroup(SolidFireConnection sfConnection, long sfVolumeId, long storagePoolId, - String vagUuid, List hosts, ClusterDetailsDao clusterDetailsDao) { - if (hosts == null || hosts.isEmpty()) { - throw new CloudRuntimeException("There must be at least one host in the cluster."); - } + public static void hostAddedToCluster(long hostId, long clusterId, String storageProvider, ClusterDao clusterDao, HostDao hostDao, + PrimaryDataStoreDao storagePoolDao, StoragePoolDetailsDao storagePoolDetailsDao) { + HostVO hostVO = hostDao.findById(hostId); - long lVagId; + Preconditions.checkArgument(hostVO != null, "Could not locate host for ID: " + hostId); + + ClusterVO cluster = clusterDao.findById(clusterId); + + GlobalLock lock = GlobalLock.getInternLock(cluster.getUuid()); + + if (!lock.lock(LOCK_TIME_IN_SECONDS)) { + String errMsg = "Couldn't lock the DB on the following string: " + cluster.getUuid(); + + LOGGER.warn(errMsg); + + throw new CloudRuntimeException(errMsg); + } try { - lVagId = SolidFireUtil.createVag(sfConnection, "CloudStack-" + vagUuid, - SolidFireUtil.getIqnsFromHosts(hosts), new long[] { sfVolumeId }); - } - catch (Exception ex) { - String iqnInVagAlready1 = "Exceeded maximum number of Volume Access Groups per initiator"; - String iqnInVagAlready2 = "Exceeded maximum number of VolumeAccessGroups per Initiator"; + List storagePools = storagePoolDao.findPoolsByProvider(storageProvider); - if (!ex.getMessage().contains(iqnInVagAlready1) && !ex.getMessage().contains(iqnInVagAlready2)) { - throw new CloudRuntimeException(ex.getMessage()); + if (storagePools != null && storagePools.size() > 0) { + List sfConnections = new ArrayList<>(); + + for (StoragePoolVO storagePool : storagePools) { + if (!isStorageApplicableToZoneOrCluster(storagePool, clusterId, clusterDao)) { + continue; + } + + SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePool.getId(), storagePoolDetailsDao); + + if (!sfConnections.contains(sfConnection)) { + sfConnections.add(sfConnection); + + List sfVags = SolidFireUtil.getAllVags(sfConnection); + SolidFireVag sfVag = getVolumeAccessGroup(hostVO.getStorageUrl(), sfVags); + + if (sfVag != null) { + placeVolumeIdsInVag(sfConnection, sfVags, sfVag, hostVO, hostDao); + } else { + handleVagForHost(sfConnection, sfVags, hostVO, hostDao); + } + } + } } - - // getCompatibleVag throws an exception if an existing VAG can't be located - SolidFireUtil.SolidFireVag sfVag = getCompatibleVag(sfConnection, hosts); - - lVagId = sfVag.getId(); - - long[] volumeIds = getNewVolumeIds(sfVag.getVolumeIds(), sfVolumeId, true); - - SolidFireUtil.modifyVag(sfConnection, lVagId, sfVag.getInitiators(), volumeIds); } - - ClusterDetailsVO clusterDetail = new ClusterDetailsVO(hosts.get(0).getClusterId(), getVagKey(storagePoolId), String.valueOf(lVagId)); - - clusterDetailsDao.persist(clusterDetail); - - return lVagId; + finally { + lock.unlock(); + lock.releaseRef(); + } } - public static boolean hostsSupport_iScsi(List hosts) { + // Put the host in an existing VAG or create a new one (only create a new one if all existing VAGs are full (i.e. 64 hosts max per VAG) and if + // creating a new VAG won't exceed 4 VAGs for the computer cluster). + // If none of the hosts in the cluster are in a VAG, then leave this host out of a VAG. + // Place applicable volume IDs in VAG, if need be (account of volume starts with SF_CS_ACCOUNT_PREFIX). + private static void handleVagForHost(SolidFireUtil.SolidFireConnection sfConnection, List sfVags, Host host, HostDao hostDao) { + List hostVOs = hostDao.findByClusterId(host.getClusterId()); + + if (hostVOs != null) { + int numVags = 0; + + Collections.shuffle(hostVOs, RANDOM); + + for (HostVO hostVO : hostVOs) { + if (hostVO.getId() != host.getId()) { + SolidFireVag sfVag = getVolumeAccessGroup(hostVO.getStorageUrl(), sfVags); + + if (sfVag != null) { + numVags++; + + // A volume should be visible to all hosts that are in the same compute cluster. That being the case, you + // can use MAX_NUM_VAGS_PER_VOLUME here. This is to limit the number of VAGs being used in a compute cluster + // to MAX_NUM_VAGS_PER_VOLUME. + if (numVags > MAX_NUM_VAGS_PER_VOLUME) { + throw new CloudRuntimeException("Can support at most four volume access groups per compute cluster (>)"); + } + + if (sfVag.getInitiators().length < MAX_NUM_INITIATORS_PER_VAG) { + if (!hostSupports_iScsi(host)) { + String errMsg = "Host with ID " + host.getId() + " does not support iSCSI."; + + LOGGER.warn(errMsg); + + throw new CloudRuntimeException(errMsg); + } + + addInitiatorsToSolidFireVag(sfConnection, sfVag.getId(), new String[] { host.getStorageUrl() }); + + return; + } + } + } + } + + if (numVags == MAX_NUM_VAGS_PER_VOLUME) { + throw new CloudRuntimeException("Can support at most four volume access groups per compute cluster (==)"); + } + + if (numVags > 0) { + if (!hostSupports_iScsi(host)) { + String errMsg = "Host with ID " + host.getId() + " does not support iSCSI."; + + LOGGER.warn(errMsg); + + throw new CloudRuntimeException(errMsg); + } + + SolidFireUtil.createVag(sfConnection, "CloudStack-" + UUID.randomUUID().toString(), + new String[]{host.getStorageUrl()}, getVolumeIds(sfConnection, sfVags, host, hostDao)); + } + } + } + + /** + * Make use of the volume access group (VAG) of a random host in the cluster. With this VAG, collect all of its volume IDs that are for + * volumes that are in SolidFire accounts that are for CloudStack. + */ + private static long[] getVolumeIds(SolidFireUtil.SolidFireConnection sfConnection, List sfVags, + Host host, HostDao hostDao) { + List volumeIdsToReturn = new ArrayList<>(); + + SolidFireVag sfVagForRandomHostInCluster = getVagForRandomHostInCluster(sfVags, host, hostDao); + + if (sfVagForRandomHostInCluster != null) { + long[] volumeIds = sfVagForRandomHostInCluster.getVolumeIds(); + + if (volumeIds != null) { + List knownSfAccountsForCs = new ArrayList<>(); + List knownSfAccountsNotForCs = new ArrayList<>(); + + for (long volumeId : volumeIds) { + SolidFireVolume sfVolume = getVolume(sfConnection, volumeId); + long sfAccountId = sfVolume.getAccountId(); + + if (knownSfAccountsForCs.contains(sfAccountId)) { + volumeIdsToReturn.add(volumeId); + } + else if (!knownSfAccountsNotForCs.contains(sfAccountId)) { + SolidFireAccount sfAccount = getAccountById(sfConnection, sfAccountId); + + if (sfAccount.getName().startsWith(SF_CS_ACCOUNT_PREFIX)) { + knownSfAccountsForCs.add(sfAccountId); + + volumeIdsToReturn.add(volumeId); + } + else { + knownSfAccountsNotForCs.add(sfAccountId); + } + } + } + } + } + + return volumeIdsToReturn.stream().mapToLong(l -> l).toArray(); + } + + private static void placeVolumeIdsInVag(SolidFireUtil.SolidFireConnection sfConnection, List sfVags, + SolidFireVag sfVag, Host host, HostDao hostDao) { + SolidFireVag sfVagForRandomHostInCluster = getVagForRandomHostInCluster(sfVags, host, hostDao); + + if (sfVagForRandomHostInCluster != null) { + long[] volumeIds = sfVagForRandomHostInCluster.getVolumeIds(); + + if (volumeIds != null) { + List knownSfAccountsForCs = new ArrayList<>(); + List knownSfAccountsNotForCs = new ArrayList<>(); + + List newVolumeIds = new ArrayList<>(); + + for (long volumeId : volumeIds) { + SolidFireVolume sfVolume = getVolume(sfConnection, volumeId); + long sfAccountId = sfVolume.getAccountId(); + + if (knownSfAccountsForCs.contains(sfAccountId)) { + addVolumeIdToSolidFireVag(volumeId, sfVag, newVolumeIds); + } + else if (!knownSfAccountsNotForCs.contains(sfAccountId)) { + SolidFireAccount sfAccount = getAccountById(sfConnection, sfAccountId); + + if (sfAccount.getName().startsWith(SF_CS_ACCOUNT_PREFIX)) { + knownSfAccountsForCs.add(sfAccountId); + + addVolumeIdToSolidFireVag(volumeId, sfVag, newVolumeIds); + } + else { + knownSfAccountsNotForCs.add(sfAccountId); + } + } + } + + if (newVolumeIds.size() > 0) { + addVolumeIdsToSolidFireVag(sfConnection, sfVag.getId(), newVolumeIds.toArray(new Long[0])); + } + } + } + } + + private static void addVolumeIdToSolidFireVag(long volumeId, SolidFireVag sfVag, List newVolumeIds) { + List existingVolumeIds = Longs.asList(sfVag.getVolumeIds()); + + if (!existingVolumeIds.contains(volumeId) && !newVolumeIds.contains(volumeId)) { + newVolumeIds.add(volumeId); + } + } + + private static SolidFireVag getVagForRandomHostInCluster(List sfVags, Host host, HostDao hostDao) { + List hostVOs = hostDao.findByClusterId(host.getClusterId()); + + if (hostVOs != null) { + Collections.shuffle(hostVOs, RANDOM); + + for (HostVO hostVO : hostVOs) { + if (hostVO.getId() != host.getId() && hostSupports_iScsi(hostVO)) { + SolidFireVag sfVag = getVolumeAccessGroup(hostVO.getStorageUrl(), sfVags); + + if (sfVag != null) { + return sfVag; + } + } + } + } + + return null; + } + + public static void placeVolumeInVolumeAccessGroups(SolidFireConnection sfConnection, long sfVolumeId, List hosts) { + if (!SolidFireUtil.hostsSupport_iScsi(hosts)) { + String errMsg = "Not all hosts in the compute cluster support iSCSI."; + + LOGGER.warn(errMsg); + + throw new CloudRuntimeException(errMsg); + } + + List sfVags = SolidFireUtil.getAllVags(sfConnection); + + Map> sfVagToIqnsMap = new HashMap<>(); + + for (HostVO hostVO : hosts) { + String iqn = hostVO.getStorageUrl(); + + SolidFireUtil.SolidFireVag sfVag = getVolumeAccessGroup(iqn, sfVags); + + List iqnsInVag = sfVagToIqnsMap.computeIfAbsent(sfVag, k -> new ArrayList<>()); + + iqnsInVag.add(iqn); + } + + if (sfVagToIqnsMap.size() > MAX_NUM_VAGS_PER_VOLUME) { + throw new CloudRuntimeException("A SolidFire volume can be in at most four volume access groups simultaneously."); + } + + for (SolidFireUtil.SolidFireVag sfVag : sfVagToIqnsMap.keySet()) { + if (sfVag != null) { + if (!SolidFireUtil.isVolumeIdInSfVag(sfVolumeId, sfVag)) { + SolidFireUtil.addVolumeIdsToSolidFireVag(sfConnection, sfVag.getId(), new Long[] { sfVolumeId }); + } + } + else { + List iqnsNotInVag = sfVagToIqnsMap.get(null); + + SolidFireUtil.createVag(sfConnection, "CloudStack-" + UUID.randomUUID().toString(), + iqnsNotInVag.toArray(new String[0]), new long[] { sfVolumeId }); + } + } + } + + public static SolidFireUtil.SolidFireVag getVolumeAccessGroup(String hostIqn, List sfVags) { + if (hostIqn == null) { + return null; + } + + hostIqn = hostIqn.toLowerCase(); + + if (sfVags != null) { + for (SolidFireUtil.SolidFireVag sfVag : sfVags) { + List lstInitiators = getStringArrayAsLowerCaseStringList(sfVag.getInitiators()); + + // lstInitiators should not be returned from getStringArrayAsLowerCaseStringList as null + if (lstInitiators.contains(hostIqn)) { + return sfVag; + } + } + } + + return null; + } + + public static boolean sfVagContains(SolidFireUtil.SolidFireVag sfVag, long sfVolumeId, long clusterId, HostDao hostDao) { + if (isVolumeIdInSfVag(sfVolumeId, sfVag)) { + String[] iqns = sfVag.getInitiators(); + List hosts = hostDao.findByClusterId(clusterId); + + for (String iqn : iqns) { + for (HostVO host : hosts) { + String hostIqn = host.getStorageUrl(); + + if (iqn.equalsIgnoreCase(hostIqn)) { + return true; + } + } + } + } + + return false; + } + + private static boolean isVolumeIdInSfVag(long sfVolumeIdToCheck, SolidFireUtil.SolidFireVag sfVag) { + long[] sfVolumeIds = sfVag.getVolumeIds(); + + for (long sfVolumeId : sfVolumeIds) { + if (sfVolumeId == sfVolumeIdToCheck) { + return true; + } + } + + return false; + } + + private static boolean hostSupports_iScsi(Host host) { + return host != null && host.getStorageUrl() != null && host.getStorageUrl().trim().length() > 0 && host.getStorageUrl().startsWith("iqn"); + } + + private static boolean hostsSupport_iScsi(List hosts) { if (hosts == null || hosts.size() == 0) { return false; } for (Host host : hosts) { - if (host == null || host.getStorageUrl() == null || host.getStorageUrl().trim().length() == 0 || !host.getStorageUrl().startsWith("iqn")) { + if (!hostSupports_iScsi(host)) { return false; } } @@ -448,14 +795,6 @@ public class SolidFireUtil { return true; } - public static long[] getNewVolumeIds(long[] volumeIds, long volumeIdToAddOrRemove, boolean add) { - if (add) { - return getNewVolumeIdsAdd(volumeIds, volumeIdToAddOrRemove); - } - - return getNewVolumeIdsRemove(volumeIds, volumeIdToAddOrRemove); - } - public static String getVagKey(long storagePoolId) { return "sfVolumeAccessGroup_" + storagePoolId; } @@ -851,32 +1190,43 @@ public class SolidFireUtil { return getSolidFireElement(sfConnection).createVolumeAccessGroup(request).getVolumeAccessGroupID(); } - public static void modifyVag(SolidFireConnection sfConnection, long vagId, String[] iqns, long[] volumeIds) { - ModifyVolumeAccessGroupRequest request = ModifyVolumeAccessGroupRequest.builder() + private static void addInitiatorsToSolidFireVag(SolidFireConnection sfConnection, long vagId, String[] initiators) { + AddInitiatorsToVolumeAccessGroupRequest request = AddInitiatorsToVolumeAccessGroupRequest.builder() .volumeAccessGroupID(vagId) - .optionalInitiators(iqns) - .optionalVolumes(Longs.asList(volumeIds).toArray(new Long[volumeIds.length])) + .initiators(initiators) .build(); - getSolidFireElement(sfConnection).modifyVolumeAccessGroup(request); + getSolidFireElement(sfConnection).addInitiatorsToVolumeAccessGroup(request); } - public static SolidFireVag getVag(SolidFireConnection sfConnection, long vagId) - { - ListVolumeAccessGroupsRequest request = ListVolumeAccessGroupsRequest.builder() - .optionalStartVolumeAccessGroupID(vagId) - .optionalLimit(1L) + private static void removeInitiatorsFromSolidFireVag(SolidFireConnection sfConnection, long vagId, String[] initiators) { + RemoveInitiatorsFromVolumeAccessGroupRequest request = RemoveInitiatorsFromVolumeAccessGroupRequest.builder() + .volumeAccessGroupID(vagId) + .initiators(initiators) .build(); - VolumeAccessGroup vag = getSolidFireElement(sfConnection).listVolumeAccessGroups(request).getVolumeAccessGroups()[0]; - - String[] vagIqns = vag.getInitiators(); - long[] vagVolumeIds = toPrimitive(vag.getVolumes()); - - return new SolidFireVag(vagId, vagIqns, vagVolumeIds); + getSolidFireElement(sfConnection).removeInitiatorsFromVolumeAccessGroup(request); } - private static List getAllVags(SolidFireConnection sfConnection) + private static void addVolumeIdsToSolidFireVag(SolidFireConnection sfConnection, long vagId, Long[] volumeIds) { + AddVolumesToVolumeAccessGroupRequest request = AddVolumesToVolumeAccessGroupRequest.builder() + .volumeAccessGroupID(vagId) + .volumes(volumeIds) + .build(); + + getSolidFireElement(sfConnection).addVolumesToVolumeAccessGroup(request); + } + + public static void removeVolumeIdsFromSolidFireVag(SolidFireConnection sfConnection, long vagId, Long[] volumeIds) { + RemoveVolumesFromVolumeAccessGroupRequest request = RemoveVolumesFromVolumeAccessGroupRequest.builder() + .volumeAccessGroupID(vagId) + .volumes(volumeIds) + .build(); + + getSolidFireElement(sfConnection).removeVolumesFromVolumeAccessGroup(request); + } + + public static List getAllVags(SolidFireConnection sfConnection) { ListVolumeAccessGroupsRequest request = ListVolumeAccessGroupsRequest.builder().build(); @@ -980,113 +1330,6 @@ public class SolidFireUtil { return portNumber; } - private static String[] getNewHostIqns(String[] iqns, String[] iqnsToAddOrRemove, boolean add) { - if (add) { - return getNewHostIqnsAdd(iqns, iqnsToAddOrRemove); - } - - return getNewHostIqnsRemove(iqns, iqnsToAddOrRemove); - } - - private static String[] getNewHostIqnsAdd(String[] iqns, String[] iqnsToAdd) { - List lstIqns = iqns != null ? new ArrayList<>(Arrays.asList(iqns)) : new ArrayList(); - - if (iqnsToAdd != null) { - for (String iqnToAdd : iqnsToAdd) { - if (!lstIqns.contains(iqnToAdd)) { - lstIqns.add(iqnToAdd); - } - } - } - - return lstIqns.toArray(new String[0]); - } - - private static String[] getNewHostIqnsRemove(String[] iqns, String[] iqnsToRemove) { - List lstIqns = iqns != null ? new ArrayList<>(Arrays.asList(iqns)) : new ArrayList(); - - if (iqnsToRemove != null) { - for (String iqnToRemove : iqnsToRemove) { - lstIqns.remove(iqnToRemove); - } - } - - return lstIqns.toArray(new String[0]); - } - - private static long[] getNewVolumeIdsAdd(long[] volumeIds, long volumeIdToAdd) { - List lstVolumeIds = new ArrayList<>(); - - if (volumeIds != null) { - for (long volumeId : volumeIds) { - lstVolumeIds.add(volumeId); - } - } - - if (lstVolumeIds.contains(volumeIdToAdd)) { - return volumeIds; - } - - lstVolumeIds.add(volumeIdToAdd); - - return toPrimitive(lstVolumeIds.toArray(new Long[lstVolumeIds.size()])); - } - - private static long[] getNewVolumeIdsRemove(long[] volumeIds, long volumeIdToRemove) { - List lstVolumeIds = new ArrayList<>(); - - if (volumeIds != null) { - for (long volumeId : volumeIds) { - lstVolumeIds.add(volumeId); - } - } - - lstVolumeIds.remove(volumeIdToRemove); - - return toPrimitive(lstVolumeIds.toArray(new Long[lstVolumeIds.size()])); - } - - private static String[] getIqnsFromHosts(List hosts) { - if (hosts == null || hosts.size() == 0) { - throw new CloudRuntimeException("There do not appear to be any hosts in this cluster."); - } - - List lstIqns = new ArrayList<>(); - - for (Host host : hosts) { - lstIqns.add(host.getStorageUrl()); - } - - return lstIqns.toArray(new String[0]); - } - - // this method takes in a collection of hosts and tries to find an existing VAG that has all of them in it - // if successful, the VAG is returned; else, a CloudRuntimeException is thrown and this issue should be corrected by an admin - private static SolidFireUtil.SolidFireVag getCompatibleVag(SolidFireConnection sfConnection, List hosts) { - List sfVags = SolidFireUtil.getAllVags(sfConnection); - - if (sfVags != null) { - List hostIqns = new ArrayList<>(); - - // where the method we're in is called, hosts should not be null - for (HostVO host : hosts) { - // where the method we're in is called, host.getStorageUrl() should not be null (it actually should start with "iqn") - hostIqns.add(host.getStorageUrl().toLowerCase()); - } - - for (SolidFireUtil.SolidFireVag sfVag : sfVags) { - List lstInitiators = getStringArrayAsLowerCaseStringList(sfVag.getInitiators()); - - // lstInitiators should not be returned from getStringArrayAsLowerCaseStringList as null - if (lstInitiators.containsAll(hostIqns)) { - return sfVag; - } - } - } - - throw new CloudRuntimeException("Unable to locate the appropriate SolidFire Volume Access Group"); - } - private static List getStringArrayAsLowerCaseStringList(String[] aString) { List lstLowerCaseString = new ArrayList<>(); @@ -1106,10 +1349,6 @@ public class SolidFireUtil { return null; } - Map convertedMap = new HashMap<>(); - - convertedMap.putAll(map); - - return convertedMap; + return new HashMap<>(map); } } diff --git a/test/integration/plugins/solidfire/TestAddRemoveHosts.py b/test/integration/plugins/solidfire/TestAddRemoveHosts.py index d9118dd6cab..1dd29bbcf1c 100644 --- a/test/integration/plugins/solidfire/TestAddRemoveHosts.py +++ b/test/integration/plugins/solidfire/TestAddRemoveHosts.py @@ -34,7 +34,7 @@ from marvin.cloudstackTestCase import cloudstackTestCase from marvin.lib.base import Account, ServiceOffering, User, Host, StoragePool, VirtualMachine # common - commonly used methods for all tests are listed here -from marvin.lib.common import get_domain, get_template, get_zone, list_hosts, list_clusters, list_volumes +from marvin.lib.common import get_domain, get_template, get_zone, list_clusters, list_hosts, list_volumes # utils - utility classes for common cleanup, external library wrappers, etc. from marvin.lib.utils import cleanup_resources @@ -46,14 +46,15 @@ from marvin.lib.utils import cleanup_resources # # Running the tests: # Change the "hypervisor_type" variable to control which hypervisor type to test. -# If using XenServer, set a breakpoint on each test after the first one. When the breakpoint is hit, reset the added/removed -# host to a snapshot state and re-start it. Once it's up and running, run the test code. +# If using XenServer, set a breakpoint on each test after the first one. When the breakpoint is hit, reset the +# added/removed host to a snapshot state and re-start it. Once it's up and running, run the test code. # Check that ip_address_of_new_xenserver_host / ip_address_of_new_kvm_host is correct. # If using XenServer, verify the "xen_server_master_hostname" variable is correct. # If using KVM, verify the "kvm_1_ip_address" variable is correct. # # Note: -# If you do have more than one cluster, you might need to change this line: cls.cluster = list_clusters(cls.apiClient)[0] +# If you do have more than one cluster, you might need to change this line: cls.cluster = list_clusters(cls.apiClient)[0] and +# this variable's value: TestData.clusterId. class TestData: @@ -95,18 +96,18 @@ class TestData: # modify to control which hypervisor type to test hypervisor_type = xenServer xen_server_master_hostname = "XenServer-6.5-1" - kvm_1_ip_address = "10.117.40.112" - ip_address_of_new_xenserver_host = "10.117.40.107" - ip_address_of_new_kvm_host = "10.117.40.116" + kvm_1_ip_address = "10.117.40.111" + ip_address_of_new_xenserver_host = "10.117.40.118" + ip_address_of_new_kvm_host = "10.117.40.115" def __init__(self): self.testdata = { TestData.solidFire: { - TestData.mvip: "10.117.40.120", + TestData.mvip: "10.117.78.225", TestData.username: "admin", TestData.password: "admin", TestData.port: 443, - TestData.url: "https://10.117.40.120:443" + TestData.url: "https://10.117.78.225:443" }, TestData.kvm: { TestData.username: "root", @@ -147,7 +148,7 @@ class TestData: TestData.primaryStorage: { TestData.name: "SolidFire-%d" % random.randint(0, 100), TestData.scope: "ZONE", - TestData.url: "MVIP=10.117.40.120;SVIP=10.117.41.120;" + + TestData.url: "MVIP=10.117.78.225;SVIP=10.117.94.225;" + "clusterAdminUsername=admin;clusterAdminPassword=admin;" + "clusterDefaultMinIops=10000;clusterDefaultMaxIops=15000;" + "clusterDefaultBurstIopsPercentOfMaxIops=1.5;", @@ -160,7 +161,7 @@ class TestData: TestData.primaryStorage2: { TestData.name: "SolidFireShared-%d" % random.randint(0, 100), TestData.scope: "CLUSTER", - TestData.url: "MVIP=10.117.40.120;SVIP=10.117.41.120;" + + TestData.url: "MVIP=10.117.78.225;SVIP=10.117.94.225;" + "clusterAdminUsername=admin;clusterAdminPassword=admin;" + "minIops=5000;maxIops=50000;burstIops=75000", TestData.provider: "SolidFireShared", @@ -454,6 +455,143 @@ class TestAddRemoveHosts(cloudstackTestCase): self._perform_add_remove_xenserver_host(primary_storage_2.id, sf_iscsi_name) + # Make sure each host is in its own VAG. + # Create a VM that needs a new volume from the storage that has a VAG per host. + # Verify the volume is in all VAGs. + # Remove one of the hosts. + # Check that the IQN is no longer in its previous VAG, but that the volume ID is still in that VAG, though. + # Add the host back into the cluster. The IQN should be added to a VAG that already has an IQN from this cluster in it. + def test_vag_per_host_5(self): + hosts = list_hosts(self.apiClient, clusterid=self.cluster.id) + + self.assertTrue( + len(hosts) >= 2, + "There needs to be at least two hosts." + ) + + unique_vag_ids = self._get_unique_vag_ids(hosts) + + self.assertTrue(len(hosts) == len(unique_vag_ids), "To run this test, each host should be in its own VAG.") + + primarystorage = self.testdata[TestData.primaryStorage] + + primary_storage = StoragePool.create( + self.apiClient, + primarystorage, + scope=primarystorage[TestData.scope], + zoneid=self.zone.id, + provider=primarystorage[TestData.provider], + tags=primarystorage[TestData.tags], + capacityiops=primarystorage[TestData.capacityIops], + capacitybytes=primarystorage[TestData.capacityBytes], + hypervisor=primarystorage[TestData.hypervisor] + ) + + self.cleanup.append(primary_storage) + + self.virtual_machine = VirtualMachine.create( + self.apiClient, + self.testdata[TestData.virtualMachine], + accountid=self.account.name, + zoneid=self.zone.id, + serviceofferingid=self.compute_offering.id, + templateid=self.template.id, + domainid=self.domain.id, + startvm=True + ) + + root_volume = self._get_root_volume(self.virtual_machine) + + sf_account_id = sf_util.get_sf_account_id(self.cs_api, self.account.id, primary_storage.id, self, TestAddRemoveHosts._sf_account_id_should_be_non_zero_int_err_msg) + + sf_volumes = sf_util.get_active_sf_volumes(self.sfe, sf_account_id) + + sf_volume = sf_util.check_and_get_sf_volume(sf_volumes, root_volume.name, self) + + sf_vag_ids = sf_util.get_vag_ids(self.cs_api, self.cluster.id, primary_storage.id, self) + + sf_util.check_vags(sf_volume, sf_vag_ids, self) + + host = Host(hosts[0].__dict__) + + host_iqn = self._get_host_iqn(host) + + all_vags = sf_util.get_all_vags(self.sfe) + + host_vag = self._get_host_vag(host_iqn, all_vags) + + self.assertTrue(host_vag != None, "The host should be in a VAG.") + + host.delete(self.apiClient) + + sf_volumes = sf_util.get_active_sf_volumes(self.sfe, sf_account_id) + + sf_volume = sf_util.check_and_get_sf_volume(sf_volumes, root_volume.name, self) + + sf_util.check_vags(sf_volume, sf_vag_ids, self) + + all_vags = sf_util.get_all_vags(self.sfe) + + host_vag = self._get_host_vag(host_iqn, all_vags) + + self.assertTrue(host_vag == None, "The host should not be in a VAG.") + + details = { + TestData.username: "root", + TestData.password: "solidfire", + TestData.url: "http://" + host.ipaddress, + TestData.podId : host.podid, + TestData.zoneId: host.zoneid + } + + host = Host.create( + self.apiClient, + self.cluster, + details, + hypervisor=host.hypervisor + ) + + self.assertTrue( + isinstance(host, Host), + "'host' is not a 'Host'." + ) + + hosts = list_hosts(self.apiClient, clusterid=self.cluster.id) + + unique_vag_ids = self._get_unique_vag_ids(hosts) + + self.assertTrue(len(hosts) == len(unique_vag_ids) + 1, "There should be one more host than unique VAG.") + + def _get_unique_vag_ids(self, hosts): + all_vags = sf_util.get_all_vags(self.sfe) + + unique_vag_ids = [] + + for host in hosts: + host = Host(host.__dict__) + + host_iqn = self._get_host_iqn(host) + + host_vag = self._get_host_vag(host_iqn, all_vags) + + if host_vag != None and host_vag.volume_access_group_id not in unique_vag_ids: + unique_vag_ids.append(host_vag.volume_access_group_id) + + return unique_vag_ids + + def _get_host_vag(self, host_iqn, vags): + self.assertTrue(host_iqn, "'host_iqn' should not be 'None'.") + self.assertTrue(vags, "'vags' should not be 'None'.") + + self.assertTrue(isinstance(host_iqn, basestring), "'host_iqn' should be a 'string'.") + self.assertTrue(isinstance(vags, list), "'vags' should be a 'list'.") + + for vag in vags: + if host_iqn in vag.initiators: + return vag + + return None + def _perform_add_remove_xenserver_host(self, primary_storage_id, sr_name): xen_sr = self.xen_session.xenapi.SR.get_by_name_label(sr_name)[0] @@ -463,7 +601,7 @@ class TestAddRemoveHosts(cloudstackTestCase): num_pbds = len(pbds) - sf_vag_id = self._get_sf_vag_id(self.cluster.id, primary_storage_id) + sf_vag_id = sf_util.get_vag_id(self.cs_api, self.cluster.id, primary_storage_id, self) host_iscsi_iqns = self._get_xenserver_host_iscsi_iqns() @@ -604,7 +742,7 @@ class TestAddRemoveHosts(cloudstackTestCase): ) def _perform_add_remove_kvm_host(self, primary_storage_id): - sf_vag_id = self._get_sf_vag_id(self.cluster.id, primary_storage_id) + sf_vag_id = sf_util.get_vag_id(self.cs_api, self.cluster.id, primary_storage_id, self) kvm_login = self.testdata[TestData.kvm] @@ -720,6 +858,14 @@ class TestAddRemoveHosts(cloudstackTestCase): return sql_result[0][0] + def _get_host_iqn(self, host): + sql_query = "Select url From host Where uuid = '" + str(host.id) + "'" + + # make sure you can connect to MySQL: https://teamtreehouse.com/community/cant-connect-remotely-to-mysql-server-with-mysql-workbench + sql_result = self.dbConnection.execute(sql_query) + + return sql_result[0][0] + def _get_xenserver_host_iscsi_iqns(self): hosts = self.xen_session.xenapi.host.get_all() @@ -767,20 +913,6 @@ class TestAddRemoveHosts(cloudstackTestCase): return result[len(searchFor):].strip() - def _get_sf_vag_id(self, cluster_id, primary_storage_id): - # Get SF Volume Access Group ID - sf_vag_id_request = {'clusterid': cluster_id, 'storageid': primary_storage_id} - sf_vag_id_result = self.cs_api.getSolidFireVolumeAccessGroupId(sf_vag_id_request) - sf_vag_id = sf_vag_id_result['apisolidfirevolumeaccessgroupid']['solidFireVolumeAccessGroupId'] - - self.assertEqual( - isinstance(sf_vag_id, int), - True, - TestAddRemoveHosts._vag_id_should_be_non_zero_int_err_msg - ) - - return sf_vag_id - def _get_sf_vag(self, sf_vag_id): return self.sfe.list_volume_access_groups(sf_vag_id, 1).volume_access_groups[0] diff --git a/test/integration/plugins/solidfire/TestCapacityManagement.py b/test/integration/plugins/solidfire/TestCapacityManagement.py index ab6eff124cd..0cc9db2dae1 100644 --- a/test/integration/plugins/solidfire/TestCapacityManagement.py +++ b/test/integration/plugins/solidfire/TestCapacityManagement.py @@ -33,7 +33,7 @@ from marvin.cloudstackTestCase import cloudstackTestCase from marvin.lib.base import Account, ServiceOffering, StoragePool, User, VirtualMachine # common - commonly used methods for all tests are listed here -from marvin.lib.common import get_domain, get_template, get_zone, list_clusters, list_hosts +from marvin.lib.common import get_domain, get_template, get_zone, list_hosts # utils - utility classes for common cleanup, external library wrappers, etc. from marvin.lib.utils import cleanup_resources @@ -47,7 +47,7 @@ from marvin.lib.utils import cleanup_resources # If using XenServer, verify the "xen_server_hostname" variable is correct. # # Note: -# If you do have more than one cluster, you might need to change this line: cls.cluster = list_clusters(cls.apiClient)[0] +# If you do have more than one cluster, you might need to change this variable: TestData.clusterId. class TestData(): @@ -193,7 +193,6 @@ class TestCapacityManagement(cloudstackTestCase): # Get Resources from Cloud Infrastructure cls.zone = get_zone(cls.apiClient, zone_id=cls.testdata[TestData.zoneId]) - cls.cluster = list_clusters(cls.apiClient)[0] cls.template = get_template(cls.apiClient, cls.zone.id, hypervisor=TestData.hypervisor_type) cls.domain = get_domain(cls.apiClient, cls.testdata[TestData.domainId]) diff --git a/test/integration/plugins/solidfire/TestSnapshots.py b/test/integration/plugins/solidfire/TestSnapshots.py index fab509e4b69..d9ab3b2b3e9 100644 --- a/test/integration/plugins/solidfire/TestSnapshots.py +++ b/test/integration/plugins/solidfire/TestSnapshots.py @@ -35,7 +35,7 @@ from nose.plugins.attrib import attr from marvin.lib.base import Account, DiskOffering, ServiceOffering, Snapshot, StoragePool, Template, User, VirtualMachine, Volume # common - commonly used methods for all tests are listed here -from marvin.lib.common import get_domain, get_template, get_zone, list_clusters, list_volumes, list_snapshots +from marvin.lib.common import get_domain, get_template, get_zone, list_volumes, list_snapshots # utils - utility classes for common cleanup, external library wrappers, etc. from marvin.lib.utils import cleanup_resources, wait_until @@ -87,16 +87,16 @@ class TestData(): def __init__(self): self.testdata = { TestData.solidFire: { - TestData.mvip: "10.117.40.120", + TestData.mvip: "10.117.78.225", TestData.username: "admin", TestData.password: "admin", TestData.port: 443, - TestData.url: "https://10.117.40.120:443" + TestData.url: "https://10.117.78.225:443" }, TestData.primaryStorage: { "name": "SolidFire-%d" % random.randint(0, 100), TestData.scope: "ZONE", - "url": "MVIP=10.117.40.120;SVIP=10.117.41.120;" + + "url": "MVIP=10.117.78.225;SVIP=10.117.94.225;" + "clusterAdminUsername=admin;clusterAdminPassword=admin;" + "clusterDefaultMinIops=10000;clusterDefaultMaxIops=15000;" + "clusterDefaultBurstIopsPercentOfMaxIops=1.5;", @@ -155,7 +155,6 @@ class TestData(): TestData.diskName: "test-volume-2", }, TestData.zoneId: 1, - TestData.clusterId: 1, TestData.domainId: 1, TestData.url: "10.117.40.114" } @@ -198,7 +197,6 @@ class TestSnapshots(cloudstackTestCase): # Get Resources from Cloud Infrastructure cls.zone = get_zone(cls.apiClient, zone_id=cls.testdata[TestData.zoneId]) - cls.cluster = list_clusters(cls.apiClient)[0] cls.template = get_template(cls.apiClient, cls.zone.id, hypervisor=TestData.hypervisor_type) cls.domain = get_domain(cls.apiClient, cls.testdata[TestData.domainId]) diff --git a/test/integration/plugins/solidfire/TestUploadDownload.py b/test/integration/plugins/solidfire/TestUploadDownload.py index d81600e9128..a15f27b98bf 100644 --- a/test/integration/plugins/solidfire/TestUploadDownload.py +++ b/test/integration/plugins/solidfire/TestUploadDownload.py @@ -185,7 +185,7 @@ class TestUploadDownload(cloudstackTestCase): # Get Resources from Cloud Infrastructure cls.zone = get_zone(cls.apiClient, zone_id=cls.testdata[TestData.zoneId]) - cls.cluster = list_clusters(cls.apiClient)[1] + cls.cluster = list_clusters(cls.apiClient)[0] cls.template = get_template(cls.apiClient, cls.zone.id, hypervisor=TestData.hypervisor_type) cls.domain = get_domain(cls.apiClient, cls.testdata[TestData.domainId]) diff --git a/test/integration/plugins/solidfire/TestVMMigrationWithStorage.py b/test/integration/plugins/solidfire/TestVMMigrationWithStorage.py index 93ab3b6ff61..7da6c9d542b 100644 --- a/test/integration/plugins/solidfire/TestVMMigrationWithStorage.py +++ b/test/integration/plugins/solidfire/TestVMMigrationWithStorage.py @@ -40,6 +40,9 @@ from marvin.lib.utils import cleanup_resources # Only one zone # Only one pod # Two clusters (have system VMs (including the VR) running on local or NFS storage) +# +# Running the tests: +# Verify the "xen_server_hostname_src" and "xen_server_hostname_dest" variables are correct. class TestData(): @@ -81,6 +84,9 @@ class TestData(): xenServer = "xenserver" zoneId = "zoneid" + xen_server_hostname_src = "XenServer-6.5-1" + xen_server_hostname_dest = "XenServer-6.5-3" + def __init__(self): self.testdata = { TestData.solidFire: { @@ -233,7 +239,7 @@ class TestVMMigrationWithStorage(cloudstackTestCase): # Set up xenAPI connection host_ip = "https://" + \ - list_hosts(cls.apiClient, clusterid=cls.testdata[TestData.clusterId1], name="XenServer-6.5-1")[0].ipaddress + list_hosts(cls.apiClient, clusterid=cls.testdata[TestData.clusterId1], name=TestData.xen_server_hostname_src)[0].ipaddress # Set up XenAPI connection cls.xen_session_1 = XenAPI.Session(host_ip) @@ -242,7 +248,7 @@ class TestVMMigrationWithStorage(cloudstackTestCase): # Set up xenAPI connection host_ip = "https://" + \ - list_hosts(cls.apiClient, clusterid=cls.testdata[TestData.clusterId2], name="XenServer-6.5-3")[0].ipaddress + list_hosts(cls.apiClient, clusterid=cls.testdata[TestData.clusterId2], name=TestData.xen_server_hostname_dest)[0].ipaddress # Set up XenAPI connection cls.xen_session_2 = XenAPI.Session(host_ip) @@ -532,9 +538,9 @@ class TestVMMigrationWithStorage(cloudstackTestCase): hosts = list_hosts(self.apiClient) for host in hosts: - if host.name == "XenServer-6.5-1": + if host.name == TestData.xen_server_hostname_src: src_host = host - elif host.name == "XenServer-6.5-3": + elif host.name == TestData.xen_server_hostname_dest: dest_host = host self.assertIsNotNone(src_host, "Could not locate the source host") diff --git a/test/integration/plugins/solidfire/TestVMSnapshots.py b/test/integration/plugins/solidfire/TestVMSnapshots.py index 45c42429843..106d5836fa7 100644 --- a/test/integration/plugins/solidfire/TestVMSnapshots.py +++ b/test/integration/plugins/solidfire/TestVMSnapshots.py @@ -44,6 +44,10 @@ from marvin.lib.utils import cleanup_resources # Only one pod # Only one cluster +# Running the tests: +# Change the "hypervisor_type" variable to control which hypervisor type to test. +# If using XenServer, verify the "xen_server_hostname" variable is correct. + class TestData: account = "account" @@ -74,6 +78,7 @@ class TestData: # modify to control which hypervisor type to test hypervisor_type = xenServer + xen_server_hostname = "XenServer-6.5-1" def __init__(self): self.testdata = { @@ -129,7 +134,7 @@ class TestData: "customizediops": False, "miniops": "10000", "maxiops": "15000", - "hypervisorsnapshotreserve": 200, + "hypervisorsnapshotreserve": 400, TestData.tags: TestData.storageTag }, TestData.diskOffering: { @@ -139,7 +144,7 @@ class TestData: "customizediops": False, "miniops": 300, "maxiops": 500, - "hypervisorsnapshotreserve": 200, + "hypervisorsnapshotreserve": 400, TestData.tags: TestData.storageTag, "storagetype": "shared" }, @@ -179,7 +184,7 @@ class TestVMSnapshots(cloudstackTestCase): # Set up XenAPI connection host_ip = "https://" + \ - list_hosts(cls.apiClient, clusterid=cls.testdata[TestData.clusterId], name="XenServer-6.5-1")[0].ipaddress + list_hosts(cls.apiClient, clusterid=cls.testdata[TestData.clusterId], name=TestData.xen_server_hostname)[0].ipaddress cls.xen_session = XenAPI.Session(host_ip) diff --git a/test/integration/plugins/solidfire/TestVolumes.py b/test/integration/plugins/solidfire/TestVolumes.py index 9d3ab2f43ee..9685e509cb8 100644 --- a/test/integration/plugins/solidfire/TestVolumes.py +++ b/test/integration/plugins/solidfire/TestVolumes.py @@ -51,7 +51,8 @@ from marvin.lib.utils import cleanup_resources # If using XenServer, change the "supports_cloning" variable to True or False as desired. # # Note: -# If you do have more than one cluster, you might need to change this line: cls.cluster = list_clusters(cls.apiClient)[0] +# If you do have more than one cluster, you might need to change this line: cls.cluster = list_clusters(cls.apiClient)[0] and +# this variable's value: TestData.clusterId. class TestData(): @@ -79,6 +80,7 @@ class TestData(): tags = "tags" templateCacheNameKvm = "centos55-x86-64" templateCacheNameXenServer = "centos56-x86-64-xen" + # templateCacheNameXenServer = "centos65-x86-64-XenServer" testAccount = "testaccount" url = "url" user = "user" @@ -91,17 +93,17 @@ class TestData(): zoneId = "zoneId" # modify to control which hypervisor type to test - hypervisor_type = kvm + hypervisor_type = xenServer xen_server_hostname = "XenServer-6.5-1" def __init__(self): self.testdata = { TestData.solidFire: { - TestData.mvip: "10.117.40.120", + TestData.mvip: "10.117.78.225", TestData.username: "admin", TestData.password: "admin", TestData.port: 443, - TestData.url: "https://10.117.40.120:443" + TestData.url: "https://10.117.78.225:443" }, TestData.kvm: { TestData.username: "root", @@ -135,7 +137,7 @@ class TestData(): TestData.primaryStorage: { "name": "SolidFire-%d" % random.randint(0, 100), TestData.scope: "ZONE", - "url": "MVIP=10.117.40.120;SVIP=10.117.41.120;" + + "url": "MVIP=10.117.78.225;SVIP=10.117.94.225;" + "clusterAdminUsername=admin;clusterAdminPassword=admin;" + "clusterDefaultMinIops=10000;clusterDefaultMaxIops=15000;" + "clusterDefaultBurstIopsPercentOfMaxIops=1.5;",