Skip to content

Commit a15b706

Browse files
authored
Linstor: Allow snapshot backup also to work on non hyperconverged setups (#8271)
On no access to the storage nodes, we now create a temporary resource from the snapshot and copy that data into the secondary storage. Revert works the same, just that we now also look additionally for any Linstor agent node. Also enables now backup snapshot by default. This whole BackupSnapshot functionality was introduced in 4.19, so I would be happy if this still could be merged.
1 parent d3cad42 commit a15b706

File tree

2 files changed

+81
-10
lines changed

2 files changed

+81
-10
lines changed

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

Lines changed: 79 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,16 @@ private String cloneResource(long csCloneId, VolumeInfo volumeInfo, StoragePoolV
527527
}
528528
}
529529

530+
private ResourceDefinitionCreate createResourceDefinitionCreate(String rscName, String rscGrpName)
531+
throws ApiException {
532+
ResourceDefinitionCreate rdCreate = new ResourceDefinitionCreate();
533+
ResourceDefinition rd = new ResourceDefinition();
534+
rd.setName(rscName);
535+
rd.setResourceGroupName(rscGrpName);
536+
rdCreate.setResourceDefinition(rd);
537+
return rdCreate;
538+
}
539+
530540
private String createResourceFromSnapshot(long csSnapshotId, String rscName, StoragePoolVO storagePoolVO) {
531541
final String rscGrp = getRscGrp(storagePoolVO);
532542
final DevelopersApi linstorApi = LinstorUtil.getLinstorAPI(storagePoolVO.getHostAddress());
@@ -539,11 +549,7 @@ private String createResourceFromSnapshot(long csSnapshotId, String rscName, Sto
539549
try
540550
{
541551
s_logger.debug("Create new resource definition: " + rscName);
542-
ResourceDefinitionCreate rdCreate = new ResourceDefinitionCreate();
543-
ResourceDefinition rd = new ResourceDefinition();
544-
rd.setName(rscName);
545-
rd.setResourceGroupName(rscGrp);
546-
rdCreate.setResourceDefinition(rd);
552+
ResourceDefinitionCreate rdCreate = createResourceDefinitionCreate(rscName, rscGrp);
547553
ApiCallRcList answers = linstorApi.resourceDefinitionCreate(rdCreate);
548554
checkLinstorAnswersThrow(answers);
549555

@@ -712,6 +718,10 @@ private String revertSnapshotFromImageStore(
712718
VirtualMachineManager.ExecuteInSequence.value());
713719

714720
Optional<RemoteHostEndPoint> optEP = getDiskfullEP(linstorApi, rscName);
721+
if (optEP.isEmpty()) {
722+
optEP = getLinstorEP(linstorApi, rscName);
723+
}
724+
715725
if (optEP.isPresent()) {
716726
Answer answer = optEP.get().sendMessage(cmd);
717727
if (!answer.getResult()) {
@@ -840,6 +850,14 @@ public void copyAsync(DataObject srcData, DataObject dstData, AsyncCompletionCal
840850
callback.complete(res);
841851
}
842852

853+
/**
854+
* Tries to get a Linstor cloudstack end point, that is at least diskless.
855+
*
856+
* @param api Linstor java api object
857+
* @param rscName resource name to make available on node
858+
* @return Optional RemoteHostEndPoint if one could get found.
859+
* @throws ApiException
860+
*/
843861
private Optional<RemoteHostEndPoint> getLinstorEP(DevelopersApi api, String rscName) throws ApiException {
844862
List<String> linstorNodeNames = LinstorUtil.getLinstorNodeNames(api);
845863
Collections.shuffle(linstorNodeNames); // do not always pick the first linstor node
@@ -892,6 +910,25 @@ private Optional<RemoteHostEndPoint> getDiskfullEP(DevelopersApi api, String rsc
892910
return Optional.empty();
893911
}
894912

913+
private String restoreResourceFromSnapshot(
914+
DevelopersApi api,
915+
StoragePoolVO storagePoolVO,
916+
String rscName,
917+
String snapshotName,
918+
String restoredName) throws ApiException {
919+
final String rscGrp = getRscGrp(storagePoolVO);
920+
ResourceDefinitionCreate rdc = createResourceDefinitionCreate(restoredName, rscGrp);
921+
api.resourceDefinitionCreate(rdc);
922+
923+
SnapshotRestore sr = new SnapshotRestore();
924+
sr.toResource(restoredName);
925+
api.resourceSnapshotsRestoreVolumeDefinition(rscName, snapshotName, sr);
926+
927+
api.resourceSnapshotRestore(rscName, snapshotName, sr);
928+
929+
return getDeviceName(api, restoredName);
930+
}
931+
895932
private Answer copyTemplate(DataObject srcData, DataObject dstData) {
896933
TemplateInfo tInfo = (TemplateInfo) dstData;
897934
final StoragePoolVO pool = _storagePoolDao.findById(dstData.getDataStore().getId());
@@ -929,6 +966,39 @@ private Answer copyTemplate(DataObject srcData, DataObject dstData) {
929966
return answer;
930967
}
931968

969+
/**
970+
* Create a temporary resource from the snapshot to backup, so we can copy the data on a diskless agent
971+
* @param api Linstor Developer api object
972+
* @param pool StoragePool this resource resides on
973+
* @param rscName rscName of the snapshotted resource
974+
* @param snapshotInfo snapshot info of the snapshot
975+
* @param origCmd original LinstorBackupSnapshotCommand that needs to have a patched path
976+
* @return answer from agent operation
977+
* @throws ApiException if any Linstor api operation fails
978+
*/
979+
private Answer copyFromTemporaryResource(
980+
DevelopersApi api, StoragePoolVO pool, String rscName, SnapshotInfo snapshotInfo, CopyCommand origCmd)
981+
throws ApiException {
982+
Answer answer;
983+
String restoreName = rscName + "-rst";
984+
String snapshotName = LinstorUtil.RSC_PREFIX + snapshotInfo.getUuid();
985+
String devName = restoreResourceFromSnapshot(api, pool, rscName, snapshotName, restoreName);
986+
987+
Optional<RemoteHostEndPoint> optEPAny = getLinstorEP(api, restoreName);
988+
if (optEPAny.isPresent()) {
989+
// patch the src device path to the temporary linstor resource
990+
SnapshotObjectTO soTO = (SnapshotObjectTO)snapshotInfo.getTO();
991+
soTO.setPath(devName);
992+
origCmd.setSrcTO(soTO);
993+
answer = optEPAny.get().sendMessage(origCmd);
994+
} else{
995+
answer = new Answer(origCmd, false, "Unable to get matching Linstor endpoint.");
996+
}
997+
// delete the temporary resource, noop if already gone
998+
api.resourceDefinitionDelete(restoreName);
999+
return answer;
1000+
}
1001+
9321002
protected Answer copySnapshot(DataObject srcData, DataObject destData) {
9331003
String value = _configDao.getValue(Config.BackupSnapshotWait.toString());
9341004
int _backupsnapshotwait = NumbersUtil.parseInt(
@@ -956,13 +1026,14 @@ protected Answer copySnapshot(DataObject srcData, DataObject destData) {
9561026
VirtualMachineManager.ExecuteInSequence.value());
9571027
cmd.setOptions(options);
9581028

959-
Optional<RemoteHostEndPoint> optEP = getDiskfullEP(
960-
api, LinstorUtil.RSC_PREFIX + snapshotInfo.getBaseVolume().getUuid());
1029+
String rscName = LinstorUtil.RSC_PREFIX + snapshotInfo.getBaseVolume().getUuid();
1030+
Optional<RemoteHostEndPoint> optEP = getDiskfullEP(api, rscName);
9611031
Answer answer;
9621032
if (optEP.isPresent()) {
9631033
answer = optEP.get().sendMessage(cmd);
9641034
} else {
965-
answer = new Answer(cmd, false, "Unable to get matching Linstor endpoint.");
1035+
s_logger.debug("No diskfull endpoint found to copy image, creating diskless endpoint");
1036+
answer = copyFromTemporaryResource(api, pool, rscName, snapshotInfo, cmd);
9661037
}
9671038
return answer;
9681039
} catch (Exception e) {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@
2121

2222
public class LinstorConfigurationManager implements Configurable
2323
{
24-
public static final ConfigKey<Boolean> BackupSnapshots = new ConfigKey<>(Boolean.class, "lin.backup.snapshots", "Advanced", "false",
25-
"Backup Linstor primary storage snapshots to secondary storage (deleting ps snapshot), only works on hyperconverged setups.", true, ConfigKey.Scope.Global, null);
24+
public static final ConfigKey<Boolean> BackupSnapshots = new ConfigKey<>(Boolean.class, "lin.backup.snapshots", "Advanced", "true",
25+
"Backup Linstor primary storage snapshots to secondary storage (deleting ps snapshot)", true, ConfigKey.Scope.Global, null);
2626

2727
public static final ConfigKey<?>[] CONFIG_KEYS = new ConfigKey<?>[] { BackupSnapshots };
2828

0 commit comments

Comments
 (0)