SolidFire (shared-access) Provider

This commit is contained in:
Mike Tutkowski 2014-05-20 10:58:28 -06:00
parent 520ff00083
commit 42d00cae58
15 changed files with 1428 additions and 815 deletions

View File

@ -18,7 +18,16 @@ package com.cloud.agent.api;
import com.cloud.storage.StoragePool;
import java.util.Map;
public class CreateStoragePoolCommand extends ModifyStoragePoolCommand {
public static final String DATASTORE_NAME = "datastoreName";
public static final String IQN = "iqn";
public static final String STORAGE_HOST = "storageHost";
public static final String STORAGE_PORT = "storagePort";
private boolean _createDatastore;
private Map<String, String> _details;
public CreateStoragePoolCommand() {
}
@ -26,4 +35,20 @@ public class CreateStoragePoolCommand extends ModifyStoragePoolCommand {
public CreateStoragePoolCommand(boolean add, StoragePool pool) {
super(add, pool);
}
public void setCreateDatastore(boolean createDatastore) {
_createDatastore = createDatastore;
}
public boolean getCreateDatastore() {
return _createDatastore;
}
public void setDetails(Map<String, String> details) {
_details = details;
}
public Map<String, String> getDetails() {
return _details;
}
}

View File

@ -17,45 +17,68 @@
package com.cloud.agent.api;
import java.io.File;
import java.util.Map;
import java.util.UUID;
import com.cloud.agent.api.to.StorageFilerTO;
import com.cloud.storage.StoragePool;
public class DeleteStoragePoolCommand extends Command {
public static final String DATASTORE_NAME = "datastoreName";
public static final String IQN = "iqn";
public static final String STORAGE_HOST = "storageHost";
public static final String STORAGE_PORT = "storagePort";
StorageFilerTO pool;
public static final String LOCAL_PATH_PREFIX = "/mnt/";
String localPath;
private StorageFilerTO _pool;
private String _localPath;
private boolean _removeDatastore;
private Map<String, String> _details;
public DeleteStoragePoolCommand() {
}
public DeleteStoragePoolCommand(StoragePool pool, String localPath) {
this.pool = new StorageFilerTO(pool);
this.localPath = localPath;
_pool = new StorageFilerTO(pool);
_localPath = localPath;
}
public DeleteStoragePoolCommand(StoragePool pool) {
this(pool, LOCAL_PATH_PREFIX + File.separator + UUID.nameUUIDFromBytes((pool.getHostAddress() + pool.getPath()).getBytes()));
}
public StorageFilerTO getPool() {
return pool;
public void setPool(StoragePool pool) {
_pool = new StorageFilerTO(pool);
}
public void setPool(StoragePool pool) {
this.pool = new StorageFilerTO(pool);
public StorageFilerTO getPool() {
return _pool;
}
public String getLocalPath() {
return _localPath;
}
public void setRemoveDatastore(boolean removeDatastore) {
_removeDatastore = removeDatastore;
}
public boolean getRemoveDatastore() {
return _removeDatastore;
}
public void setDetails(Map<String, String> details) {
_details = details;
}
public Map<String, String> getDetails() {
return _details;
}
@Override
public boolean executeInSequence() {
return false;
}
public String getLocalPath() {
return localPath;
}
}

View File

@ -3177,6 +3177,18 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
}
protected Answer execute(CreateStoragePoolCommand cmd) {
if (cmd.getCreateDatastore()) {
try {
VmwareContext context = getServiceContext();
_storageProcessor.prepareManagedDatastore(context, getHyperHost(context),
cmd.getDetails().get(CreateStoragePoolCommand.DATASTORE_NAME), cmd.getDetails().get(CreateStoragePoolCommand.IQN),
cmd.getDetails().get(CreateStoragePoolCommand.STORAGE_HOST), Integer.parseInt(cmd.getDetails().get(CreateStoragePoolCommand.STORAGE_PORT)));
} catch (Exception ex) {
return new Answer(cmd, false, "Issue creating datastore");
}
}
return new Answer(cmd, true, "success");
}
@ -3223,22 +3235,32 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
s_logger.info("Executing resource DeleteStoragePoolCommand: " + _gson.toJson(cmd));
}
StorageFilerTO pool = cmd.getPool();
try {
// We will leave datastore cleanup management to vCenter. Since for cluster VMFS datastore, it will always
// be mounted by vCenter.
if (cmd.getRemoveDatastore()) {
_storageProcessor.handleDatastoreAndVmdkDetach(cmd.getDetails().get(DeleteStoragePoolCommand.DATASTORE_NAME), cmd.getDetails().get(DeleteStoragePoolCommand.IQN),
cmd.getDetails().get(DeleteStoragePoolCommand.STORAGE_HOST), Integer.parseInt(cmd.getDetails().get(DeleteStoragePoolCommand.STORAGE_PORT)));
// VmwareHypervisorHost hyperHost = this.getHyperHost(getServiceContext());
// hyperHost.unmountDatastore(pool.getUuid());
Answer answer = new Answer(cmd, true, "success");
return answer;
return new Answer(cmd, true, "success");
}
else {
// We will leave datastore cleanup management to vCenter. Since for cluster VMFS datastore, it will always
// be mounted by vCenter.
// VmwareHypervisorHost hyperHost = this.getHyperHost(getServiceContext());
// hyperHost.unmountDatastore(pool.getUuid());
return new Answer(cmd, true, "success");
}
} catch (Throwable e) {
if (e instanceof RemoteException) {
s_logger.warn("Encounter remote exception to vCenter, invalidate VMware session context");
invalidateServiceContext();
}
StorageFilerTO pool = cmd.getPool();
String msg = "DeleteStoragePoolCommand (pool: " + pool.getHost() + ", path: " + pool.getPath() + ") failed due to " + VmwareHelper.getExceptionMessage(e);
return new Answer(cmd, false, msg);
}
}

View File

