mirror of https://github.com/apache/cloudstack.git
CLOUDSTACK-4810: Enable hypervisor snapshots for CloudStack-managed storage (for XenServer and VMware)
This commit is contained in:
parent
6916665623
commit
10c513a259
|
|
@ -89,4 +89,7 @@ public interface DiskOffering extends InfrastructureEntity, Identity, InternalId
|
|||
|
||||
Long getIopsWriteRate();
|
||||
|
||||
void setHypervisorSnapshotReserve(Integer hypervisorSnapshotReserve);
|
||||
|
||||
Integer getHypervisorSnapshotReserve();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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" />',
|
||||
|
|
|
|||
|
|
@ -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, {
|
||||
|
|
|
|||
|
|
@ -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: ''
|
||||
|
|
|
|||
Loading…
Reference in New Issue