Skip to content

Commit 7aefcec

Browse files
Gupta, SuryaGupta, Surya
authored andcommitted
CSTACKEX-16 Lun Copy to secondary
1 parent 82c36ca commit 7aefcec

File tree

1 file changed

+146
-1
lines changed

1 file changed

+146
-1
lines changed

plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/driver/OntapPrimaryDatastoreDriver.java

Lines changed: 146 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import com.cloud.agent.api.to.DataObjectType;
2323
import com.cloud.agent.api.to.DataStoreTO;
2424
import com.cloud.agent.api.to.DataTO;
25+
import com.cloud.agent.api.to.DiskTO;
2526
import com.cloud.exception.InvalidParameterValueException;
2627
import com.cloud.host.Host;
2728
import com.cloud.storage.Storage;
@@ -186,7 +187,12 @@ public void createAsync(DataStore dataStore, DataObject dataObject, AsyncComplet
186187
volumeDao.update(volumeVO.getId(), volumeVO);
187188
}
188189
} else if (dataObject.getType() == DataObjectType.SNAPSHOT) {
189-
createTempVolume((SnapshotInfo)dataObject, dataStore.getId());
190+
//createTempVolume((SnapshotInfo)dataObject, dataStore.getId());
191+
// No-op: ONTAP's takeSnapshot() already creates a LUN clone that is directly accessible.
192+
// The framework calls createAsync(SNAPSHOT) via createVolumeFromSnapshot/deleteVolumeFromSnapshot,
193+
// but ONTAP doesn't need a separate temp volume — the cloned LUN is used as-is.
194+
s_logger.info("createAsync: SNAPSHOT type — no-op for ONTAP (LUN clone already exists from takeSnapshot)");
195+
createCmdResult = new CreateCmdResult(null, new Answer(null, true, null));
190196
} else {
191197
errMsg = "Invalid DataObjectType (" + dataObject.getType() + ") passed to createAsync";
192198
s_logger.error(errMsg);
@@ -268,6 +274,11 @@ public void deleteAsync(DataStore store, DataObject data, AsyncCompletionCallbac
268274
s_logger.error("deleteAsync : Volume deleted: " + volumeInfo.getId());
269275
commandResult.setResult(null);
270276
commandResult.setSuccess(true);
277+
} else if (data.getType() == DataObjectType.SNAPSHOT) {
278+
s_logger.info("deleteAsync: SNAPSHOT type — no-op for ONTAP (LUN clone already exists from takeSnapshot)");
279+
//deleteSnapshotClone((SnapshotInfo) data, store);
280+
commandResult.setResult(null);
281+
commandResult.setSuccess(true);
271282
}
272283
} catch (Exception e) {
273284
s_logger.error("deleteAsync: Failed for data object [{}]: {}", data, e.getMessage());
@@ -373,6 +384,9 @@ public boolean grantAccess(DataObject dataObject, Host host, DataStore dataStore
373384
volumeVO.setPoolType(storagePool.getPoolType());
374385
volumeVO.setPoolId(storagePool.getId());
375386
volumeDao.update(volumeVO.getId(), volumeVO);
387+
} else if (dataObject.getType() == DataObjectType.SNAPSHOT) {
388+
s_logger.info("grantAccess: SNAPSHOT type — no-op for ONTAP (LUN clone already exists from takeSnapshot)");
389+
//grantAccessForSnapshot((SnapshotInfo) dataObject, host, storagePool);
376390
} else {
377391
s_logger.error("Invalid DataObjectType (" + dataObject.getType() + ") passed to grantAccess");
378392
throw new CloudRuntimeException("Invalid DataObjectType (" + dataObject.getType() + ") passed to grantAccess");
@@ -434,6 +448,9 @@ public void revokeAccess(DataObject dataObject, Host host, DataStore dataStore)
434448
throw new CloudRuntimeException("revokeAccess: CloudStack Volume not found for id: " + dataObject.getId());
435449
}
436450
revokeAccessForVolume(storagePool, volumeVO, host);
451+
} else if (dataObject.getType() == DataObjectType.SNAPSHOT) {
452+
s_logger.info("revokeAccess: SNAPSHOT type — no-op for ONTAP (LUN clone already exists from takeSnapshot)");
453+
//revokeAccessForSnapshot((SnapshotInfo) dataObject, host, storagePool);
437454
} else {
438455
s_logger.error("revokeAccess: Invalid DataObjectType (" + dataObject.getType() + ") passed to revokeAccess");
439456
throw new CloudRuntimeException("Invalid DataObjectType (" + dataObject.getType() + ") passed to revokeAccess");
@@ -482,6 +499,134 @@ private void revokeAccessForVolume(StoragePoolVO storagePool, VolumeVO volumeVO,
482499
}
483500
}
484501