@ -1688,6 +1688,11 @@ public class VmwareStorageProcessor implements StorageProcessor {
}
}
public ManagedObjectReference prepareManagedDatastore(VmwareContext context, VmwareHypervisorHost hyperHost, String datastoreName,
String iScsiName, String storageHost, int storagePort) throws Exception {
return getVmfsDatastore(context, hyperHost, datastoreName, storageHost, storagePort, trimIqn(iScsiName), null, null, null, null);
}
private ManagedObjectReference prepareManagedDatastore(VmwareContext context, VmwareHypervisorHost hyperHost, String iScsiName,
String storageHost, int storagePort, String chapInitiatorUsername, String chapInitiatorSecret,
String chapTargetUsername, String chapTargetSecret) throws Exception {
@ -1800,8 +1805,8 @@ public class VmwareStorageProcessor implements StorageProcessor {
return null;
}
private void removeVmfsDatastore(VmwareHypervisorHost hyperHost, String volumeUuid, String storageIpAddress, int storagePortNumber, String iqn) throws Exception {
// hyperHost.unmountDatastore(volumeUuid);
private void removeVmfsDatastore(VmwareHypervisorHost hyperHost, String datastoreName, String storageIpAddress, int storagePortNumber, String iqn) throws Exception {
// hyperHost.unmountDatastore(datastoreName);
VmwareContext context = hostService.getServiceContext(null);
ManagedObjectReference morCluster = hyperHost.getHyperHostCluster();
@ -1990,11 +1995,15 @@ public class VmwareStorageProcessor implements StorageProcessor {
return morDs;
}
private void handleDatastoreAndVmdkDetach(String iqn, String storageHost, int storagePort) throws Exception {
public void handleDatastoreAndVmdkDetach(String datastoreName, String iqn, String storageHost, int storagePort) throws Exception {
VmwareContext context = hostService.getServiceContext(null);
VmwareHypervisorHost hyperHost = hostService.getHyperHost(context, null);
removeVmfsDatastore(hyperHost, VmwareResource.getDatastoreName(iqn), storageHost, storagePort, trimIqn(iqn));
removeVmfsDatastore(hyperHost, datastoreName, storageHost, storagePort, trimIqn(iqn));
}
private void handleDatastoreAndVmdkDetach(String iqn, String storageHost, int storagePort) throws Exception {
handleDatastoreAndVmdkDetach(VmwareResource.getDatastoreName(iqn), iqn, storageHost, storagePort);
}
private void removeManagedTargetsFromCluster(List<String> iqns) throws Exception {

View File

@ -53,7 +53,6 @@ import org.apache.log4j.Logger;
import org.apache.xmlrpc.XmlRpcException;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.CreateStoragePoolCommand;
import com.cloud.agent.api.to.DataObjectType;
import com.cloud.agent.api.to.DataStoreTO;
import com.cloud.agent.api.to.DataTO;
@ -66,7 +65,6 @@ import com.cloud.exception.InternalErrorException;
import com.cloud.hypervisor.xenserver.resource.CitrixResourceBase.SRType;
import com.cloud.storage.DataStoreRole;
import com.cloud.storage.Storage.ImageFormat;
import com.cloud.storage.Storage.StoragePoolType;
import com.cloud.storage.resource.StorageProcessor;
import com.cloud.utils.S3Utils;
import com.cloud.utils.exception.CloudRuntimeException;
@ -76,7 +74,6 @@ import com.cloud.utils.storage.encoding.Decoder;
import com.xensource.xenapi.Connection;
import com.xensource.xenapi.Host;
import com.xensource.xenapi.PBD;
import com.xensource.xenapi.Pool;
import com.xensource.xenapi.SR;
import com.xensource.xenapi.Types;
import com.xensource.xenapi.Types.BadServerResponse;
@ -572,150 +569,6 @@ public class XenServerStorageProcessor implements StorageProcessor {
}
}
protected SR getIscsiSR(Connection conn, StorageFilerTO pool) {
synchronized (pool.getUuid().intern()) {
Map<String, String> deviceConfig = new HashMap<String, String>();
try {
String target = pool.getHost();
String path = pool.getPath();
if (path.endsWith("/")) {
path = path.substring(0, path.length() - 1);
}
String tmp[] = path.split("/");
if (tmp.length != 3) {
String msg = "Wrong iscsi path " + pool.getPath() + " it should be /targetIQN/LUN";
s_logger.warn(msg);
throw new CloudRuntimeException(msg);
}
String targetiqn = tmp[1].trim();
String lunid = tmp[2].trim();
String scsiid = "";
Set<SR> srs = SR.getByNameLabel(conn, pool.getUuid());
for (SR sr : srs) {
if (!SRType.LVMOISCSI.equals(sr.getType(conn))) {
continue;
}
Set<PBD> pbds = sr.getPBDs(conn);
if (pbds.isEmpty()) {
continue;
}
PBD pbd = pbds.iterator().next();
Map<String, String> dc = pbd.getDeviceConfig(conn);
if (dc == null) {
continue;
}
if (dc.get("target") == null) {
continue;
}
if (dc.get("targetIQN") == null) {
continue;
}
if (dc.get("lunid") == null) {
continue;
}
if (target.equals(dc.get("target")) && targetiqn.equals(dc.get("targetIQN")) && lunid.equals(dc.get("lunid"))) {
throw new CloudRuntimeException("There is a SR using the same configuration target:" + dc.get("target") + ", targetIQN:" + dc.get("targetIQN") +
", lunid:" + dc.get("lunid") + " for pool " + pool.getUuid() + "on host:" + hypervisorResource.getHost().uuid);
}
}
deviceConfig.put("target", target);
deviceConfig.put("targetIQN", targetiqn);
Host host = Host.getByUuid(conn, hypervisorResource.getHost().uuid);
Map<String, String> smConfig = new HashMap<String, String>();
String type = SRType.LVMOISCSI.toString();
String poolId = Long.toString(pool.getId());
SR sr = null;
try {
sr = SR.create(conn, host, deviceConfig, new Long(0), pool.getUuid(), poolId, type, "user", true, smConfig);
} catch (XenAPIException e) {
String errmsg = e.toString();
if (errmsg.contains("SR_BACKEND_FAILURE_107")) {
String lun[] = errmsg.split("<LUN>");
boolean found = false;
for (int i = 1; i < lun.length; i++) {
int blunindex = lun[i].indexOf("<LUNid>") + 7;
int elunindex = lun[i].indexOf("</LUNid>");
String ilun = lun[i].substring(blunindex, elunindex);
ilun = ilun.trim();
if (ilun.equals(lunid)) {
int bscsiindex = lun[i].indexOf("<SCSIid>") + 8;
int escsiindex = lun[i].indexOf("</SCSIid>");
scsiid = lun[i].substring(bscsiindex, escsiindex);
scsiid = scsiid.trim();
found = true;
break;
}
}
if (!found) {
String msg = "can not find LUN " + lunid + " in " + errmsg;
s_logger.warn(msg);
throw new CloudRuntimeException(msg);
}
} else {
String msg = "Unable to create Iscsi SR " + deviceConfig + " due to " + e.toString();
s_logger.warn(msg, e);
throw new CloudRuntimeException(msg, e);
}
}
deviceConfig.put("SCSIid", scsiid);
String result = SR.probe(conn, host, deviceConfig, type, smConfig);
String pooluuid = null;
if (result.indexOf("<UUID>") != -1) {
pooluuid = result.substring(result.indexOf("<UUID>") + 6, result.indexOf("</UUID>")).trim();
}
if (pooluuid == null || pooluuid.length() != 36) {
sr = SR.create(conn, host, deviceConfig, new Long(0), pool.getUuid(), poolId, type, "user", true, smConfig);
} else {
sr = SR.introduce(conn, pooluuid, pool.getUuid(), poolId, type, "user", true, smConfig);
Pool.Record pRec = XenServerConnectionPool.getPoolRecord(conn);
PBD.Record rec = new PBD.Record();
rec.deviceConfig = deviceConfig;
rec.host = pRec.master;
rec.SR = sr;
PBD pbd = PBD.create(conn, rec);
pbd.plug(conn);
}
sr.scan(conn);
return sr;
} catch (XenAPIException e) {
String msg = "Unable to create Iscsi SR " + deviceConfig + " due to " + e.toString();
s_logger.warn(msg, e);
throw new CloudRuntimeException(msg, e);
} catch (Exception e) {
String msg = "Unable to create Iscsi SR " + deviceConfig + " due to " + e.getMessage();
s_logger.warn(msg, e);
throw new CloudRuntimeException(msg, e);
}
}
}
protected Answer execute(CreateStoragePoolCommand cmd) {
Connection conn = hypervisorResource.getConnection();
StorageFilerTO pool = cmd.getPool();
try {
if (pool.getType() == StoragePoolType.NetworkFilesystem) {
getNfsSR(conn, pool);
} else if (pool.getType() == StoragePoolType.IscsiLUN) {
getIscsiSR(conn, pool);
} else if (pool.getType() == StoragePoolType.PreSetup) {
} else {
return new Answer(cmd, false, "The pool type: " + pool.getType().name() + " is not supported.");
}
return new Answer(cmd, true, "success");
} catch (Exception e) {
String msg =
"Catch Exception " + e.getClass().getName() + ", create StoragePool failed due to " + e.toString() + " on host:" +
hypervisorResource.getHost().uuid + " pool: " + pool.getHost() + pool.getPath();
s_logger.warn(msg, e);
return new Answer(cmd, false, msg);
}
}
protected Answer directDownloadHttpTemplate(CopyCommand cmd, DecodedDataObject srcObj, DecodedDataObject destObj) {
Connection conn = hypervisorResource.getConnection();
SR poolsr = null;

View File

@ -20,6 +20,11 @@
<relativePath>../../../pom.xml</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloud-plugin-storage-volume-default</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloud-engine-storage-volume</artifactId>

View File

@ -29,5 +29,7 @@
<bean id="solidFireDataStoreProvider"
class="org.apache.cloudstack.storage.datastore.provider.SolidfirePrimaryDataStoreProvider" />
<bean id="solidFireSharedDataStoreProvider"
class="org.apache.cloudstack.storage.datastore.provider.SolidFireSharedPrimaryDataStoreProvider" />
</beans>

View File

@ -0,0 +1,65 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.apache.cloudstack.storage.datastore.driver;
import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult;
import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
import org.apache.cloudstack.storage.command.CommandResult;
import com.cloud.agent.api.to.DataStoreTO;
import com.cloud.agent.api.to.DataTO;
public class SolidFireSharedPrimaryDataStoreDriver extends CloudStackPrimaryDataStoreDriverImpl {
@Override
public DataTO getTO(DataObject data) {
return null;
}
@Override
public DataStoreTO getStoreTO(DataStore store) {
return null;
}
@Override
public boolean canCopy(DataObject srcData, DataObject destData) {
return false;
}
@Override
public void copyAsync(DataObject srcdata, DataObject destData, AsyncCompletionCallback<CopyCommandResult> callback) {
throw new UnsupportedOperationException();
}
@Override
public void resize(DataObject data, AsyncCompletionCallback<CreateCmdResult> callback) {
throw new UnsupportedOperationException();
}
@Override
public void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CreateCmdResult> callback) {
throw new UnsupportedOperationException();
}
@Override
public void revertSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CommandResult> callback) {
throw new UnsupportedOperationException();
}
}

View File

@ -17,15 +17,11 @@
package org.apache.cloudstack.storage.datastore.driver;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.inject.Inject;
import org.apache.commons.lang.StringUtils;
import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo;
import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult;
@ -57,23 +53,20 @@ import com.cloud.storage.StoragePool;
import com.cloud.storage.Volume;
import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.VolumeDao;
import com.cloud.storage.dao.VolumeDetailsDao;
import com.cloud.user.AccountDetailVO;
import com.cloud.user.AccountDetailsDao;
import com.cloud.user.AccountVO;
import com.cloud.user.dao.AccountDao;
import com.cloud.utils.exception.CloudRuntimeException;
public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
@Inject private PrimaryDataStoreDao _storagePoolDao;
@Inject private StoragePoolDetailsDao _storagePoolDetailsDao;
@Inject private VolumeDao _volumeDao;
@Inject private VolumeDetailsDao _volumeDetailsDao;
@Inject private DataCenterDao _zoneDao;
@Inject private AccountDao _accountDao;
@Inject private AccountDetailsDao _accountDetailsDao;
@Inject private ClusterDetailsDao _clusterDetailsDao;
@Inject private DataCenterDao _zoneDao;
@Inject private HostDao _hostDao;
@Inject private PrimaryDataStoreDao _storagePoolDao;
@Inject private StoragePoolDetailsDao _storagePoolDetailsDao;
@Inject private VolumeDao _volumeDao;
@Override
public Map<String, String> getCapabilities() {
@ -90,131 +83,10 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
return null;
}
private static class SolidFireConnection {
private final String _managementVip;
private final int _managementPort;
private final String _clusterAdminUsername;
private final String _clusterAdminPassword;
private SolidFireUtil.SolidFireAccount createSolidFireAccount(SolidFireUtil.SolidFireConnection sfConnection, String sfAccountName) {
long accountNumber = SolidFireUtil.createSolidFireAccount(sfConnection, sfAccountName);
public SolidFireConnection(String managementVip, int managementPort, String clusterAdminUsername, String clusterAdminPassword) {
_managementVip = managementVip;
_managementPort = managementPort;
_clusterAdminUsername = clusterAdminUsername;
_clusterAdminPassword = clusterAdminPassword;
}
public String getManagementVip() {
return _managementVip;
}
public int getManagementPort() {
return _managementPort;
}
public String getClusterAdminUsername() {
return _clusterAdminUsername;
}
public String getClusterAdminPassword() {
return _clusterAdminPassword;
}
}
private SolidFireConnection getSolidFireConnection(long storagePoolId) {
StoragePoolDetailVO storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.MANAGEMENT_VIP);
String mVip = storagePoolDetail.getValue();
storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.MANAGEMENT_PORT);
int mPort = Integer.parseInt(storagePoolDetail.getValue());
storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.CLUSTER_ADMIN_USERNAME);
String clusterAdminUsername = storagePoolDetail.getValue();
storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.CLUSTER_ADMIN_PASSWORD);
String clusterAdminPassword = storagePoolDetail.getValue();
return new SolidFireConnection(mVip, mPort, clusterAdminUsername, clusterAdminPassword);
}
private SolidFireUtil.SolidFireAccount createSolidFireAccount(String sfAccountName, SolidFireConnection sfConnection) {
String mVip = sfConnection.getManagementVip();
int mPort = sfConnection.getManagementPort();
String clusterAdminUsername = sfConnection.getClusterAdminUsername();
String clusterAdminPassword = sfConnection.getClusterAdminPassword();
long accountNumber = SolidFireUtil.createSolidFireAccount(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfAccountName);
return SolidFireUtil.getSolidFireAccountById(mVip, mPort, clusterAdminUsername, clusterAdminPassword, accountNumber);
}
private void updateCsDbWithAccountInfo(long csAccountId, SolidFireUtil.SolidFireAccount sfAccount) {
AccountDetailVO accountDetail = new AccountDetailVO(csAccountId,
SolidFireUtil.ACCOUNT_ID,
String.valueOf(sfAccount.getId()));
_accountDetailsDao.persist(accountDetail);
accountDetail = new AccountDetailVO(csAccountId,
SolidFireUtil.CHAP_INITIATOR_USERNAME,
String.valueOf(sfAccount.getName()));
_accountDetailsDao.persist(accountDetail);
accountDetail = new AccountDetailVO(csAccountId,
SolidFireUtil.CHAP_INITIATOR_SECRET,
String.valueOf(sfAccount.getInitiatorSecret()));
_accountDetailsDao.persist(accountDetail);
accountDetail = new AccountDetailVO(csAccountId,
SolidFireUtil.CHAP_TARGET_USERNAME,
sfAccount.getName());
_accountDetailsDao.persist(accountDetail);
accountDetail = new AccountDetailVO(csAccountId,
SolidFireUtil.CHAP_TARGET_SECRET,
sfAccount.getTargetSecret());
_accountDetailsDao.persist(accountDetail);
}
private class ChapInfoImpl implements ChapInfo {
private final String _initiatorUsername;
private final String _initiatorSecret;
private final String _targetUsername;
private final String _targetSecret;
public ChapInfoImpl(String initiatorUsername, String initiatorSecret, String targetUsername, String targetSecret) {
_initiatorUsername = initiatorUsername;
_initiatorSecret = initiatorSecret;
_targetUsername = targetUsername;
_targetSecret = targetSecret;
}
@Override
public String getInitiatorUsername() {
return _initiatorUsername;
}
@Override
public String getInitiatorSecret() {
return _initiatorSecret;
}
@Override
public String getTargetUsername() {
return _targetUsername;
}
@Override
public String getTargetSecret() {
return _targetSecret;
}
return SolidFireUtil.getSolidFireAccountById(sfConnection, accountNumber);
}
@Override
@ -222,38 +94,13 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
return null;
}
/*
@Override
public ChapInfo getChapInfo(VolumeInfo volumeInfo) {
long accountId = volumeInfo.getAccountId();
AccountDetailVO accountDetail = _accountDetailsDao.findDetail(accountId, SolidFireUtil.CHAP_INITIATOR_USERNAME);
String chapInitiatorUsername = accountDetail.getValue();
accountDetail = _accountDetailsDao.findDetail(accountId, SolidFireUtil.CHAP_INITIATOR_SECRET);
String chapInitiatorSecret = accountDetail.getValue();
accountDetail = _accountDetailsDao.findDetail(accountId, SolidFireUtil.CHAP_TARGET_USERNAME);
String chapTargetUsername = accountDetail.getValue();
accountDetail = _accountDetailsDao.findDetail(accountId, SolidFireUtil.CHAP_TARGET_SECRET);
String chapTargetSecret = accountDetail.getValue();
return new ChapInfoImpl(chapInitiatorUsername, chapInitiatorSecret, chapTargetUsername, chapTargetSecret);
}
*/
// 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 connectVolumeToHost(VolumeInfo volumeInfo, Host host, DataStore dataStore)
public synchronized boolean connectVolumeToHost(VolumeInfo volumeInfo, Host host, DataStore dataStore)
{
if (volumeInfo == null || host == null || dataStore == null) {
return false;
@ -263,111 +110,38 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
long clusterId = host.getClusterId();
long storagePoolId = dataStore.getId();
ClusterDetailsVO clusterDetail = _clusterDetailsDao.findDetail(clusterId, getVagKey(storagePoolId));
ClusterDetailsVO clusterDetail = _clusterDetailsDao.findDetail(clusterId, SolidFireUtil.getVagKey(storagePoolId));
String vagId = clusterDetail != null ? clusterDetail.getValue() : null;
List<HostVO> hosts = _hostDao.findByClusterId(clusterId);
if (!hostsSupport_iScsi(hosts)) {
if (!SolidFireUtil.hostsSupport_iScsi(hosts)) {
return false;
}
SolidFireConnection sfConnection = getSolidFireConnection(storagePoolId);
SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, _storagePoolDetailsDao);
if (vagId != null) {
SolidFireUtil.SolidFireVag sfVag = SolidFireUtil.getSolidFireVag(sfConnection.getManagementVip(), sfConnection.getManagementPort(),
sfConnection.getClusterAdminUsername(), sfConnection.getClusterAdminPassword(), Long.parseLong(vagId));
SolidFireUtil.SolidFireVag sfVag = SolidFireUtil.getSolidFireVag(sfConnection, Long.parseLong(vagId));
String[] hostIqns = getNewHostIqns(sfVag.getInitiators(), getIqnsFromHosts(hosts));
long[] volumeIds = getNewVolumeIds(sfVag.getVolumeIds(), sfVolumeId, true);
String[] hostIqns = SolidFireUtil.getNewHostIqns(sfVag.getInitiators(), SolidFireUtil.getIqnsFromHosts(hosts));
long[] volumeIds = SolidFireUtil.getNewVolumeIds(sfVag.getVolumeIds(), sfVolumeId, true);
SolidFireUtil.modifySolidFireVag(sfConnection.getManagementVip(), sfConnection.getManagementPort(),
sfConnection.getClusterAdminUsername(), sfConnection.getClusterAdminPassword(), sfVag.getId(),
hostIqns, volumeIds);
SolidFireUtil.modifySolidFireVag(sfConnection, sfVag.getId(), hostIqns, volumeIds);
}
else {
long lVagId;
try {
lVagId = SolidFireUtil.createSolidFireVag(sfConnection.getManagementVip(), sfConnection.getManagementPort(),
sfConnection.getClusterAdminUsername(), sfConnection.getClusterAdminPassword(), "CloudStack-" + UUID.randomUUID().toString(),
getIqnsFromHosts(hosts), new long[] { sfVolumeId });
}
catch (Exception ex) {
String iqnInVagAlready = "Exceeded maximum number of Volume Access Groups per initiator";
if (!ex.getMessage().contains(iqnInVagAlready)) {
throw new CloudRuntimeException(ex.getMessage());
}
// getCompatibleVag throws an exception if an existing VAG can't be located
SolidFireUtil.SolidFireVag sfVag = getCompatibleVag(hosts, sfConnection);
long[] volumeIds = getNewVolumeIds(sfVag.getVolumeIds(), sfVolumeId, true);
SolidFireUtil.modifySolidFireVag(sfConnection.getManagementVip(), sfConnection.getManagementPort(),
sfConnection.getClusterAdminUsername(), sfConnection.getClusterAdminPassword(), sfVag.getId(),
sfVag.getInitiators(), volumeIds);
lVagId = sfVag.getId();
}
clusterDetail = new ClusterDetailsVO(clusterId, getVagKey(storagePoolId), String.valueOf(lVagId));
_clusterDetailsDao.persist(clusterDetail);
SolidFireUtil.placeVolumeInVolumeAccessGroup(sfConnection, sfVolumeId, storagePoolId, hosts, _clusterDetailsDao);
}
return true;
}
// this method takes in a collection of hosts and tries to find an existing VAG that has all three 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 SolidFireUtil.SolidFireVag getCompatibleVag(List<HostVO> hosts, SolidFireConnection sfConnection) {
List<SolidFireUtil.SolidFireVag> sfVags = SolidFireUtil.getAllSolidFireVags(sfConnection.getManagementVip(), sfConnection.getManagementPort(),
sfConnection.getClusterAdminUsername(), sfConnection.getClusterAdminPassword());
if (sfVags != null) {
List<String> hostIqns = new ArrayList<String>();
// 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 List<String> getStringArrayAsLowerCaseStringList(String[] aString) {
List<String> lstLowerCaseString = new ArrayList<String>();
if (aString != null) {
for (String str : aString) {
if (str != null) {
lstLowerCaseString.add(str.toLowerCase());
}
}
}
return lstLowerCaseString;
}
// 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 disconnectVolumeFromHost(VolumeInfo volumeInfo, Host host, DataStore dataStore)
public synchronized void disconnectVolumeFromHost(VolumeInfo volumeInfo, Host host, DataStore dataStore)
{
if (volumeInfo == null || host == null || dataStore == null) {
return;
@ -377,135 +151,24 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
long clusterId = host.getClusterId();
long storagePoolId = dataStore.getId();
ClusterDetailsVO clusterDetail = _clusterDetailsDao.findDetail(clusterId, getVagKey(storagePoolId));
ClusterDetailsVO clusterDetail = _clusterDetailsDao.findDetail(clusterId, SolidFireUtil.getVagKey(storagePoolId));
String vagId = clusterDetail != null ? clusterDetail.getValue() : null;
if (vagId != null) {
List<HostVO> hosts = _hostDao.findByClusterId(clusterId);
SolidFireConnection sfConnection = getSolidFireConnection(storagePoolId);
SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, _storagePoolDetailsDao);
SolidFireUtil.SolidFireVag sfVag = SolidFireUtil.getSolidFireVag(sfConnection.getManagementVip(), sfConnection.getManagementPort(),
sfConnection.getClusterAdminUsername(), sfConnection.getClusterAdminPassword(), Long.parseLong(vagId));
SolidFireUtil.SolidFireVag sfVag = SolidFireUtil.getSolidFireVag(sfConnection, Long.parseLong(vagId));
String[] hostIqns = getNewHostIqns(sfVag.getInitiators(), getIqnsFromHosts(hosts));
long[] volumeIds = getNewVolumeIds(sfVag.getVolumeIds(), sfVolumeId, false);
String[] hostIqns = SolidFireUtil.getNewHostIqns(sfVag.getInitiators(), SolidFireUtil.getIqnsFromHosts(hosts));
long[] volumeIds = SolidFireUtil.getNewVolumeIds(sfVag.getVolumeIds(), sfVolumeId, false);
SolidFireUtil.modifySolidFireVag(sfConnection.getManagementVip(), sfConnection.getManagementPort(),
sfConnection.getClusterAdminUsername(), sfConnection.getClusterAdminPassword(), sfVag.getId(),
hostIqns, volumeIds);
SolidFireUtil.modifySolidFireVag(sfConnection, sfVag.getId(), hostIqns, volumeIds);
}
}
private 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")) {
return false;
}
}
return true;
}
private String[] getNewHostIqns(String[] currentIqns, String[] newIqns) {
List<String> lstIqns = new ArrayList<String>();
if (currentIqns != null) {
for (String currentIqn : currentIqns) {
lstIqns.add(currentIqn);
}
}
if (newIqns != null) {
for (String newIqn : newIqns) {
if (!lstIqns.contains(newIqn)) {
lstIqns.add(newIqn);
}
}
}
return lstIqns.toArray(new String[0]);
}
private long[] getNewVolumeIds(long[] volumeIds, long volumeIdToAddOrRemove, boolean add) {
if (add) {
return getNewVolumeIdsAdd(volumeIds, volumeIdToAddOrRemove);
}
return getNewVolumeIdsRemove(volumeIds, volumeIdToAddOrRemove);
}
private long[] getNewVolumeIdsAdd(long[] volumeIds, long volumeIdToAdd) {
List<Long> lstVolumeIds = new ArrayList<Long>();
if (volumeIds != null) {
for (long volumeId : volumeIds) {
lstVolumeIds.add(volumeId);
}
}
if (lstVolumeIds.contains(volumeIdToAdd)) {
return volumeIds;
}
lstVolumeIds.add(volumeIdToAdd);
return convertArray(lstVolumeIds);
}
private long[] getNewVolumeIdsRemove(long[] volumeIds, long volumeIdToRemove) {
List<Long> lstVolumeIds = new ArrayList<Long>();
if (volumeIds != null) {
for (long volumeId : volumeIds) {
lstVolumeIds.add(volumeId);
}
}
lstVolumeIds.remove(volumeIdToRemove);
return convertArray(lstVolumeIds);
}
private long[] convertArray(List<Long> items) {
if (items == null) {
return new long[0];
}
long[] outArray = new long[items.size()];
for (int i = 0; i < items.size(); i++) {
Long value = items.get(i);
outArray[i] = value;
}
return outArray;
}
private String getVagKey(long storagePoolId) {
return "sfVolumeAccessGroup_" + storagePoolId;
}
private 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<String>();
for (Host host : hosts) {
lstIqns.add(host.getStorageUrl());
}
return lstIqns.toArray(new String[0]);
}
private long getDefaultMinIops(long storagePoolId) {
StoragePoolDetailVO storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.CLUSTER_DEFAULT_MIN_IOPS);
@ -532,12 +195,7 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
return (long)(maxIops * fClusterDefaultBurstIopsPercentOfMaxIops);
}
private SolidFireUtil.SolidFireVolume createSolidFireVolume(VolumeInfo volumeInfo, SolidFireConnection sfConnection) {
String mVip = sfConnection.getManagementVip();
int mPort = sfConnection.getManagementPort();
String clusterAdminUsername = sfConnection.getClusterAdminUsername();
String clusterAdminPassword = sfConnection.getClusterAdminPassword();
private SolidFireUtil.SolidFireVolume createSolidFireVolume(SolidFireUtil.SolidFireConnection sfConnection, VolumeInfo volumeInfo) {
AccountDetailVO accountDetail = _accountDetailsDao.findDetail(volumeInfo.getAccountId(), SolidFireUtil.ACCOUNT_ID);
long sfAccountId = Long.parseLong(accountDetail.getValue());
@ -558,12 +216,10 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
long volumeSize = getVolumeSizeIncludingHypervisorSnapshotReserve(volumeInfo, _storagePoolDao.findById(storagePoolId));
long sfVolumeId = SolidFireUtil.createSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword,
getSolidFireVolumeName(volumeInfo.getName()), sfAccountId, volumeSize, true,
NumberFormat.getInstance().format(volumeInfo.getSize()),
iops.getMinIops(), iops.getMaxIops(), iops.getBurstIops());
long sfVolumeId = SolidFireUtil.createSolidFireVolume(sfConnection, SolidFireUtil.getSolidFireVolumeName(volumeInfo.getName()), sfAccountId, volumeSize, true,
NumberFormat.getInstance().format(volumeInfo.getSize()), iops.getMinIops(), iops.getMaxIops(), iops.getBurstIops());
return SolidFireUtil.getSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfVolumeId);
return SolidFireUtil.getSolidFireVolume(sfConnection, sfVolumeId);
}
@Override
@ -582,24 +238,6 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
return volumeSize;
}
private String getSolidFireVolumeName(String strCloudStackVolumeName) {
final String specialChar = "-";
StringBuilder strSolidFireVolumeName = new StringBuilder();
for (int i = 0; i < strCloudStackVolumeName.length(); i++) {
String strChar = strCloudStackVolumeName.substring(i, i + 1);
if (StringUtils.isAlphanumeric(strChar)) {
strSolidFireVolumeName.append(strChar);
} else {
strSolidFireVolumeName.append(specialChar);
}
}
return strSolidFireVolumeName.toString();
}
private static class Iops {
private final long _minIops;
private final long _maxIops;
@ -636,7 +274,7 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
}
}
private SolidFireUtil.SolidFireVolume deleteSolidFireVolume(VolumeInfo volumeInfo, SolidFireConnection sfConnection)
private SolidFireUtil.SolidFireVolume deleteSolidFireVolume(SolidFireUtil.SolidFireConnection sfConnection, VolumeInfo volumeInfo)
{
Long storagePoolId = volumeInfo.getPoolId();
@ -644,33 +282,9 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
return null; // this volume was never assigned to a storage pool, so no SAN volume should exist for it
}
String mVip = sfConnection.getManagementVip();
int mPort = sfConnection.getManagementPort();
String clusterAdminUsername = sfConnection.getClusterAdminUsername();
String clusterAdminPassword = sfConnection.getClusterAdminPassword();
long sfVolumeId = Long.parseLong(volumeInfo.getFolder());
return SolidFireUtil.deleteSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfVolumeId);
}
private String getSfAccountName(String csAccountUuid, long csAccountId) {
return "CloudStack_" + csAccountUuid + "_" + csAccountId;
}
private boolean sfAccountExists(String sfAccountName, SolidFireConnection sfConnection) {
String mVip = sfConnection.getManagementVip();
int mPort = sfConnection.getManagementPort();
String clusterAdminUsername = sfConnection.getClusterAdminUsername();
String clusterAdminPassword = sfConnection.getClusterAdminPassword();
try {
SolidFireUtil.getSolidFireAccountByName(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfAccountName);
} catch (Exception ex) {
return false;
}
return true;
return SolidFireUtil.deleteSolidFireVolume(sfConnection, sfVolumeId);
}
@Override
@ -681,18 +295,18 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
if (dataObject.getType() == DataObjectType.VOLUME) {
VolumeInfo volumeInfo = (VolumeInfo)dataObject;
AccountVO account = _accountDao.findById(volumeInfo.getAccountId());
String sfAccountName = getSfAccountName(account.getUuid(), account.getAccountId());
String sfAccountName = SolidFireUtil.getSolidFireAccountName(account.getUuid(), account.getAccountId());
long storagePoolId = dataStore.getId();
SolidFireConnection sfConnection = getSolidFireConnection(storagePoolId);
SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, _storagePoolDetailsDao);
if (!sfAccountExists(sfAccountName, sfConnection)) {
SolidFireUtil.SolidFireAccount sfAccount = createSolidFireAccount(sfAccountName, sfConnection);
if (SolidFireUtil.getSolidFireAccount(sfConnection, sfAccountName) == null) {
SolidFireUtil.SolidFireAccount sfAccount = createSolidFireAccount(sfConnection, sfAccountName);
updateCsDbWithAccountInfo(account.getId(), sfAccount);
SolidFireUtil.updateCsDbWithSolidFireAccountInfo(account.getId(), sfAccount, _accountDetailsDao);
}
SolidFireUtil.SolidFireVolume sfVolume = createSolidFireVolume(volumeInfo, sfConnection);
SolidFireUtil.SolidFireVolume sfVolume = createSolidFireVolume(sfConnection, volumeInfo);
iqn = sfVolume.getIqn();
@ -728,74 +342,20 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
callback.complete(result);
}
/*
private void deleteSolidFireAccount(long sfAccountId, SolidFireConnection sfConnection) {
String mVip = sfConnection.getManagementVip();
int mPort = sfConnection.getManagementPort();
String clusterAdminUsername = sfConnection.getClusterAdminUsername();
String clusterAdminPassword = sfConnection.getClusterAdminPassword();
List<SolidFireUtil.SolidFireVolume> sfVolumes = SolidFireUtil.getDeletedVolumes(mVip, mPort, clusterAdminUsername, clusterAdminPassword);
// if there are volumes for this account in the trash, delete them (so the account can be deleted)
if (sfVolumes != null) {
for (SolidFireUtil.SolidFireVolume sfVolume : sfVolumes) {
if (sfVolume.getAccountId() == sfAccountId) {
SolidFireUtil.purgeSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfVolume.getId());
}
}
}
SolidFireUtil.deleteSolidFireAccount(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfAccountId);
}
private boolean sfAccountHasVolume(long sfAccountId, SolidFireConnection sfConnection) {
String mVip = sfConnection.getManagementVip();
int mPort = sfConnection.getManagementPort();
String clusterAdminUsername = sfConnection.getClusterAdminUsername();
String clusterAdminPassword = sfConnection.getClusterAdminPassword();
List<SolidFireUtil.SolidFireVolume> sfVolumes =
SolidFireUtil.getSolidFireVolumesForAccountId(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfAccountId);
if (sfVolumes != null) {
for (SolidFireUtil.SolidFireVolume sfVolume : sfVolumes) {
if (sfVolume.isActive()) {
return true;
}
}
}
return false;
}
*/
@Override
public void deleteAsync(DataStore dataStore, DataObject dataObject, AsyncCompletionCallback<CommandResult> callback) {
String errMsg = null;
if (dataObject.getType() == DataObjectType.VOLUME) {
VolumeInfo volumeInfo = (VolumeInfo)dataObject;
// AccountVO account = _accountDao.findById(volumeInfo.getAccountId());
// AccountDetailVO accountDetails = _accountDetailsDao.findDetail(account.getAccountId(), SolidFireUtil.ACCOUNT_ID);
// long sfAccountId = Long.parseLong(accountDetails.getValue());
long storagePoolId = dataStore.getId();
SolidFireConnection sfConnection = getSolidFireConnection(storagePoolId);
SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, _storagePoolDetailsDao);
SolidFireUtil.SolidFireVolume sfVolume = deleteSolidFireVolume(volumeInfo, sfConnection);
SolidFireUtil.SolidFireVolume sfVolume = deleteSolidFireVolume(sfConnection, volumeInfo);
_volumeDao.deleteVolumesByInstance(volumeInfo.getId());
// if (!sfAccountHasVolume(sfAccountId, sfConnection)) {
// // delete the account from the SolidFire SAN
// deleteSolidFireAccount(sfAccountId, sfConnection);
//
// // delete the info in the account_details table
// // that's related to the SolidFire account
// _accountDetailsDao.deleteDetails(account.getAccountId());
// }
StoragePoolVO storagePool = _storagePoolDao.findById(storagePoolId);
long usedBytes = storagePool.getUsedBytes();

View File

@ -21,7 +21,6 @@ package org.apache.cloudstack.storage.datastore.lifecycle;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import javax.inject.Inject;
@ -34,7 +33,6 @@ import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreLifeCy
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreParameters;
import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
import org.apache.cloudstack.storage.datastore.util.SolidFireUtil;
import org.apache.cloudstack.storage.volume.datastore.PrimaryDataStoreHelper;
@ -61,14 +59,9 @@ public class SolidFirePrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeC
@Inject
private ResourceManager _resourceMgr;
@Inject
StorageManager _storageMgr;
private StorageManager _storageMgr;
@Inject
private StoragePoolAutomation storagePoolAutomation;
@Inject
private StoragePoolDetailsDao storagePoolDetailsDao;
private static final int DEFAULT_MANAGEMENT_PORT = 443;
private static final int DEFAULT_STORAGE_PORT = 3260;
// invoked to add primary storage that is based on the SolidFire plug-in
@Override
@ -80,10 +73,11 @@ public class SolidFirePrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeC
Long capacityBytes = (Long)dsInfos.get("capacityBytes");
Long capacityIops = (Long)dsInfos.get("capacityIops");
String tags = (String)dsInfos.get("tags");
@SuppressWarnings("unchecked")
Map<String, String> details = (Map<String, String>)dsInfos.get("details");
String storageVip = getStorageVip(url);
int storagePort = getStoragePort(url);
String storageVip = SolidFireUtil.getStorageVip(url);
int storagePort = SolidFireUtil.getStoragePort(url);
DataCenterVO zone = zoneDao.findById(zoneId);
@ -101,7 +95,7 @@ public class SolidFirePrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeC
parameters.setHost(storageVip);
parameters.setPort(storagePort);
parameters.setPath(getModifiedUrl(url));
parameters.setPath(SolidFireUtil.getModifiedUrl(url));
parameters.setType(StoragePoolType.Iscsi);
parameters.setUuid(uuid);
parameters.setZoneId(zoneId);
@ -115,14 +109,14 @@ public class SolidFirePrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeC
parameters.setTags(tags);
parameters.setDetails(details);
String managementVip = getManagementVip(url);
int managementPort = getManagementPort(url);
String managementVip = SolidFireUtil.getManagementVip(url);
int managementPort = SolidFireUtil.getManagementPort(url);
details.put(SolidFireUtil.MANAGEMENT_VIP, managementVip);
details.put(SolidFireUtil.MANAGEMENT_PORT, String.valueOf(managementPort));
String clusterAdminUsername = getValue(SolidFireUtil.CLUSTER_ADMIN_USERNAME, url);
String clusterAdminPassword = getValue(SolidFireUtil.CLUSTER_ADMIN_PASSWORD, url);
String clusterAdminUsername = SolidFireUtil.getValue(SolidFireUtil.CLUSTER_ADMIN_USERNAME, url);
String clusterAdminPassword = SolidFireUtil.getValue(SolidFireUtil.CLUSTER_ADMIN_PASSWORD, url);
details.put(SolidFireUtil.CLUSTER_ADMIN_USERNAME, clusterAdminUsername);
details.put(SolidFireUtil.CLUSTER_ADMIN_PASSWORD, clusterAdminPassword);
@ -132,7 +126,7 @@ public class SolidFirePrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeC
float fClusterDefaultBurstIopsPercentOfMaxIops = 1.5f;
try {
String clusterDefaultMinIops = getValue(SolidFireUtil.CLUSTER_DEFAULT_MIN_IOPS, url);
String clusterDefaultMinIops = SolidFireUtil.getValue(SolidFireUtil.CLUSTER_DEFAULT_MIN_IOPS, url);
if (clusterDefaultMinIops != null && clusterDefaultMinIops.trim().length() > 0) {
lClusterDefaultMinIops = Long.parseLong(clusterDefaultMinIops);
@ -144,7 +138,7 @@ public class SolidFirePrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeC
}
try {
String clusterDefaultMaxIops = getValue(SolidFireUtil.CLUSTER_DEFAULT_MAX_IOPS, url);
String clusterDefaultMaxIops = SolidFireUtil.getValue(SolidFireUtil.CLUSTER_DEFAULT_MAX_IOPS, url);
if (clusterDefaultMaxIops != null && clusterDefaultMaxIops.trim().length() > 0) {
lClusterDefaultMaxIops = Long.parseLong(clusterDefaultMaxIops);
@ -156,7 +150,7 @@ public class SolidFirePrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeC
}
try {
String clusterDefaultBurstIopsPercentOfMaxIops = getValue(SolidFireUtil.CLUSTER_DEFAULT_BURST_IOPS_PERCENT_OF_MAX_IOPS, url);
String clusterDefaultBurstIopsPercentOfMaxIops = SolidFireUtil.getValue(SolidFireUtil.CLUSTER_DEFAULT_BURST_IOPS_PERCENT_OF_MAX_IOPS, url);
if (clusterDefaultBurstIopsPercentOfMaxIops != null && clusterDefaultBurstIopsPercentOfMaxIops.trim().length() > 0) {
fClusterDefaultBurstIopsPercentOfMaxIops = Float.parseFloat(clusterDefaultBurstIopsPercentOfMaxIops);
@ -168,7 +162,7 @@ public class SolidFirePrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeC
}
if (lClusterDefaultMinIops > lClusterDefaultMaxIops) {
throw new CloudRuntimeException("The parameter '" + SolidFireUtil.CLUSTER_DEFAULT_MIN_IOPS + "' must be less than " + "or equal to the parameter '" +
throw new CloudRuntimeException("The parameter '" + SolidFireUtil.CLUSTER_DEFAULT_MIN_IOPS + "' must be less than or equal to the parameter '" +
SolidFireUtil.CLUSTER_DEFAULT_MAX_IOPS + "'.");
}
@ -184,111 +178,6 @@ public class SolidFirePrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeC
return dataStoreHelper.createPrimaryDataStore(parameters);
}
// remove the clusterAdmin and password key/value pairs
private String getModifiedUrl(String originalUrl) {
StringBuilder sb = new StringBuilder();
String delimiter = ";";
StringTokenizer st = new StringTokenizer(originalUrl, delimiter);
while (st.hasMoreElements()) {
String token = st.nextElement().toString().toUpperCase();
if (token.startsWith(SolidFireUtil.MANAGEMENT_VIP.toUpperCase()) || token.startsWith(SolidFireUtil.STORAGE_VIP.toUpperCase())) {
sb.append(token).append(delimiter);
}
}
String modifiedUrl = sb.toString();
int lastIndexOf = modifiedUrl.lastIndexOf(delimiter);
if (lastIndexOf == (modifiedUrl.length() - delimiter.length())) {
return modifiedUrl.substring(0, lastIndexOf);
}
return modifiedUrl;
}
private String getManagementVip(String url) {
return getVip(SolidFireUtil.MANAGEMENT_VIP, url);
}
private String getStorageVip(String url) {
return getVip(SolidFireUtil.STORAGE_VIP, url);
}
private int getManagementPort(String url) {
return getPort(SolidFireUtil.MANAGEMENT_VIP, url, DEFAULT_MANAGEMENT_PORT);
}
private int getStoragePort(String url) {
return getPort(SolidFireUtil.STORAGE_VIP, url, DEFAULT_STORAGE_PORT);
}
private String getVip(String keyToMatch, String url) {
String delimiter = ":";
String storageVip = getValue(keyToMatch, url);
int index = storageVip.indexOf(delimiter);
if (index != -1) {
return storageVip.substring(0, index);
}
return storageVip;
}
private int getPort(String keyToMatch, String url, int defaultPortNumber) {
String delimiter = ":";
String storageVip = getValue(keyToMatch, url);
int index = storageVip.indexOf(delimiter);
int portNumber = defaultPortNumber;
if (index != -1) {
String port = storageVip.substring(index + delimiter.length());
try {
portNumber = Integer.parseInt(port);
} catch (NumberFormatException ex) {
throw new IllegalArgumentException("Invalid URL format (port is not an integer)");
}
}
return portNumber;
}
private String getValue(String keyToMatch, String url) {
String delimiter1 = ";";
String delimiter2 = "=";
StringTokenizer st = new StringTokenizer(url, delimiter1);
while (st.hasMoreElements()) {
String token = st.nextElement().toString();
int index = token.indexOf(delimiter2);
if (index == -1) {
throw new RuntimeException("Invalid URL format");
}
String key = token.substring(0, index);
if (key.equalsIgnoreCase(keyToMatch)) {
String valueToReturn = token.substring(index + delimiter2.length());
return valueToReturn;
}
}
throw new RuntimeException("Key not found in URL");
}
// do not implement this method for SolidFire's plug-in
@Override
public boolean attachHost(DataStore store, HostScope scope, StoragePoolInfo existingInfo) {

View File

@ -0,0 +1,548 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.cloudstack.storage.datastore.lifecycle;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import javax.inject.Inject;
import org.apache.log4j.Logger;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.engine.subsystem.api.storage.ClusterScope;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
import org.apache.cloudstack.engine.subsystem.api.storage.HostScope;
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreInfo;
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreLifeCycle;
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreParameters;
import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO;
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.cloudstack.storage.datastore.util.SolidFireUtil;
import org.apache.cloudstack.storage.volume.datastore.PrimaryDataStoreHelper;
import com.cloud.template.TemplateManager;
import com.cloud.user.AccountDetailsDao;
import com.cloud.user.AccountVO;
import com.cloud.agent.AgentManager;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.CreateStoragePoolCommand;
import com.cloud.agent.api.DeleteStoragePoolCommand;
import com.cloud.agent.api.StoragePoolInfo;
import com.cloud.dc.ClusterDetailsDao;
import com.cloud.dc.ClusterVO;
import com.cloud.dc.dao.ClusterDao;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.host.dao.HostDao;
import com.cloud.host.Host;
import com.cloud.host.HostVO;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.resource.ResourceManager;
import com.cloud.storage.Storage.StoragePoolType;
import com.cloud.storage.dao.StoragePoolHostDao;
import com.cloud.storage.StorageManager;
import com.cloud.storage.StoragePool;
import com.cloud.storage.StoragePoolAutomation;
import com.cloud.storage.StoragePoolHostVO;
import com.cloud.storage.VMTemplateStoragePoolVO;
import com.cloud.user.Account;
import com.cloud.user.dao.AccountDao;
import com.cloud.utils.exception.CloudRuntimeException;
public class SolidFireSharedPrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeCycle {
private static final Logger s_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 protected TemplateManager _tmpltMgr;
// invoked to add primary storage that is based on the SolidFire plug-in
@Override
public DataStore initialize(Map<String, Object> dsInfos) {
final String CAPACITY_IOPS = "capacityIops";
String url = (String)dsInfos.get("url");
Long zoneId = (Long)dsInfos.get("zoneId");
Long podId = (Long)dsInfos.get("podId");
Long clusterId = (Long)dsInfos.get("clusterId");
String storagePoolName = (String)dsInfos.get("name");
String providerName = (String)dsInfos.get("providerName");
Long capacityBytes = (Long)dsInfos.get("capacityBytes");
Long capacityIops = (Long)dsInfos.get(CAPACITY_IOPS);
String tags = (String)dsInfos.get("tags");
@SuppressWarnings("unchecked")
Map<String, String> details = (Map<String, String>)dsInfos.get("details");
if (podId == null) {
throw new CloudRuntimeException("The Pod ID must be specified.");
}
if (clusterId == null) {
throw new CloudRuntimeException("The Cluster ID must be specified.");
}
String storageVip = SolidFireUtil.getStorageVip(url);
int storagePort = SolidFireUtil.getStoragePort(url);
if (capacityBytes == null || capacityBytes <= 0) {
throw new IllegalArgumentException("'capacityBytes' must be present and greater than 0.");
}
if (capacityIops == null || capacityIops <= 0) {
throw new IllegalArgumentException("'capacityIops' must be present and greater than 0.");
}
HypervisorType hypervisorType = getHypervisorTypeForCluster(clusterId);
if (!isSupportedHypervisorType(hypervisorType)) {
throw new CloudRuntimeException(hypervisorType + " is not a supported hypervisor type.");
}
String datacenter = SolidFireUtil.getValue(SolidFireUtil.DATACENTER, url, false);
if (HypervisorType.VMware.equals(hypervisorType) && datacenter == null) {
throw new CloudRuntimeException("'Datacenter' must be set for hypervisor type of " + HypervisorType.VMware);
}
PrimaryDataStoreParameters parameters = new PrimaryDataStoreParameters();
parameters.setType(getStorageType(hypervisorType));
parameters.setZoneId(zoneId);
parameters.setPodId(podId);
parameters.setClusterId(clusterId);
parameters.setName(storagePoolName);
parameters.setProviderName(providerName);
parameters.setManaged(false);
parameters.setCapacityBytes(capacityBytes);
parameters.setUsedBytes(0);
parameters.setCapacityIops(capacityIops);
parameters.setHypervisorType(hypervisorType);
parameters.setTags(tags);
parameters.setDetails(details);
String managementVip = SolidFireUtil.getManagementVip(url);
int managementPort = SolidFireUtil.getManagementPort(url);
details.put(SolidFireUtil.MANAGEMENT_VIP, managementVip);
details.put(SolidFireUtil.MANAGEMENT_PORT, String.valueOf(managementPort));
String clusterAdminUsername = SolidFireUtil.getValue(SolidFireUtil.CLUSTER_ADMIN_USERNAME, url);
String clusterAdminPassword = SolidFireUtil.getValue(SolidFireUtil.CLUSTER_ADMIN_PASSWORD, url);
details.put(SolidFireUtil.CLUSTER_ADMIN_USERNAME, clusterAdminUsername);
details.put(SolidFireUtil.CLUSTER_ADMIN_PASSWORD, clusterAdminPassword);
long lMinIops = 100;
long lMaxIops = 15000;
long lBurstIops = 15000;
try {
String minIops = SolidFireUtil.getValue(SolidFireUtil.MIN_IOPS, url);
if (minIops != null && minIops.trim().length() > 0) {
lMinIops = Long.parseLong(minIops);
}
} catch (Exception ex) {
}
try {
String maxIops = SolidFireUtil.getValue(SolidFireUtil.MAX_IOPS, url);
if (maxIops != null && maxIops.trim().length() > 0) {
lMaxIops = Long.parseLong(maxIops);
}
} catch (Exception ex) {
}
try {
String burstIops = SolidFireUtil.getValue(SolidFireUtil.BURST_IOPS, url);
if (burstIops != null && burstIops.trim().length() > 0) {
lBurstIops = Long.parseLong(burstIops);
}
} catch (Exception ex) {
}
if (lMinIops > lMaxIops) {
throw new CloudRuntimeException("The parameter '" + SolidFireUtil.MIN_IOPS + "' must be less than or equal to the parameter '" + SolidFireUtil.MAX_IOPS + "'.");
}
if (lMaxIops > lBurstIops) {
throw new CloudRuntimeException("The parameter '" + SolidFireUtil.MAX_IOPS + "' must be less than or equal to the parameter '" + SolidFireUtil.BURST_IOPS + "'.");
}
if (lMinIops != capacityIops) {
throw new CloudRuntimeException("The parameter '" + CAPACITY_IOPS + "' must be equal to the parameter '" + SolidFireUtil.MIN_IOPS + "'.");
}
details.put(SolidFireUtil.MIN_IOPS, String.valueOf(lMinIops));
details.put(SolidFireUtil.MAX_IOPS, String.valueOf(lMaxIops));
details.put(SolidFireUtil.BURST_IOPS, String.valueOf(lBurstIops));
SolidFireUtil.SolidFireConnection sfConnection = new SolidFireUtil.SolidFireConnection(managementVip, managementPort, clusterAdminUsername, clusterAdminPassword);
SolidFireUtil.SolidFireVolume sfVolume = createSolidFireVolume(sfConnection, storagePoolName, capacityBytes, lMinIops, lMaxIops, lBurstIops);
String iqn = sfVolume.getIqn();
details.put(SolidFireUtil.VOLUME_ID, String.valueOf(sfVolume.getId()));
parameters.setUuid(iqn);
if (HypervisorType.VMware.equals(hypervisorType)) {
String datastore = iqn.replace("/", "_");
String path = "/" + datacenter + "/" + datastore;
parameters.setHost("VMFS datastore: " + path);
parameters.setPort(0);
parameters.setPath(path);
details.put(SolidFireUtil.DATASTORE_NAME, datastore);
details.put(SolidFireUtil.IQN, iqn);
details.put(SolidFireUtil.STORAGE_VIP, storageVip);
details.put(SolidFireUtil.STORAGE_PORT, String.valueOf(storagePort));
}
else {
parameters.setHost(storageVip);
parameters.setPort(storagePort);
parameters.setPath(iqn);
}
// this adds a row in the cloud.storage_pool table for this SolidFire volume
DataStore 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
try {
List<HostVO> hosts = _hostDao.findByClusterId(clusterId);
SolidFireUtil.placeVolumeInVolumeAccessGroup(sfConnection, sfVolume.getId(), dataStore.getId(), hosts, _clusterDetailsDao);
} catch (Exception ex) {
_primaryDataStoreDao.expunge(dataStore.getId());
throw new CloudRuntimeException(ex.getMessage());
}
return dataStore;
}
private HypervisorType getHypervisorTypeForCluster(long clusterId) {
ClusterVO cluster = _clusterDao.findById(clusterId);
if (cluster == null) {
throw new CloudRuntimeException("Cluster ID '" + clusterId + "' was not found in the database.");
}
return cluster.getHypervisorType();
}
private StoragePoolType getStorageType(HypervisorType hypervisorType) {
if (HypervisorType.XenServer.equals(hypervisorType)) {
return StoragePoolType.IscsiLUN;
}
if (HypervisorType.VMware.equals(hypervisorType)) {
return StoragePoolType.VMFS;
}
throw new CloudRuntimeException("The 'hypervisor' parameter must be '" + HypervisorType.XenServer + "' or '" + HypervisorType.VMware + "'.");
}
private SolidFireUtil.SolidFireVolume createSolidFireVolume(SolidFireUtil.SolidFireConnection sfConnection,
String volumeName, long volumeSize, long minIops, long maxIops, long burstIops) {
try {
Account csAccount = CallContext.current().getCallingAccount();
long csAccountId = csAccount.getId();
AccountVO accountVo = _accountDao.findById(csAccountId);
String sfAccountName = SolidFireUtil.getSolidFireAccountName(accountVo.getUuid(), csAccountId);
SolidFireUtil.SolidFireAccount sfAccount = SolidFireUtil.getSolidFireAccount(sfConnection, sfAccountName);
if (sfAccount == null) {
long accountNumber = SolidFireUtil.createSolidFireAccount(sfConnection, sfAccountName);
sfAccount = SolidFireUtil.getSolidFireAccountById(sfConnection, accountNumber);
SolidFireUtil.updateCsDbWithSolidFireAccountInfo(csAccountId, sfAccount, _accountDetailsDao);
}
long sfVolumeId = SolidFireUtil.createSolidFireVolume(sfConnection, SolidFireUtil.getSolidFireVolumeName(volumeName), sfAccount.getId(), volumeSize,
true, NumberFormat.getInstance().format(volumeSize), minIops, maxIops, burstIops);
SolidFireUtil.SolidFireVolume sfVolume = SolidFireUtil.getSolidFireVolume(sfConnection, sfVolumeId);
return sfVolume;
} catch (Throwable e) {
throw new CloudRuntimeException("Failed to create a SolidFire volume: " + e.toString());
}
}
@Override
public boolean attachHost(DataStore store, HostScope scope, StoragePoolInfo existingInfo) {
return true;
}
@Override
public boolean attachCluster(DataStore store, ClusterScope scope) {
PrimaryDataStoreInfo primaryDataStoreInfo = (PrimaryDataStoreInfo)store;
// check if there is at least one host up in this cluster
List<HostVO> allHosts = _resourceMgr.listAllUpAndEnabledHosts(Host.Type.Routing, primaryDataStoreInfo.getClusterId(),
primaryDataStoreInfo.getPodId(), primaryDataStoreInfo.getDataCenterId());
if (allHosts.isEmpty()) {
_primaryDataStoreDao.expunge(primaryDataStoreInfo.getId());
throw new CloudRuntimeException("No host up to associate a storage pool with in cluster " + primaryDataStoreInfo.getClusterId());
}
boolean success = false;
for (HostVO host : allHosts) {
success = createStoragePool(host, primaryDataStoreInfo);
if (success) {
break;
}
}
if (!success) {
throw new CloudRuntimeException("Unable to create storage in cluster " + primaryDataStoreInfo.getClusterId());
}
List<HostVO> poolHosts = new ArrayList<HostVO>();
for (HostVO host : allHosts) {
try {
_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);
}
}
if (poolHosts.isEmpty()) {
s_logger.warn("No host can access storage pool '" + primaryDataStoreInfo + "' on cluster '" + primaryDataStoreInfo.getClusterId() + "'.");
_primaryDataStoreDao.expunge(primaryDataStoreInfo.getId());
throw new CloudRuntimeException("Failed to access storage pool");
}
_primaryDataStoreHelper.attachCluster(store);
return true;
}
private boolean createStoragePool(HostVO host, StoragePool storagePool) {
long hostId = host.getId();
HypervisorType hypervisorType = host.getHypervisorType();
CreateStoragePoolCommand cmd = new CreateStoragePoolCommand(true, storagePool);
if (HypervisorType.VMware.equals(hypervisorType)) {
cmd.setCreateDatastore(true);
Map<String, String> details = new HashMap<String, String>();
StoragePoolDetailVO storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.DATASTORE_NAME);
details.put(CreateStoragePoolCommand.DATASTORE_NAME, storagePoolDetail.getValue());
storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.IQN);
details.put(CreateStoragePoolCommand.IQN, storagePoolDetail.getValue());
storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.STORAGE_VIP);
details.put(CreateStoragePoolCommand.STORAGE_HOST, storagePoolDetail.getValue());
storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.STORAGE_PORT);
details.put(CreateStoragePoolCommand.STORAGE_PORT, storagePoolDetail.getValue());
cmd.setDetails(details);
}
Answer answer = _agentMgr.easySend(hostId, cmd);
if (answer != null && answer.getResult()) {
return true;
} else {
_primaryDataStoreDao.expunge(storagePool.getId());
String msg = "";
if (answer != null) {
msg = "Cannot create storage pool through host '" + hostId + "' due to the following: " + answer.getDetails();
} else {
msg = "Cannot create storage pool through host '" + hostId + "' due to CreateStoragePoolCommand returns null";
}
s_logger.warn(msg);
throw new CloudRuntimeException(msg);
}
}
@Override
public boolean attachZone(DataStore dataStore, ZoneScope scope, HypervisorType hypervisorType) {
return true;
}
@Override
public boolean maintain(DataStore dataStore) {
_storagePoolAutomation.maintain(dataStore);
_primaryDataStoreHelper.maintain(dataStore);
return true;
}
@Override
public boolean cancelMaintain(DataStore store) {
_primaryDataStoreHelper.cancelMaintain(store);
_storagePoolAutomation.cancelMaintain(store);
return true;
}
// 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());
HypervisorType hypervisorType = null;
if (hostPoolRecords.size() > 0 ) {
hypervisorType = getHypervisorType(hostPoolRecords.get(0).getHostId());
}
if (!isSupportedHypervisorType(hypervisorType)) {
throw new CloudRuntimeException(hypervisorType + " is not a supported hypervisor type.");
}
StoragePool storagePool = (StoragePool)dataStore;
StoragePoolVO storagePoolVO = _primaryDataStoreDao.findById(storagePool.getId());
List<VMTemplateStoragePoolVO> unusedTemplatesInPool = _tmpltMgr.getUnusedTemplatesInPool(storagePoolVO);
for (VMTemplateStoragePoolVO templatePoolVO : unusedTemplatesInPool) {
_tmpltMgr.evictTemplateFromStoragePool(templatePoolVO);
}
for (StoragePoolHostVO host : hostPoolRecords) {
DeleteStoragePoolCommand deleteCmd = new DeleteStoragePoolCommand(storagePool);
if (HypervisorType.VMware.equals(hypervisorType)) {
deleteCmd.setRemoveDatastore(true);
Map<String, String> details = new HashMap<String, String>();
StoragePoolDetailVO storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.DATASTORE_NAME);
details.put(DeleteStoragePoolCommand.DATASTORE_NAME, storagePoolDetail.getValue());
storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.IQN);
details.put(DeleteStoragePoolCommand.IQN, storagePoolDetail.getValue());
storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.STORAGE_VIP);
details.put(DeleteStoragePoolCommand.STORAGE_HOST, storagePoolDetail.getValue());
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);
if (answer != null && answer.getResult()) {
s_logger.info("Successfully deleted storage pool using Host ID " + host.getHostId());
break;
}
else {
s_logger.error("Failed to delete storage pool using Host ID " + host.getHostId() + ": " + answer.getResult());
}
}
deleteSolidFireVolume(storagePool.getId());
return _primaryDataStoreHelper.deletePrimaryDataStore(dataStore);
}
private void deleteSolidFireVolume(long storagePoolId) {
SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, _storagePoolDetailsDao);
long sfVolumeId = getVolumeId(storagePoolId);
SolidFireUtil.deleteSolidFireVolume(sfConnection, sfVolumeId);
}
private long getVolumeId(long storagePoolId) {
StoragePoolDetailVO storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.VOLUME_ID);
String volumeId = storagePoolDetail.getValue();
return Long.parseLong(volumeId);
}
private static boolean isSupportedHypervisorType(HypervisorType hypervisorType) {
return HypervisorType.XenServer.equals(hypervisorType) || HypervisorType.VMware.equals(hypervisorType);
}
private HypervisorType getHypervisorType(long hostId) {
HostVO host = _hostDao.findById(hostId);
if (host != null) {
return host.getHypervisorType();
}
return HypervisorType.None;
}
/* (non-Javadoc)
* @see org.apache.cloudstack.engine.subsystem.api.storage.DataStoreLifeCycle#migrateToObjectStore(org.apache.cloudstack.engine.subsystem.api.storage.DataStore)
*/
@Override
public boolean migrateToObjectStore(DataStore store) {
return false;
}
}

