Skip to content

Commit 03118c2

Browse files
author
Mike Tutkowski
committed
Merge from 4.3: CLOUDSTACK-4810: Enable hypervisor snapshots for CloudStack-managed storage (for XenServer and VMware)
1 parent 09da515 commit 03118c2

12 files changed

Lines changed: 129 additions & 44 deletions

File tree

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,5 @@ enum Event {
189189

190190
Long getVmSnapshotChainSize();
191191

192-
void setHypervisorSnapshotReserve(Integer hypervisorSnapshotReserve);
193-
194192
Integer getHypervisorSnapshotReserve();
195193
}

engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreDriver.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,14 @@
2121
import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
2222
import org.apache.cloudstack.storage.command.CommandResult;
2323

24+
import com.cloud.storage.StoragePool;
25+
import com.cloud.storage.Volume;
26+
2427
public interface PrimaryDataStoreDriver extends DataStoreDriver {
2528
public ChapInfo getChapInfo(VolumeInfo volumeInfo);
2629

30+
public long getVolumeSizeIncludingHypervisorSnapshotReserve(Volume volume, StoragePool pool);
31+
2732
public void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CreateCmdResult> callback);
2833

2934
public void revertSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CommandResult> callback);

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,8 @@ public VolumeInfo copyVolumeFromSecToPrimary(VolumeInfo volume, VirtualMachine v
404404
@DB
405405
public VolumeInfo createVolume(VolumeInfo volume, VirtualMachine vm, VirtualMachineTemplate template, DataCenter dc, Pod pod, Long clusterId, ServiceOffering offering,
406406
DiskOffering diskOffering, List<StoragePool> avoids, long size, HypervisorType hyperType) {
407+
volume = updateHypervisorSnapshotReserveForVolume(diskOffering, volume, hyperType);
408+
407409
StoragePool pool = null;
408410

409411
DiskProfile dskCh = null;
@@ -423,8 +425,8 @@ public VolumeInfo createVolume(VolumeInfo volume, VirtualMachine vm, VirtualMach
423425

424426
pool = findStoragePool(dskCh, dc, pod, clusterId, vm.getHostId(), vm, avoidPools);
425427
if (pool == null) {
426-
s_logger.warn("Unable to find storage pool when create volume " + volume.getName());
427-
throw new CloudRuntimeException("Unable to find storage pool when create volume" + volume.getName());
428+
s_logger.warn("Unable to find suitable primary storage when creating volume " + volume.getName());
429+
throw new CloudRuntimeException("Unable to find suitable primary storage when creating volume " + volume.getName());
428430
}
429431

430432
if (s_logger.isDebugEnabled()) {
@@ -436,7 +438,6 @@ public VolumeInfo createVolume(VolumeInfo volume, VirtualMachine vm, VirtualMach
436438
AsyncCallFuture<VolumeApiResult> future = null;
437439
boolean isNotCreatedFromTemplate = volume.getTemplateId() == null ? true : false;
438440
if (isNotCreatedFromTemplate) {
439-
volume = updateHypervisorSnapshotReserveForVolume(diskOffering, volume, hyperType);
440441
future = volService.createVolumeAsync(volume, store);
441442
} else {
442443
TemplateInfo templ = tmplFactory.getTemplate(template.getId(), DataStoreRole.Image);

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -587,7 +587,6 @@ public void setState(State state) {
587587
this.state = state;
588588
}
589589

590-
@Override
591590
public void setHypervisorSnapshotReserve(Integer hypervisorSnapshotReserve) {
592591
this.hypervisorSnapshotReserve = hypervisorSnapshotReserve;
593592
}

engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakePrimaryDataStoreDriver.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636

3737
import com.cloud.agent.api.to.DataStoreTO;
3838
import com.cloud.agent.api.to.DataTO;
39+
import com.cloud.storage.StoragePool;
40+
import com.cloud.storage.Volume;
3941

4042
public class FakePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
4143
boolean snapshotResult = true;
@@ -45,6 +47,11 @@ public ChapInfo getChapInfo(VolumeInfo volumeInfo) {
4547
return null; //To change body of implemented methods use File | Settings | File Templates.
4648
}
4749

50+
@Override
51+
public long getVolumeSizeIncludingHypervisorSnapshotReserve(Volume volume, StoragePool pool) {
52+
return volume.getSize();
53+
}
54+
4855
@Override
4956
public void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CreateCmdResult> callback) {
5057
CreateCmdResult result = new CreateCmdResult(null, null);

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,6 @@ public Long getMaxIops() {
153153
return volumeVO.getMaxIops();
154154
}
155155

156-
@Override
157156
public void setHypervisorSnapshotReserve(Integer hypervisorSnapshotReserve) {
158157
volumeVO.setHypervisorSnapshotReserve(hypervisorSnapshotReserve);
159158
}

plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
import com.cloud.storage.Storage;
6969
import com.cloud.storage.StorageManager;
7070
import com.cloud.storage.StoragePool;
71+
import com.cloud.storage.Volume;
7172
import com.cloud.storage.dao.DiskOfferingDao;
7273
import com.cloud.storage.dao.SnapshotDao;
7374
import com.cloud.storage.dao.VMTemplateDao;
@@ -148,6 +149,11 @@ public ChapInfo getChapInfo(VolumeInfo volumeInfo) {
148149
return null;
149150
}
150151

152+
@Override
153+
public long getVolumeSizeIncludingHypervisorSnapshotReserve(Volume volume, StoragePool pool) {
154+
return volume.getSize();
155+
}
156+
151157
@Override
152158
public void createAsync(DataStore dataStore, DataObject data, AsyncCompletionCallback<CreateCmdResult> callback) {
153159
String errMsg = null;

plugins/storage/volume/sample/src/org/apache/cloudstack/storage/datastore/driver/SamplePrimaryDataStoreDriverImpl.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@
4242
import com.cloud.agent.api.Answer;
4343
import com.cloud.agent.api.to.DataStoreTO;
4444
import com.cloud.agent.api.to.DataTO;
45+
import com.cloud.storage.StoragePool;
46+
import com.cloud.storage.Volume;
4547
import com.cloud.storage.dao.StoragePoolHostDao;
4648
import com.cloud.utils.exception.CloudRuntimeException;
4749

@@ -78,6 +80,11 @@ public ChapInfo getChapInfo(VolumeInfo volumeInfo) {
7880
return null;
7981
}
8082

83+
@Override
84+
public long getVolumeSizeIncludingHypervisorSnapshotReserve(Volume volume, StoragePool pool) {
85+
return volume.getSize();
86+
}
87+
8188
private class CreateVolumeContext<T> extends AsyncRpcContext<T> {
8289
private final DataObject volume;
8390

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

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@
4646
import com.cloud.agent.api.to.DataTO;
4747
import com.cloud.dc.dao.DataCenterDao;
4848
import com.cloud.storage.Storage.StoragePoolType;
49+
import com.cloud.storage.StoragePool;
50+
import com.cloud.storage.Volume;
4951
import com.cloud.storage.VolumeVO;
5052
import com.cloud.storage.dao.VolumeDao;
5153
import com.cloud.storage.dao.VolumeDetailsDao;
@@ -284,8 +286,20 @@ private SolidFireUtil.SolidFireVolume createSolidFireVolume(VolumeInfo volumeInf
284286
iops = new Iops(volumeInfo.getMinIops(), volumeInfo.getMaxIops(), getDefaultBurstIops(storagePoolId, volumeInfo.getMaxIops()));
285287
}
286288

287-
long volumeSize = volumeInfo.getSize();
288-
Integer hypervisorSnapshotReserve = volumeInfo.getHypervisorSnapshotReserve();
289+
long volumeSize = getVolumeSizeIncludingHypervisorSnapshotReserve(volumeInfo, _storagePoolDao.findById(storagePoolId));
290+
291+
long sfVolumeId = SolidFireUtil.createSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword,
292+
getSolidFireVolumeName(volumeInfo.getName()), sfAccountId, volumeSize, true,
293+
NumberFormat.getInstance().format(volumeInfo.getSize()),
294+
iops.getMinIops(), iops.getMaxIops(), iops.getBurstIops());
295+
296+
return SolidFireUtil.getSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfVolumeId);
297+
}
298+
299+
@Override
300+
public long getVolumeSizeIncludingHypervisorSnapshotReserve(Volume volume, StoragePool pool) {
301+
long volumeSize = volume.getSize();
302+
Integer hypervisorSnapshotReserve = volume.getHypervisorSnapshotReserve();
289303

290304
if (hypervisorSnapshotReserve != null) {
291305
if (hypervisorSnapshotReserve < 25) {
@@ -295,11 +309,7 @@ private SolidFireUtil.SolidFireVolume createSolidFireVolume(VolumeInfo volumeInf
295309
volumeSize += volumeSize * (hypervisorSnapshotReserve / 100f);
296310
}
297311

298-
long sfVolumeId =
299-
SolidFireUtil.createSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword, getSolidFireVolumeName(volumeInfo.getName()), sfAccountId,
300-
volumeSize, true, NumberFormat.getNumberInstance().format(volumeInfo.getSize().toString()), iops.getMinIops(), iops.getMaxIops(), iops.getBurstIops());
301-
302-
return SolidFireUtil.getSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfVolumeId);
312+
return volumeSize;
303313
}
304314

305315
private String getSolidFireVolumeName(String strCloudStackVolumeName) {
@@ -356,11 +366,12 @@ public long getBurstIops() {
356366
}
357367
}
358368

359-
private void deleteSolidFireVolume(VolumeInfo volumeInfo, SolidFireConnection sfConnection) {
369+
private SolidFireUtil.SolidFireVolume deleteSolidFireVolume(VolumeInfo volumeInfo, SolidFireConnection sfConnection)
370+
{
360371
Long storagePoolId = volumeInfo.getPoolId();
361372

362373
if (storagePoolId == null) {
363-
return; // this volume was never assigned to a storage pool, so no SAN volume should exist for it
374+
return null; // this volume was never assigned to a storage pool, so no SAN volume should exist for it
364375
}
365376

366377
String mVip = sfConnection.getManagementVip();
@@ -370,7 +381,7 @@ private void deleteSolidFireVolume(VolumeInfo volumeInfo, SolidFireConnection sf
370381

371382
long sfVolumeId = Long.parseLong(volumeInfo.getFolder());
372383

373-
SolidFireUtil.deleteSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfVolumeId);
384+
return SolidFireUtil.deleteSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfVolumeId);
374385
}
375386

376387
private String getSfAccountName(String csAccountUuid, long csAccountId) {
@@ -429,7 +440,7 @@ public void createAsync(DataStore dataStore, DataObject dataObject, AsyncComplet
429440
long capacityBytes = storagePool.getCapacityBytes();
430441
long usedBytes = storagePool.getUsedBytes();
431442

432-
usedBytes += volumeInfo.getSize();
443+
usedBytes += sfVolume.getTotalSize();
433444

434445
storagePool.setUsedBytes(usedBytes > capacityBytes ? capacityBytes : usedBytes);
435446

@@ -493,31 +504,31 @@ public void deleteAsync(DataStore dataStore, DataObject dataObject, AsyncComplet
493504

494505
if (dataObject.getType() == DataObjectType.VOLUME) {
495506
VolumeInfo volumeInfo = (VolumeInfo)dataObject;
496-
AccountVO account = _accountDao.findById(volumeInfo.getAccountId());
497-
AccountDetailVO accountDetails = _accountDetailsDao.findDetail(account.getAccountId(), SolidFireUtil.ACCOUNT_ID);
498-
long sfAccountId = Long.parseLong(accountDetails.getValue());
507+
// AccountVO account = _accountDao.findById(volumeInfo.getAccountId());
508+
// AccountDetailVO accountDetails = _accountDetailsDao.findDetail(account.getAccountId(), SolidFireUtil.ACCOUNT_ID);
509+
// long sfAccountId = Long.parseLong(accountDetails.getValue());
499510

500511
long storagePoolId = dataStore.getId();
501512
SolidFireConnection sfConnection = getSolidFireConnection(storagePoolId);
502513

503-
deleteSolidFireVolume(volumeInfo, sfConnection);
514+
SolidFireUtil.SolidFireVolume sfVolume = deleteSolidFireVolume(volumeInfo, sfConnection);
504515

505516
_volumeDao.deleteVolumesByInstance(volumeInfo.getId());
506517

507-
// if (!sfAccountHasVolume(sfAccountId, sfConnection)) {
508-
// // delete the account from the SolidFire SAN
509-
// deleteSolidFireAccount(sfAccountId, sfConnection);
518+
// if (!sfAccountHasVolume(sfAccountId, sfConnection)) {
519+
// // delete the account from the SolidFire SAN
520+
// deleteSolidFireAccount(sfAccountId, sfConnection);
510521
//
511-
// // delete the info in the account_details table
512-
// // that's related to the SolidFire account
513-
// _accountDetailsDao.deleteDetails(account.getAccountId());
514-
// }
522+
// // delete the info in the account_details table
523+
// // that's related to the SolidFire account
524+
// _accountDetailsDao.deleteDetails(account.getAccountId());
525+
// }
515526

516527
StoragePoolVO storagePool = _storagePoolDao.findById(storagePoolId);
517528

518529
long usedBytes = storagePool.getUsedBytes();
519530

520-
usedBytes -= volumeInfo.getSize();
531+
usedBytes -= sfVolume != null ? sfVolume.getTotalSize() : 0;
521532

522533
storagePool.setUsedBytes(usedBytes < 0 ? 0 : usedBytes);
523534

plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -95,14 +95,19 @@ public static long createSolidFireVolume(String strSfMvip, int iSfPort, String s
9595
return volumeCreateResult.result.volumeID;
9696
}
9797

98-
public static void deleteSolidFireVolume(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lVolumeId) {
98+
public static SolidFireVolume deleteSolidFireVolume(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lVolumeId)
99+
{
100+
SolidFireVolume sfVolume = getSolidFireVolume(strSfMvip, iSfPort, strSfAdmin, strSfPassword, lVolumeId);
101+
99102
final Gson gson = new GsonBuilder().create();
100103

101104
VolumeToDelete volumeToDelete = new VolumeToDelete(lVolumeId);
102105

103106
String strVolumeToDeleteJson = gson.toJson(volumeToDelete);
104107

105108
executeJsonRpc(strVolumeToDeleteJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword);
109+
110+
return sfVolume;
106111
}
107112

108113
public static void purgeSolidFireVolume(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lVolumeId) {
@@ -132,8 +137,9 @@ public static SolidFireVolume getSolidFireVolume(String strSfMvip, int iSfPort,
132137
String strVolumeIqn = getVolumeIqn(volumeGetResult, lVolumeId);
133138
long lAccountId = getVolumeAccountId(volumeGetResult, lVolumeId);
134139
String strVolumeStatus = getVolumeStatus(volumeGetResult, lVolumeId);
140+
long lTotalSize = getVolumeTotalSize(volumeGetResult, lVolumeId);
135141

136-
return new SolidFireVolume(lVolumeId, strVolumeName, strVolumeIqn, lAccountId, strVolumeStatus);
142+
return new SolidFireVolume(lVolumeId, strVolumeName, strVolumeIqn, lAccountId, strVolumeStatus, lTotalSize);
137143
}
138144

139145
public static List<SolidFireVolume> getSolidFireVolumesForAccountId(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lAccountId) {
@@ -152,7 +158,7 @@ public static List<SolidFireVolume> getSolidFireVolumesForAccountId(String strSf
152158
List<SolidFireVolume> sfVolumes = new ArrayList<SolidFireVolume>();
153159

154160
for (VolumeGetResult.Result.Volume volume : volumeGetResult.result.volumes) {
155-
sfVolumes.add(new SolidFireVolume(volume.volumeID, volume.name, volume.iqn, volume.accountID, volume.status));
161+
sfVolumes.add(new SolidFireVolume(volume.volumeID, volume.name, volume.iqn, volume.accountID, volume.status, volume.totalSize));
156162
}
157163

158164
return sfVolumes;
@@ -166,13 +172,17 @@ public static class SolidFireVolume {
166172
private final String _iqn;
167173
private final long _accountId;
168174
private final String _status;
175+
private final long _totalSize;
169176

170-
public SolidFireVolume(long id, String name, String iqn, long accountId, String status) {
177+
public SolidFireVolume(long id, String name, String iqn,
178+
long accountId, String status, long totalSize)
179+
{
171180
_id = id;
172181
_name = name;
173182
_iqn = "/" + iqn + "/0";
174183
_accountId = accountId;
175184
_status = status;
185+
_totalSize = totalSize;
176186
}
177187

178188
public long getId() {
@@ -195,6 +205,10 @@ public boolean isActive() {
195205
return ACTIVE.equalsIgnoreCase(_status);
196206
}
197207

208+
public long getTotalSize() {
209+
return _totalSize;
210+
}
211+
198212
@Override
199213
public int hashCode() {
200214
return _iqn.hashCode();
@@ -217,7 +231,9 @@ public boolean equals(Object obj) {
217231

218232
SolidFireVolume sfv = (SolidFireVolume)obj;
219233

220-
if (_id == sfv._id && _name.equals(sfv._name) && _iqn.equals(sfv._iqn) && _accountId == sfv._accountId && isActive() == sfv.isActive()) {
234+
if (_id == sfv._id && _name.equals(sfv._name) &&
235+
_iqn.equals(sfv._iqn) && _accountId == sfv._accountId &&
236+
isActive() == sfv.isActive() && getTotalSize() == sfv.getTotalSize()) {
221237
return true;
222238
}
223239

@@ -366,7 +382,7 @@ public static List<SolidFireVolume> getDeletedVolumes(String strSfMvip, int iSfP
366382
List<SolidFireVolume> deletedVolumes = new ArrayList<SolidFireVolume>();
367383

368384
for (VolumeGetResult.Result.Volume volume : volumeGetResult.result.volumes) {
369-
deletedVolumes.add(new SolidFireVolume(volume.volumeID, volume.name, volume.iqn, volume.accountID, volume.status));
385+
deletedVolumes.add(new SolidFireVolume(volume.volumeID, volume.name, volume.iqn, volume.accountID, volume.status, volume.totalSize));
370386
}
371387

372388
return deletedVolumes;
@@ -655,6 +671,7 @@ private static final class Volume {
655671
private String iqn;
656672
private long accountID;
657673
private String status;
674+
private long totalSize;
658675
}
659676
}
660677
}
@@ -811,30 +828,41 @@ private static String getVolumeName(VolumeGetResult volumeGetResult, long lVolum
811828
return volumeGetResult.result.volumes[0].name;
812829
}
813830

814-
throw new CloudRuntimeException("Could not determine the name of the volume, " + "but the volume was created with an ID of " + lVolumeId + ".");
831+
throw new CloudRuntimeException("Could not determine the name of the volume for volume ID of " + lVolumeId + ".");
815832
}
816833

817834
private static String getVolumeIqn(VolumeGetResult volumeGetResult, long lVolumeId) {
818835
if (volumeGetResult.result.volumes != null && volumeGetResult.result.volumes.length == 1 && volumeGetResult.result.volumes[0].volumeID == lVolumeId) {
819836
return volumeGetResult.result.volumes[0].iqn;
820837
}
821838

822-
throw new CloudRuntimeException("Could not determine the IQN of the volume, " + "but the volume was created with an ID of " + lVolumeId + ".");
839+
throw new CloudRuntimeException("Could not determine the IQN of the volume for volume ID of " + lVolumeId + ".");
823840
}
824841

825842
private static long getVolumeAccountId(VolumeGetResult volumeGetResult, long lVolumeId) {
826843
if (volumeGetResult.result.volumes != null && volumeGetResult.result.volumes.length == 1 && volumeGetResult.result.volumes[0].volumeID == lVolumeId) {
827844
return volumeGetResult.result.volumes[0].accountID;
828845
}
829846

830-
throw new CloudRuntimeException("Could not determine the volume's account ID, " + "but the volume was created with an ID of " + lVolumeId + ".");
847+
throw new CloudRuntimeException("Could not determine the account ID of the volume for volume ID of " + lVolumeId + ".");
831848
}
832849

833850
private static String getVolumeStatus(VolumeGetResult volumeGetResult, long lVolumeId) {
834851
if (volumeGetResult.result.volumes != null && volumeGetResult.result.volumes.length == 1 && volumeGetResult.result.volumes[0].volumeID == lVolumeId) {
835852
return volumeGetResult.result.volumes[0].status;
836853
}
837854

838-
throw new CloudRuntimeException("Could not determine the status of the volume, " + "but the volume was created with an ID of " + lVolumeId + ".");
855+
throw new CloudRuntimeException("Could not determine the status of the volume for volume ID of " + lVolumeId + ".");
856+
}
857+
858+
private static long getVolumeTotalSize(VolumeGetResult volumeGetResult, long lVolumeId)
859+
{
860+
if (volumeGetResult.result.volumes != null && volumeGetResult.result.volumes.length == 1 &&
861+
volumeGetResult.result.volumes[0].volumeID == lVolumeId)
862+
{
863+
return volumeGetResult.result.volumes[0].totalSize;
864+
}
865+
866+
throw new CloudRuntimeException("Could not determine the total size of the volume for volume ID of " + lVolumeId + ".");
839867
}
840868
}

0 commit comments

Comments
 (0)