Skip to content

Commit b7a11cb

Browse files
NAS backup provider: Support restore from backup to volumes on Ceph storage pool(s), and take backup for stopped instances with volumes on Ceph storage pool(s) (#11684)
Co-authored-by: Abhisar Sinha <[email protected]>
1 parent 5a8a1e2 commit b7a11cb

File tree

9 files changed

+255
-41
lines changed

9 files changed

+255
-41
lines changed

api/src/main/java/org/apache/cloudstack/backup/BackupManager.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer
5555
ConfigKey<String> BackupProviderPlugin = new ConfigKey<>("Advanced", String.class,
5656
"backup.framework.provider.plugin",
5757
"dummy",
58-
"The backup and recovery provider plugin.", true, ConfigKey.Scope.Zone, BackupFrameworkEnabled.key());
58+
"The backup and recovery provider plugin. Valid plugin values: dummy, veeam, networker and nas", true, ConfigKey.Scope.Zone, BackupFrameworkEnabled.key());
5959

6060
ConfigKey<Long> BackupSyncPollingInterval = new ConfigKey<>("Advanced", Long.class,
6161
"backup.framework.sync.interval",

core/src/main/java/org/apache/cloudstack/backup/RestoreBackupCommand.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import com.cloud.agent.api.Command;
2323
import com.cloud.agent.api.LogLevel;
2424
import com.cloud.vm.VirtualMachine;
25+
import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
2526

2627
import java.util.List;
2728

@@ -31,6 +32,7 @@ public class RestoreBackupCommand extends Command {
3132
private String backupRepoType;
3233
private String backupRepoAddress;
3334
private List<String> backupVolumesUUIDs;
35+
private List<PrimaryDataStoreTO> restoreVolumePools;
3436
private List<String> restoreVolumePaths;
3537
private String diskType;
3638
private Boolean vmExists;
@@ -74,6 +76,14 @@ public void setBackupRepoAddress(String backupRepoAddress) {
7476
this.backupRepoAddress = backupRepoAddress;
7577
}
7678

79+
public List<PrimaryDataStoreTO> getRestoreVolumePools() {
80+
return restoreVolumePools;
81+
}
82+
83+
public void setRestoreVolumePools(List<PrimaryDataStoreTO> restoreVolumePools) {
84+
this.restoreVolumePools = restoreVolumePools;
85+
}
86+
7787
public List<String> getRestoreVolumePaths() {
7888
return restoreVolumePaths;
7989
}

core/src/main/java/org/apache/cloudstack/backup/TakeBackupCommand.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import com.cloud.agent.api.Command;
2323
import com.cloud.agent.api.LogLevel;
24+
import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
2425

2526
import java.util.List;
2627

@@ -29,6 +30,7 @@ public class TakeBackupCommand extends Command {
2930
private String backupPath;
3031
private String backupRepoType;
3132
private String backupRepoAddress;
33+
private List<PrimaryDataStoreTO> volumePools;
3234
private List<String> volumePaths;
3335
private Boolean quiesce;
3436
@LogLevel(LogLevel.Log4jLevel.Off)
@@ -80,6 +82,14 @@ public void setMountOptions(String mountOptions) {
8082
this.mountOptions = mountOptions;
8183
}
8284

85+
public List<PrimaryDataStoreTO> getVolumePools() {
86+
return volumePools;
87+
}
88+
89+
public void setVolumePools(List<PrimaryDataStoreTO> volumePools) {
90+
this.volumePools = volumePools;
91+
}
92+
8393
public List<String> getVolumePaths() {
8494
return volumePaths;
8595
}

plugins/backup/nas/src/main/java/org/apache/cloudstack/backup/NASBackupProvider.java

Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@
2626
import com.cloud.hypervisor.Hypervisor;
2727
import com.cloud.offering.DiskOffering;
2828
import com.cloud.resource.ResourceManager;
29+
import com.cloud.storage.DataStoreRole;
2930
import com.cloud.storage.ScopeType;
3031
import com.cloud.storage.Storage;
31-
import com.cloud.storage.StoragePoolHostVO;
3232
import com.cloud.storage.Volume;
3333
import com.cloud.storage.VolumeApiServiceImpl;
3434
import com.cloud.storage.VolumeVO;
@@ -49,10 +49,13 @@
4949

5050
import org.apache.cloudstack.backup.dao.BackupDao;
5151
import org.apache.cloudstack.backup.dao.BackupRepositoryDao;
52+
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
53+
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
5254
import org.apache.cloudstack.framework.config.ConfigKey;
5355
import org.apache.cloudstack.framework.config.Configurable;
5456
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
5557
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
58+
import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
5659
import org.apache.logging.log4j.Logger;
5760
import org.apache.logging.log4j.LogManager;
5861

@@ -106,6 +109,9 @@ public class NASBackupProvider extends AdapterBase implements BackupProvider, Co
106109
@Inject
107110
private PrimaryDataStoreDao primaryDataStoreDao;
108111

112+
@Inject
113+
DataStoreManager dataStoreMgr;
114+
109115
@Inject
110116
private AgentManager agentManager;
111117

@@ -203,8 +209,9 @@ public Pair<Boolean, Backup> takeBackup(final VirtualMachine vm, Boolean quiesce
203209
if (VirtualMachine.State.Stopped.equals(vm.getState())) {
204210
List<VolumeVO> vmVolumes = volumeDao.findByInstance(vm.getId());
205211
vmVolumes.sort(Comparator.comparing(Volume::getDeviceId));
206-
List<String> volumePaths = getVolumePaths(vmVolumes);
207-
command.setVolumePaths(volumePaths);
212+
Pair<List<PrimaryDataStoreTO>, List<String>> volumePoolsAndPaths = getVolumePoolsAndPaths(vmVolumes);
213+
command.setVolumePools(volumePoolsAndPaths.first());
214+
command.setVolumePaths(volumePoolsAndPaths.second());
208215
}
209216

210217
BackupAnswer answer;
@@ -303,7 +310,9 @@ private Pair<Boolean, String> restoreVMBackup(VirtualMachine vm, Backup backup)
303310
restoreCommand.setMountOptions(backupRepository.getMountOptions());
304311
restoreCommand.setVmName(vm.getName());
305312
restoreCommand.setBackupVolumesUUIDs(backedVolumesUUIDs);
306-
restoreCommand.setRestoreVolumePaths(getVolumePaths(restoreVolumes));
313+
Pair<List<PrimaryDataStoreTO>, List<String>> volumePoolsAndPaths = getVolumePoolsAndPaths(restoreVolumes);
314+
restoreCommand.setRestoreVolumePools(volumePoolsAndPaths.first());
315+
restoreCommand.setRestoreVolumePaths(volumePoolsAndPaths.second());
307316
restoreCommand.setVmExists(vm.getRemoved() == null);
308317
restoreCommand.setVmState(vm.getState());
309318
restoreCommand.setMountTimeout(NASBackupRestoreMountTimeout.value());
@@ -319,31 +328,42 @@ private Pair<Boolean, String> restoreVMBackup(VirtualMachine vm, Backup backup)
319328
return new Pair<>(answer.getResult(), answer.getDetails());
320329
}
321330

322-
private List<String> getVolumePaths(List<VolumeVO> volumes) {
331+
private Pair<List<PrimaryDataStoreTO>, List<String>> getVolumePoolsAndPaths(List<VolumeVO> volumes) {
332+
List<PrimaryDataStoreTO> volumePools = new ArrayList<>();
323333
List<String> volumePaths = new ArrayList<>();
324334
for (VolumeVO volume : volumes) {
325335
StoragePoolVO storagePool = primaryDataStoreDao.findById(volume.getPoolId());
326336
if (Objects.isNull(storagePool)) {
327337
throw new CloudRuntimeException("Unable to find storage pool associated to the volume");
328338
}
329-
String volumePathPrefix;
330-
if (ScopeType.HOST.equals(storagePool.getScope())) {
331-
volumePathPrefix = storagePool.getPath();
332-
} else if (Storage.StoragePoolType.SharedMountPoint.equals(storagePool.getPoolType())) {
333-
volumePathPrefix = storagePool.getPath();
334-
} else {
335-
volumePathPrefix = String.format("/mnt/%s", storagePool.getUuid());
336-
}
339+
340+
DataStore dataStore = dataStoreMgr.getDataStore(storagePool.getId(), DataStoreRole.Primary);
341+
volumePools.add(dataStore != null ? (PrimaryDataStoreTO)dataStore.getTO() : null);
342+
343+
String volumePathPrefix = getVolumePathPrefix(storagePool);
337344
volumePaths.add(String.format("%s/%s", volumePathPrefix, volume.getPath()));
338345
}
339-
return volumePaths;
346+
return new Pair<>(volumePools, volumePaths);
347+
}
348+
349+
private String getVolumePathPrefix(StoragePoolVO storagePool) {
350+
String volumePathPrefix;
351+
if (ScopeType.HOST.equals(storagePool.getScope()) ||
352+
Storage.StoragePoolType.SharedMountPoint.equals(storagePool.getPoolType()) ||
353+
Storage.StoragePoolType.RBD.equals(storagePool.getPoolType())) {
354+
volumePathPrefix = storagePool.getPath();
355+
} else {
356+
// Should be Storage.StoragePoolType.NetworkFilesystem
357+
volumePathPrefix = String.format("/mnt/%s", storagePool.getUuid());
358+
}
359+
return volumePathPrefix;
340360
}
341361

342362
@Override
343363
public Pair<Boolean, String> restoreBackedUpVolume(Backup backup, Backup.VolumeInfo backupVolumeInfo, String hostIp, String dataStoreUuid, Pair<String, VirtualMachine.State> vmNameAndState) {
344364
final VolumeVO volume = volumeDao.findByUuid(backupVolumeInfo.getUuid());
345365
final DiskOffering diskOffering = diskOfferingDao.findByUuid(backupVolumeInfo.getDiskOfferingId());
346-
final StoragePoolHostVO dataStore = storagePoolHostDao.findByUuid(dataStoreUuid);
366+
final StoragePoolVO pool = primaryDataStoreDao.findByUuid(dataStoreUuid);
347367
final HostVO hostVO = hostDao.findByIp(hostIp);
348368

349369
LOG.debug("Restoring vm volume {} from backup {} on the NAS Backup Provider", backupVolumeInfo, backup);
@@ -360,19 +380,26 @@ public Pair<Boolean, String> restoreBackedUpVolume(Backup backup, Backup.VolumeI
360380
restoredVolume.setUuid(volumeUUID);
361381
restoredVolume.setRemoved(null);
362382
restoredVolume.setDisplayVolume(true);
363-
restoredVolume.setPoolId(dataStore.getPoolId());
383+
restoredVolume.setPoolId(pool.getId());
384+
restoredVolume.setPoolType(pool.getPoolType());
364385
restoredVolume.setPath(restoredVolume.getUuid());
365386
restoredVolume.setState(Volume.State.Copying);
366-
restoredVolume.setFormat(Storage.ImageFormat.QCOW2);
367387
restoredVolume.setSize(backupVolumeInfo.getSize());
368388
restoredVolume.setDiskOfferingId(diskOffering.getId());
389+
if (pool.getPoolType() != Storage.StoragePoolType.RBD) {
390+
restoredVolume.setFormat(Storage.ImageFormat.QCOW2);
391+
} else {
392+
restoredVolume.setFormat(Storage.ImageFormat.RAW);
393+
}
369394

370395
RestoreBackupCommand restoreCommand = new RestoreBackupCommand();
371396
restoreCommand.setBackupPath(backup.getExternalId());
372397
restoreCommand.setBackupRepoType(backupRepository.getType());
373398
restoreCommand.setBackupRepoAddress(backupRepository.getAddress());
374399
restoreCommand.setVmName(vmNameAndState.first());
375-
restoreCommand.setRestoreVolumePaths(Collections.singletonList(String.format("%s/%s", dataStore.getLocalPath(), volumeUUID)));
400+
restoreCommand.setRestoreVolumePaths(Collections.singletonList(String.format("%s/%s", getVolumePathPrefix(pool), volumeUUID)));
401+
DataStore dataStore = dataStoreMgr.getDataStore(pool.getId(), DataStoreRole.Primary);
402+
restoreCommand.setRestoreVolumePools(Collections.singletonList(dataStore != null ? (PrimaryDataStoreTO)dataStore.getTO() : null));
376403
restoreCommand.setDiskType(backupVolumeInfo.getType().name().toLowerCase(Locale.ROOT));
377404
restoreCommand.setMountOptions(backupRepository.getMountOptions());
378405
restoreCommand.setVmExists(null);

0 commit comments

Comments
 (0)