View File

@ -0,0 +1,92 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.cloudstack.storage.datastore.provider;
import javax.inject.Inject;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
import org.apache.cloudstack.engine.subsystem.api.storage.HypervisorHostListener;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.log4j.Logger;
import com.cloud.agent.AgentManager;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.ModifyStoragePoolAnswer;
import com.cloud.agent.api.ModifyStoragePoolCommand;
import com.cloud.alert.AlertManager;
import com.cloud.storage.DataStoreRole;
import com.cloud.storage.StoragePool;
import com.cloud.storage.StoragePoolHostVO;
import com.cloud.storage.dao.StoragePoolHostDao;
import com.cloud.utils.exception.CloudRuntimeException;
public class SolidFireSharedHostListener implements HypervisorHostListener {
private static final Logger s_logger = Logger.getLogger(DefaultHostListener.class);
@Inject private AgentManager agentMgr;
@Inject private DataStoreManager dataStoreMgr;
@Inject private AlertManager alertMgr;
@Inject private StoragePoolHostDao storagePoolHostDao;
@Inject private PrimaryDataStoreDao primaryStoreDao;
@Override
public boolean hostConnect(long hostId, long storagePoolId) {
StoragePoolHostVO storagePoolHost = storagePoolHostDao.findByPoolHost(storagePoolId, hostId);
if (storagePoolHost == null) {
storagePoolHost = new StoragePoolHostVO(storagePoolId, hostId, "");
storagePoolHostDao.persist(storagePoolHost);
}
StoragePool storagePool = (StoragePool)dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary);
ModifyStoragePoolCommand cmd = new ModifyStoragePoolCommand(true, storagePool);
Answer answer = agentMgr.easySend(hostId, cmd);
if (answer == null) {
throw new CloudRuntimeException("Unable to get an answer to the modify storage pool command for storage pool: " + storagePool.getId());
}
if (!answer.getResult()) {
String msg = "Unable to attach storage pool " + storagePoolId + " to the host " + hostId;
alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_HOST, storagePool.getDataCenterId(), storagePool.getPodId(), msg, msg);
throw new CloudRuntimeException(msg);
}
assert (answer instanceof ModifyStoragePoolAnswer) : "ModifyStoragePoolAnswer not returned from ModifyStoragePoolCommand; Storage pool = " +
storagePool.getId() + "; Host=" + hostId;
s_logger.info("Connection established between storage pool " + storagePool + " and host + " + hostId);
return true;
}
@Override
public boolean hostDisconnected(long hostId, long storagePoolId) {
StoragePoolHostVO storagePoolHost = storagePoolHostDao.findByPoolHost(storagePoolId, hostId);
if (storagePoolHost != null) {
storagePoolHostDao.deleteStoragePoolHostDetails(hostId, storagePoolId);
}
return true;
}
}

