Repository: cloudstack Updated Branches: refs/heads/master 3f5b3a16d -> 82b702dc9
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/provider/SolidFireSharedHostListener.java ---------------------------------------------------------------------- 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 index e505cd0..f88041a 100644 --- 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 @@ -52,24 +52,24 @@ 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(SolidFireSharedHostListener.class); - - @Inject private AgentManager _agentMgr; - @Inject private AlertManager _alertMgr; - @Inject private ClusterDao _clusterDao; - @Inject private ClusterDetailsDao _clusterDetailsDao; - @Inject private DataStoreManager _dataStoreMgr; - @Inject private HostDao _hostDao; - @Inject private PrimaryDataStoreDao _storagePoolDao; - @Inject private StoragePoolHostDao _storagePoolHostDao; - @Inject private StoragePoolDetailsDao _storagePoolDetailsDao; + private static final Logger LOGGER = Logger.getLogger(SolidFireSharedHostListener.class); + + @Inject private AgentManager agentMgr; + @Inject private AlertManager alertMgr; + @Inject private ClusterDao clusterDao; + @Inject private ClusterDetailsDao clusterDetailsDao; + @Inject private DataStoreManager dataStoreMgr; + @Inject private HostDao hostDao; + @Inject private PrimaryDataStoreDao storagePoolDao; + @Inject private StoragePoolHostDao storagePoolHostDao; + @Inject private StoragePoolDetailsDao storagePoolDetailsDao; @Override public boolean hostAdded(long hostId) { - HostVO host = _hostDao.findById(hostId); + HostVO host = hostDao.findById(hostId); SolidFireUtil.hostAddedToOrRemovedFromCluster(hostId, host.getClusterId(), true, SolidFireUtil.SHARED_PROVIDER_NAME, - _clusterDao, _clusterDetailsDao, _storagePoolDao, _storagePoolDetailsDao, _hostDao); + clusterDao, clusterDetailsDao, storagePoolDao, storagePoolDetailsDao, hostDao); handleVMware(hostId, true); @@ -78,37 +78,37 @@ public class SolidFireSharedHostListener implements HypervisorHostListener { @Override public boolean hostConnect(long hostId, long storagePoolId) { - StoragePool storagePool = (StoragePool)_dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary); + StoragePool storagePool = (StoragePool) dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary); ModifyStoragePoolCommand cmd = new ModifyStoragePoolCommand(true, storagePool); ModifyStoragePoolAnswer answer = sendModifyStoragePoolCommand(cmd, storagePool, hostId); - StoragePoolHostVO storagePoolHost = _storagePoolHostDao.findByPoolHost(storagePoolId, hostId); + StoragePoolHostVO storagePoolHost = storagePoolHostDao.findByPoolHost(storagePoolId, hostId); if (storagePoolHost != null) { storagePoolHost.setLocalPath(answer.getPoolInfo().getLocalPath().replaceAll("//", "/")); } else { storagePoolHost = new StoragePoolHostVO(storagePoolId, hostId, answer.getPoolInfo().getLocalPath().replaceAll("//", "/")); - _storagePoolHostDao.persist(storagePoolHost); + storagePoolHostDao.persist(storagePoolHost); } - StoragePoolVO storagePoolVO = _storagePoolDao.findById(storagePoolId); + StoragePoolVO storagePoolVO = storagePoolDao.findById(storagePoolId); storagePoolVO.setCapacityBytes(answer.getPoolInfo().getCapacityBytes()); storagePoolVO.setUsedBytes(answer.getPoolInfo().getCapacityBytes() - answer.getPoolInfo().getAvailableBytes()); - _storagePoolDao.update(storagePoolId, storagePoolVO); + storagePoolDao.update(storagePoolId, storagePoolVO); return true; } @Override public boolean hostDisconnected(long hostId, long storagePoolId) { - StoragePoolHostVO storagePoolHost = _storagePoolHostDao.findByPoolHost(storagePoolId, hostId); + StoragePoolHostVO storagePoolHost = storagePoolHostDao.findByPoolHost(storagePoolId, hostId); if (storagePoolHost != null) { - _storagePoolHostDao.deleteStoragePoolHostDetails(hostId, storagePoolId); + storagePoolHostDao.deleteStoragePoolHostDetails(hostId, storagePoolId); } return true; @@ -124,16 +124,16 @@ public class SolidFireSharedHostListener implements HypervisorHostListener { @Override public boolean hostRemoved(long hostId, long clusterId) { SolidFireUtil.hostAddedToOrRemovedFromCluster(hostId, clusterId, false, SolidFireUtil.SHARED_PROVIDER_NAME, - _clusterDao, _clusterDetailsDao, _storagePoolDao, _storagePoolDetailsDao, _hostDao); + clusterDao, clusterDetailsDao, storagePoolDao, storagePoolDetailsDao, hostDao); return true; } private void handleVMware(long hostId, boolean add) { - HostVO host = _hostDao.findById(hostId); + HostVO host = hostDao.findById(hostId); if (HypervisorType.VMware.equals(host.getHypervisorType())) { - List<StoragePoolVO> storagePools = _storagePoolDao.findPoolsByProvider(SolidFireUtil.SHARED_PROVIDER_NAME); + List<StoragePoolVO> storagePools = storagePoolDao.findPoolsByProvider(SolidFireUtil.SHARED_PROVIDER_NAME); if (storagePools != null && storagePools.size() > 0) { List<Map<String, String>> targets = new ArrayList<>(); @@ -142,15 +142,15 @@ public class SolidFireSharedHostListener implements HypervisorHostListener { if (storagePool.getClusterId().equals(host.getClusterId())) { long storagePoolId = storagePool.getId(); - StoragePoolDetailVO storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.IQN); + StoragePoolDetailVO storagePoolDetail = storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.IQN); String iqn = storagePoolDetail.getValue(); - storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.STORAGE_VIP); + storagePoolDetail = storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.STORAGE_VIP); String sVip = storagePoolDetail.getValue(); - storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.STORAGE_PORT); + storagePoolDetail = storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.STORAGE_PORT); String sPort = storagePoolDetail.getValue(); @@ -177,7 +177,7 @@ public class SolidFireSharedHostListener implements HypervisorHostListener { } private void sendModifyTargetsCommand(ModifyTargetsCommand cmd, long hostId) { - Answer answer = _agentMgr.easySend(hostId, cmd); + Answer answer = agentMgr.easySend(hostId, cmd); if (answer == null) { throw new CloudRuntimeException("Unable to get an answer to the modify targets command"); @@ -186,16 +186,16 @@ public class SolidFireSharedHostListener implements HypervisorHostListener { if (!answer.getResult()) { String msg = "Unable to modify targets on the following host: " + hostId; - HostVO host = _hostDao.findById(hostId); + HostVO host = hostDao.findById(hostId); - _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_HOST, host.getDataCenterId(), host.getPodId(), msg, msg); + alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_HOST, host.getDataCenterId(), host.getPodId(), msg, msg); throw new CloudRuntimeException(msg); } } private ModifyStoragePoolAnswer sendModifyStoragePoolCommand(ModifyStoragePoolCommand cmd, StoragePool storagePool, long hostId) { - Answer answer = _agentMgr.easySend(hostId, cmd); + Answer answer = agentMgr.easySend(hostId, cmd); if (answer == null) { throw new CloudRuntimeException("Unable to get an answer to the modify storage pool command for storage pool: " + storagePool.getId()); @@ -204,7 +204,7 @@ public class SolidFireSharedHostListener implements HypervisorHostListener { if (!answer.getResult()) { String msg = "Unable to attach storage pool " + storagePool.getId() + " to the host " + hostId; - _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_HOST, storagePool.getDataCenterId(), storagePool.getPodId(), msg, msg); + alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_HOST, storagePool.getDataCenterId(), storagePool.getPodId(), msg, msg); throw new CloudRuntimeException(msg); } @@ -212,7 +212,7 @@ public class SolidFireSharedHostListener implements HypervisorHostListener { 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); + LOGGER.info("Connection established between storage pool " + storagePool + " and host " + hostId); return (ModifyStoragePoolAnswer)answer; } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java ---------------------------------------------------------------------- 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 7268e72..a9c1227 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 @@ -30,6 +30,7 @@ import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; @@ -55,6 +56,7 @@ import org.apache.log4j.Logger; import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import com.google.gson.JsonObject; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO; @@ -104,6 +106,15 @@ public class SolidFireUtil { public static final String ACCOUNT_ID = "accountId"; public static final String VOLUME_ID = "volumeId"; + public static final String TEMP_VOLUME_ID = "tempVolumeId"; + public static final String SNAPSHOT_ID = "snapshotId"; + + public static final String CloudStackVolumeId = "CloudStackVolumeId"; + public static final String CloudStackVolumeSize = "CloudStackVolumeSize"; + public static final String CloudStackSnapshotId = "CloudStackSnapshotId"; + public static final String CloudStackSnapshotSize = "CloudStackSnapshotSize"; + public static final String CloudStackTemplateId = "CloudStackTemplateId"; + public static final String CloudStackTemplateSize = "CloudStackTemplateSize"; public static final String VOLUME_SIZE = "sfVolumeSize"; @@ -562,13 +573,44 @@ public class SolidFireUtil { } public static long createSolidFireVolume(SolidFireConnection sfConnection, String strSfVolumeName, long lSfAccountId, long lTotalSize, - boolean bEnable512e, String strCloudStackVolumeSize, long minIops, long maxIops, long burstIops) + boolean bEnable512e, Map<String, String> mapAttributes, long minIops, long maxIops, long burstIops) { - final Gson gson = new GsonBuilder().create(); + JsonObject volumeToCreate = new JsonObject(); + + volumeToCreate.addProperty("method", "CreateVolume"); + + JsonObject params = new JsonObject(); + + volumeToCreate.add("params", params); + + params.addProperty("name", strSfVolumeName); + params.addProperty("accountID", lSfAccountId); + params.addProperty("totalSize", lTotalSize); + params.addProperty("enable512e", bEnable512e); + + JsonObject qos = new JsonObject(); + + params.add("qos", qos); + + qos.addProperty("minIOPS", minIops); + qos.addProperty("maxIOPS", maxIops); + qos.addProperty("burstIOPS", burstIops); + + if (mapAttributes != null && mapAttributes.size() > 0) { + JsonObject attributes = new JsonObject(); + + params.add("attributes", attributes); + + Iterator<Map.Entry<String, String>> itr = mapAttributes.entrySet().iterator(); + + while (itr.hasNext()) { + Map.Entry<String, String> pair = itr.next(); + + attributes.addProperty(pair.getKey(), pair.getValue()); + } + } - Object volumeToCreate = strCloudStackVolumeSize != null && strCloudStackVolumeSize.trim().length() > 0 ? - new VolumeToCreateWithCloudStackVolumeSize(strSfVolumeName, lSfAccountId, lTotalSize, bEnable512e, strCloudStackVolumeSize, minIops, maxIops, burstIops) : - new VolumeToCreate(strSfVolumeName, lSfAccountId, lTotalSize, bEnable512e, minIops, maxIops, burstIops); + final Gson gson = new GsonBuilder().create(); String strVolumeToCreateJson = gson.toJson(volumeToCreate); @@ -581,14 +623,46 @@ public class SolidFireUtil { return volumeCreateResult.result.volumeID; } - public static void modifySolidFireVolume(SolidFireConnection sfConnection, long volumeId, long totalSize, String strCloudStackVolumeSize, + public static void modifySolidFireVolume(SolidFireConnection sfConnection, long volumeId, Long totalSize, Map<String, String> mapAttributes, long minIops, long maxIops, long burstIops) { - final Gson gson = new GsonBuilder().create(); + JsonObject volumeToModify = new JsonObject(); + + volumeToModify.addProperty("method", "ModifyVolume"); + + JsonObject params = new JsonObject(); + + volumeToModify.add("params", params); + + params.addProperty("volumeID", volumeId); + + if (totalSize != null) { + params.addProperty("totalSize", totalSize); + } + + JsonObject qos = new JsonObject(); + + params.add("qos", qos); + + qos.addProperty("minIOPS", minIops); + qos.addProperty("maxIOPS", maxIops); + qos.addProperty("burstIOPS", burstIops); + + if (mapAttributes != null && mapAttributes.size() > 0) { + JsonObject attributes = new JsonObject(); + + params.add("attributes", attributes); + + Iterator<Map.Entry<String, String>> itr = mapAttributes.entrySet().iterator(); - Object volumeToModify = strCloudStackVolumeSize != null && strCloudStackVolumeSize.trim().length() > 0 ? - new VolumeToModifyWithCloudStackVolumeSize(volumeId, totalSize, strCloudStackVolumeSize, minIops, maxIops, burstIops) : - new VolumeToModify(volumeId, totalSize, minIops, maxIops, burstIops); + while (itr.hasNext()) { + Map.Entry<String, String> pair = itr.next(); + + attributes.addProperty(pair.getKey(), pair.getValue()); + } + } + + final Gson gson = new GsonBuilder().create(); String strVolumeToModifyJson = gson.toJson(volumeToModify); @@ -687,7 +761,7 @@ public class SolidFireUtil { executeJsonRpc(sfConnection, strVolumeToDeleteJson); } - public static void purgeSolidFireVolume(SolidFireConnection sfConnection, long lVolumeId) + public static void purgeSolidFireVolume(SolidFireConnection sfConnection, long lVolumeId) { final Gson gson = new GsonBuilder().create(); @@ -800,10 +874,51 @@ public class SolidFireUtil { } } - public static long createSolidFireSnapshot(SolidFireConnection sfConnection, long lVolumeId, String snapshotName) { - final Gson gson = new GsonBuilder().create(); + public static class SolidFireSnapshot { + private final long _id; + private final String _name; + + public SolidFireSnapshot(long id, String name) { + _id = id; + _name = name; + } + + public long getId() { + return _id; + } + + public String getName() { + return _name; + } + } + + public static long createSolidFireSnapshot(SolidFireConnection sfConnection, long lVolumeId, String snapshotName, Map<String, String> mapAttributes) { + JsonObject snapshotToCreate = new JsonObject(); + + snapshotToCreate.addProperty("method", "CreateSnapshot"); - SnapshotToCreate snapshotToCreate = new SnapshotToCreate(lVolumeId, snapshotName); + JsonObject params = new JsonObject(); + + snapshotToCreate.add("params", params); + + params.addProperty("volumeID", lVolumeId); + params.addProperty("name", snapshotName); + + if (mapAttributes != null && mapAttributes.size() > 0) { + JsonObject attributes = new JsonObject(); + + params.add("attributes", attributes); + + Iterator<Map.Entry<String, String>> itr = mapAttributes.entrySet().iterator(); + + while (itr.hasNext()) { + Map.Entry<String, String> pair = itr.next(); + + attributes.addProperty(pair.getKey(), pair.getValue()); + } + } + + final Gson gson = new GsonBuilder().create(); String strSnapshotToCreateJson = gson.toJson(snapshotToCreate); @@ -816,6 +931,38 @@ public class SolidFireUtil { return snapshotCreateResult.result.snapshotID; } + public static SolidFireSnapshot getSolidFireSnapshot(SolidFireConnection sfConnection, long lVolumeId, long lSnapshotId) { + final Gson gson = new GsonBuilder().create(); + + SnapshotsToGet snapshotsToGet = new SnapshotsToGet(lVolumeId); + + String strSnapshotsToGetJson = gson.toJson(snapshotsToGet); + + String strSnapshotsGetResultJson = executeJsonRpc(sfConnection, strSnapshotsToGetJson); + + SnapshotsGetResult snapshotsGetResult = gson.fromJson(strSnapshotsGetResultJson, SnapshotsGetResult.class); + + verifyResult(snapshotsGetResult.result, strSnapshotsGetResultJson, gson); + + String snapshotName = null; + + if (snapshotsGetResult.result.snapshots != null) { + for (SnapshotsGetResult.Result.Snapshot snapshot : snapshotsGetResult.result.snapshots) { + if (snapshot.snapshotID == lSnapshotId) { + snapshotName = snapshot.name; + + break; + } + } + } + + if (snapshotName == null) { + throw new CloudRuntimeException("Could not find SolidFire snapshot ID: " + lSnapshotId + " for the following SolidFire volume ID: " + lVolumeId); + } + + return new SolidFireSnapshot(lSnapshotId, snapshotName); + } + public static void deleteSolidFireSnapshot(SolidFireConnection sfConnection, long lSnapshotId) { final Gson gson = new GsonBuilder().create(); @@ -841,10 +988,40 @@ public class SolidFireUtil { verifyResult(rollbackInitiatedResult.result, strRollbackInitiatedResultJson, gson); } - public static long createSolidFireClone(SolidFireConnection sfConnection, long lVolumeId, long lSnapshotId, String cloneName) { - final Gson gson = new GsonBuilder().create(); + public static long createSolidFireClone(SolidFireConnection sfConnection, long lVolumeId, long lSnapshotId, long sfAccountId, + String cloneName, Map<String, String> mapAttributes) { + JsonObject cloneToCreate = new JsonObject(); + + cloneToCreate.addProperty("method", "CloneVolume"); + + JsonObject params = new JsonObject(); + + cloneToCreate.add("params", params); + + params.addProperty("volumeID", lVolumeId); + + if (lSnapshotId > 0) { + params.addProperty("snapshotID", lSnapshotId); + } + + params.addProperty("newAccountID", sfAccountId); + params.addProperty("name", cloneName); + + if (mapAttributes != null && mapAttributes.size() > 0) { + JsonObject attributes = new JsonObject(); - CloneToCreate cloneToCreate = new CloneToCreate(lVolumeId, lSnapshotId, cloneName); + params.add("attributes", attributes); + + Iterator<Map.Entry<String, String>> itr = mapAttributes.entrySet().iterator(); + + while (itr.hasNext()) { + Map.Entry<String, String> pair = itr.next(); + + attributes.addProperty(pair.getKey(), pair.getValue()); + } + } + + final Gson gson = new GsonBuilder().create(); String strCloneToCreateJson = gson.toJson(cloneToCreate); @@ -854,7 +1031,33 @@ public class SolidFireUtil { verifyResult(cloneCreateResult.result, strCloneCreateResultJson, gson); - return cloneCreateResult.result.cloneID; + // Clone is an async operation. Poll until we get data. + + AsyncJobToPoll asyncJobToPoll = new AsyncJobToPoll(cloneCreateResult.result.asyncHandle); + + String strAsyncJobToPollJson = gson.toJson(asyncJobToPoll); + + do { + String strAsyncJobResultJson = executeJsonRpc(sfConnection, strAsyncJobToPollJson); + + AsyncJobResult asyncJobResult = gson.fromJson(strAsyncJobResultJson, AsyncJobResult.class); + + verifyResult(asyncJobResult.result, strAsyncJobResultJson, gson); + + if (asyncJobResult.result.status.equals("complete")) { + break; + } + + try { + Thread.sleep(500); // sleep for 1/2 of a second + } + catch (Exception ex) { + // ignore + } + } + while (true); + + return cloneCreateResult.result.volumeID; } public static long createSolidFireAccount(SolidFireConnection sfConnection, String strAccountName) @@ -1135,189 +1338,6 @@ public class SolidFireUtil { } @SuppressWarnings("unused") - private static final class VolumeToCreateWithCloudStackVolumeSize { - private final String method = "CreateVolume"; - private final VolumeToCreateParams params; - - private VolumeToCreateWithCloudStackVolumeSize(final String strVolumeName, final long lAccountId, final long lTotalSize, - final boolean bEnable512e, final String strCloudStackVolumeSize, final long lMinIOPS, final long lMaxIOPS, final long lBurstIOPS) { - params = new VolumeToCreateParams(strVolumeName, lAccountId, lTotalSize, bEnable512e, strCloudStackVolumeSize, lMinIOPS, lMaxIOPS, lBurstIOPS); - } - - private static final class VolumeToCreateParams { - private final String name; - private final long accountID; - private final long totalSize; - private final boolean enable512e; - private final VolumeToCreateParamsAttributes attributes; - private final VolumeToCreateParamsQoS qos; - - private VolumeToCreateParams(final String strVolumeName, final long lAccountId, final long lTotalSize, final boolean bEnable512e, - final String strCloudStackVolumeSize, final long lMinIOPS, final long lMaxIOPS, final long lBurstIOPS) { - name = strVolumeName; - accountID = lAccountId; - totalSize = lTotalSize; - enable512e = bEnable512e; - - attributes = new VolumeToCreateParamsAttributes(strCloudStackVolumeSize); - qos = new VolumeToCreateParamsQoS(lMinIOPS, lMaxIOPS, lBurstIOPS); - } - - private static final class VolumeToCreateParamsAttributes { - private final String CloudStackVolumeSize; - - private VolumeToCreateParamsAttributes(final String strCloudStackVolumeSize) { - CloudStackVolumeSize = strCloudStackVolumeSize; - } - } - - private static final class VolumeToCreateParamsQoS { - private final long minIOPS; - private final long maxIOPS; - private final long burstIOPS; - - private VolumeToCreateParamsQoS(final long lMinIOPS, final long lMaxIOPS, final long lBurstIOPS) { - minIOPS = lMinIOPS; - maxIOPS = lMaxIOPS; - burstIOPS = lBurstIOPS; - } - } - } - } - - @SuppressWarnings("unused") - private static final class VolumeToCreate { - private final String method = "CreateVolume"; - private final VolumeToCreateParams params; - - private VolumeToCreate(final String strVolumeName, final long lAccountId, final long lTotalSize, final boolean bEnable512e, - final long lMinIOPS, final long lMaxIOPS, final long lBurstIOPS) { - params = new VolumeToCreateParams(strVolumeName, lAccountId, lTotalSize, bEnable512e, lMinIOPS, lMaxIOPS, lBurstIOPS); - } - - private static final class VolumeToCreateParams { - private final String name; - private final long accountID; - private final long totalSize; - private final boolean enable512e; - private final VolumeToCreateParamsQoS qos; - - private VolumeToCreateParams(final String strVolumeName, final long lAccountId, final long lTotalSize, final boolean bEnable512e, - final long lMinIOPS, final long lMaxIOPS, final long lBurstIOPS) { - name = strVolumeName; - accountID = lAccountId; - totalSize = lTotalSize; - enable512e = bEnable512e; - - qos = new VolumeToCreateParamsQoS(lMinIOPS, lMaxIOPS, lBurstIOPS); - } - - private static final class VolumeToCreateParamsQoS { - private final long minIOPS; - private final long maxIOPS; - private final long burstIOPS; - - private VolumeToCreateParamsQoS(final long lMinIOPS, final long lMaxIOPS, final long lBurstIOPS) { - minIOPS = lMinIOPS; - maxIOPS = lMaxIOPS; - burstIOPS = lBurstIOPS; - } - } - } - } - - @SuppressWarnings("unused") - private static final class VolumeToModifyWithCloudStackVolumeSize - { - private final String method = "ModifyVolume"; - private final VolumeToModifyParams params; - - private VolumeToModifyWithCloudStackVolumeSize(final long lVolumeId, final long lTotalSize, final String strCloudStackVolumeSize, - final long lMinIOPS, final long lMaxIOPS, final long lBurstIOPS) - { - params = new VolumeToModifyParams(lVolumeId, lTotalSize, strCloudStackVolumeSize, lMinIOPS, lMaxIOPS, lBurstIOPS); - } - - private static final class VolumeToModifyParams - { - private final long volumeID; - private final long totalSize; - private final VolumeToModifyParamsAttributes attributes; - private final VolumeToModifyParamsQoS qos; - - private VolumeToModifyParams(final long lVolumeId, final long lTotalSize, String strCloudStackVolumeSize, final long lMinIOPS, final long lMaxIOPS, final long lBurstIOPS) - { - volumeID = lVolumeId; - - totalSize = lTotalSize; - - attributes = new VolumeToModifyParamsAttributes(strCloudStackVolumeSize); - qos = new VolumeToModifyParamsQoS(lMinIOPS, lMaxIOPS, lBurstIOPS); - } - } - - private static final class VolumeToModifyParamsAttributes { - private final String CloudStackVolumeSize; - - private VolumeToModifyParamsAttributes(final String strCloudStackVolumeSize) { - CloudStackVolumeSize = strCloudStackVolumeSize; - } - } - - private static final class VolumeToModifyParamsQoS { - private final long minIOPS; - private final long maxIOPS; - private final long burstIOPS; - - private VolumeToModifyParamsQoS(final long lMinIOPS, final long lMaxIOPS, final long lBurstIOPS) { - minIOPS = lMinIOPS; - maxIOPS = lMaxIOPS; - burstIOPS = lBurstIOPS; - } - } - } - - @SuppressWarnings("unused") - private static final class VolumeToModify - { - private final String method = "ModifyVolume"; - private final VolumeToModifyParams params; - - private VolumeToModify(final long lVolumeId, final long lTotalSize, final long lMinIOPS, final long lMaxIOPS, final long lBurstIOPS) - { - params = new VolumeToModifyParams(lVolumeId, lTotalSize, lMinIOPS, lMaxIOPS, lBurstIOPS); - } - - private static final class VolumeToModifyParams - { - private final long volumeID; - private final long totalSize; - private final VolumeToModifyParamsQoS qos; - - private VolumeToModifyParams(final long lVolumeId, final long lTotalSize, final long lMinIOPS, final long lMaxIOPS, final long lBurstIOPS) - { - volumeID = lVolumeId; - - totalSize = lTotalSize; - - qos = new VolumeToModifyParamsQoS(lMinIOPS, lMaxIOPS, lBurstIOPS); - } - } - - private static final class VolumeToModifyParamsQoS { - private final long minIOPS; - private final long maxIOPS; - private final long burstIOPS; - - private VolumeToModifyParamsQoS(final long lMinIOPS, final long lMaxIOPS, final long lBurstIOPS) { - minIOPS = lMinIOPS; - maxIOPS = lMaxIOPS; - burstIOPS = lBurstIOPS; - } - } - } - - @SuppressWarnings("unused") private static final class VolumeToGet { private final String method = "ListActiveVolumes"; @@ -1407,21 +1427,20 @@ public class SolidFireUtil { } @SuppressWarnings("unused") - private static final class SnapshotToCreate { - private final String method = "CreateSnapshot"; - private final SnapshotToCreateParams params; + private static final class SnapshotsToGet + { + private final String method = "ListSnapshots"; + private final SnapshotsToGetParams params; - private SnapshotToCreate(final long lVolumeId, final String snapshotName) { - params = new SnapshotToCreateParams(lVolumeId, snapshotName); + private SnapshotsToGet(final long lVolumeId) { + params = new SnapshotsToGetParams(lVolumeId); } - private static final class SnapshotToCreateParams { + private static final class SnapshotsToGetParams { private final long volumeID; - private final String name; - private SnapshotToCreateParams(final long lVolumeId, final String snapshotName) { + private SnapshotsToGetParams(final long lVolumeId) { volumeID = lVolumeId; - name = snapshotName; } } } @@ -1466,28 +1485,6 @@ public class SolidFireUtil { } @SuppressWarnings("unused") - private static final class CloneToCreate { - private final String method = "CloneVolume"; - private final CloneToCreateParams params; - - private CloneToCreate(final long lVolumeId, final long lSnapshotId, final String cloneName) { - params = new CloneToCreateParams(lVolumeId, lSnapshotId, cloneName); - } - - private static final class CloneToCreateParams { - private final long volumeID; - private final long snapshotID; - private final String name; - - private CloneToCreateParams(final long lVolumeId, final long lSnapshotId, final String cloneName) { - volumeID = lVolumeId; - snapshotID = lSnapshotId; - name = cloneName; - } - } - } - - @SuppressWarnings("unused") private static final class AccountToAdd { private final String method = "AddAccount"; @@ -1680,6 +1677,28 @@ public class SolidFireUtil { } } + @SuppressWarnings("unused") + private static final class AsyncJobToPoll + { + private final String method = "GetAsyncResult"; + private final AsyncJobToPollParams params; + + private AsyncJobToPoll(final long asyncHandle) + { + params = new AsyncJobToPollParams(asyncHandle); + } + + private static final class AsyncJobToPollParams + { + private final long asyncHandle; + + private AsyncJobToPollParams(final long asyncHandle) + { + this.asyncHandle = asyncHandle; + } + } + } + private static final class VolumeCreateResult { private Result result; @@ -1721,6 +1740,19 @@ public class SolidFireUtil { } } + private static final class SnapshotsGetResult { + private Result result; + + private static final class Result { + private Snapshot[] snapshots; + + private static final class Snapshot { + private long snapshotID; + private String name; + } + } + } + @SuppressWarnings("unused") private static final class RollbackInitiatedResult { private Result result; @@ -1734,7 +1766,8 @@ public class SolidFireUtil { private Result result; private static final class Result { - private long cloneID; + private long volumeID; + private long asyncHandle; } } @@ -1786,6 +1819,15 @@ public class SolidFireUtil { } } + private static final class AsyncJobResult { + private AsyncResult result; + + private static final class AsyncResult + { + private String status; + } + } + private static final class JsonError { private Error error; http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/server/src/com/cloud/capacity/CapacityManagerImpl.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/capacity/CapacityManagerImpl.java b/server/src/com/cloud/capacity/CapacityManagerImpl.java index 13794c7..d0ae3e9 100644 --- a/server/src/com/cloud/capacity/CapacityManagerImpl.java +++ b/server/src/com/cloud/capacity/CapacityManagerImpl.java @@ -549,28 +549,35 @@ public class CapacityManagerImpl extends ManagerBase implements CapacityManager, return getUsedBytes(pool); } else { - // Get size for all the non-destroyed volumes + // Get size for all the non-destroyed volumes. Pair<Long, Long> sizes = _volumeDao.getNonDestroyedCountAndTotalByPool(pool.getId()); totalAllocatedSize = sizes.second() + sizes.first() * _extraBytesPerVolume; } - // Get size for VM Snapshots - totalAllocatedSize = totalAllocatedSize + _volumeDao.getVMSnapshotSizeByPool(pool.getId()); + // Get size for VM Snapshots. + totalAllocatedSize += _volumeDao.getVMSnapshotSizeByPool(pool.getId()); - // Iterate through all templates on this storage pool - boolean tmpinstalled = false; - List<VMTemplateStoragePoolVO> templatePoolVOs; - templatePoolVOs = _templatePoolDao.listByPoolId(pool.getId()); + boolean tmpInstalled = false; + // Iterate through all templates on this storage pool. + List<VMTemplateStoragePoolVO> templatePoolVOs = _templatePoolDao.listByPoolId(pool.getId()); for (VMTemplateStoragePoolVO templatePoolVO : templatePoolVOs) { - if ((templateForVmCreation != null) && !tmpinstalled && (templatePoolVO.getTemplateId() == templateForVmCreation.getId())) { - tmpinstalled = true; + if ((templateForVmCreation != null) && !tmpInstalled && (templatePoolVO.getTemplateId() == templateForVmCreation.getId())) { + tmpInstalled = true; } + long templateSize = templatePoolVO.getTemplateSize(); + totalAllocatedSize += templateSize + _extraBytesPerVolume; } + if ((templateForVmCreation != null) && !tmpInstalled) { + long templateForVmCreationSize = templateForVmCreation.getSize() != null ? templateForVmCreation.getSize() : 0; + + totalAllocatedSize += templateForVmCreationSize + _extraBytesPerVolume; + } + return totalAllocatedSize; } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java b/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java index 2b3358a..ef0ad19 100644 --- a/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java +++ b/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java @@ -1234,7 +1234,8 @@ StateListener<State, VirtualMachine.Event, VirtualMachine> { requestVolumes = new ArrayList<Volume>(); requestVolumes.add(vol); - if (!_storageMgr.storagePoolHasEnoughSpace(requestVolumes, potentialSPool)) + if (!_storageMgr.storagePoolHasEnoughIops(requestVolumes, potentialSPool) || + !_storageMgr.storagePoolHasEnoughSpace(requestVolumes, potentialSPool, potentialHost.getClusterId())) continue; volumeAllocationMap.put(potentialSPool, requestVolumes); } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/server/src/com/cloud/hypervisor/CloudZonesStartupProcessor.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/hypervisor/CloudZonesStartupProcessor.java b/server/src/com/cloud/hypervisor/CloudZonesStartupProcessor.java index 4bdb2bc..2aa9b04 100644 --- a/server/src/com/cloud/hypervisor/CloudZonesStartupProcessor.java +++ b/server/src/com/cloud/hypervisor/CloudZonesStartupProcessor.java @@ -44,9 +44,11 @@ import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.DataCenterDetailsDao; import com.cloud.dc.dao.HostPodDao; import com.cloud.exception.ConnectionException; +import com.cloud.host.DetailVO; import com.cloud.host.Host; import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; +import com.cloud.host.dao.HostDetailsDao; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.utils.component.AdapterBase; import com.cloud.utils.exception.CloudRuntimeException; @@ -69,6 +71,8 @@ public class CloudZonesStartupProcessor extends AdapterBase implements StartupCo @Inject HostDao _hostDao = null; @Inject + private HostDetailsDao hostDetailsDao; + @Inject HostPodDao _podDao = null; @Inject DataCenterDetailsDao _zoneDetailsDao = null; @@ -319,6 +323,25 @@ public class CloudZonesStartupProcessor extends AdapterBase implements StartupCo host.setHypervisorType(hyType); host.setHypervisorVersion(scc.getHypervisorVersion()); + updateHostDetails(host, scc); + } + + private void updateHostDetails(HostVO host, StartupRoutingCommand startupRoutingCmd) { + final String name = "supportsResign"; + final String value = String.valueOf(startupRoutingCmd.getSupportsClonedVolumes()); + + DetailVO hostDetail = hostDetailsDao.findDetail(host.getId(), name); + + if (hostDetail != null) { + hostDetail.setValue(value); + + hostDetailsDao.update(hostDetail.getId(), hostDetail); + } + else { + hostDetail = new DetailVO(host.getId(), name, value); + + hostDetailsDao.persist(hostDetail); + } } private boolean checkCIDR(Host.Type type, HostPodVO pod, String serverPrivateIP, String serverPrivateNetmask) { http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/server/src/com/cloud/resource/ResourceManagerImpl.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/resource/ResourceManagerImpl.java b/server/src/com/cloud/resource/ResourceManagerImpl.java index cca4add..0b59e39 100644 --- a/server/src/com/cloud/resource/ResourceManagerImpl.java +++ b/server/src/com/cloud/resource/ResourceManagerImpl.java @@ -1733,6 +1733,12 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager, _hostDao.update(host.getId(), host); } + if (startup instanceof StartupRoutingCommand) { + final StartupRoutingCommand ssCmd = (StartupRoutingCommand)startup; + + updateHostDetails(host, ssCmd); + } + try { resourceStateTransitTo(host, ResourceState.Event.InternalCreated, _nodeId); /* Agent goes to Connecting status */ @@ -1750,6 +1756,24 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager, return host; } + private void updateHostDetails(HostVO host, StartupRoutingCommand startupRoutingCmd) { + final String name = "supportsResign"; + final String value = String.valueOf(startupRoutingCmd.getSupportsClonedVolumes()); + + DetailVO hostDetail = _hostDetailsDao.findDetail(host.getId(), name); + + if (hostDetail != null) { + hostDetail.setValue(value); + + _hostDetailsDao.update(hostDetail.getId(), hostDetail); + } + else { + hostDetail = new DetailVO(host.getId(), name, value); + + _hostDetailsDao.persist(hostDetail); + } + } + private boolean isFirstHostInCluster(final HostVO host) { boolean isFirstHost = true; if (host.getClusterId() != null) { http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/server/src/com/cloud/storage/ResizeVolumePayload.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/storage/ResizeVolumePayload.java b/server/src/com/cloud/storage/ResizeVolumePayload.java index 7a927b2..9e4c3ec 100644 --- a/server/src/com/cloud/storage/ResizeVolumePayload.java +++ b/server/src/com/cloud/storage/ResizeVolumePayload.java @@ -21,16 +21,21 @@ public class ResizeVolumePayload { public final Long newSize; public final Long newMinIops; public final Long newMaxIops; + public final Integer newHypervisorSnapshotReserve; public final boolean shrinkOk; public final String instanceName; public final long[] hosts; + public final boolean isManaged; - public ResizeVolumePayload(Long newSize, Long newMinIops, Long newMaxIops, boolean shrinkOk, String instanceName, long[] hosts) { + public ResizeVolumePayload(Long newSize, Long newMinIops, Long newMaxIops, Integer newHypervisorSnapshotReserve, boolean shrinkOk, + String instanceName, long[] hosts, boolean isManaged) { this.newSize = newSize; this.newMinIops = newMinIops; this.newMaxIops = newMaxIops; + this.newHypervisorSnapshotReserve = newHypervisorSnapshotReserve; this.shrinkOk = shrinkOk; this.instanceName = instanceName; this.hosts = hosts; + this.isManaged = isManaged; } } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/server/src/com/cloud/storage/StorageManager.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/storage/StorageManager.java b/server/src/com/cloud/storage/StorageManager.java index a399a08..0bf0011 100644 --- a/server/src/com/cloud/storage/StorageManager.java +++ b/server/src/com/cloud/storage/StorageManager.java @@ -106,6 +106,30 @@ public interface StorageManager extends StorageService { boolean storagePoolHasEnoughSpace(List<Volume> volume, StoragePool pool); + /** + * This comment is relevant to managed storage only. + * + * Long clusterId = only used for managed storage + * + * Some managed storage can be more efficient handling VM templates (via cloning) if it knows the capabilities of the compute cluster it is dealing with. + * If the compute cluster supports UUID resigning and the storage system can clone a volume from a volume, then this determines how much more space a + * new root volume (that makes use of a template) will take up on the storage system. + * + * For example, if a storage system can clone a volume from a volume and the compute cluster supports UUID resigning (relevant for hypervisors like + * XenServer and ESXi that put virtual disks in clustered file systems), then the storage system will need to determine if it already has a copy of + * the template or if it will need to create one first before cloning the template to a new volume to be used for the new root disk (assuming the root + * disk is being deployed from a template). If the template doesn't already exists on the storage system, then you need to take into consideration space + * required for that template (stored in one volume) and space required for a new volume created from that template volume (for your new root volume). + * + * If UUID resigning is not available in the compute cluster or the storage system doesn't support cloning a volume from a volume, then for each new + * root disk that uses a template, CloudStack will have the template be copied down to a newly created volume on the storage system (i.e. no need + * to take into consideration the possible need to first create a volume on the storage system for a template that will be used for the root disk + * via cloning). + * + * Cloning volumes on the back-end instead of copying down a new template for each new volume helps to alleviate load on the hypervisors. + */ + boolean storagePoolHasEnoughSpace(List<Volume> volume, StoragePool pool, Long clusterId); + boolean registerHostListener(String providerUuid, HypervisorHostListener listener); void connectHostToSharedPool(long hostId, long poolId) throws StorageUnavailableException, StorageConflictException; http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/server/src/com/cloud/storage/StorageManagerImpl.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/storage/StorageManagerImpl.java b/server/src/com/cloud/storage/StorageManagerImpl.java index 0509abc..331f09e 100644 --- a/server/src/com/cloud/storage/StorageManagerImpl.java +++ b/server/src/com/cloud/storage/StorageManagerImpl.java @@ -70,6 +70,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreLifeCy import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; import org.apache.cloudstack.engine.subsystem.api.storage.TemplateDataFactory; +import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo; import org.apache.cloudstack.engine.subsystem.api.storage.TemplateService; import org.apache.cloudstack.engine.subsystem.api.storage.TemplateService.TemplateApiResult; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory; @@ -1668,9 +1669,9 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C return false; } - // Only IOPS guaranteed primary storage like SolidFire is using/setting IOPS. + // Only IOPS-guaranteed primary storage like SolidFire is using/setting IOPS. // This check returns true for storage that does not specify IOPS. - if (pool.getCapacityIops() == null ) { + if (pool.getCapacityIops() == null) { s_logger.info("Storage pool " + pool.getName() + " (" + pool.getId() + ") does not supply IOPS capacity, assuming enough capacity"); return true; @@ -1696,6 +1697,11 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C @Override public boolean storagePoolHasEnoughSpace(List<Volume> volumes, StoragePool pool) { + return storagePoolHasEnoughSpace(volumes, pool, null); + } + + @Override + public boolean storagePoolHasEnoughSpace(List<Volume> volumes, StoragePool pool, Long clusterId) { if (volumes == null || volumes.isEmpty()) { return false; } @@ -1704,10 +1710,11 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C return false; } - // allocated space includes template of specified volume + // allocated space includes templates StoragePoolVO poolVO = _storagePoolDao.findById(pool.getId()); - long allocatedSizeWithtemplate = _capacityMgr.getAllocatedPoolCapacity(poolVO, null); + long allocatedSizeWithTemplate = _capacityMgr.getAllocatedPoolCapacity(poolVO, null); long totalAskingSize = 0; + for (Volume volume : volumes) { // refreshing the volume from the DB to get latest hv_ss_reserve (hypervisor snapshot reserve) field // I could have just assigned this to "volume", but decided to make a new variable for it so that it @@ -1718,18 +1725,37 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C // update the volume's hv_ss_reserve (hypervisor snapshot reserve) from a disk offering (used for managed storage) volService.updateHypervisorSnapshotReserveForVolume(getDiskOfferingVO(volumeVO), volumeVO.getId(), getHypervisorType(volumeVO)); - // hv_ss_reserve field might have been updated; refresh from DB to make use of it in getVolumeSizeIncludingHypervisorSnapshotReserve + // hv_ss_reserve field might have been updated; refresh from DB to make use of it in getDataObjectSizeIncludingHypervisorSnapshotReserve volumeVO = _volumeDao.findById(volume.getId()); } - if (volumeVO.getTemplateId() != null) { - VMTemplateVO tmpl = _templateDao.findByIdIncludingRemoved(volumeVO.getTemplateId()); - if (tmpl != null && tmpl.getFormat() != ImageFormat.ISO) { - allocatedSizeWithtemplate = _capacityMgr.getAllocatedPoolCapacity(poolVO, tmpl); + // this if statement should resolve to true at most once per execution of the for loop its contained within (for a root disk that is + // to leverage a template) + if (volume.getTemplateId() != null) { + VMTemplateVO tmpl = _templateDao.findByIdIncludingRemoved(volume.getTemplateId()); + + if (tmpl != null && !ImageFormat.ISO.equals(tmpl.getFormat())) { + allocatedSizeWithTemplate = _capacityMgr.getAllocatedPoolCapacity(poolVO, tmpl); } } + if (volumeVO.getState() != Volume.State.Ready) { - totalAskingSize = totalAskingSize + getVolumeSizeIncludingHypervisorSnapshotReserve(volumeVO, pool); + totalAskingSize += getDataObjectSizeIncludingHypervisorSnapshotReserve(volumeVO, pool); + + if (ScopeType.ZONE.equals(poolVO.getScope()) && volumeVO.getTemplateId() != null) { + VMTemplateVO tmpl = _templateDao.findByIdIncludingRemoved(volumeVO.getTemplateId()); + + if (tmpl != null && !ImageFormat.ISO.equals(tmpl.getFormat())) { + // Storage plug-ins for zone-wide primary storage can be designed in such a way as to store a template on the + // primary storage once and make use of it in different clusters (via cloning). + // This next call leads to CloudStack asking how many more bytes it will need for the template (if the template is + // already stored on the primary storage, then the answer is 0). + + if (clusterId != null && _clusterDao.computeWhetherClusterSupportsResigning(clusterId)) { + totalAskingSize += getBytesRequiredForTemplate(tmpl, pool); + } + } + } } } @@ -1749,11 +1775,11 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C double storageAllocatedThreshold = CapacityManager.StorageAllocatedCapacityDisableThreshold.valueIn(pool.getDataCenterId()); if (s_logger.isDebugEnabled()) { s_logger.debug("Checking pool: " + pool.getId() + " for volume allocation " + volumes.toString() + ", maxSize : " + totalOverProvCapacity + - ", totalAllocatedSize : " + allocatedSizeWithtemplate + ", askingSize : " + totalAskingSize + ", allocated disable threshold: " + + ", totalAllocatedSize : " + allocatedSizeWithTemplate + ", askingSize : " + totalAskingSize + ", allocated disable threshold: " + storageAllocatedThreshold); } - double usedPercentage = (allocatedSizeWithtemplate + totalAskingSize) / (double)(totalOverProvCapacity); + double usedPercentage = (allocatedSizeWithTemplate + totalAskingSize) / (double)(totalOverProvCapacity); if (usedPercentage > storageAllocatedThreshold) { if (s_logger.isDebugEnabled()) { s_logger.debug("Insufficient un-allocated capacity on: " + pool.getId() + " for volume allocation: " + volumes.toString() + @@ -1763,10 +1789,10 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C return false; } - if (totalOverProvCapacity < (allocatedSizeWithtemplate + totalAskingSize)) { + if (totalOverProvCapacity < (allocatedSizeWithTemplate + totalAskingSize)) { if (s_logger.isDebugEnabled()) { s_logger.debug("Insufficient un-allocated capacity on: " + pool.getId() + " for volume allocation: " + volumes.toString() + - ", not enough storage, maxSize : " + totalOverProvCapacity + ", totalAllocatedSize : " + allocatedSizeWithtemplate + ", askingSize : " + + ", not enough storage, maxSize : " + totalOverProvCapacity + ", totalAllocatedSize : " + allocatedSizeWithTemplate + ", askingSize : " + totalAskingSize); } return false; @@ -1792,19 +1818,36 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C return null; } - private long getVolumeSizeIncludingHypervisorSnapshotReserve(Volume volume, StoragePool pool) { + private long getDataObjectSizeIncludingHypervisorSnapshotReserve(Volume volume, StoragePool pool) { DataStoreProvider storeProvider = _dataStoreProviderMgr.getDataStoreProvider(pool.getStorageProviderName()); DataStoreDriver storeDriver = storeProvider.getDataStoreDriver(); if (storeDriver instanceof PrimaryDataStoreDriver) { PrimaryDataStoreDriver primaryStoreDriver = (PrimaryDataStoreDriver)storeDriver; - return primaryStoreDriver.getVolumeSizeIncludingHypervisorSnapshotReserve(volume, pool); + VolumeInfo volumeInfo = volFactory.getVolume(volume.getId()); + + return primaryStoreDriver.getDataObjectSizeIncludingHypervisorSnapshotReserve(volumeInfo, pool); } return volume.getSize(); } + private long getBytesRequiredForTemplate(VMTemplateVO tmpl, StoragePool pool) { + DataStoreProvider storeProvider = _dataStoreProviderMgr.getDataStoreProvider(pool.getStorageProviderName()); + DataStoreDriver storeDriver = storeProvider.getDataStoreDriver(); + + if (storeDriver instanceof PrimaryDataStoreDriver) { + PrimaryDataStoreDriver primaryStoreDriver = (PrimaryDataStoreDriver)storeDriver; + + TemplateInfo templateInfo = tmplFactory.getReadyTemplateOnImageStore(tmpl.getId(), pool.getDataCenterId()); + + return primaryStoreDriver.getBytesRequiredForTemplate(templateInfo, pool); + } + + return tmpl.getSize(); + } + @Override public void createCapacityEntry(long poolId) { StoragePoolVO storage = _storagePoolDao.findById(poolId); http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/server/src/com/cloud/storage/VolumeApiServiceImpl.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/com/cloud/storage/VolumeApiServiceImpl.java index 4c3de3e..395b00c 100644 --- a/server/src/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/com/cloud/storage/VolumeApiServiceImpl.java @@ -839,6 +839,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic Long newSize = null; Long newMinIops = null; Long newMaxIops = null; + Integer newHypervisorSnapshotReserve = null; boolean shrinkOk = cmd.getShrinkOk(); VolumeVO volume = _volsDao.findById(cmd.getEntityId()); @@ -881,6 +882,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic // if we are to use the existing disk offering if (newDiskOffering == null) { newSize = cmd.getSize(); + newHypervisorSnapshotReserve = volume.getHypervisorSnapshotReserve(); // if the caller is looking to change the size of the volume if (newSize != null) { @@ -939,10 +941,6 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic throw new InvalidParameterValueException("There are no tags on the current disk offering. The new disk offering needs to have no tags, as well."); } - if (!areIntegersEqual(diskOffering.getHypervisorSnapshotReserve(), newDiskOffering.getHypervisorSnapshotReserve())) { - throw new InvalidParameterValueException("The hypervisor snapshot reverse on the new and old disk offerings must be equal."); - } - if (newDiskOffering.getDomainId() != null) { // not a public offering; check access _configMgr.checkDiskOfferingAccess(CallContext.current().getCallingAccount(), newDiskOffering); @@ -975,6 +973,9 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic newMinIops = newDiskOffering.getMinIops(); newMaxIops = newDiskOffering.getMaxIops(); } + + // if the hypervisor snapshot reserve value is null, it must remain null (currently only KVM uses null and null is all KVM uses for a value here) + newHypervisorSnapshotReserve = volume.getHypervisorSnapshotReserve() != null ? newDiskOffering.getHypervisorSnapshotReserve() : null; } long currentSize = volume.getSize(); @@ -1013,6 +1014,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic volume.setSize(newSize); volume.setMinIops(newMinIops); volume.setMaxIops(newMaxIops); + volume.setHypervisorSnapshotReserve(newHypervisorSnapshotReserve); if (newDiskOffering != null) { volume.setDiskOfferingId(cmd.getNewDiskOfferingId()); @@ -1038,13 +1040,13 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic try { return orchestrateResizeVolume(volume.getId(), currentSize, newSize, newMinIops, newMaxIops, - newDiskOffering != null ? cmd.getNewDiskOfferingId() : null, shrinkOk); + newHypervisorSnapshotReserve, newDiskOffering != null ? cmd.getNewDiskOfferingId() : null, shrinkOk); } finally { _workJobDao.expunge(placeHolder.getId()); } } else { Outcome<Volume> outcome = resizeVolumeThroughJobQueue(userVm.getId(), volume.getId(), currentSize, newSize, newMinIops, newMaxIops, - newDiskOffering != null ? cmd.getNewDiskOfferingId() : null, shrinkOk); + newHypervisorSnapshotReserve, newDiskOffering != null ? cmd.getNewDiskOfferingId() : null, shrinkOk); try { outcome.get(); @@ -1079,19 +1081,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic } return orchestrateResizeVolume(volume.getId(), currentSize, newSize, newMinIops, newMaxIops, - newDiskOffering != null ? cmd.getNewDiskOfferingId() : null, shrinkOk); - } - - private static boolean areIntegersEqual(Integer i1, Integer i2) { - if (i1 == null) { - i1 = 0; - } - - if (i2 == null) { - i2 = 0; - } - - return i1.equals(i2); + newHypervisorSnapshotReserve, newDiskOffering != null ? cmd.getNewDiskOfferingId() : null, shrinkOk); } private void validateIops(Long minIops, Long maxIops) { @@ -1106,9 +1096,12 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic } } - private VolumeVO orchestrateResizeVolume(long volumeId, long currentSize, long newSize, Long newMinIops, Long newMaxIops, Long newDiskOfferingId, boolean shrinkOk) { + private VolumeVO orchestrateResizeVolume(long volumeId, long currentSize, long newSize, Long newMinIops, Long newMaxIops, + Integer newHypervisorSnapshotReserve, Long newDiskOfferingId, boolean shrinkOk) { VolumeVO volume = _volsDao.findById(volumeId); UserVmVO userVm = _userVmDao.findById(volume.getInstanceId()); + StoragePoolVO storagePool = _storagePoolDao.findById(volume.getPoolId()); + boolean isManaged = storagePool.isManaged(); /* * get a list of hosts to send the commands to, try the system the * associated vm is running on first, then the last known place it ran. @@ -1127,8 +1120,6 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic final String errorMsg = "The VM must be stopped or the disk detached in order to resize with the XenServer Hypervisor."; - StoragePoolVO storagePool = _storagePoolDao.findById(volume.getPoolId()); - if (storagePool.isManaged() && storagePool.getHypervisor() == HypervisorType.Any && hosts != null && hosts.length > 0) { HostVO host = _hostDao.findById(hosts[0]); @@ -1143,13 +1134,20 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic } } - ResizeVolumePayload payload = new ResizeVolumePayload(newSize, newMinIops, newMaxIops, shrinkOk, instanceName, hosts); + ResizeVolumePayload payload = new ResizeVolumePayload(newSize, newMinIops, newMaxIops, newHypervisorSnapshotReserve, + shrinkOk, instanceName, hosts, isManaged); try { VolumeInfo vol = volFactory.getVolume(volume.getId()); vol.addPayload(payload); - StoragePoolVO storagePool = _storagePoolDao.findById(vol.getPoolId()); + // this call to resize has a different impact depending on whether the + // underlying primary storage is managed or not + // if managed, this is the chance for the plug-in to change IOPS value, if applicable + // if not managed, this is the chance for the plug-in to talk to the hypervisor layer + // to change the size of the disk + AsyncCallFuture<VolumeApiResult> future = volService.resize(vol); + VolumeApiResult result = future.get(); // managed storage is designed in such a way that the storage plug-in does not // talk to the hypervisor layer; as such, if the storage is managed and the @@ -1165,14 +1163,6 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic _volsDao.update(volume.getId(), volume); } - // this call to resize has a different impact depending on whether the - // underlying primary storage is managed or not - // if managed, this is the chance for the plug-in to change IOPS value, if applicable - // if not managed, this is the chance for the plug-in to talk to the hypervisor layer - // to change the size of the disk - AsyncCallFuture<VolumeApiResult> future = volService.resize(vol); - VolumeApiResult result = future.get(); - if (result.isFailed()) { s_logger.warn("Failed to resize the volume " + volume); String details = ""; @@ -2758,9 +2748,9 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic return new VmJobVolumeOutcome(workJob, volumeId); } - public Outcome<Volume> resizeVolumeThroughJobQueue(final Long vmId, final long volumeId, - final long currentSize, final long newSize, final Long newMinIops, final Long newMaxIops, final Long newServiceOfferingId, final boolean shrinkOk) { - + public Outcome<Volume> resizeVolumeThroughJobQueue(final Long vmId, final long volumeId, final long currentSize, final long newSize, + final Long newMinIops, final Long newMaxIops, final Integer newHypervisorSnapshotReserve, + final Long newServiceOfferingId, final boolean shrinkOk) { final CallContext context = CallContext.current(); final User callingUser = context.getCallingUser(); final Account callingAccount = context.getCallingAccount(); @@ -2781,7 +2771,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic // save work context info (there are some duplications) VmWorkResizeVolume workInfo = new VmWorkResizeVolume(callingUser.getId(), callingAccount.getId(), vm.getId(), - VolumeApiServiceImpl.VM_WORK_JOB_HANDLER, volumeId, currentSize, newSize, newMinIops, newMaxIops, newServiceOfferingId, shrinkOk); + VolumeApiServiceImpl.VM_WORK_JOB_HANDLER, volumeId, currentSize, newSize, newMinIops, newMaxIops, newHypervisorSnapshotReserve, newServiceOfferingId, shrinkOk); workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo)); _jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vm.getId()); @@ -2915,7 +2905,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic @ReflectionUse private Pair<JobInfo.Status, String> orchestrateResizeVolume(VmWorkResizeVolume work) throws Exception { Volume vol = orchestrateResizeVolume(work.getVolumeId(), work.getCurrentSize(), work.getNewSize(), work.getNewMinIops(), work.getNewMaxIops(), - work.getNewServiceOfferingId(), work.isShrinkOk()); + work.getNewHypervisorSnapshotReserve(), work.getNewServiceOfferingId(), work.isShrinkOk()); return new Pair<JobInfo.Status, String>(JobInfo.Status.SUCCEEDED, _jobMgr.marshallResultObject(new Long(vol.getId()))); } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java index 757ab61..16762c5 100644 --- a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java +++ b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java @@ -35,6 +35,7 @@ import org.apache.cloudstack.api.command.user.snapshot.ListSnapshotsCmd; import org.apache.cloudstack.api.command.user.snapshot.UpdateSnapshotPolicyCmd; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreCapabilities; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint; import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector; @@ -1013,9 +1014,14 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement try { postCreateSnapshot(volume.getId(), snapshotId, payload.getSnapshotPolicyId()); - SnapshotDataStoreVO snapshotStoreRef = _snapshotStoreDao.findBySnapshot(snapshotId, DataStoreRole.Image); + + DataStoreRole dataStoreRole = getDataStoreRole(snapshot, _snapshotStoreDao, dataStoreMgr); + + SnapshotDataStoreVO snapshotStoreRef = _snapshotStoreDao.findBySnapshot(snapshotId, dataStoreRole); + UsageEventUtils.publishUsageEvent(EventTypes.EVENT_SNAPSHOT_CREATE, snapshot.getAccountId(), snapshot.getDataCenterId(), snapshotId, snapshot.getName(), null, null, snapshotStoreRef.getPhysicalSize(), volume.getSize(), snapshot.getClass().getName(), snapshot.getUuid()); + // Correct the resource count of snapshot in case of delta snapshots. _resourceLimitMgr.decrementResourceCount(snapshotOwner.getId(), ResourceType.secondary_storage, new Long(volume.getSize() - snapshotStoreRef.getPhysicalSize())); } catch (Exception e) { @@ -1030,6 +1036,30 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement return snapshot; } + private static DataStoreRole getDataStoreRole(Snapshot snapshot, SnapshotDataStoreDao snapshotStoreDao, DataStoreManager dataStoreMgr) { + SnapshotDataStoreVO snapshotStore = snapshotStoreDao.findBySnapshot(snapshot.getId(), DataStoreRole.Primary); + + if (snapshotStore == null) { + return DataStoreRole.Image; + } + + long storagePoolId = snapshotStore.getDataStoreId(); + DataStore dataStore = dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary); + + Map<String, String> mapCapabilities = dataStore.getDriver().getCapabilities(); + + if (mapCapabilities != null) { + String value = mapCapabilities.get(DataStoreCapabilities.STORAGE_SYSTEM_SNAPSHOT.toString()); + Boolean supportsStorageSystemSnapshots = new Boolean(value); + + if (supportsStorageSystemSnapshots) { + return DataStoreRole.Primary; + } + } + + return DataStoreRole.Image; + } + @Override public boolean configure(String name, Map<String, Object> params) throws ConfigurationException { http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/server/src/com/cloud/template/TemplateManagerImpl.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/template/TemplateManagerImpl.java b/server/src/com/cloud/template/TemplateManagerImpl.java index 4b27c8b..ceee616 100644 --- a/server/src/com/cloud/template/TemplateManagerImpl.java +++ b/server/src/com/cloud/template/TemplateManagerImpl.java @@ -38,7 +38,7 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import org.apache.cloudstack.api.command.user.template.GetUploadParamsForTemplateCmd; -import org.apache.cloudstack.api.response.GetUploadParamsResponse; +import org.apache.cloudstack.framework.async.AsyncCallFuture; import org.apache.cloudstack.storage.command.TemplateOrVolumePostUploadCommand; import org.apache.cloudstack.utils.imagestore.ImageStoreUtil; import org.apache.commons.collections.CollectionUtils; @@ -62,12 +62,14 @@ import org.apache.cloudstack.api.command.user.template.ListTemplatePermissionsCm import org.apache.cloudstack.api.command.user.template.RegisterTemplateCmd; import org.apache.cloudstack.api.command.user.template.UpdateTemplateCmd; import org.apache.cloudstack.api.command.user.template.UpdateTemplatePermissionsCmd; +import org.apache.cloudstack.api.response.GetUploadParamsResponse; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint; import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector; +import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore; import org.apache.cloudstack.engine.subsystem.api.storage.Scope; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; @@ -79,7 +81,6 @@ import org.apache.cloudstack.engine.subsystem.api.storage.TemplateService.Templa import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope; -import org.apache.cloudstack.framework.async.AsyncCallFuture; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; @@ -689,7 +690,9 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, } try { + templateStoragePoolRef.setTemplateSize(0); templateStoragePoolRef.setDownloadState(VMTemplateStorageResourceAssoc.Status.NOT_DOWNLOADED); + _tmpltPoolDao.update(templateStoragePoolRefId, templateStoragePoolRef); } finally { _tmpltPoolDao.releaseFromLockTable(templateStoragePoolRefId); @@ -873,41 +876,55 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, @Override @DB public void evictTemplateFromStoragePool(VMTemplateStoragePoolVO templatePoolVO) { - //Need to hold the lock, otherwise, another thread may create a volume from the template at the same time. - //Assumption here is that, we will hold the same lock during create volume from template + // Need to hold the lock; otherwise, another thread may create a volume from the template at the same time. + // Assumption here is that we will hold the same lock during create volume from template. VMTemplateStoragePoolVO templatePoolRef = _tmpltPoolDao.acquireInLockTable(templatePoolVO.getId()); + if (templatePoolRef == null) { - s_logger.debug("can't aquire the lock for template pool ref:" + templatePoolVO.getId()); + s_logger.debug("Can't aquire the lock for template pool ref: " + templatePoolVO.getId()); + return; } - try { - StoragePool pool = (StoragePool)_dataStoreMgr.getPrimaryDataStore(templatePoolVO.getPoolId()); - VMTemplateVO template = _tmpltDao.findByIdIncludingRemoved(templatePoolVO.getTemplateId()); + PrimaryDataStore pool = (PrimaryDataStore)_dataStoreMgr.getPrimaryDataStore(templatePoolVO.getPoolId()); + TemplateInfo template = _tmplFactory.getTemplate(templatePoolRef.getTemplateId(), pool); + try { if (s_logger.isDebugEnabled()) { s_logger.debug("Evicting " + templatePoolVO); } - DestroyCommand cmd = new DestroyCommand(pool, templatePoolVO); - try { + if (pool.isManaged()) { + // For managed store, just delete the template volume. + AsyncCallFuture<TemplateApiResult> future = _tmpltSvr.deleteTemplateOnPrimary(template, pool); + TemplateApiResult result = future.get(); + + if (result.isFailed()) { + s_logger.debug("Failed to delete template " + template.getId() + " from storage pool " + pool.getId()); + } else { + // Remove the templatePoolVO. + if (_tmpltPoolDao.remove(templatePoolVO.getId())) { + s_logger.debug("Successfully evicted template " + template.getName() + " from storage pool " + pool.getName()); + } + } + } else { + DestroyCommand cmd = new DestroyCommand(pool, templatePoolVO); Answer answer = _storageMgr.sendToPool(pool, cmd); if (answer != null && answer.getResult()) { - // Remove the templatePoolVO + // Remove the templatePoolVO. if (_tmpltPoolDao.remove(templatePoolVO.getId())) { - s_logger.debug("Successfully evicted template: " + template.getName() + " from storage pool: " + pool.getName()); + s_logger.debug("Successfully evicted template " + template.getName() + " from storage pool " + pool.getName()); } } else { - s_logger.info("Will retry evicte template: " + template.getName() + " from storage pool: " + pool.getName()); + s_logger.info("Will retry evict template " + template.getName() + " from storage pool " + pool.getName()); } - } catch (StorageUnavailableException e) { - s_logger.info("Storage is unavailable currently. Will retry evicte template: " + template.getName() + " from storage pool: " + pool.getName()); } + } catch (StorageUnavailableException | InterruptedException | ExecutionException e) { + s_logger.info("Storage is unavailable currently. Will retry evicte template " + template.getName() + " from storage pool " + pool.getName()); } finally { _tmpltPoolDao.releaseFromLockTable(templatePoolRef.getId()); } - } @Override @@ -1482,14 +1499,17 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, future = _tmpltSvr.createTemplateFromSnapshotAsync(snapInfo, tmplInfo, store); } else if (volumeId != null) { VolumeInfo volInfo = _volFactory.getVolume(volumeId); + future = _tmpltSvr.createTemplateFromVolumeAsync(volInfo, tmplInfo, store); } else { throw new CloudRuntimeException("Creating private Template need to specify snapshotId or volumeId"); } CommandResult result = null; + try { result = future.get(); + if (result.isFailed()) { privateTemplate = null; s_logger.debug("Failed to create template" + result.getResult()); http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/ui/scripts/storage.js ---------------------------------------------------------------------- diff --git a/ui/scripts/storage.js b/ui/scripts/storage.js index 39dfccf..29cd4c1 100644 --- a/ui/scripts/storage.js +++ b/ui/scripts/storage.js @@ -1550,6 +1550,8 @@ preFilter: function(args) { if (args.context.volumes != null && args.context.volumes[0].type == 'ROOT') { args.$form.find('.form-item[rel=newdiskoffering]').hide(); + + selectedDiskOfferingObj = null; } else { args.$form.find('.form-item[rel=newsize]').hide(); } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/utils/src/main/java/com/cloud/utils/Utils.java ---------------------------------------------------------------------- diff --git a/utils/src/main/java/com/cloud/utils/Utils.java b/utils/src/main/java/com/cloud/utils/Utils.java new file mode 100644 index 0000000..53f0a80 --- /dev/null +++ b/utils/src/main/java/com/cloud/utils/Utils.java @@ -0,0 +1,38 @@ +// +// 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 com.cloud.utils; + +import java.util.Map; + +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; + +public class Utils { + public static <K, V> Map getImmutableMap(Map<K, V> map) { + Map<K, V> filteredMap = Maps.filterValues(map, new Predicate<V>() { + public boolean apply(final V input) { + return input != null; + } + }); + + return ImmutableMap.<K, V>builder().putAll(filteredMap).build(); + } +}
