Skip to content

Commit 06806a6

Browse files
author
Mike Tutkowski
committed
CLOUDSTACK-9619: Updates for SAN-assisted snapshots
1 parent f82873e commit 06806a6

9 files changed

Lines changed: 110 additions & 204 deletions

File tree

engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java

Lines changed: 36 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
package org.apache.cloudstack.storage.motion;
2020

2121
import com.cloud.agent.AgentManager;
22-
import com.cloud.agent.api.Answer;
2322
import com.cloud.agent.api.to.DataStoreTO;
2423
import com.cloud.agent.api.to.DataTO;
2524
import com.cloud.agent.api.to.DiskTO;
@@ -37,9 +36,9 @@
3736
import com.cloud.server.ManagementService;
3837
import com.cloud.storage.DataStoreRole;
3938
import com.cloud.storage.DiskOfferingVO;
39+
import com.cloud.storage.Snapshot;
4040
import com.cloud.storage.SnapshotVO;
4141
import com.cloud.storage.Storage.ImageFormat;
42-
import com.cloud.storage.Volume;
4342
import com.cloud.storage.VolumeDetailVO;
4443
import com.cloud.storage.VolumeVO;
4544
import com.cloud.storage.dao.DiskOfferingDao;
@@ -79,15 +78,11 @@
7978
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
8079
import org.apache.cloudstack.storage.command.CopyCmdAnswer;
8180
import org.apache.cloudstack.storage.command.CopyCommand;
82-
import org.apache.cloudstack.storage.command.DeleteCommand;
83-
import org.apache.cloudstack.storage.command.DettachAnswer;
84-
import org.apache.cloudstack.storage.command.DettachCommand;
8581
import org.apache.cloudstack.storage.command.ResignatureAnswer;
8682
import org.apache.cloudstack.storage.command.ResignatureCommand;
8783
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
8884
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
8985
import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
90-
import org.apache.cloudstack.storage.to.SnapshotObjectTO;
9186
import org.apache.cloudstack.storage.to.VolumeObjectTO;
9287
import org.apache.commons.lang.StringUtils;
9388
import org.apache.log4j.Logger;
@@ -225,6 +220,7 @@ public void copyAsync(DataObject srcData, DataObject destData, Host destHost, As
225220

226221
if (canHandleDest) {
227222
handleCreateVolumeFromSnapshotOnSecondaryStorage(snapshotInfo, volumeInfo, callback);
223+
228224
return;
229225
}
230226

@@ -415,8 +411,14 @@ private void handleCopyDataToSecondaryStorage(SnapshotInfo snapshotInfo, DataObj
415411

416412
copyCmdAnswer = (CopyCmdAnswer)_agentMgr.send(hostVO.getId(), copyCommand);
417413

418-
if (needCache) {
414+
if (!copyCmdAnswer.getResult()) {
415+
// We were not able to copy. Handle it.
416+
errMsg = copyCmdAnswer.getDetails();
419417

418+
throw new CloudRuntimeException(errMsg);
419+
}
420+
421+
if (needCache) {
420422
// If cached storage was needed (in case of object store as secondary
421423
// storage), at this point, the data has been copied from the primary
422424
// to the NFS cache by the hypervisor. We now invoke another copy
@@ -430,34 +432,34 @@ private void handleCopyDataToSecondaryStorage(SnapshotInfo snapshotInfo, DataObj
430432

431433
if (ep == null) {
432434
errMsg = "No remote endpoint to send command, check if host or ssvm is down?";
435+
433436
LOGGER.error(errMsg);
437+
434438
copyCmdAnswer = new CopyCmdAnswer(errMsg);
435439
} else {
436-
copyCmdAnswer = (CopyCmdAnswer) ep.sendMessage(cmd);
440+
copyCmdAnswer = (CopyCmdAnswer)ep.sendMessage(cmd);
437441
}
438442

439443
// clean up snapshot copied to staging
440-
performCleanupCacheStorage(destOnStore);
444+
cacheMgr.deleteCacheObject(destOnStore);
441445
}
442446

443-
444447
} catch (CloudRuntimeException | AgentUnavailableException | OperationTimedoutException ex) {
445448
String msg = "Failed to create template from snapshot (Snapshot ID = " + snapshotInfo.getId() + ") : ";
446449

447450
LOGGER.warn(msg, ex);
448451

449452
throw new CloudRuntimeException(msg + ex.getMessage());
450-
451453
} finally {
452-
453-
// detach and remove access after the volume is created
454-
SnapshotObjectTO snapshotObjectTO = (SnapshotObjectTO) snapshotInfo.getTO();
455-
DiskTO disk = new DiskTO(snapshotObjectTO, null, snapshotInfo.getPath(), Volume.Type.UNKNOWN);
456-
detachManagedStoreVolume(snapshotInfo, hostVO, getProperty(snapshotInfo.getId(), DiskTO.IQN), srcDataStore.getId(), disk);
454+
_volumeService.revokeAccess(snapshotInfo, hostVO, srcDataStore);
457455

458456
if (copyCmdAnswer == null || !copyCmdAnswer.getResult()) {
459457
if (copyCmdAnswer != null && !StringUtils.isEmpty(copyCmdAnswer.getDetails())) {
460458
errMsg = copyCmdAnswer.getDetails();
459+
460+
if (needCache) {
461+
cacheMgr.deleteCacheObject(destOnStore);
462+
}
461463
}
462464
else {
463465
errMsg = "Unable to create template from snapshot";
@@ -497,11 +499,9 @@ private void handleCopyDataToSecondaryStorage(SnapshotInfo snapshotInfo, DataObj
497499
* @param callback for async
498500
*/
499501
private void handleCreateVolumeFromSnapshotOnSecondaryStorage(SnapshotInfo snapshotInfo, VolumeInfo volumeInfo, AsyncCompletionCallback<CopyCommandResult> callback) {
500-
501502
// at this point, the snapshotInfo and volumeInfo should have the same disk offering ID (so either one should be OK to get a DiskOfferingVO instance)
502503
DiskOfferingVO diskOffering = _diskOfferingDao.findByIdIncludingRemoved(volumeInfo.getDiskOfferingId());
503504
SnapshotVO snapshot = _snapshotDao.findById(snapshotInfo.getId());
504-
DataStore destDataStore = volumeInfo.getDataStore();
505505

506506
// update the volume's hv_ss_reserve (hypervisor snapshot reserve) from a disk offering (used for managed storage)
507507
_volumeService.updateHypervisorSnapshotReserveForVolume(diskOffering, volumeInfo.getId(), snapshot.getHypervisorType());
@@ -510,9 +510,9 @@ private void handleCreateVolumeFromSnapshotOnSecondaryStorage(SnapshotInfo snaps
510510
String errMsg = null;
511511

512512
HostVO hostVO = null;
513-
try {
514513

515-
//create a volume on the storage
514+
try {
515+
// create a volume on the storage
516516
AsyncCallFuture<VolumeApiResult> future = _volumeService.createVolumeAsync(volumeInfo, volumeInfo.getDataStore());
517517
VolumeApiResult result = future.get();
518518

@@ -529,7 +529,7 @@ private void handleCreateVolumeFromSnapshotOnSecondaryStorage(SnapshotInfo snaps
529529

530530
hostVO = getHost(snapshotInfo.getDataCenterId(), false);
531531

532-
//copy the volume from secondary via the hypervisor
532+
// copy the volume from secondary via the hypervisor
533533
copyCmdAnswer = performCopyOfVdi(volumeInfo, snapshotInfo, hostVO);
534534

535535
if (copyCmdAnswer == null || !copyCmdAnswer.getResult()) {
@@ -543,86 +543,13 @@ private void handleCreateVolumeFromSnapshotOnSecondaryStorage(SnapshotInfo snaps
543543
}
544544
catch (Exception ex) {
545545
errMsg = ex.getMessage() != null ? ex.getMessage() : "Copy operation failed in 'StorageSystemDataMotionStrategy.handleCreateVolumeFromSnapshotBothOnStorageSystem'";
546-
} finally {
547-
548-
549-
DiskTO disk = new DiskTO(volumeInfo.getTO(), volumeInfo.getDeviceId(), volumeInfo.getPath(),volumeInfo.getVolumeType());
550-
long storagePoolId = volumeInfo.getPoolId();
551-
detachManagedStoreVolume(volumeInfo, hostVO, volumeInfo.get_iScsiName(), storagePoolId, disk);
552546
}
553547

554548
CopyCommandResult result = new CopyCommandResult(null, copyCmdAnswer);
555549

556550
result.setResult(errMsg);
557551

558552
callback.complete(result);
559-
560-
561-
}
562-
563-
/**
564-
* Detaches a managed volume from a host
565-
* @param dataObject Volume to be detached
566-
* @param hostVO Host where the volume is currently attached
567-
* @param storagePoolId Storage where volume resides
568-
* @param disk Object which stores disk attributes to send to the agents
569-
*/
570-
private void detachManagedStoreVolume(DataObject dataObject, HostVO hostVO, String iqn, long storagePoolId, DiskTO disk) {
571-
572-
DataStore destDataStore = dataObject.getDataStore();
573-
DettachCommand cmd = new DettachCommand(disk, null);
574-
StoragePoolVO storagePool = _storagePoolDao.findById(storagePoolId);
575-
576-
cmd.setManaged(true);
577-
cmd.setStorageHost(storagePool.getHostAddress());
578-
cmd.setStoragePort(storagePool.getPort());
579-
cmd.set_iScsiName(iqn);
580-
581-
try {
582-
DettachAnswer dettachAnswer = (DettachAnswer) _agentMgr.send(hostVO.getId(), cmd);
583-
584-
if (!dettachAnswer.getResult()) {
585-
LOGGER.warn("Error detaching DataObject:" + dettachAnswer.getDetails());
586-
}
587-
588-
} catch (Exception e) {
589-
LOGGER.warn("Error detaching DataObject " + dataObject.getId() + " Error: " + e.getMessage());
590-
}
591-
592-
593-
try {
594-
_volumeService.revokeAccess(dataObject, hostVO, destDataStore);
595-
} catch (Exception e) {
596-
LOGGER.warn("Error revoking access to DataObject (DataObject ID = " + dataObject.getId() + "): " + e.getMessage(), e);
597-
}
598-
}
599-
600-
private void performCleanupCacheStorage(DataObject destOnStore) {
601-
destOnStore.processEvent(Event.DestroyRequested);
602-
603-
DeleteCommand deleteCommand = new DeleteCommand(destOnStore.getTO());
604-
EndPoint ep = selector.select(destOnStore);
605-
try {
606-
if (ep == null) {
607-
LOGGER.warn("Unable to cleanup staging NFS because no endpoint was found " +
608-
"Object: " + destOnStore.getType() + " ID: " + destOnStore.getId());
609-
610-
destOnStore.processEvent(Event.OperationFailed);
611-
} else {
612-
Answer deleteAnswer = ep.sendMessage(deleteCommand);
613-
if (deleteAnswer != null && deleteAnswer.getResult()) {
614-
LOGGER.warn("Unable to cleanup staging NFS " + deleteAnswer.getDetails() +
615-
"Object: " + destOnStore.getType() + " ID: " + destOnStore.getId());
616-
destOnStore.processEvent(Event.OperationFailed);
617-
}
618-
}
619-
620-
destOnStore.processEvent(Event.OperationSuccessed);
621-
} catch (Exception e) {
622-
destOnStore.processEvent(Event.OperationFailed);
623-
LOGGER.warn("Unable to clean up staging cache Exception " + e.getMessage() +
624-
"Object: " + destOnStore.getType() + " ID: " + destOnStore.getId());
625-
}
626553
}
627554

628555
/**
@@ -899,7 +826,7 @@ private Map<String, String> getVolumeDetails(VolumeInfo volumeInfo) {
899826
}
900827

901828
private Map<String, String> getSnapshotDetails(SnapshotInfo snapshotInfo) {
902-
Map<String, String> snapshotDetails = new HashMap<String, String>();
829+
Map<String, String> snapshotDetails = new HashMap<>();
903830

904831
long storagePoolId = snapshotInfo.getDataStore().getId();
905832
StoragePoolVO storagePoolVO = _storagePoolDao.findById(storagePoolId);
@@ -1066,30 +993,34 @@ protected DataObject cacheSnapshotChain(SnapshotInfo snapshot, Scope scope) {
1066993
* @return result of the copy
1067994
*/
1068995
private CopyCmdAnswer performCopyOfVdi(VolumeInfo volumeInfo, SnapshotInfo snapshotInfo, HostVO hostVO) {
996+
Snapshot.LocationType locationType = snapshotInfo.getLocationType();
997+
1069998
String value = _configDao.getValue(Config.PrimaryStorageDownloadWait.toString());
1070999
int primaryStorageDownloadWait = NumbersUtil.parseInt(value, Integer.parseInt(Config.PrimaryStorageDownloadWait.getDefaultValue()));
10711000

10721001
DataObject srcData = snapshotInfo;
10731002
CopyCmdAnswer copyCmdAnswer = null;
10741003
DataObject cacheData = null;
1004+
10751005
boolean needCacheStorage = needCacheStorage(snapshotInfo, volumeInfo);
10761006

10771007
if (needCacheStorage) {
10781008
cacheData = cacheSnapshotChain(snapshotInfo, new ZoneScope(volumeInfo.getDataCenterId()));
10791009
srcData = cacheData;
1080-
10811010
}
10821011

10831012
CopyCommand copyCommand = new CopyCommand(srcData.getTO(), volumeInfo.getTO(), primaryStorageDownloadWait, VirtualMachineManager.ExecuteInSequence.value());
10841013

10851014
try {
1015+
if (Snapshot.LocationType.PRIMARY.equals(locationType)) {
1016+
_volumeService.grantAccess(snapshotInfo, hostVO, snapshotInfo.getDataStore());
10861017

1087-
_volumeService.grantAccess(snapshotInfo, hostVO, snapshotInfo.getDataStore());
1088-
_volumeService.grantAccess(volumeInfo, hostVO, volumeInfo.getDataStore());
1018+
Map<String, String> srcDetails = getSnapshotDetails(snapshotInfo);
10891019

1090-
Map<String, String> srcDetails = getSnapshotDetails(snapshotInfo);
1020+
copyCommand.setOptions(srcDetails);
1021+
}
10911022

1092-
copyCommand.setOptions(srcDetails);
1023+
_volumeService.grantAccess(volumeInfo, hostVO, volumeInfo.getDataStore());
10931024

10941025
Map<String, String> destDetails = getVolumeDetails(volumeInfo);
10951026

@@ -1105,11 +1036,14 @@ private CopyCmdAnswer performCopyOfVdi(VolumeInfo volumeInfo, SnapshotInfo snaps
11051036
throw new CloudRuntimeException(msg + ex.getMessage());
11061037
}
11071038
finally {
1108-
_volumeService.revokeAccess(snapshotInfo, hostVO, snapshotInfo.getDataStore());
1039+
if (Snapshot.LocationType.PRIMARY.equals(locationType)) {
1040+
_volumeService.revokeAccess(snapshotInfo, hostVO, snapshotInfo.getDataStore());
1041+
}
1042+
11091043
_volumeService.revokeAccess(volumeInfo, hostVO, volumeInfo.getDataStore());
11101044

11111045
if (needCacheStorage && copyCmdAnswer != null && copyCmdAnswer.getResult()) {
1112-
performCleanupCacheStorage(cacheData);
1046+
cacheMgr.deleteCacheObject(cacheData);
11131047
}
11141048
}
11151049

engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/StorageSystemSnapshotStrategy.java

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -90,34 +90,32 @@ public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase {
9090

9191
@Override
9292
public SnapshotInfo backupSnapshot(SnapshotInfo snapshotInfo) {
93-
9493
Preconditions.checkArgument(snapshotInfo != null, "backupSnapshot expects a valid snapshot");
9594

9695
if (snapshotInfo.getLocationType() != Snapshot.LocationType.SECONDARY) {
97-
9896
markAsBackedUp((SnapshotObject)snapshotInfo);
97+
9998
return snapshotInfo;
10099
}
101100

102-
// At this point the snapshot is either taken as a native
101+
// At this point, the snapshot is either taken as a native
103102
// snapshot on the storage or exists as a volume on the storage (clone).
104-
// If archive flag is passed, we will copy this snapshot to secondary
103+
// If archive flag is passed in, we should copy this snapshot to secondary
105104
// storage and delete it from the primary storage.
106105

107106
HostVO host = getHost(snapshotInfo.getVolumeId());
108107
boolean canStorageSystemCreateVolumeFromSnapshot = canStorageSystemCreateVolumeFromSnapshot(snapshotInfo.getBaseVolume().getPoolId());
109108
boolean computeClusterSupportsResign = clusterDao.getSupportsResigning(host.getClusterId());
110109

111110
if (!canStorageSystemCreateVolumeFromSnapshot || !computeClusterSupportsResign) {
112-
String mesg = "Cannot archive snapshot: Either canStorageSystemCreateVolumeFromSnapshot or " +
113-
"computeClusterSupportsResign were false. ";
111+
String msg = "Cannot archive snapshot: canStorageSystemCreateVolumeFromSnapshot and/or computeClusterSupportsResign were false.";
112+
113+
s_logger.warn(msg);
114114

115-
s_logger.warn(mesg);
116-
throw new CloudRuntimeException(mesg);
115+
throw new CloudRuntimeException(msg);
117116
}
118117

119118
return snapshotSvr.backupSnapshot(snapshotInfo);
120-
121119
}
122120

123121
@Override
@@ -255,14 +253,13 @@ public SnapshotInfo takeSnapshot(SnapshotInfo snapshotInfo) {
255253
backedUpSnapshot = backupSnapshot(snapshotOnPrimary);
256254

257255
updateLocationTypeInDb(backedUpSnapshot);
258-
259256
}
260257
finally {
261258
if (result != null && result.isSuccess()) {
262259
volumeInfo.stateTransit(Volume.Event.OperationSucceeded);
263260

264261
if (snapshotOnPrimary != null && snapshotInfo.getLocationType() == Snapshot.LocationType.SECONDARY) {
265-
// cleanup the snapshot on primary
262+
// remove the snapshot on primary storage
266263
try {
267264
snapshotSvr.deleteSnapshot(snapshotOnPrimary);
268265
} catch (Exception e) {
@@ -276,6 +273,7 @@ public SnapshotInfo takeSnapshot(SnapshotInfo snapshotInfo) {
276273
}
277274

278275
snapshotDao.releaseFromLockTable(snapshotInfo.getId());
276+
279277
return backedUpSnapshot;
280278
}
281279

@@ -586,7 +584,7 @@ public StrategyPriority canHandle(Snapshot snapshot, SnapshotOperation op) {
586584

587585
Snapshot.LocationType locationType = snapshot.getLocationType();
588586

589-
//If the snapshot exisits on Secondary Storage, we can't delete it.
587+
// If the snapshot exists on Secondary Storage, we can't delete it.
590588
if (SnapshotOperation.DELETE.equals(op) && Snapshot.LocationType.SECONDARY.equals(locationType)) {
591589
return StrategyPriority.CANT_HANDLE;
592590
}

0 commit comments

Comments
 (0)