View File

@ -0,0 +1,83 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.cloudstack.storage.datastore.provider;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.springframework.stereotype.Component;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreLifeCycle;
import org.apache.cloudstack.engine.subsystem.api.storage.HypervisorHostListener;
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver;
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreProvider;
import org.apache.cloudstack.storage.datastore.driver.SolidFireSharedPrimaryDataStoreDriver;
import org.apache.cloudstack.storage.datastore.lifecycle.SolidFireSharedPrimaryDataStoreLifeCycle;
import org.apache.cloudstack.storage.datastore.util.SolidFireUtil;
import com.cloud.utils.component.ComponentContext;
@Component
public class SolidFireSharedPrimaryDataStoreProvider implements PrimaryDataStoreProvider {
private DataStoreLifeCycle lifecycle;
private PrimaryDataStoreDriver driver;
private HypervisorHostListener listener;
SolidFireSharedPrimaryDataStoreProvider() {
}
@Override
public String getName() {
return SolidFireUtil.SHARED_PROVIDER_NAME;
}
@Override
public DataStoreLifeCycle getDataStoreLifeCycle() {
return lifecycle;
}
@Override
public PrimaryDataStoreDriver getDataStoreDriver() {
return driver;
}
@Override
public HypervisorHostListener getHostListener() {
return listener;
}
@Override
public boolean configure(Map<String, Object> params) {
lifecycle = ComponentContext.inject(SolidFireSharedPrimaryDataStoreLifeCycle.class);
driver = ComponentContext.inject(SolidFireSharedPrimaryDataStoreDriver.class);
listener = ComponentContext.inject(SolidFireSharedHostListener.class);
return true;
}
@Override
public Set<DataStoreProviderType> getTypes() {
Set<DataStoreProviderType> types = new HashSet<DataStoreProviderType>();
types.add(DataStoreProviderType.PRIMARY);
return types;
}
}

