diff --git a/core/src/com/cloud/agent/api/CreateStoragePoolCommand.java b/core/src/com/cloud/agent/api/CreateStoragePoolCommand.java index 891ea2c4a27..f2f742ac94f 100644 --- a/core/src/com/cloud/agent/api/CreateStoragePoolCommand.java +++ b/core/src/com/cloud/agent/api/CreateStoragePoolCommand.java @@ -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 _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 details) { + _details = details; + } + + public Map getDetails() { + return _details; + } } diff --git a/core/src/com/cloud/agent/api/DeleteStoragePoolCommand.java b/core/src/com/cloud/agent/api/DeleteStoragePoolCommand.java index f80accbf127..dae5678bd65 100644 --- a/core/src/com/cloud/agent/api/DeleteStoragePoolCommand.java +++ b/core/src/com/cloud/agent/api/DeleteStoragePoolCommand.java @@ -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 _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 details) { + _details = details; + } + + public Map getDetails() { + return _details; } @Override public boolean executeInSequence() { return false; } - - public String getLocalPath() { - return localPath; - } - } diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java index 52421fa4406..8dc278495a9 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -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); } } diff --git a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java index 7bc5bd35cd3..77ee4ce06bd 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java +++ b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java @@ -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 iqns) throws Exception { diff --git a/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/XenServerStorageProcessor.java b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/XenServerStorageProcessor.java index 9c86fbed82d..e96ebf46222 100644 --- a/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/XenServerStorageProcessor.java +++ b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/XenServerStorageProcessor.java @@ -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 deviceConfig = new HashMap(); - 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 srs = SR.getByNameLabel(conn, pool.getUuid()); - for (SR sr : srs) { - if (!SRType.LVMOISCSI.equals(sr.getType(conn))) { - continue; - } - Set pbds = sr.getPBDs(conn); - if (pbds.isEmpty()) { - continue; - } - PBD pbd = pbds.iterator().next(); - Map 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 smConfig = new HashMap(); - 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(""); - boolean found = false; - for (int i = 1; i < lun.length; i++) { - int blunindex = lun[i].indexOf("") + 7; - int elunindex = lun[i].indexOf(""); - String ilun = lun[i].substring(blunindex, elunindex); - ilun = ilun.trim(); - if (ilun.equals(lunid)) { - int bscsiindex = lun[i].indexOf("") + 8; - int escsiindex = lun[i].indexOf(""); - 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("") != -1) { - pooluuid = result.substring(result.indexOf("") + 6, result.indexOf("")).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; diff --git a/plugins/storage/volume/solidfire/pom.xml b/plugins/storage/volume/solidfire/pom.xml index c3cd11f1cdf..180392514d0 100644 --- a/plugins/storage/volume/solidfire/pom.xml +++ b/plugins/storage/volume/solidfire/pom.xml @@ -20,6 +20,11 @@ ../../../pom.xml + + org.apache.cloudstack + cloud-plugin-storage-volume-default + ${project.version} + org.apache.cloudstack cloud-engine-storage-volume diff --git a/plugins/storage/volume/solidfire/resources/META-INF/cloudstack/storage-volume-solidfire/spring-storage-volume-solidfire-context.xml b/plugins/storage/volume/solidfire/resources/META-INF/cloudstack/storage-volume-solidfire/spring-storage-volume-solidfire-context.xml index a83e3cab7e3..df32f1ece08 100644 --- a/plugins/storage/volume/solidfire/resources/META-INF/cloudstack/storage-volume-solidfire/spring-storage-volume-solidfire-context.xml +++ b/plugins/storage/volume/solidfire/resources/META-INF/cloudstack/storage-volume-solidfire/spring-storage-volume-solidfire-context.xml @@ -29,5 +29,7 @@ - + + diff --git a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidFireSharedPrimaryDataStoreDriver.java b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidFireSharedPrimaryDataStoreDriver.java new file mode 100644 index 00000000000..a16408e308a --- /dev/null +++ b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidFireSharedPrimaryDataStoreDriver.java @@ -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 callback) { + throw new UnsupportedOperationException(); + } + + @Override + public void resize(DataObject data, AsyncCompletionCallback callback) { + throw new UnsupportedOperationException(); + } + + @Override + public void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback callback) { + throw new UnsupportedOperationException(); + } + + @Override + public void revertSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback callback) { + throw new UnsupportedOperationException(); + } +} diff --git a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidfirePrimaryDataStoreDriver.java b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidfirePrimaryDataStoreDriver.java index 014413d3452..766e1f884ed 100644 --- a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidfirePrimaryDataStoreDriver.java +++ b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidfirePrimaryDataStoreDriver.java @@ -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 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 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 hosts, SolidFireConnection sfConnection) { - List sfVags = SolidFireUtil.getAllSolidFireVags(sfConnection.getManagementVip(), sfConnection.getManagementPort(), - sfConnection.getClusterAdminUsername(), sfConnection.getClusterAdminPassword()); - - if (sfVags != null) { - List hostIqns = new ArrayList(); - - // where the method we're in is called, hosts should not be null - for (HostVO host : hosts) { - // where the method we're in is called, host.getStorageUrl() should not be null (it actually should start with "iqn") - hostIqns.add(host.getStorageUrl().toLowerCase()); - } - - for (SolidFireUtil.SolidFireVag sfVag : sfVags) { - List lstInitiators = getStringArrayAsLowerCaseStringList(sfVag.getInitiators()); - - // lstInitiators should not be returned from getStringArrayAsLowerCaseStringList as null - if (lstInitiators.containsAll(hostIqns)) { - return sfVag; - } - } - } - - throw new CloudRuntimeException("Unable to locate the appropriate SolidFire Volume Access Group"); - } - - private List getStringArrayAsLowerCaseStringList(String[] aString) { - List lstLowerCaseString = new ArrayList(); - - 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 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 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 lstIqns = new ArrayList(); - - 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 lstVolumeIds = new ArrayList(); - - 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 lstVolumeIds = new ArrayList(); - - if (volumeIds != null) { - for (long volumeId : volumeIds) { - lstVolumeIds.add(volumeId); - } - } - - lstVolumeIds.remove(volumeIdToRemove); - - return convertArray(lstVolumeIds); - } - - private long[] convertArray(List 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 hosts) { - if (hosts == null || hosts.size() == 0) { - throw new CloudRuntimeException("There do not appear to be any hosts in this cluster."); - } - - List lstIqns = new ArrayList(); - - for (Host host : hosts) { - lstIqns.add(host.getStorageUrl()); - } - - return lstIqns.toArray(new String[0]); - } - 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 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 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 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(); diff --git a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/lifecycle/SolidFirePrimaryDataStoreLifeCycle.java b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/lifecycle/SolidFirePrimaryDataStoreLifeCycle.java index d30374f045d..7fc343622f3 100644 --- a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/lifecycle/SolidFirePrimaryDataStoreLifeCycle.java +++ b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/lifecycle/SolidFirePrimaryDataStoreLifeCycle.java @@ -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 details = (Map)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) { diff --git a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/lifecycle/SolidFireSharedPrimaryDataStoreLifeCycle.java b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/lifecycle/SolidFireSharedPrimaryDataStoreLifeCycle.java new file mode 100644 index 00000000000..ec2a52fc39e --- /dev/null +++ b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/lifecycle/SolidFireSharedPrimaryDataStoreLifeCycle.java @@ -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 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 details = (Map)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 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 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 poolHosts = new ArrayList(); + + 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 details = new HashMap(); + + 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 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 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 details = new HashMap(); + + 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; + } + +} diff --git a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/provider/SolidFireSharedHostListener.java b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/provider/SolidFireSharedHostListener.java new file mode 100644 index 00000000000..9881d1d179b --- /dev/null +++ b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/provider/SolidFireSharedHostListener.java @@ -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; + } +} diff --git a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/provider/SolidFireSharedPrimaryDataStoreProvider.java b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/provider/SolidFireSharedPrimaryDataStoreProvider.java new file mode 100644 index 00000000000..d5b82a16a0c --- /dev/null +++ b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/provider/SolidFireSharedPrimaryDataStoreProvider.java @@ -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 params) { + lifecycle = ComponentContext.inject(SolidFireSharedPrimaryDataStoreLifeCycle.class); + driver = ComponentContext.inject(SolidFireSharedPrimaryDataStoreDriver.class); + listener = ComponentContext.inject(SolidFireSharedHostListener.class); + + return true; + } + + @Override + public Set getTypes() { + Set types = new HashSet(); + + types.add(DataStoreProviderType.PRIMARY); + + return types; + } +} diff --git a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java index 3c457ba8067..a27917b1c8c 100644 --- a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java +++ b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java @@ -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 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 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 lstIqns = new ArrayList(); + + 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 lstVolumeIds = new ArrayList(); + + 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 lstVolumeIds = new ArrayList(); + + if (volumeIds != null) { + for (long volumeId : volumeIds) { + lstVolumeIds.add(volumeId); + } + } + + lstVolumeIds.remove(volumeIdToRemove); + + return convertArray(lstVolumeIds); + } + + private static long[] convertArray(List 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 hosts) { + if (hosts == null || hosts.size() == 0) { + throw new CloudRuntimeException("There do not appear to be any hosts in this cluster."); + } + + List lstIqns = new ArrayList(); + + for (Host host : hosts) { + lstIqns.add(host.getStorageUrl()); + } + + return lstIqns.toArray(new String[0]); + } + + // this method takes in a collection of hosts and tries to find an existing VAG that has all of them in it + // if successful, the VAG is returned; else, a CloudRuntimeException is thrown and this issue should be corrected by an admin + private static SolidFireUtil.SolidFireVag getCompatibleVag(SolidFireConnection sfConnection, List hosts) { + List sfVags = SolidFireUtil.getAllSolidFireVags(sfConnection); + + if (sfVags != null) { + List hostIqns = new ArrayList(); + + // where the method we're in is called, hosts should not be null + for (HostVO host : hosts) { + // where the method we're in is called, host.getStorageUrl() should not be null (it actually should start with "iqn") + hostIqns.add(host.getStorageUrl().toLowerCase()); + } + + for (SolidFireUtil.SolidFireVag sfVag : sfVags) { + List lstInitiators = getStringArrayAsLowerCaseStringList(sfVag.getInitiators()); + + // lstInitiators should not be returned from getStringArrayAsLowerCaseStringList as null + if (lstInitiators.containsAll(hostIqns)) { + return sfVag; + } + } + } + + throw new CloudRuntimeException("Unable to locate the appropriate SolidFire Volume Access Group"); + } + + private static List getStringArrayAsLowerCaseStringList(String[] aString) { + List lstLowerCaseString = new ArrayList(); + + 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 getSolidFireVolumesForAccountId(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lAccountId) { + public static List 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 getDeletedVolumes(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword) + public static List 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 getAllSolidFireVags(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword) + public static List 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; + } } diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java index 298f560388c..b5844e9a98b 100755 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java @@ -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(morDs, name)); } }