502+
/**
503+
* Grants host access to a snapshot's cloned LUN for copy-to-secondary-storage.
504+
* Maps the snapshot LUN to the host's igroup and stores the IQN in snapshot_details
505+
* so that the KVM agent can connect via iSCSI to copy the data.
506+
*/
507+
private void grantAccessForSnapshot(SnapshotInfo snapshotInfo, Host host, StoragePoolVO storagePool) {
508+
s_logger.info("grantAccessForSnapshot: Granting access to snapshot [{}] for host [{}]", snapshotInfo.getId(), host.getName());
509+
510+
Map<String, String> details = storagePoolDetailsDao.listDetailsKeyPairs(storagePool.getId());
511+
512+
if (ProtocolType.ISCSI.name().equalsIgnoreCase(details.get(Constants.PROTOCOL))) {
513+
String svmName = details.get(Constants.SVM_NAME);
514+
String storagePoolUuid = storagePool.getUuid();
515+
516+
// snapshotInfo.getPath() contains the cloned LUN name set during takeSnapshot()
517+
String snapshotLunName = snapshotInfo.getPath();
518+
if (snapshotLunName == null) {
519+
throw new CloudRuntimeException("grantAccessForSnapshot: Snapshot path (LUN name) is null for snapshot id: " + snapshotInfo.getId());
520+
}
521+
522+
UnifiedSANStrategy sanStrategy = (UnifiedSANStrategy) Utility.getStrategyByStoragePoolDetails(details);
523+
String accessGroupName = Utility.getIgroupName(svmName, storagePoolUuid);
524+
525+
// Map the snapshot's cloned LUN to the igroup
526+
String lunNumber = sanStrategy.ensureLunMapped(svmName, snapshotLunName, accessGroupName);
527+
528+
// Store DiskTO.IQN in snapshot_details so StorageSystemDataMotionStrategy.getSnapshotDetails()
529+
// can build the iSCSI source details for the CopyCommand sent to the KVM agent
530+
String iqnPath = storagePool.getPath() + Constants.SLASH + lunNumber;
531+
snapshotDetailsDao.addDetail(snapshotInfo.getId(), DiskTO.IQN, iqnPath, false);
532+
533+
s_logger.info("grantAccessForSnapshot: Snapshot LUN [{}] mapped as LUN number [{}], IQN path [{}]",
534+
snapshotLunName, lunNumber, iqnPath);
535+
}
536+
}
537+
538+
/**
539+
* Revokes host access to a snapshot's cloned LUN after copy-to-secondary-storage completes.
540+
* Unmaps the snapshot LUN from the igroup and removes the IQN from snapshot_details.
541+
*/
542+
private void revokeAccessForSnapshot(SnapshotInfo snapshotInfo, Host host, StoragePoolVO storagePool) {
543+
s_logger.info("revokeAccessForSnapshot: Revoking access to snapshot [{}] for host [{}]", snapshotInfo.getId(), host.getName());
544+
545+
Map<String, String> details = storagePoolDetailsDao.listDetailsKeyPairs(storagePool.getId());
546+
547+
if (ProtocolType.ISCSI.name().equalsIgnoreCase(details.get(Constants.PROTOCOL))) {
548+
StorageStrategy storageStrategy = Utility.getStrategyByStoragePoolDetails(details);
549+
String storagePoolUuid = storagePool.getUuid();
550+
551+
// Build a CloudStackVolume request using the snapshot's cloned LUN name
552+
String snapshotLunName = snapshotInfo.getPath();
553+
if (snapshotLunName == null) {
554+
s_logger.warn("revokeAccessForSnapshot: Snapshot path is null for snapshot id: {}, skipping", snapshotInfo.getId());
555+
return;
556+
}
557+
558+
CloudStackVolume snapshotVolumeRequest = new CloudStackVolume();
559+
Lun snapshotLun = new Lun();
560+
snapshotLun.setName(snapshotLunName);
561+
Svm svm = new Svm();
562+
svm.setName(details.get(Constants.SVM_NAME));
563+
snapshotLun.setSvm(svm);
564+
snapshotVolumeRequest.setLun(snapshotLun);
565+
566+
// Retrieve the LUN from ONTAP to get its UUID
567+
CloudStackVolume cloudStackVolume = storageStrategy.getCloudStackVolume(snapshotVolumeRequest);
568+
569+
// Get the igroup to get its UUID
570+
AccessGroup accessGroupRequest = getAccessGroupRequestByProtocol(details, storagePoolUuid);
571+
AccessGroup accessGroup = storageStrategy.getAccessGroup(accessGroupRequest);
572+
573+
// Remove the LUN mapping from the igroup
574+
Map<String, String> disableLogicalAccessMap = new HashMap<>();
575+
disableLogicalAccessMap.put(Constants.LUN_DOT_UUID, cloudStackVolume.getLun().getUuid());
576+
disableLogicalAccessMap.put(Constants.IGROUP_DOT_UUID, accessGroup.getIgroup().getUuid());
577+
storageStrategy.disableLogicalAccess(disableLogicalAccessMap);
578+
579+
// Remove the IQN detail from snapshot_details
580+
snapshotDetailsDao.removeDetail(snapshotInfo.getId(), DiskTO.IQN);
581+
582+
s_logger.info("revokeAccessForSnapshot: Successfully revoked access to snapshot LUN [{}] for host [{}]",
583+
snapshotLunName, host.getName());
584+
}
585+
}
586+
587+
/**
588+
* Deletes the cloned LUN that was created during takeSnapshot().
589+
* Called by the framework after the snapshot data has been copied to secondary storage.
590+
*/
591+
private void deleteSnapshotClone(SnapshotInfo snapshotInfo, DataStore store) {
592+
s_logger.info("deleteSnapshotClone: Deleting snapshot clone for snapshot [{}]", snapshotInfo.getId());
593+
594+
StoragePoolVO storagePool = storagePoolDao.findById(store.getId());
595+
if (storagePool == null) {
596+
throw new CloudRuntimeException("deleteSnapshotClone: Storage Pool not found for id: " + store.getId());
597+
}
598+
599+
Map<String, String> details = storagePoolDetailsDao.listDetailsKeyPairs(store.getId());
600+
StorageStrategy storageStrategy = Utility.getStrategyByStoragePoolDetails(details);
601+
602+
// Get the cloned LUN name from snapshotInfo.getPath() (set during takeSnapshot)
603+
String snapshotLunName = snapshotInfo.getPath();
604+
if (snapshotLunName == null) {
605+
s_logger.warn("deleteSnapshotClone: Snapshot path is null for snapshot id: {}, nothing to delete", snapshotInfo.getId());
606+
return;
607+
}
608+
609+
// Get the cloned LUN UUID from snapshot_details
610+
SnapshotDetailsVO snapDetail = snapshotDetailsDao.findDetail(snapshotInfo.getSnapshotId(), Constants.ONTAP_SNAP_ID);
611+
String lunUuid = (snapDetail != null) ? snapDetail.getValue() : null;
612+
613+
// Build delete request for the cloned LUN
614+
CloudStackVolume deleteRequest = new CloudStackVolume();
615+
Lun lun = new Lun();
616+
lun.setName(snapshotLunName);
617+
if (lunUuid != null) {
618+
lun.setUuid(lunUuid);
619+
}
620+
deleteRequest.setLun(lun);
621+
622+
storageStrategy.deleteCloudStackVolume(deleteRequest);
623+
s_logger.info("deleteSnapshotClone: Successfully deleted cloned LUN [{}] for snapshot [{}]", snapshotLunName, snapshotInfo.getId());
624+
625+
// Clean up snapshot details
626+
snapshotDetailsDao.removeDetail(snapshotInfo.getSnapshotId(), Constants.ONTAP_SNAP_ID);
627+
snapshotDetailsDao.removeDetail(snapshotInfo.getSnapshotId(), Constants.ONTAP_SNAP_SIZE);
628+
}
629+
485630
@Override
486631
public long getDataObjectSizeIncludingHypervisorSnapshotReserve(DataObject dataObject, StoragePool storagePool) {
487632
return 0;

0 commit comments

Comments
 (0)