View File

@ -29,11 +29,16 @@ import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import java.util.UUID;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
@ -49,10 +54,17 @@ import org.apache.http.impl.conn.BasicClientConnectionManager;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.cloud.dc.ClusterDetailsDao;
import com.cloud.dc.ClusterDetailsVO;
import com.cloud.host.Host;
import com.cloud.host.HostVO;
import com.cloud.user.AccountDetailVO;
import com.cloud.user.AccountDetailsDao;
import com.cloud.utils.exception.CloudRuntimeException;
public class SolidFireUtil {
public static final String PROVIDER_NAME = "SolidFire";
public static final String SHARED_PROVIDER_NAME = "SolidFireShared";
public static final String MANAGEMENT_VIP = "mVip";
public static final String STORAGE_VIP = "sVip";
@ -63,11 +75,18 @@ public class SolidFireUtil {
public static final String CLUSTER_ADMIN_USERNAME = "clusterAdminUsername";
public static final String CLUSTER_ADMIN_PASSWORD = "clusterAdminPassword";
// these three variables should only be used for the SolidFire plug-in with the name SolidFireUtil.PROVIDER_NAME
public static final String CLUSTER_DEFAULT_MIN_IOPS = "clusterDefaultMinIops";
public static final String CLUSTER_DEFAULT_MAX_IOPS = "clusterDefaultMaxIops";
public static final String CLUSTER_DEFAULT_BURST_IOPS_PERCENT_OF_MAX_IOPS = "clusterDefaultBurstIopsPercentOfMaxIops";
// these three variables should only be used for the SolidFire plug-in with the name SolidFireUtil.SHARED_PROVIDER_NAME
public static final String MIN_IOPS = "minIops";
public static final String MAX_IOPS = "maxIops";
public static final String BURST_IOPS = "burstIops";
public static final String ACCOUNT_ID = "accountId";
public static final String VOLUME_ID = "volumeId";
public static final String CHAP_INITIATOR_USERNAME = "chapInitiatorUsername";
public static final String CHAP_INITIATOR_SECRET = "chapInitiatorSecret";
@ -75,18 +94,323 @@ public class SolidFireUtil {
public static final String CHAP_TARGET_USERNAME = "chapTargetUsername";
public static final String CHAP_TARGET_SECRET = "chapTargetSecret";
public static long createSolidFireVolume(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword,
String strSfVolumeName, long lSfAccountId, long lTotalSize, boolean bEnable512e, final String strCloudStackVolumeSize,
long lMinIops, long lMaxIops, long lBurstIops)
public static final String DATACENTER = "datacenter";
public static final String DATASTORE_NAME = "datastoreName";
public static final String IQN = "iqn";
private static final int DEFAULT_MANAGEMENT_PORT = 443;
private static final int DEFAULT_STORAGE_PORT = 3260;
public static class SolidFireConnection {
private final String _managementVip;
private final int _managementPort;
private final String _clusterAdminUsername;
private final String _clusterAdminPassword;
public SolidFireConnection(String managementVip, int managementPort, String clusterAdminUsername, String clusterAdminPassword) {
_managementVip = managementVip;
_managementPort = managementPort;
_clusterAdminUsername = clusterAdminUsername;
_clusterAdminPassword = clusterAdminPassword;
}
public String getManagementVip() {
return _managementVip;
}
public int getManagementPort() {
return _managementPort;
}
public String getClusterAdminUsername() {
return _clusterAdminUsername;
}
public String getClusterAdminPassword() {
return _clusterAdminPassword;
}
}
public static SolidFireConnection getSolidFireConnection(long storagePoolId, StoragePoolDetailsDao storagePoolDetailsDao) {
StoragePoolDetailVO storagePoolDetail = storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.MANAGEMENT_VIP);
String mVip = storagePoolDetail.getValue();
storagePoolDetail = storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.MANAGEMENT_PORT);
int mPort = Integer.parseInt(storagePoolDetail.getValue());
storagePoolDetail = storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.CLUSTER_ADMIN_USERNAME);
String clusterAdminUsername = storagePoolDetail.getValue();
storagePoolDetail = storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.CLUSTER_ADMIN_PASSWORD);
String clusterAdminPassword = storagePoolDetail.getValue();
return new SolidFireConnection(mVip, mPort, clusterAdminUsername, clusterAdminPassword);
}
public static String getSolidFireAccountName(String csAccountUuid, long csAccountId) {
return "CloudStack_" + csAccountUuid + "_" + csAccountId;
}
public static void updateCsDbWithSolidFireAccountInfo(long csAccountId, SolidFireUtil.SolidFireAccount sfAccount,
AccountDetailsDao accountDetailsDao) {
AccountDetailVO accountDetail = new AccountDetailVO(csAccountId,
SolidFireUtil.ACCOUNT_ID,
String.valueOf(sfAccount.getId()));
accountDetailsDao.persist(accountDetail);
accountDetail = new AccountDetailVO(csAccountId,
SolidFireUtil.CHAP_INITIATOR_USERNAME,
String.valueOf(sfAccount.getName()));
accountDetailsDao.persist(accountDetail);
accountDetail = new AccountDetailVO(csAccountId,
SolidFireUtil.CHAP_INITIATOR_SECRET,
String.valueOf(sfAccount.getInitiatorSecret()));
accountDetailsDao.persist(accountDetail);
accountDetail = new AccountDetailVO(csAccountId,
SolidFireUtil.CHAP_TARGET_USERNAME,
sfAccount.getName());
accountDetailsDao.persist(accountDetail);
accountDetail = new AccountDetailVO(csAccountId,
SolidFireUtil.CHAP_TARGET_SECRET,
sfAccount.getTargetSecret());
accountDetailsDao.persist(accountDetail);
}
public static SolidFireAccount getSolidFireAccount(SolidFireConnection sfConnection, String sfAccountName) {
try {
return getSolidFireAccountByName(sfConnection, sfAccountName);
} catch (Exception ex) {
return null;
}
}
public static long placeVolumeInVolumeAccessGroup(SolidFireConnection sfConnection, long sfVolumeId, long storagePoolId, List<HostVO> hosts,
ClusterDetailsDao clusterDetailsDao) {
if (hosts == null || hosts.isEmpty()) {
throw new CloudRuntimeException("There must be at least one host in the cluster.");
}
long lVagId;
try {
lVagId = SolidFireUtil.createSolidFireVag(sfConnection, "CloudStack-" + UUID.randomUUID().toString(),
SolidFireUtil.getIqnsFromHosts(hosts), new long[] { sfVolumeId });
}
catch (Exception ex) {
String iqnInVagAlready = "Exceeded maximum number of Volume Access Groups per initiator";
if (!ex.getMessage().contains(iqnInVagAlready)) {
throw new CloudRuntimeException(ex.getMessage());
}
// 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.modifySolidFireVag(sfConnection, lVagId,
sfVag.getInitiators(), volumeIds);
}
ClusterDetailsVO clusterDetail = new ClusterDetailsVO(hosts.get(0).getClusterId(), getVagKey(storagePoolId), String.valueOf(lVagId));
clusterDetailsDao.persist(clusterDetail);
return lVagId;
}
public 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")) {
return false;
}
}
return true;
}
public static String[] getNewHostIqns(String[] currentIqns, String[] newIqns) {
List<String> lstIqns = new ArrayList<String>();
if (currentIqns != null) {
for (String currentIqn : currentIqns) {
lstIqns.add(currentIqn);
}
}
if (newIqns != null) {
for (String newIqn : newIqns) {
if (!lstIqns.contains(newIqn)) {
lstIqns.add(newIqn);
}
}
}
return lstIqns.toArray(new String[0]);
}
public static long[] getNewVolumeIds(long[] volumeIds, long volumeIdToAddOrRemove, boolean add) {
if (add) {
return getNewVolumeIdsAdd(volumeIds, volumeIdToAddOrRemove);
}
return getNewVolumeIdsRemove(volumeIds, volumeIdToAddOrRemove);
}
private static long[] getNewVolumeIdsAdd(long[] volumeIds, long volumeIdToAdd) {
List<Long> lstVolumeIds = new ArrayList<Long>();
if (volumeIds != null) {
for (long volumeId : volumeIds) {
lstVolumeIds.add(volumeId);
}
}
if (lstVolumeIds.contains(volumeIdToAdd)) {
return volumeIds;
}
lstVolumeIds.add(volumeIdToAdd);
return convertArray(lstVolumeIds);
}
private static long[] getNewVolumeIdsRemove(long[] volumeIds, long volumeIdToRemove) {
List<Long> lstVolumeIds = new ArrayList<Long>();
if (volumeIds != null) {
for (long volumeId : volumeIds) {
lstVolumeIds.add(volumeId);
}
}
lstVolumeIds.remove(volumeIdToRemove);
return convertArray(lstVolumeIds);
}
private static long[] convertArray(List<Long> items) {
if (items == null) {
return new long[0];
}
long[] outArray = new long[items.size()];
for (int i = 0; i < items.size(); i++) {
Long value = items.get(i);
outArray[i] = value;
}
return outArray;
}
public static String getVagKey(long storagePoolId) {
return "sfVolumeAccessGroup_" + storagePoolId;
}
public 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<String>();
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.getAllSolidFireVags(sfConnection);
if (sfVags != null) {
List<String> hostIqns = new ArrayList<String>();
// 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<String>();
if (aString != null) {
for (String str : aString) {
if (str != null) {
lstLowerCaseString.add(str.toLowerCase());
}
}
}
return lstLowerCaseString;
}
public static String getSolidFireVolumeName(String strCloudStackVolumeName) {
final String specialChar = "-";
StringBuilder strSolidFireVolumeName = new StringBuilder();
for (int i = 0; i < strCloudStackVolumeName.length(); i++) {
String strChar = strCloudStackVolumeName.substring(i, i + 1);
if (StringUtils.isAlphanumeric(strChar)) {
strSolidFireVolumeName.append(strChar);
} else {
strSolidFireVolumeName.append(specialChar);
}
}
return strSolidFireVolumeName.toString();
}
public static long createSolidFireVolume(SolidFireConnection sfConnection, String strSfVolumeName, long lSfAccountId, long lTotalSize,
boolean bEnable512e, final String strCloudStackVolumeSize, long lMinIops, long lMaxIops, long lBurstIops)
{
final Gson gson = new GsonBuilder().create();
VolumeToCreate volumeToCreate =
new VolumeToCreate(strSfVolumeName, lSfAccountId, lTotalSize, bEnable512e, strCloudStackVolumeSize, lMinIops, lMaxIops, lBurstIops);
VolumeToCreate volumeToCreate = new VolumeToCreate(strSfVolumeName, lSfAccountId, lTotalSize, bEnable512e, strCloudStackVolumeSize, lMinIops, lMaxIops, lBurstIops);
String strVolumeToCreateJson = gson.toJson(volumeToCreate);
String strVolumeCreateResultJson = executeJsonRpc(strVolumeToCreateJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword);
String strVolumeCreateResultJson = executeJsonRpc(sfConnection, strVolumeToCreateJson);
VolumeCreateResult volumeCreateResult = gson.fromJson(strVolumeCreateResultJson, VolumeCreateResult.class);
@ -95,7 +419,7 @@ public class SolidFireUtil {
return volumeCreateResult.result.volumeID;
}
public static SolidFireVolume getSolidFireVolume(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lVolumeId)
public static SolidFireVolume getSolidFireVolume(SolidFireConnection sfConnection, long lVolumeId)
{
final Gson gson = new GsonBuilder().create();
@ -103,7 +427,7 @@ public class SolidFireUtil {
String strVolumeToGetJson = gson.toJson(volumeToGet);
String strVolumeGetResultJson = executeJsonRpc(strVolumeToGetJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword);
String strVolumeGetResultJson = executeJsonRpc(sfConnection, strVolumeToGetJson);
VolumeGetResult volumeGetResult = gson.fromJson(strVolumeGetResultJson, VolumeGetResult.class);
@ -118,14 +442,14 @@ public class SolidFireUtil {
return new SolidFireVolume(lVolumeId, strVolumeName, strVolumeIqn, lAccountId, strVolumeStatus, lTotalSize);
}
public static List<SolidFireVolume> getSolidFireVolumesForAccountId(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lAccountId) {
public static List<SolidFireVolume> getSolidFireVolumesForAccountId(SolidFireConnection sfConnection, long lAccountId) {
final Gson gson = new GsonBuilder().create();
VolumesToGetForAccount volumesToGetForAccount = new VolumesToGetForAccount(lAccountId);
String strVolumesToGetForAccountJson = gson.toJson(volumesToGetForAccount);
String strVolumesGetForAccountResultJson = executeJsonRpc(strVolumesToGetForAccountJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword);
String strVolumesGetForAccountResultJson = executeJsonRpc(sfConnection, strVolumesToGetForAccountJson);
VolumeGetResult volumeGetResult = gson.fromJson(strVolumesGetForAccountResultJson, VolumeGetResult.class);
@ -140,7 +464,7 @@ public class SolidFireUtil {
return sfVolumes;
}
public static List<SolidFireVolume> getDeletedVolumes(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword)
public static List<SolidFireVolume> getDeletedVolumes(SolidFireConnection sfConnection)
{
final Gson gson = new GsonBuilder().create();
@ -148,8 +472,7 @@ public class SolidFireUtil {
String strListDeletedVolumesJson = gson.toJson(listDeletedVolumes);
String strListDeletedVolumesResultJson = executeJsonRpc(strListDeletedVolumesJson, strSfMvip, iSfPort,
strSfAdmin, strSfPassword);
String strListDeletedVolumesResultJson = executeJsonRpc(sfConnection, strListDeletedVolumesJson);
VolumeGetResult volumeGetResult = gson.fromJson(strListDeletedVolumesResultJson, VolumeGetResult.class);
@ -164,9 +487,9 @@ public class SolidFireUtil {
return deletedVolumes;
}
public static SolidFireVolume deleteSolidFireVolume(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lVolumeId)
public static SolidFireVolume deleteSolidFireVolume(SolidFireConnection sfConnection, long lVolumeId)
{
SolidFireVolume sfVolume = getSolidFireVolume(strSfMvip, iSfPort, strSfAdmin, strSfPassword, lVolumeId);
SolidFireVolume sfVolume = getSolidFireVolume(sfConnection, lVolumeId);
final Gson gson = new GsonBuilder().create();
@ -174,12 +497,12 @@ public class SolidFireUtil {
String strVolumeToDeleteJson = gson.toJson(volumeToDelete);
executeJsonRpc(strVolumeToDeleteJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword);
executeJsonRpc(sfConnection, strVolumeToDeleteJson);
return sfVolume;
}
public static void purgeSolidFireVolume(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lVolumeId)
public static void purgeSolidFireVolume(SolidFireConnection sfConnection, long lVolumeId)
{
final Gson gson = new GsonBuilder().create();
@ -187,7 +510,7 @@ public class SolidFireUtil {
String strVolumeToPurgeJson = gson.toJson(volumeToPurge);
executeJsonRpc(strVolumeToPurgeJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword);
executeJsonRpc(sfConnection, strVolumeToPurgeJson);
}
private static final String ACTIVE = "active";
@ -267,7 +590,7 @@ public class SolidFireUtil {
}
}
public static long createSolidFireAccount(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, String strAccountName)
public static long createSolidFireAccount(SolidFireConnection sfConnection, String strAccountName)
{
final Gson gson = new GsonBuilder().create();
@ -275,7 +598,7 @@ public class SolidFireUtil {
String strAccountAddJson = gson.toJson(accountToAdd);
String strAccountAddResultJson = executeJsonRpc(strAccountAddJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword);
String strAccountAddResultJson = executeJsonRpc(sfConnection, strAccountAddJson);
AccountAddResult accountAddResult = gson.fromJson(strAccountAddResultJson, AccountAddResult.class);
@ -284,7 +607,7 @@ public class SolidFireUtil {
return accountAddResult.result.accountID;
}
public static SolidFireAccount getSolidFireAccountById(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lSfAccountId)
public static SolidFireAccount getSolidFireAccountById(SolidFireConnection sfConnection, long lSfAccountId)
{
final Gson gson = new GsonBuilder().create();
@ -292,7 +615,7 @@ public class SolidFireUtil {
String strAccountToGetByIdJson = gson.toJson(accountToGetById);
String strAccountGetByIdResultJson = executeJsonRpc(strAccountToGetByIdJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword);
String strAccountGetByIdResultJson = executeJsonRpc(sfConnection, strAccountToGetByIdJson);
AccountGetResult accountGetByIdResult = gson.fromJson(strAccountGetByIdResultJson, AccountGetResult.class);
@ -305,7 +628,7 @@ public class SolidFireUtil {
return new SolidFireAccount(lSfAccountId, strSfAccountName, strSfAccountInitiatorSecret, strSfAccountTargetSecret);
}
public static SolidFireAccount getSolidFireAccountByName(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, String strSfAccountName)
public static SolidFireAccount getSolidFireAccountByName(SolidFireConnection sfConnection, String strSfAccountName)
{
final Gson gson = new GsonBuilder().create();
@ -313,7 +636,7 @@ public class SolidFireUtil {
String strAccountToGetByNameJson = gson.toJson(accountToGetByName);
String strAccountGetByNameResultJson = executeJsonRpc(strAccountToGetByNameJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword);
String strAccountGetByNameResultJson = executeJsonRpc(sfConnection, strAccountToGetByNameJson);
AccountGetResult accountGetByNameResult = gson.fromJson(strAccountGetByNameResultJson, AccountGetResult.class);
@ -326,7 +649,7 @@ public class SolidFireUtil {
return new SolidFireAccount(lSfAccountId, strSfAccountName, strSfAccountInitiatorSecret, strSfAccountTargetSecret);
}
public static void deleteSolidFireAccount(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lAccountId)
public static void deleteSolidFireAccount(SolidFireConnection sfConnection, long lAccountId)
{
final Gson gson = new GsonBuilder().create();
@ -334,7 +657,7 @@ public class SolidFireUtil {
String strAccountToRemoveJson = gson.toJson(accountToRemove);
executeJsonRpc(strAccountToRemoveJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword);
executeJsonRpc(sfConnection, strAccountToRemoveJson);
}
public static class SolidFireAccount
@ -399,7 +722,7 @@ public class SolidFireUtil {
}
}
public static long createSolidFireVag(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, String strVagName,
public static long createSolidFireVag(SolidFireConnection sfConnection, String strVagName,
String[] iqns, long[] volumeIds)
{
final Gson gson = new GsonBuilder().create();
@ -408,7 +731,7 @@ public class SolidFireUtil {
String strVagCreateJson = gson.toJson(vagToCreate);
String strVagCreateResultJson = executeJsonRpc(strVagCreateJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword);
String strVagCreateResultJson = executeJsonRpc(sfConnection, strVagCreateJson);
VagCreateResult vagCreateResult = gson.fromJson(strVagCreateResultJson, VagCreateResult.class);
@ -417,8 +740,7 @@ public class SolidFireUtil {
return vagCreateResult.result.volumeAccessGroupID;
}
public static void modifySolidFireVag(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lVagId,
String[] iqns, long[] volumeIds)
public static void modifySolidFireVag(SolidFireConnection sfConnection, long lVagId, String[] iqns, long[] volumeIds)
{
final Gson gson = new GsonBuilder().create();
@ -426,10 +748,10 @@ public class SolidFireUtil {
String strVagModifyJson = gson.toJson(vagToModify);
executeJsonRpc(strVagModifyJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword);
executeJsonRpc(sfConnection, strVagModifyJson);
}
public static SolidFireVag getSolidFireVag(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lVagId)
public static SolidFireVag getSolidFireVag(SolidFireConnection sfConnection, long lVagId)
{
final Gson gson = new GsonBuilder().create();
@ -437,7 +759,7 @@ public class SolidFireUtil {
String strVagToGetJson = gson.toJson(vagToGet);
String strVagGetResultJson = executeJsonRpc(strVagToGetJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword);
String strVagGetResultJson = executeJsonRpc(sfConnection, strVagToGetJson);
VagGetResult vagGetResult = gson.fromJson(strVagGetResultJson, VagGetResult.class);
@ -449,7 +771,7 @@ public class SolidFireUtil {
return new SolidFireVag(lVagId, vagIqns, vagVolumeIds);
}
public static List<SolidFireVag> getAllSolidFireVags(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword)
public static List<SolidFireVag> getAllSolidFireVags(SolidFireConnection sfConnection)
{
final Gson gson = new GsonBuilder().create();
@ -457,7 +779,7 @@ public class SolidFireUtil {
String strAllVagsJson = gson.toJson(allVags);
String strAllVagsGetResultJson = executeJsonRpc(strAllVagsJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword);
String strAllVagsGetResultJson = executeJsonRpc(sfConnection, strAllVagsJson);
VagGetResult allVagsGetResult = gson.fromJson(strAllVagsGetResultJson, VagGetResult.class);
@ -476,7 +798,7 @@ public class SolidFireUtil {
return lstSolidFireVags;
}
public static void deleteSolidFireVag(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lVagId)
public static void deleteSolidFireVag(SolidFireConnection sfConnection, long lVagId)
{
final Gson gson = new GsonBuilder().create();
@ -484,7 +806,7 @@ public class SolidFireUtil {
String strVagToDeleteJson = gson.toJson(vagToDelete);
executeJsonRpc(strVagToDeleteJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword);
executeJsonRpc(sfConnection, strVagToDeleteJson);
}
public static class SolidFireVag
@ -996,7 +1318,7 @@ public class SolidFireUtil {
}
}
private static String executeJsonRpc(String strJsonToExecute, String strMvip, int iPort, String strAdmin, String strPassword) {
private static String executeJsonRpc(SolidFireConnection sfConnection, String strJsonToExecute) {
DefaultHttpClient httpClient = null;
StringBuilder sb = new StringBuilder();
@ -1005,11 +1327,11 @@ public class SolidFireUtil {
input.setContentType("application/json");
httpClient = getHttpClient(iPort);
httpClient = getHttpClient(sfConnection.getManagementPort());
URI uri = new URI("https://" + strMvip + ":" + iPort + "/json-rpc/5.0");
URI uri = new URI("https://" + sfConnection.getManagementVip() + ":" + sfConnection.getManagementPort() + "/json-rpc/5.0");
AuthScope authScope = new AuthScope(uri.getHost(), uri.getPort(), AuthScope.ANY_SCHEME);
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(strAdmin, strPassword);
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(sfConnection.getClusterAdminUsername(), sfConnection.getClusterAdminPassword());
httpClient.getCredentialsProvider().setCredentials(authScope, credentials);
@ -1132,4 +1454,119 @@ public class SolidFireUtil {
throw new CloudRuntimeException("Could not determine the volume IDs of the volume access group for volume access group ID of " + lVagId + ".");
}
// used to parse the "url" parameter when creating primary storage that's based on the SolidFire plug-in with the
// name SolidFireUtil.PROVIDER_NAME (as opposed to the SolidFire plug-in with the name SolidFireUtil.SHARED_PROVIDER_NAME)
// return a String instance that contains at most the MVIP and SVIP info
public static String getModifiedUrl(String originalUrl) {
StringBuilder sb = new StringBuilder();
String delimiter = ";";
StringTokenizer st = new StringTokenizer(originalUrl, delimiter);
while (st.hasMoreElements()) {
String token = st.nextElement().toString().toUpperCase();
if (token.startsWith(SolidFireUtil.MANAGEMENT_VIP.toUpperCase()) || token.startsWith(SolidFireUtil.STORAGE_VIP.toUpperCase())) {
sb.append(token).append(delimiter);
}
}
String modifiedUrl = sb.toString();
int lastIndexOf = modifiedUrl.lastIndexOf(delimiter);
if (lastIndexOf == (modifiedUrl.length() - delimiter.length())) {
return modifiedUrl.substring(0, lastIndexOf);
}
return modifiedUrl;
}
public static String getManagementVip(String url) {
return getVip(SolidFireUtil.MANAGEMENT_VIP, url);
}
public static String getStorageVip(String url) {
return getVip(SolidFireUtil.STORAGE_VIP, url);
}
public static int getManagementPort(String url) {
return getPort(SolidFireUtil.MANAGEMENT_VIP, url, DEFAULT_MANAGEMENT_PORT);
}
public static int getStoragePort(String url) {
return getPort(SolidFireUtil.STORAGE_VIP, url, DEFAULT_STORAGE_PORT);
}
private static String getVip(String keyToMatch, String url) {
String delimiter = ":";
String storageVip = getValue(keyToMatch, url);
int index = storageVip.indexOf(delimiter);
if (index != -1) {
return storageVip.substring(0, index);
}
return storageVip;
}
private static int getPort(String keyToMatch, String url, int defaultPortNumber) {
String delimiter = ":";
String storageVip = getValue(keyToMatch, url);
int index = storageVip.indexOf(delimiter);
int portNumber = defaultPortNumber;
if (index != -1) {
String port = storageVip.substring(index + delimiter.length());
try {
portNumber = Integer.parseInt(port);
} catch (NumberFormatException ex) {
throw new IllegalArgumentException("Invalid URL format (port is not an integer)");
}
}
return portNumber;
}
public static String getValue(String keyToMatch, String url) {
return getValue(keyToMatch, url, true);
}
public static String getValue(String keyToMatch, String url, boolean throwExceptionIfNotFound) {
String delimiter1 = ";";
String delimiter2 = "=";
StringTokenizer st = new StringTokenizer(url, delimiter1);
while (st.hasMoreElements()) {
String token = st.nextElement().toString();
int index = token.indexOf(delimiter2);
if (index == -1) {
throw new RuntimeException("Invalid URL format");
}
String key = token.substring(0, index);
if (key.equalsIgnoreCase(keyToMatch)) {
String valueToReturn = token.substring(index + delimiter2.length());
return valueToReturn;
}
}
if (throwExceptionIfNotFound) {
throw new RuntimeException("Key not found in URL");
}
return null;
}
}

View File

@ -699,7 +699,7 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost {
ManagedObjectReference morDs = oc.getObj();
String name = (String)VmwareHelper.getPropValue(oc, "name");
if (!name.startsWith("-iqn.")) {
if (!name.startsWith("-iqn.") && !name.startsWith("_iqn.")) {
dsList.add(new Pair<ManagedObjectReference, String>(morDs, name));
}
}