Skip to content

Commit 10c513a

Browse files
author
Mike Tutkowski
committed
CLOUDSTACK-4810: Enable hypervisor snapshots for CloudStack-managed storage (for XenServer and VMware)
1 parent 6916665 commit 10c513a

15 files changed

Lines changed: 126 additions & 12 deletions

File tree

api/src/com/cloud/offering/DiskOffering.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,4 +89,7 @@ enum State {
8989

9090
Long getIopsWriteRate();
9191

92+
void setHypervisorSnapshotReserve(Integer hypervisorSnapshotReserve);
93+
94+
Integer getHypervisorSnapshotReserve();
9295
}

api/src/com/cloud/storage/Volume.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,4 +185,8 @@ enum Event {
185185
void setReservationId(String reserv);
186186
Storage.ImageFormat getFormat();
187187
Long getVmSnapshotChainSize();
188+
189+
void setHypervisorSnapshotReserve(Integer hypervisorSnapshotReserve);
190+
191+
Integer getHypervisorSnapshotReserve();
188192
}

api/src/org/apache/cloudstack/api/ApiConstants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ public class ApiConstants {
5454
public static final String CUSTOMIZED_IOPS = "customizediops";
5555
public static final String MIN_IOPS = "miniops";
5656
public static final String MAX_IOPS = "maxiops";
57+
public static final String HYPERVISOR_SNAPSHOT_RESERVE = "hypervisorsnapshotreserve";
5758
public static final String DESCRIPTION = "description";
5859
public static final String DESTINATION_ZONE_ID = "destzoneid";
5960
public static final String DETAILS = "details";

api/src/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmd.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ public class CreateDiskOfferingCmd extends BaseCmd {
8686
@Parameter(name=ApiConstants.MAX_IOPS, type=CommandType.LONG, required=false, description="max iops of the disk offering")
8787
private Long maxIops;
8888

89+
@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)")
90+
private Integer hypervisorSnapshotReserve;
91+
8992
/////////////////////////////////////////////////////
9093
/////////////////// Accessors ///////////////////////
9194
/////////////////////////////////////////////////////
@@ -150,6 +153,10 @@ public Boolean getDisplayOffering() {
150153
return displayOffering;
151154
}
152155

156+
public Integer getHypervisorSnapshotReserve() {
157+
return hypervisorSnapshotReserve;
158+
}
159+
153160
/////////////////////////////////////////////////////
154161
/////////////// API Implementation///////////////////
155162
/////////////////////////////////////////////////////

client/WEB-INF/classes/resources/messages.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ label.custom.disk.iops=Custom IOPS
3939
label.disk.iops.min=Min IOPS
4040
label.disk.iops.max=Max IOPS
4141
label.disk.iops.total=IOPS Total
42+
label.hypervisor.snapshot.reserve=Hypervisor Snapshot Reserve
4243
label.view.secondary.ips=View secondary IPs
4344
message.validate.invalid.characters=Invalid characters found; please correct.
4445
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.

engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,7 @@ public VolumeInfo createVolume(VolumeInfo volume, VirtualMachine vm, VirtualMach
406406
AsyncCallFuture<VolumeApiResult> future = null;
407407
boolean isNotCreatedFromTemplate = volume.getTemplateId() == null ? true : false;
408408
if (isNotCreatedFromTemplate) {
409+
volume = updateHypervisorSnapshotReserveForVolume(diskOffering, volume, hyperType);
409410
future = volService.createVolumeAsync(volume, store);
410411
} else {
411412
TemplateInfo templ = tmplFactory.getTemplate(template.getId(), DataStoreRole.Image);
@@ -435,6 +436,29 @@ public VolumeInfo createVolume(VolumeInfo volume, VirtualMachine vm, VirtualMach
435436
throw new CloudRuntimeException("create volume failed even after template re-deploy");
436437
}
437438

439+
// For managed storage on Xen and VMware, we need to potentially make space for hypervisor snapshots.
440+
// The disk offering can collect this information and pass it on to the volume that's about to be created.
441+
// Ex. if you want a 10 GB CloudStack volume to reside on managed storage on Xen, this leads to an SR
442+
// that is a total size of (10 GB * (hypervisorSnapshotReserveSpace / 100) + 10 GB).
443+
private VolumeInfo updateHypervisorSnapshotReserveForVolume(DiskOffering diskOffering, VolumeInfo volumeInfo, HypervisorType hyperType) {
444+
Integer hypervisorSnapshotReserve = diskOffering.getHypervisorSnapshotReserve();
445+
446+
if (hyperType == HypervisorType.KVM) {
447+
hypervisorSnapshotReserve = null;
448+
}
449+
else if (hypervisorSnapshotReserve == null || hypervisorSnapshotReserve < 0) {
450+
hypervisorSnapshotReserve = 0;
451+
}
452+
453+
VolumeVO volume = _volsDao.findById(volumeInfo.getId());
454+
455+
volume.setHypervisorSnapshotReserve(hypervisorSnapshotReserve);
456+
457+
_volsDao.update(volume.getId(), volume);
458+
459+
return volFactory.getVolume(volume.getId());
460+
}
461+
438462
public String getRandomVolumeName() {
439463
return UUID.randomUUID().toString();
440464
}

engine/schema/src/com/cloud/storage/DiskOfferingVO.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,9 @@ public enum Type {
127127
@Column(name = "state")
128128
State state;
129129

130+
@Column(name="hv_ss_reserve")
131+
Integer hypervisorSnapshotReserve;
132+
130133
public DiskOfferingVO() {
131134
uuid = UUID.randomUUID().toString();
132135
}
@@ -440,4 +443,12 @@ public void setIopsWriteRate(Long iopsWriteRate) {
440443
public Long getIopsWriteRate() {
441444
return iopsWriteRate;
442445
}
446+
447+
public void setHypervisorSnapshotReserve(Integer hypervisorSnapshotReserve) {
448+
this.hypervisorSnapshotReserve = hypervisorSnapshotReserve;
449+
}
450+
451+
public Integer getHypervisorSnapshotReserve() {
452+
return hypervisorSnapshotReserve;
453+
}
443454
}

engine/schema/src/com/cloud/storage/VolumeVO.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
import com.cloud.storage.Storage.StoragePoolType;
3636
import com.cloud.utils.NumbersUtil;
3737
import com.cloud.utils.db.GenericDao;
38-
import com.cloud.vm.VirtualMachine.State;
3938

4039
@Entity
4140
@Table(name = "volumes")
@@ -161,6 +160,9 @@ public class VolumeVO implements Volume {
161160
// @Column(name="reservation")
162161
String reservationId;
163162

163+
@Column(name="hv_ss_reserve")
164+
Integer hypervisorSnapshotReserve;
165+
164166
// Real Constructor
165167
public VolumeVO(Type type, String name, long dcId, long domainId,
166168
long accountId, long diskOfferingId, long size,
@@ -580,4 +582,12 @@ public void setIsoId(Long isoId) {
580582
public void setState(State state) {
581583
this.state = state;
582584
}
585+
586+
public void setHypervisorSnapshotReserve(Integer hypervisorSnapshotReserve) {
587+
this.hypervisorSnapshotReserve = hypervisorSnapshotReserve;
588+
}
589+
590+
public Integer getHypervisorSnapshotReserve() {
591+
return hypervisorSnapshotReserve;
592+
}
583593
}

engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,16 @@ public Long getMaxIops() {
141141
return volumeVO.getMaxIops();
142142
}
143143

144+
@Override
145+
public void setHypervisorSnapshotReserve(Integer hypervisorSnapshotReserve) {
146+
volumeVO.setHypervisorSnapshotReserve(hypervisorSnapshotReserve);
147+
}
148+
149+
@Override
150+
public Integer getHypervisorSnapshotReserve() {
151+
return volumeVO.getHypervisorSnapshotReserve();
152+
}
153+
144154
public long getVolumeId() {
145155
return volumeVO.getId();
146156
}

plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidfirePrimaryDataStoreDriver.java

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
// under the License.
1717
package org.apache.cloudstack.storage.datastore.driver;
1818

19+
import java.text.NumberFormat;
1920
import java.util.List;
2021

2122
import javax.inject.Inject;
@@ -278,8 +279,7 @@ private SolidFireUtil.SolidFireVolume createSolidFireVolume(VolumeInfo volumeInf
278279
Long minIops = volumeInfo.getMinIops();
279280
Long maxIops = volumeInfo.getMaxIops();
280281

281-
if (minIops == null || minIops <= 0 ||
282-
maxIops == null || maxIops <= 0) {
282+
if (minIops == null || minIops <= 0 || maxIops == null || maxIops <= 0) {
283283
long defaultMaxIops = getDefaultMaxIops(storagePoolId);
284284

285285
iops = new Iops(getDefaultMinIops(storagePoolId), defaultMaxIops, getDefaultBurstIops(storagePoolId, defaultMaxIops));
@@ -288,10 +288,20 @@ private SolidFireUtil.SolidFireVolume createSolidFireVolume(VolumeInfo volumeInf
288288
iops = new Iops(volumeInfo.getMinIops(), volumeInfo.getMaxIops(), getDefaultBurstIops(storagePoolId, volumeInfo.getMaxIops()));
289289
}
290290

291-
long volumeSize = volumeInfo.getSize() * 2; // in reality, use a multiplier that's at cluster-level scope
291+
long volumeSize = volumeInfo.getSize();
292+
Integer hypervisorSnapshotReserve = volumeInfo.getHypervisorSnapshotReserve();
293+
294+
if (hypervisorSnapshotReserve != null) {
295+
if (hypervisorSnapshotReserve < 25) {
296+
hypervisorSnapshotReserve = 25;
297+
}
298+
299+
volumeSize += volumeSize * (hypervisorSnapshotReserve / 100f);
300+
}
292301

293302
long sfVolumeId = SolidFireUtil.createSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword,
294-
getSolidFireVolumeName(volumeInfo.getName()), sfAccountId, volumeSize, true, volumeInfo.getSize().toString(),
303+
getSolidFireVolumeName(volumeInfo.getName()), sfAccountId, volumeSize, true,
304+
NumberFormat.getNumberInstance().format(volumeInfo.getSize().toString()),
295305
iops.getMinIops(), iops.getMaxIops(), iops.getBurstIops());
296306

297307
return SolidFireUtil.getSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfVolumeId);
@@ -410,8 +420,7 @@ public void createAsync(DataStore dataStore, DataObject dataObject,
410420
SolidFireConnection sfConnection = getSolidFireConnection(storagePoolId);
411421

412422
if (!sfAccountExists(sfAccountName, sfConnection)) {
413-
SolidFireUtil.SolidFireAccount sfAccount = createSolidFireAccount(sfAccountName,
414-
sfConnection);
423+
SolidFireUtil.SolidFireAccount sfAccount = createSolidFireAccount(sfAccountName, sfConnection);
415424

416425
updateCsDbWithAccountInfo(account.getId(), sfAccount);
417426
}

0 commit comments

Comments
 (0)