Skip to content

Commit 9db5099

Browse files
authored
Merge pull request #544 from jschoiRR/mold-diplo-2024-main
[Mold API, Agent, UI] GFS 스토리지 가상머신 복제기능 추가(Qcow2형식의 볼륨 스냅샷, vm 스냅샷)
2 parents 9cc16e7 + 8deaa32 commit 9db5099

21 files changed

Lines changed: 278 additions & 28 deletions

File tree

api/src/main/java/org/apache/cloudstack/api/command/user/vm/CloneVMCmd.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ public class CloneVMCmd extends BaseAsyncCreateCmd implements UserCmd {
7373
@Parameter(name = ApiConstants.START_VM, type = CommandType.BOOLEAN, required = true, description = "true if start vm after creating; defaulted to false if not specified")
7474
private Boolean startVm;
7575

76-
@Parameter(name = ApiConstants.CLONE_TYPE, type = CommandType.STRING, required = true, description = "select fast(linked) clone type or full clone type(default is fast clone)")
76+
@Parameter(name = ApiConstants.CLONE_TYPE, type = CommandType.STRING, description = "select fast(linked) clone type or full clone type(default is fast clone)")
7777
private String type;
7878

7979
@Parameter(name=ApiConstants.COUNT, type=CommandType.INTEGER, description="count of clone vm")

engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/DataMotionService.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,6 @@ public interface DataMotionService {
3232

3333
void copyAsync(Map<VolumeInfo, DataStore> volumeMap, VirtualMachineTO vmTo, Host srcHost, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback);
3434

35+
void cloneAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback<CopyCommandResult> callback);
36+
3537
}

engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/DataMotionStrategy.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,6 @@ public interface DataMotionStrategy {
5050
void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback);
5151

5252
void copyAsync(Map<VolumeInfo, DataStore> volumeMap, VirtualMachineTO vmTo, Host srcHost, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback);
53+
54+
void cloneAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback);
5355
}

engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/VolumeService.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ public VolumeInfo getVolume() {
7777

7878
AsyncCallFuture<VolumeApiResult> createVolumeFromSnapshot(VolumeInfo volume, DataStore store, SnapshotInfo snapshot);
7979

80+
AsyncCallFuture<VolumeApiResult> cloneVolumeFromSnapshot(VolumeInfo volume, DataStore store, SnapshotInfo snapshot);
81+
8082
VolumeEntity getVolumeEntity(long volumeId);
8183

8284
TemplateInfo createManagedStorageTemplate(long srcTemplateId, long destDataStoreId, long destHostId) throws StorageAccessException;

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -560,10 +560,10 @@ public VolumeInfo cloneVolumeFromSnapshot(Volume volume, Snapshot snapshot, User
560560
}
561561
VolumeInfo vol = volFactory.getVolume(volume.getId());
562562
DataStore store = dataStoreMgr.getDataStore(pool.getId(), DataStoreRole.Primary);
563-
DataStoreRole dataStoreRole = snapshotHelper.getDataStoreRole(snapshot);
563+
// DataStoreRole dataStoreRole = snapshotHelper.getDataStoreRole(snapshot);
564564
SnapshotInfo snapInfo = snapshotFactory.getSnapshotOnPrimaryStore(snapshot.getId());
565565
// create volume on primary from snapshot
566-
AsyncCallFuture<VolumeApiResult> future = volService.createVolumeFromSnapshot(vol, store, snapInfo);
566+
AsyncCallFuture<VolumeApiResult> future = volService.cloneVolumeFromSnapshot(vol, store, snapInfo);
567567
String snapshotToString = getReflectOnlySelectedFields(snapInfo.getSnapshotVO());
568568

569569
try {

engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ protected Answer copyObject(DataObject srcData, DataObject destData) {
248248
protected DataObject cacheSnapshotChain(SnapshotInfo snapshot, Scope scope) {
249249
DataObject leafData = null;
250250
DataStore store = cacheMgr.getCacheStorage(snapshot, scope);
251+
logger.info("store::::::"+store);
251252
while (snapshot != null) {
252253
DataObject cacheData = cacheMgr.createCacheObject(snapshot, store);
253254
if (leafData == null) {
@@ -281,6 +282,7 @@ protected Answer copyVolumeFromSnapshot(DataObject snapObj, DataObject volObj) {
281282
DataStore store = snapObj.getDataStore();
282283
DataStoreTO storTO = store.getTO();
283284
DataObject srcData = snapObj;
285+
logger.info("storTO::::::"+ storTO);
284286
try {
285287
if (!(storTO instanceof NfsTO)) {
286288
// cache snapshot to zone-wide staging store for the volume to be created
@@ -552,6 +554,49 @@ public void copyAsync(DataObject srcData, DataObject destData, Host destHost, As
552554
callback.complete(result);
553555
}
554556

557+
@Override
558+
public void cloneAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback) {
559+
Answer answer = null;
560+
String errMsg = null;
561+
try {
562+
if (logger.isDebugEnabled()) logger.debug("cloneAsync inspecting src type " + srcData.getType().toString() + " cloneAsync inspecting dest type " + destData.getType().toString());
563+
if (srcData.getType() == DataObjectType.SNAPSHOT && destData.getType() == DataObjectType.VOLUME) {
564+
answer = cloneVolumeFromSnapshot(srcData, destData);
565+
} else if (srcData.getType() == DataObjectType.SNAPSHOT && destData.getType() == DataObjectType.TEMPLATE) {
566+
answer = createTemplateFromSnapshot(srcData, destData);
567+
} else if (srcData.getType() == DataObjectType.TEMPLATE && destData.getType() == DataObjectType.VOLUME) {
568+
answer = cloneVolume(srcData, destData);
569+
} else if (destData.getType() == DataObjectType.VOLUME && srcData.getType() == DataObjectType.VOLUME &&
570+
srcData.getDataStore().getRole() == DataStoreRole.Primary && destData.getDataStore().getRole() == DataStoreRole.Primary) {
571+
if (logger.isDebugEnabled()) logger.debug("About to MIGRATE copy between datasources");
572+
if (srcData.getId() == destData.getId()) {
573+
// The volume has to be migrated across storage pools.
574+
if (logger.isDebugEnabled()) logger.debug("MIGRATE copy using migrateVolumeToPool STARTING");
575+
answer = migrateVolumeToPool(srcData, destData);
576+
if (logger.isDebugEnabled()) logger.debug("MIGRATE copy using migrateVolumeToPool DONE: " + answer.getResult());
577+
} else {
578+
if (logger.isDebugEnabled()) logger.debug("MIGRATE copy using copyVolumeBetweenPools STARTING");
579+
answer = copyVolumeBetweenPools(srcData, destData);
580+
if (logger.isDebugEnabled()) logger.debug("MIGRATE copy using copyVolumeBetweenPools DONE: " + answer.getResult());
581+
}
582+
} else if (srcData.getType() == DataObjectType.SNAPSHOT && destData.getType() == DataObjectType.SNAPSHOT) {
583+
answer = copySnapshot(srcData, destData);
584+
} else {
585+
answer = copyObject(srcData, destData, destHost);
586+
}
587+
588+
if (answer != null && !answer.getResult()) {
589+
errMsg = answer.getDetails();
590+
}
591+
} catch (Exception e) {
592+
if (logger.isDebugEnabled()) logger.debug("copy failed", e);
593+
errMsg = e.toString();
594+
}
595+
CopyCommandResult result = new CopyCommandResult(null, answer);
596+
result.setResult(errMsg);
597+
callback.complete(result);
598+
}
599+
555600
@DB
556601
protected Answer createTemplateFromSnapshot(DataObject srcData, DataObject destData) {
557602

@@ -678,4 +723,45 @@ private boolean anyVolumeRequiresEncryption(DataObject ... objects) {
678723
}
679724
return false;
680725
}
726+
727+
protected Answer cloneVolumeFromSnapshot(DataObject snapObj, DataObject volObj) {
728+
SnapshotInfo snapshot = (SnapshotInfo)snapObj;
729+
StoragePool pool = (StoragePool)volObj.getDataStore();
730+
731+
String basicErrMsg = "Failed to create volume from " + snapshot.getName() + " on pool " + pool;
732+
DataStore store = snapObj.getDataStore();
733+
DataStoreTO storTO = store.getTO();
734+
DataObject srcData = snapObj;
735+
try {
736+
String value = configDao.getValue(Config.CreateVolumeFromSnapshotWait.toString());
737+
int _createVolumeFromSnapshotWait = NumbersUtil.parseInt(value, Integer.parseInt(Config.CreateVolumeFromSnapshotWait.getDefaultValue()));
738+
739+
EndPoint ep = null;
740+
if (srcData.getDataStore().getRole() == DataStoreRole.Primary) {
741+
ep = selector.select(volObj);
742+
} else {
743+
ep = selector.select(srcData, volObj);
744+
}
745+
746+
CopyCommand cmd = new CopyCommand(srcData.getTO(), addFullCloneAndDiskprovisiongStrictnessFlagOnVMwareDest(volObj.getTO()), _createVolumeFromSnapshotWait, VirtualMachineManager.ExecuteInSequence.value());
747+
748+
Answer answer = null;
749+
if (ep == null) {
750+
logger.error(NO_REMOTE_ENDPOINT_SSVM);
751+
answer = new Answer(cmd, false, NO_REMOTE_ENDPOINT_SSVM);
752+
} else {
753+
answer = ep.sendMessage(cmd);
754+
}
755+
756+
return answer;
757+
} catch (Exception e) {
758+
logger.error(basicErrMsg, e);
759+
throw new CloudRuntimeException(basicErrMsg);
760+
} finally {
761+
if (!(storTO instanceof NfsTO)) {
762+
// still keep snapshot on cache which may be migrated from previous secondary storage
763+
releaseSnapshotCacheChain((SnapshotInfo)srcData);
764+
}
765+
}
766+
}
681767
}

engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/DataMotionServiceImpl.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,36 @@ public void copyAsync(DataObject srcData, DataObject destData, Host destHost, As
8888
strategy.copyAsync(srcData, destData, destHost, callback);
8989
}
9090

91+
public void cloneAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback) {
92+
if (srcData.getDataStore() == null || destData.getDataStore() == null) {
93+
String errMsg = "can't find data store";
94+
invokeCallback(errMsg, callback);
95+
return;
96+
}
97+
98+
if (srcData.getDataStore().getDriver().canCopy(srcData, destData)) {
99+
srcData.getDataStore().getDriver().copyAsync(srcData, destData, destHost, callback);
100+
return;
101+
} else if (destData.getDataStore().getDriver().canCopy(srcData, destData)) {
102+
destData.getDataStore().getDriver().copyAsync(srcData, destData, destHost, callback);
103+
return;
104+
}
105+
106+
DataMotionStrategy strategy = storageStrategyFactory.getDataMotionStrategy(srcData, destData);
107+
if (strategy == null) {
108+
// OfflineVmware volume migration
109+
// Cleanup volumes from target and reset the state of volume at source
110+
cleanUpVolumesForFailedMigrations(srcData, destData);
111+
String errMsg = "Can't find strategy to move data. " + "Source: " + srcData.getType().name() + " '" + srcData.getUuid() + ", Destination: " +
112+
destData.getType().name() + " '" + destData.getUuid() + "'";
113+
invokeCallback(errMsg, callback);
114+
return;
115+
}
116+
117+
strategy.cloneAsync(srcData, destData, destHost, callback);
118+
}
119+
120+
91121
/**
92122
* Offline Vmware volume migration
93123
* Cleanup volumes after failed migrations and reset state of source volume
@@ -117,6 +147,11 @@ public void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCa
117147
copyAsync(srcData, destData, null, callback);
118148
}
119149

150+
@Override
151+
public void cloneAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback<CopyCommandResult> callback) {
152+
cloneAsync(srcData, destData, null, callback);
153+
}
154+
120155
@Override
121156
public void copyAsync(Map<VolumeInfo, DataStore> volumeMap, VirtualMachineTO vmTo, Host srcHost, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback) {
122157

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3088,4 +3088,9 @@ protected Boolean isStoragePoolTypeInList(StoragePoolType storagePoolTypeToValid
30883088

30893089
return supportedTypes.contains(storagePoolTypeToValidate);
30903090
};
3091+
3092+
@Override
3093+
public void cloneAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback) {
3094+
return;
3095+
}
30913096
}

engine/storage/integration-test/src/test/java/org/apache/cloudstack/storage/test/MockStorageMotionStrategy.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,9 @@ public void copyAsync(Map<VolumeInfo, DataStore> volumeMap, VirtualMachineTO vmT
5959
CopyCommandResult result = new CopyCommandResult("something", null);
6060
callback.complete(result);
6161
}
62+
63+
@Override
64+
public void cloneAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback) {
65+
return;
66+
}
6267
}

engine/storage/volume/src/main/java/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1674,6 +1674,29 @@ public AsyncCallFuture<VolumeApiResult> createVolumeFromSnapshot(VolumeInfo volu
16741674
return future;
16751675
}
16761676

1677+
@Override
1678+
public AsyncCallFuture<VolumeApiResult> cloneVolumeFromSnapshot(VolumeInfo volume, DataStore store, SnapshotInfo snapshot) {
1679+
AsyncCallFuture<VolumeApiResult> future = new AsyncCallFuture<>();
1680+
1681+
try {
1682+
DataObject volumeOnStore = store.create(volume);
1683+
volumeOnStore.processEvent(Event.CreateOnlyRequested);
1684+
_volumeDetailsDao.addDetail(volume.getId(), SNAPSHOT_ID, Long.toString(snapshot.getId()), false);
1685+
1686+
CreateVolumeFromBaseImageContext<VolumeApiResult> context = new CreateVolumeFromBaseImageContext<>(null, volume, store, volumeOnStore, future, snapshot, null);
1687+
AsyncCallbackDispatcher<VolumeServiceImpl, CopyCommandResult> caller = AsyncCallbackDispatcher.create(this);
1688+
caller.setCallback(caller.getTarget().createVolumeFromSnapshotCallback(null, null)).setContext(context);
1689+
motionSrv.cloneAsync(snapshot, volumeOnStore, caller);
1690+
} catch (Exception e) {
1691+
logger.debug("create volume from snapshot failed", e);
1692+
VolumeApiResult result = new VolumeApiResult(volume);
1693+
result.setResult(e.toString());
1694+
future.complete(result);
1695+
}
1696+
1697+
return future;
1698+
}
1699+
16771700
protected Void createVolumeFromSnapshotCallback(AsyncCallbackDispatcher<VolumeServiceImpl, CopyCommandResult> callback, CreateVolumeFromBaseImageContext<VolumeApiResult> context) {
16781701
CopyCommandResult result = callback.getResult();
16791702
VolumeInfo volume = (VolumeInfo)context.templateOnStore;

0 commit comments

Comments
 (0)