Support multiple volume access groups per compute cluster

This commit is contained in:
Mike Tutkowski 2017-12-22 01:02:42 -07:00
parent d0c6cacd06
commit 73608dec28
19 changed files with 977 additions and 524 deletions

View File

@ -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<Map<String, String>> 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<String> sendModifyTargetsCommand(ModifyTargetsCommand cmd, long hostId) {

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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<SolidFireUtil.SolidFireVag> sfVags = SolidFireUtil.getAllVags(sfConnection);
long csClusterId = util.getClusterIdForClusterUuid(csClusterUuid);
List<HostVO> hosts = hostDao.findByClusterId(csClusterId);
if (hosts == null) {
return new long[0];
}
List<Long> 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

View File

@ -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<Pair<ManagedObjectReference, String>> 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<Pair<ManagedObjectReference, String>> lstHosts, DatastoreMO dsMO) throws Exception {
int numHostsChecked = 0;
for (Pair<ManagedObjectReference, String> host: lstHosts) {
for (Pair<ManagedObjectReference, String> 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<Pair<ManagedObjectReference, String>> 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<Pair<ManagedObjectReference, String>> lstHosts, DatastoreMO dsMO) throws Exception {
int numHostsChecked = 0;
private void waitForAllHostsToMountDatastore2(List<HostMO> lstHosts, DatastoreMO dsMO) throws Exception {
long endWaitTime = System.currentTimeMillis() + SECONDS_TO_WAIT_FOR_DATASTORE * 1000;
for (Pair<ManagedObjectReference, String> 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<Pair<ManagedObjectReference, String>> lstHosts, DatastoreMO dsMO) throws Exception {
List<HostMO> hostMOs = new ArrayList<>(lstHosts.size());
for (Pair<ManagedObjectReference, String> host : lstHosts) {
ManagedObjectReference morHostToMatch = host.first();
HostMO hostToMatchMO = new HostMO(dsMO.getContext(), morHostToMatch);
hostMOs.add(hostToMatchMO);
}
return verifyAllHostsMountedDatastore2(hostMOs, dsMO);
}
private boolean verifyAllHostsMountedDatastore2(List<HostMO> lstHosts, DatastoreMO dsMO) throws Exception {
int numHostsChecked = 0;
for (HostMO hostToMatchMO : lstHosts) {
List<DatastoreHostMount> datastoreHostMounts = dsMO.getHostMounts();
for (DatastoreHostMount datastoreHostMount : datastoreHostMounts) {
@ -2753,6 +2778,16 @@ public class VmwareStorageProcessor implements StorageProcessor {
for (Pair<ManagedObjectReference, String> host : hosts) {
HostMO hostMO = new HostMO(dsMO.getContext(), host.first());
List<HostMO> hostMOs = new ArrayList<>(1);
hostMOs.add(hostMO);
mountVmfsDatastore2(dsMO, hostMOs);
}
}
private void mountVmfsDatastore2(DatastoreMO dsMO, List<HostMO> 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<Pair<ManagedObjectReference, String>> currentHosts = new ArrayList<>(1);
s_logger.trace("'" + ex.getClass().getName() + "' exception thrown: " + ex.getMessage());
currentHosts.add(host);
List<HostMO> 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<Pair<ManagedObjectReference, String>> hosts) throws Exception {
ManagedObjectReference morDs = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, datastoreName);
DatastoreMO dsMO = new DatastoreMO(context, morDs);
for (Pair<ManagedObjectReference, String> host : hosts) {
HostMO hostMO = new HostMO(context, host.first());
List<HostMO> hostMOs = new ArrayList<>(1);
hostMOs.add(hostMO);
unmountVmfsDatastore2(context, hyperHost, datastoreName, hostMOs);
}
}
private void unmountVmfsDatastore2(VmwareContext context, VmwareHypervisorHost hyperHost, String datastoreName,
List<HostMO> 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<HostInternetScsiHbaStaticTarget> 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<HostInternetScsiHbaStaticTarget> targetsToRemove, HostMO host, List<HostMO> 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<HostInternetScsiHbaStaticTarget> targets,
List<Pair<ManagedObjectReference, String>> hostPairs) throws Exception {
List<HostMO> hosts = new ArrayList<>();

View File

@ -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<HostVO> 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<SolidFireUtil.SolidFireVag> 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,

View File

@ -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<HostVO> hosts = _hostDao.findByClusterId(clusterId);
List<HostVO> 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<HostVO> allHosts = _resourceMgr.listAllUpHosts(Host.Type.Routing, primaryDataStoreInfo.getClusterId(),
List<HostVO> 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<String, String> 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<StoragePoolHostVO> hostPoolRecords = _storagePoolHostDao.listByPoolId(dataStore.getId());
List<StoragePoolHostVO> 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<VMTemplateStoragePoolVO> unusedTemplatesInPool = _tmpltMgr.getUnusedTemplatesInPool(storagePoolVO);
StoragePoolVO storagePoolVO = primaryDataStoreDao.findById(storagePool.getId());
List<VMTemplateStoragePoolVO> 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<String, String> 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<SolidFireUtil.SolidFireVag> 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);
}
}

View File

@ -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<String> 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<StoragePoolVO> storagePools = _storagePoolDao.findPoolsByProvider(SolidFireUtil.PROVIDER_NAME);
List<StoragePoolVO> storagePools = storagePoolDao.findPoolsByProvider(SolidFireUtil.PROVIDER_NAME);
if (storagePools != null && storagePools.size() > 0) {
List<Map<String, String>> 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<String> storagePaths = new ArrayList<>();
// If you do not pass in null for the second parameter, you only get back applicable ROOT disks.
List<VolumeVO> volumes = _volumeDao.findByPoolId(storagePoolId, null);
List<VolumeVO> 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<Map<String, String>> getTargets(long clusterId, long storagePoolId) {
List<Map<String, String>> 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<VolumeVO> volumes = _volumeDao.findByPoolId(storagePoolId, null);
List<VolumeVO> 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<String, String> 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);
}
}

View File

@ -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<StoragePoolVO> 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());
}
}
}

