CLOUDSTACK-4810: Enable hypervisor snapshots for CloudStack-managed storage (for XenServer and VMware)

This commit is contained in:
Mike Tutkowski 2013-11-06 20:51:33 -07:00
parent 6916665623
commit 10c513a259
15 changed files with 126 additions and 12 deletions

View File

@ -89,4 +89,7 @@ public interface DiskOffering extends InfrastructureEntity, Identity, InternalId
Long getIopsWriteRate();
void setHypervisorSnapshotReserve(Integer hypervisorSnapshotReserve);
Integer getHypervisorSnapshotReserve();
}

View File

@ -185,4 +185,8 @@ public interface Volume extends ControlledEntity, Identity, InternalIdentity, Ba
void setReservationId(String reserv);
Storage.ImageFormat getFormat();
Long getVmSnapshotChainSize();
void setHypervisorSnapshotReserve(Integer hypervisorSnapshotReserve);
Integer getHypervisorSnapshotReserve();
}

View File

@ -54,6 +54,7 @@ public class ApiConstants {
public static final String CUSTOMIZED_IOPS = "customizediops";
public static final String MIN_IOPS = "miniops";
public static final String MAX_IOPS = "maxiops";
public static final String HYPERVISOR_SNAPSHOT_RESERVE = "hypervisorsnapshotreserve";
public static final String DESCRIPTION = "description";
public static final String DESTINATION_ZONE_ID = "destzoneid";
public static final String DETAILS = "details";

View File

@ -86,6 +86,9 @@ public class CreateDiskOfferingCmd extends BaseCmd {
@Parameter(name=ApiConstants.MAX_IOPS, type=CommandType.LONG, required=false, description="max iops of the disk offering")
private Long maxIops;
@Parameter(name=ApiConstants.HYPERVISOR_SNAPSHOT_RESERVE, type=CommandType.INTEGER, required=false, description="Hypervisor snapshot reserve space as a percent of a volume (for managed storage using Xen or VMware)")
private Integer hypervisorSnapshotReserve;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@ -150,6 +153,10 @@ public class CreateDiskOfferingCmd extends BaseCmd {
return displayOffering;
}
public Integer getHypervisorSnapshotReserve() {
return hypervisorSnapshotReserve;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////

View File

@ -39,6 +39,7 @@ label.custom.disk.iops=Custom IOPS
label.disk.iops.min=Min IOPS
label.disk.iops.max=Max IOPS
label.disk.iops.total=IOPS Total
label.hypervisor.snapshot.reserve=Hypervisor Snapshot Reserve
label.view.secondary.ips=View secondary IPs
message.validate.invalid.characters=Invalid characters found; please correct.
message.acquire.ip.nic=Please confirm that you would like to acquire a new secondary IP for this NIC.<br/>NOTE: You need to manually configure the newly-acquired secondary IP inside the virtual machine.

View File

@ -406,6 +406,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
AsyncCallFuture<VolumeApiResult> future = null;
boolean isNotCreatedFromTemplate = volume.getTemplateId() == null ? true : false;
if (isNotCreatedFromTemplate) {
volume = updateHypervisorSnapshotReserveForVolume(diskOffering, volume, hyperType);
future = volService.createVolumeAsync(volume, store);
} else {
TemplateInfo templ = tmplFactory.getTemplate(template.getId(), DataStoreRole.Image);
@ -435,6 +436,29 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
throw new CloudRuntimeException("create volume failed even after template re-deploy");
}
// For managed storage on Xen and VMware, we need to potentially make space for hypervisor snapshots.
// The disk offering can collect this information and pass it on to the volume that's about to be created.
// Ex. if you want a 10 GB CloudStack volume to reside on managed storage on Xen, this leads to an SR
// that is a total size of (10 GB * (hypervisorSnapshotReserveSpace / 100) + 10 GB).
private VolumeInfo updateHypervisorSnapshotReserveForVolume(DiskOffering diskOffering, VolumeInfo volumeInfo, HypervisorType hyperType) {
Integer hypervisorSnapshotReserve = diskOffering.getHypervisorSnapshotReserve();
if (hyperType == HypervisorType.KVM) {
hypervisorSnapshotReserve = null;
}
else if (hypervisorSnapshotReserve == null || hypervisorSnapshotReserve < 0) {
hypervisorSnapshotReserve = 0;
}
VolumeVO volume = _volsDao.findById(volumeInfo.getId());
volume.setHypervisorSnapshotReserve(hypervisorSnapshotReserve);
_volsDao.update(volume.getId(), volume);
return volFactory.getVolume(volume.getId());
}
public String getRandomVolumeName() {
return UUID.randomUUID().toString();
}

View File

@ -127,6 +127,9 @@ public class DiskOfferingVO implements DiskOffering {
@Column(name = "state")
State state;
@Column(name="hv_ss_reserve")
Integer hypervisorSnapshotReserve;
public DiskOfferingVO() {
uuid = UUID.randomUUID().toString();
}
@ -440,4 +443,12 @@ public class DiskOfferingVO implements DiskOffering {
public Long getIopsWriteRate() {
return iopsWriteRate;
}
public void setHypervisorSnapshotReserve(Integer hypervisorSnapshotReserve) {
this.hypervisorSnapshotReserve = hypervisorSnapshotReserve;
}
public Integer getHypervisorSnapshotReserve() {
return hypervisorSnapshotReserve;
}
}

View File

@ -35,7 +35,6 @@ import javax.persistence.Transient;
import com.cloud.storage.Storage.StoragePoolType;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.db.GenericDao;
import com.cloud.vm.VirtualMachine.State;
@Entity
@Table(name = "volumes")
@ -161,6 +160,9 @@ public class VolumeVO implements Volume {
// @Column(name="reservation")
String reservationId;
@Column(name="hv_ss_reserve")
Integer hypervisorSnapshotReserve;
// Real Constructor
public VolumeVO(Type type, String name, long dcId, long domainId,
long accountId, long diskOfferingId, long size,
@ -580,4 +582,12 @@ public class VolumeVO implements Volume {
public void setState(State state) {
this.state = state;
}
public void setHypervisorSnapshotReserve(Integer hypervisorSnapshotReserve) {
this.hypervisorSnapshotReserve = hypervisorSnapshotReserve;
}
public Integer getHypervisorSnapshotReserve() {
return hypervisorSnapshotReserve;
}
}

View File

@ -141,6 +141,16 @@ public class VolumeObject implements VolumeInfo {
return volumeVO.getMaxIops();
}
@Override
public void setHypervisorSnapshotReserve(Integer hypervisorSnapshotReserve) {
volumeVO.setHypervisorSnapshotReserve(hypervisorSnapshotReserve);
}
@Override
public Integer getHypervisorSnapshotReserve() {
return volumeVO.getHypervisorSnapshotReserve();
}
public long getVolumeId() {
return volumeVO.getId();
}

View File

@ -16,6 +16,7 @@
// under the License.
package org.apache.cloudstack.storage.datastore.driver;
import java.text.NumberFormat;
import java.util.List;
import javax.inject.Inject;
@ -278,8 +279,7 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
Long minIops = volumeInfo.getMinIops();
Long maxIops = volumeInfo.getMaxIops();
if (minIops == null || minIops <= 0 ||
maxIops == null || maxIops <= 0) {
if (minIops == null || minIops <= 0 || maxIops == null || maxIops <= 0) {
long defaultMaxIops = getDefaultMaxIops(storagePoolId);
iops = new Iops(getDefaultMinIops(storagePoolId), defaultMaxIops, getDefaultBurstIops(storagePoolId, defaultMaxIops));
@ -288,10 +288,20 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
iops = new Iops(volumeInfo.getMinIops(), volumeInfo.getMaxIops(), getDefaultBurstIops(storagePoolId, volumeInfo.getMaxIops()));
}
long volumeSize = volumeInfo.getSize() * 2; // in reality, use a multiplier that's at cluster-level scope
long volumeSize = volumeInfo.getSize();
Integer hypervisorSnapshotReserve = volumeInfo.getHypervisorSnapshotReserve();
if (hypervisorSnapshotReserve != null) {
if (hypervisorSnapshotReserve < 25) {
hypervisorSnapshotReserve = 25;
}
volumeSize += volumeSize * (hypervisorSnapshotReserve / 100f);
}
long sfVolumeId = SolidFireUtil.createSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword,
getSolidFireVolumeName(volumeInfo.getName()), sfAccountId, volumeSize, true, volumeInfo.getSize().toString(),
getSolidFireVolumeName(volumeInfo.getName()), sfAccountId, volumeSize, true,
NumberFormat.getNumberInstance().format(volumeInfo.getSize().toString()),
iops.getMinIops(), iops.getMaxIops(), iops.getBurstIops());
return SolidFireUtil.getSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfVolumeId);
@ -410,8 +420,7 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
SolidFireConnection sfConnection = getSolidFireConnection(storagePoolId);
if (!sfAccountExists(sfAccountName, sfConnection)) {
SolidFireUtil.SolidFireAccount sfAccount = createSolidFireAccount(sfAccountName,
sfConnection);
SolidFireUtil.SolidFireAccount sfAccount = createSolidFireAccount(sfAccountName, sfConnection);
updateCsDbWithAccountInfo(account.getId(), sfAccount);
}

View File

@ -2188,7 +2188,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
protected DiskOfferingVO createDiskOffering(Long domainId, String name, String description, Long numGibibytes, String tags, boolean isCustomized,
boolean localStorageRequired, boolean isDisplayOfferingEnabled, Boolean isCustomizedIops, Long minIops, Long maxIops,
Long bytesReadRate, Long bytesWriteRate, Long iopsReadRate, Long iopsWriteRate) {
Long bytesReadRate, Long bytesWriteRate, Long iopsReadRate, Long iopsWriteRate, Integer hypervisorSnapshotReserve) {
long diskSize = 0;// special case for custom disk offerings
if (numGibibytes != null && (numGibibytes <= 0)) {
throw new InvalidParameterValueException("Please specify a disk size of at least 1 Gb.");
@ -2254,6 +2254,12 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
if (iopsWriteRate != null && (iopsWriteRate > 0))
newDiskOffering.setIopsWriteRate(iopsWriteRate);
if (hypervisorSnapshotReserve != null && hypervisorSnapshotReserve < 0) {
throw new InvalidParameterValueException("If provided, Hypervisor Snapshot Reserve must be greater than or equal to 0.");
}
newDiskOffering.setHypervisorSnapshotReserve(hypervisorSnapshotReserve);
CallContext.current().setEventDetails("Disk offering id=" + newDiskOffering.getId());
DiskOfferingVO offering = _diskOfferingDao.persist(newDiskOffering);
if (offering != null) {
@ -2303,10 +2309,11 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
Long bytesWriteRate = cmd.getBytesWriteRate();
Long iopsReadRate = cmd.getIopsReadRate();
Long iopsWriteRate = cmd.getIopsWriteRate();
Integer hypervisorSnapshotReserve = cmd.getHypervisorSnapshotReserve();
return createDiskOffering(domainId, name, description, numGibibytes, tags, isCustomized,
localStorageRequired, isDisplayOfferingEnabled, isCustomizedIops, minIops, maxIops,
bytesReadRate, bytesWriteRate, iopsReadRate, iopsWriteRate);
localStorageRequired, isDisplayOfferingEnabled, isCustomizedIops, minIops, maxIops,
bytesReadRate, bytesWriteRate, iopsReadRate, iopsWriteRate, hypervisorSnapshotReserve);
}
@Override

View File

@ -16,7 +16,7 @@
-- under the License.
--;
-- Schema upgrade from 4.2.0 to 4.3.0;
-- Schema upgrade from 4.2.1 to 4.3.0;
--;
-- Disable foreign key checking
@ -108,6 +108,9 @@ UPDATE `cloud`.`configuration` SET `default_value` = `value`;
#Upgrade the offerings and template table to have actual remove and states
ALTER TABLE `cloud`.`disk_offering` ADD COLUMN `state` CHAR(40) NOT NULL DEFAULT 'Active' COMMENT 'state for disk offering';
ALTER TABLE `cloud`.`disk_offering` ADD COLUMN `hv_ss_reserve` int(32) unsigned DEFAULT NULL COMMENT 'Hypervisor snapshot reserve space as a percent of a volume (for managed storage using Xen or VMware)';
ALTER TABLE `cloud`.`volumes` ADD COLUMN `hv_ss_reserve` int(32) unsigned DEFAULT NULL COMMENT 'Hypervisor snapshot reserve space as a percent of a volume (for managed storage using Xen or VMware)';
UPDATE `cloud`.`disk_offering` SET `state`='Inactive' WHERE `removed` IS NOT NULL;
UPDATE `cloud`.`disk_offering` SET `removed`=NULL;

View File

@ -50,6 +50,7 @@ dictionary = {
'label.custom.disk.iops': '<fmt:message key="label.custom.disk.iops" />',
'label.disk.iops.min': '<fmt:message key="label.disk.iops.min" />',
'label.disk.iops.max': '<fmt:message key="label.disk.iops.max" />',
'label.hypervisor.snapshot.reserve': '<fmt:message key="label.hypervisor.snapshot.reserve" />',
'label.acquire.new.secondary.ip': '<fmt:message key="label.acquire.new.secondary.ip" />',
'label.view.secondary.ips': '<fmt:message key="label.view.secondary.ips" />',
'message.acquire.ip.nic': '<fmt:message key="message.acquire.ip.nic" />',

View File

@ -1241,6 +1241,7 @@
var $isCustomizedIops = $form.find('.form-item[rel=isCustomizedIops]');
var $minIops = $form.find('.form-item[rel=minIops]');
var $maxIops = $form.find('.form-item[rel=maxIops]');
var $hypervisorSnapshotReserve = $form.find('.form-item[rel=hypervisorSnapshotReserve]');
var $diskBytesReadRate = $form.find('.form-item[rel=diskBytesReadRate]');
var $diskBytesWriteRate = $form.find('.form-item[rel=diskBytesWriteRate]');
var $diskIopsReadRate = $form.find('.form-item[rel=diskIopsReadRate]');
@ -1256,17 +1257,20 @@
$isCustomizedIops.css('display', 'inline-block');
if ($isCustomizedIops == true) {
if ($isCustomizedIops.find('input[type=checkbox]').is(':checked')) {
$minIops.hide();
$maxIops.hide();
} else {
$minIops.css('display', 'inline-block');
$maxIops.css('display', 'inline-block');
}
$hypervisorSnapshotReserve.css('display', 'inline-block');
} else if (qosId == 'hypervisor') { // Hypervisor Qos
$isCustomizedIops.hide();
$minIops.hide();
$maxIops.hide();
$hypervisorSnapshotReserve.hide();
$diskBytesReadRate.css('display', 'inline-block');
$diskBytesWriteRate.css('display', 'inline-block');
@ -1280,6 +1284,7 @@
$isCustomizedIops.hide();
$minIops.hide();
$maxIops.hide();
$hypervisorSnapshotReserve.hide();
}
});
}
@ -1309,6 +1314,14 @@
number: true
}
},
hypervisorSnapshotReserve: {
label: 'label.hypervisor.snapshot.reserve',
docID: 'helpDiskOfferingHypervisorSnapshotReserve',
validation: {
required: false,
number: true
}
},
diskBytesReadRate: {
label: 'label.disk.bytes.read.rate',
docID: 'helpDiskOfferingDiskBytesReadRate',
@ -1416,6 +1429,12 @@
});
}
}
if (args.data.hypervisorSnapshotReserve != null && args.data.hypervisorSnapshotReserve.length > 0) {
$.extend(data, {
hypervisorsnapshotreserve: args.data.hypervisorSnapshotReserve
});
}
} else if (args.data.qosType == 'hypervisor') {
if (args.data.diskBytesReadRate != null && args.data.diskBytesReadRate.length > 0) {
$.extend(data, {

View File

@ -326,6 +326,10 @@ cloudStack.docs = {
desc: 'Appears only if Custom IOPS is not selected. Define the maximum volume IOPS.',
externalLink: ''
},
helpDiskOfferingHypervisorSnapshotReserve: {
desc: 'Hypervisor snapshot reserve space as a percent of a volume (for managed storage using Xen or VMware) (Ex. The value 25 means 25%.)).',
externalLink: ''
},
helpDiskOfferingStorageTags: {
desc: 'Comma-separated list of attributes that should be associated with the primary storage for this disk. For example "ssd,blue".',
externalLink: ''