Skip to content

Commit 37f4398

Browse files
authored
linstor: Support VM-Instance Disk snapshots (#8796)
* linstor: update to java-linstor 0.5.1 * linstor: Support VM-Instance Disk snapshots This adds VM-Instance disk snapshot support for Linstor primary storage. Instance snapshots are stored on the used Linstor storage pool backend and can be converted into regular volume snapshots and also reverted. Instance VM snapshots are not fully atomic but with the create multi snapshot feature as good as it gets. Snapshots are done over multiple volumes in the same devicemanager run.
1 parent 034a5c8 commit 37f4398

File tree

5 files changed

+422
-17
lines changed

5 files changed

+422
-17
lines changed

plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@
100100
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
101101
import org.apache.cloudstack.storage.datastore.util.LinstorConfigurationManager;
102102
import org.apache.cloudstack.storage.datastore.util.LinstorUtil;
103+
import org.apache.cloudstack.storage.snapshot.SnapshotObject;
103104
import org.apache.cloudstack.storage.to.SnapshotObjectTO;
104105
import org.apache.cloudstack.storage.to.VolumeObjectTO;
105106
import org.apache.cloudstack.storage.volume.VolumeObject;
@@ -230,7 +231,7 @@ private void deleteSnapshot(@Nonnull DataStore dataStore, @Nonnull String rscDef
230231

231232
try
232233
{
233-
ApiCallRcList answers = linstorApi.resourceSnapshotDelete(rscDefName, snapshotName);
234+
ApiCallRcList answers = linstorApi.resourceSnapshotDelete(rscDefName, snapshotName, Collections.emptyList());
234235
if (answers.hasError())
235236
{
236237
for (ApiCallRc answer : answers)
@@ -1004,25 +1005,29 @@ private Answer copyVolume(DataObject srcData, DataObject dstData) {
10041005
* @param api Linstor Developer api object
10051006
* @param pool StoragePool this resource resides on
10061007
* @param rscName rscName of the snapshotted resource
1007-
* @param snapshotInfo snapshot info of the snapshot
1008+
* @param snapshotName Name of the snapshot to copy from
1009+
* @param snapshotObject snapshot object of the origCmd, so the path can be modified
10081010
* @param origCmd original LinstorBackupSnapshotCommand that needs to have a patched path
10091011
* @return answer from agent operation
10101012
* @throws ApiException if any Linstor api operation fails
10111013
*/
10121014
private Answer copyFromTemporaryResource(
1013-
DevelopersApi api, StoragePoolVO pool, String rscName, SnapshotInfo snapshotInfo, CopyCommand origCmd)
1015+
DevelopersApi api,
1016+
StoragePoolVO pool,
1017+
String rscName,
1018+
String snapshotName,
1019+
SnapshotObject snapshotObject,
1020+
CopyCommand origCmd)
10141021
throws ApiException {
10151022
Answer answer;
10161023
String restoreName = rscName + "-rst";
1017-
String snapshotName = LinstorUtil.RSC_PREFIX + snapshotInfo.getUuid();
10181024
String devName = restoreResourceFromSnapshot(api, pool, rscName, snapshotName, restoreName);
10191025

10201026
Optional<RemoteHostEndPoint> optEPAny = getLinstorEP(api, restoreName);
10211027
if (optEPAny.isPresent()) {
10221028
// patch the src device path to the temporary linstor resource
1023-
SnapshotObjectTO soTO = (SnapshotObjectTO)snapshotInfo.getTO();
1024-
soTO.setPath(devName);
1025-
origCmd.setSrcTO(soTO);
1029+
snapshotObject.setPath(devName);
1030+
origCmd.setSrcTO(snapshotObject.getTO());
10261031
answer = optEPAny.get().sendMessage(origCmd);
10271032
} else{
10281033
answer = new Answer(origCmd, false, "Unable to get matching Linstor endpoint.");
@@ -1032,13 +1037,36 @@ private Answer copyFromTemporaryResource(
10321037
return answer;
10331038
}
10341039

1040+
/**
1041+
* vmsnapshots don't have our typical snapshot path set
1042+
* instead the path is the internal snapshot name e.g.: {vm}_VS_{datestr}
1043+
* we have to find out and modify the path here before
1044+
* @return the original snapshotObject.getPath()
1045+
*/
1046+
private String setCorrectSnapshotPath(DevelopersApi api, String rscName, SnapshotObject snapshotObject)
1047+
throws ApiException {
1048+
String originalPath = LinstorUtil.RSC_PREFIX + snapshotObject.getUuid();
1049+
if (!(snapshotObject.getPath().startsWith("/dev/mapper/") ||
1050+
snapshotObject.getPath().startsWith("zfs://"))) {
1051+
originalPath = snapshotObject.getPath();
1052+
com.linbit.linstor.api.model.StoragePool linStoragePool =
1053+
LinstorUtil.getDiskfulStoragePool(api, rscName);
1054+
if (linStoragePool == null) {
1055+
throw new CloudRuntimeException("Linstor: Unable to find storage pool for resource " + rscName);
1056+
}
1057+
final String path = LinstorUtil.getSnapshotPath(linStoragePool, rscName, snapshotObject.getPath());
1058+
snapshotObject.setPath(path);
1059+
}
1060+
return originalPath;
1061+
}
1062+
10351063
protected Answer copySnapshot(DataObject srcData, DataObject destData) {
10361064
String value = _configDao.getValue(Config.BackupSnapshotWait.toString());
10371065
int _backupsnapshotwait = NumbersUtil.parseInt(
10381066
value, Integer.parseInt(Config.BackupSnapshotWait.getDefaultValue()));
10391067

1040-
SnapshotInfo snapshotInfo = (SnapshotInfo)srcData;
1041-
Boolean snapshotFullBackup = snapshotInfo.getFullBackup();
1068+
SnapshotObject snapshotObject = (SnapshotObject)srcData;
1069+
Boolean snapshotFullBackup = snapshotObject.getFullBackup();
10421070
final StoragePoolVO pool = _storagePoolDao.findById(srcData.getDataStore().getId());
10431071
final DevelopersApi api = LinstorUtil.getLinstorAPI(pool.getHostAddress());
10441072
boolean fullSnapshot = true;
@@ -1049,29 +1077,31 @@ protected Answer copySnapshot(DataObject srcData, DataObject destData) {
10491077
options.put("fullSnapshot", fullSnapshot + "");
10501078
options.put(SnapshotInfo.BackupSnapshotAfterTakingSnapshot.key(),
10511079
String.valueOf(SnapshotInfo.BackupSnapshotAfterTakingSnapshot.value()));
1052-
options.put("volumeSize", snapshotInfo.getBaseVolume().getSize() + "");
1080+
options.put("volumeSize", snapshotObject.getBaseVolume().getSize() + "");
10531081

10541082
try {
1083+
final String rscName = LinstorUtil.RSC_PREFIX + snapshotObject.getBaseVolume().getUuid();
1084+
String snapshotName = setCorrectSnapshotPath(api, rscName, snapshotObject);
1085+
10551086
CopyCommand cmd = new LinstorBackupSnapshotCommand(
1056-
srcData.getTO(),
1087+
snapshotObject.getTO(),
10571088
destData.getTO(),
10581089
_backupsnapshotwait,
10591090
VirtualMachineManager.ExecuteInSequence.value());
10601091
cmd.setOptions(options);
10611092

1062-
String rscName = LinstorUtil.RSC_PREFIX + snapshotInfo.getBaseVolume().getUuid();
10631093
Optional<RemoteHostEndPoint> optEP = getDiskfullEP(api, rscName);
10641094
Answer answer;
10651095
if (optEP.isPresent()) {
10661096
answer = optEP.get().sendMessage(cmd);
10671097
} else {
10681098
s_logger.debug("No diskfull endpoint found to copy image, creating diskless endpoint");
1069-
answer = copyFromTemporaryResource(api, pool, rscName, snapshotInfo, cmd);
1099+
answer = copyFromTemporaryResource(api, pool, rscName, snapshotName, snapshotObject, cmd);
10701100
}
10711101
return answer;
10721102
} catch (Exception e) {
10731103
s_logger.debug("copy snapshot failed: ", e);
1074-
throw new CloudRuntimeException(e.toString());
1104+
throw new CloudRuntimeException("Copy snapshot failed, please cleanup snapshot manually: " + e);
10751105
}
10761106

10771107
}

plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,8 @@ public static List<String> getLinstorNodeNames(@Nonnull DevelopersApi api) throw
112112
Collections.singletonList(storagePoolName),
113113
Collections.emptyList(),
114114
null,
115-
null
115+
null,
116+
true
116117
);
117118
return sps != null ? sps : Collections.emptyList();
118119
}
@@ -166,7 +167,8 @@ public static List<StoragePool> getRscGroupStoragePools(DevelopersApi api, Strin
166167
rscGrps.get(0).getSelectFilter().getStoragePoolList(),
167168
null,
168169
null,
169-
null
170+
null,
171+
true
170172
);
171173
}
172174

0 commit comments

Comments
 (0)