View File

@ -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<Long> 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<ClusterVO> 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<SolidFireUtil.SolidFireConnection> 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<SolidFireUtil.SolidFireVag> sfVags = SolidFireUtil.getAllVags(sfConnection);
SolidFireVag sfVag = getVolumeAccessGroup(hostVO.getStorageUrl(), sfVags);
SolidFireUtil.SolidFireVag sfVag = SolidFireUtil.getVag(sfConnection, Long.parseLong(vagId));
List<HostVO> 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<HostVO> 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<StoragePoolVO> 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<SolidFireUtil.SolidFireConnection> 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<SolidFireUtil.SolidFireVag> 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<HostVO> 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<SolidFireUtil.SolidFireVag> sfVags, Host host, HostDao hostDao) {
List<HostVO> 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<SolidFireUtil.SolidFireVag> sfVags,
Host host, HostDao hostDao) {
List<Long> volumeIdsToReturn = new ArrayList<>();
SolidFireVag sfVagForRandomHostInCluster = getVagForRandomHostInCluster(sfVags, host, hostDao);
if (sfVagForRandomHostInCluster != null) {
long[] volumeIds = sfVagForRandomHostInCluster.getVolumeIds();
if (volumeIds != null) {
List<Long> knownSfAccountsForCs = new ArrayList<>();
List<Long> 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<SolidFireUtil.SolidFireVag> sfVags,
SolidFireVag sfVag, Host host, HostDao hostDao) {
SolidFireVag sfVagForRandomHostInCluster = getVagForRandomHostInCluster(sfVags, host, hostDao);
if (sfVagForRandomHostInCluster != null) {
long[] volumeIds = sfVagForRandomHostInCluster.getVolumeIds();
if (volumeIds != null) {
List<Long> knownSfAccountsForCs = new ArrayList<>();
List<Long> knownSfAccountsNotForCs = new ArrayList<>();
List<Long> 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<Long> newVolumeIds) {
List<Long> existingVolumeIds = Longs.asList(sfVag.getVolumeIds());
if (!existingVolumeIds.contains(volumeId) && !newVolumeIds.contains(volumeId)) {
newVolumeIds.add(volumeId);
}
}
private static SolidFireVag getVagForRandomHostInCluster(List<SolidFireUtil.SolidFireVag> sfVags, Host host, HostDao hostDao) {
List<HostVO> 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<HostVO> 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<SolidFireUtil.SolidFireVag> sfVags = SolidFireUtil.getAllVags(sfConnection);
Map<SolidFireUtil.SolidFireVag, List<String>> sfVagToIqnsMap = new HashMap<>();
for (HostVO hostVO : hosts) {
String iqn = hostVO.getStorageUrl();
SolidFireUtil.SolidFireVag sfVag = getVolumeAccessGroup(iqn, sfVags);
List<String> 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<String> 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<SolidFireUtil.SolidFireVag> sfVags) {
if (hostIqn == null) {
return null;
}
hostIqn = hostIqn.toLowerCase();
if (sfVags != null) {
for (SolidFireUtil.SolidFireVag sfVag : sfVags) {
List<String> 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<HostVO> 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<HostVO> 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<SolidFireVag> 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<SolidFireVag> 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<String> lstIqns = iqns != null ? new ArrayList<>(Arrays.asList(iqns)) : new ArrayList<String>();
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<String> lstIqns = iqns != null ? new ArrayList<>(Arrays.asList(iqns)) : new ArrayList<String>();
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<Long> 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<Long> 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<? extends Host> hosts) {
if (hosts == null || hosts.size() == 0) {
throw new CloudRuntimeException("There do not appear to be any hosts in this cluster.");
}
List<String> 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<HostVO> hosts) {
List<SolidFireUtil.SolidFireVag> sfVags = SolidFireUtil.getAllVags(sfConnection);
if (sfVags != null) {
List<String> 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<String> 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<String> getStringArrayAsLowerCaseStringList(String[] aString) {
List<String> lstLowerCaseString = new ArrayList<>();
@ -1106,10 +1349,6 @@ public class SolidFireUtil {
return null;
}
Map<String, Object> convertedMap = new HashMap<>();
convertedMap.putAll(map);
return convertedMap;
return new HashMap<>(map);
}
}

View File

@ -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]

View File

@ -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])

View File

@ -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])

View File

@ -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])

View File

@ -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")

View File

@ -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)

View File

@ -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;",