Skip to content

Commit 2588ee4

Browse files
committed
draas initial changes
1 parent 574ed78 commit 2588ee4

File tree

17 files changed

+198
-77
lines changed

17 files changed

+198
-77
lines changed

api/src/main/java/com/cloud/vm/VirtualMachineProfile.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ public static class Param {
7878
public static final Param BootIntoSetup = new Param("enterHardwareSetup");
7979
public static final Param PreserveNics = new Param("PreserveNics");
8080
public static final Param ConsiderLastHost = new Param("ConsiderLastHost");
81+
public static final Param ReturnAfterVolumePrepare = new Param("ReturnAfterVolumePrepare");
8182

8283
private String name;
8384

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public interface BackupProvider {
8585
*/
8686
boolean deleteBackup(Backup backup, boolean forced);
8787

88-
boolean restoreBackupToVM(VirtualMachine vm, Backup backup, String hostIp, String dataStoreUuid);
88+
Pair<Boolean, String> restoreBackupToVM(VirtualMachine vm, Backup backup, String hostIp, String dataStoreUuid);
8989

9090
/**
9191
* Restore VM from backup

engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1476,16 +1476,29 @@ public void orchestrateStart(final String vmUuid, final Map<VirtualMachineProfil
14761476

14771477
processPrepareExternalProvisioning(firstStart, dest.getHost(), vmProfile, dest.getDataCenter());
14781478

1479-
_networkMgr.prepare(vmProfile, dest, ctx);
14801479
if (vm.getHypervisorType() != HypervisorType.BareMetal && vm.getHypervisorType() != HypervisorType.External) {
14811480
checkAndAttemptMigrateVmAcrossCluster(vm, clusterId, dest.getStorageForDisks());
14821481
volumeMgr.prepare(vmProfile, dest);
14831482
}
14841483

1484+
Boolean returnAfterVolumePrepare = (Boolean) params.get(VirtualMachineProfile.Param.ReturnAfterVolumePrepare);
1485+
if (Boolean.TRUE.equals(returnAfterVolumePrepare)) {
1486+
logger.info("Returning from VM start command execution for VM {} as requested. Volumes are prepared and ready.", vm.getUuid());
1487+
1488+
if (!changeState(vm, Event.AgentReportStopped, destHostId, work, Step.Done)) {
1489+
logger.error("Unable to transition to a new state. VM uuid: {}, VM oldstate: {}, Event: {}", vm, vm.getState(), Event.AgentReportStopped);
1490+
throw new ConcurrentOperationException(String.format("Failed to deploy VM %s", vm));
1491+
}
1492+
1493+
logger.debug("Volume and preparation completed for VM {} (VM state set to Stopped)", vm);
1494+
return;
1495+
}
1496+
14851497
if (!reuseVolume) {
14861498
reuseVolume = true;
14871499
}
14881500

1501+
_networkMgr.prepare(vmProfile, dest, ctx);
14891502
vmGuru.finalizeVirtualMachineProfile(vmProfile, dest, ctx);
14901503

14911504
final VirtualMachineTO vmTO = hvGuru.implement(vmProfile);

plugins/backup/dummy/src/main/java/org/apache/cloudstack/backup/DummyBackupProvider.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ public void syncBackupStorageStats(Long zoneId) {
199199
}
200200

201201
@Override
202-
public boolean restoreBackupToVM(VirtualMachine vm, Backup backup, String hostIp, String dataStoreUuid) {
203-
return true;
202+
public Pair<Boolean, String> restoreBackupToVM(VirtualMachine vm, Backup backup, String hostIp, String dataStoreUuid) {
203+
return new Pair<>(true, null);
204204
}
205205
}

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

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,12 @@ public class NASBackupProvider extends AdapterBase implements BackupProvider, Co
103103
@Inject
104104
private VMSnapshotDao vmSnapshotDao;
105105

106+
@Inject
107+
private VolumeDao _volsDao;
108+
109+
@Inject
110+
protected PrimaryDataStoreDao _storagePoolDao = null;
111+
106112
@Inject
107113
private VMSnapshotDetailsDao vmSnapshotDetailsDao;
108114

@@ -115,27 +121,38 @@ public class NASBackupProvider extends AdapterBase implements BackupProvider, Co
115121
@Inject
116122
private DiskOfferingDao diskOfferingDao;
117123

124+
protected Long getClusterIdFromRootVolume(VirtualMachine vm) {
125+
VolumeVO rootVolume = _volsDao.getInstanceRootVolume(vm.getId());
126+
StoragePoolVO rootDiskPool = _storagePoolDao.findById(rootVolume.getPoolId());
127+
return rootDiskPool.getClusterId();
128+
}
129+
118130
protected Host getLastVMHypervisorHost(VirtualMachine vm) {
119131
Long hostId = vm.getLastHostId();
120-
if (hostId == null) {
121-
LOG.debug("Cannot find last host for vm. This should never happen, please check your database.");
122-
return null;
123-
}
124-
Host host = hostDao.findById(hostId);
132+
Long clusterId = null;
125133

126-
if (host.getStatus() == Status.Up) {
127-
return host;
134+
if (hostId != null) {
135+
Host host = hostDao.findById(hostId);
136+
if (host.getStatus() == Status.Up) {
137+
return host;
138+
}
139+
clusterId = host.getClusterId();
128140
} else {
141+
clusterId = getClusterIdFromRootVolume(vm);
142+
}
143+
144+
if (clusterId != null) {
129145
// Try to find any Up host in the same cluster
130-
for (final Host hostInCluster : hostDao.findHypervisorHostInCluster(host.getClusterId())) {
146+
for (final Host hostInCluster : hostDao.findHypervisorHostInCluster(clusterId)) {
131147
if (hostInCluster.getStatus() == Status.Up) {
132-
LOG.debug("Found Host {} in cluster {}", hostInCluster, host.getClusterId());
148+
LOG.debug("Found Host {} in cluster {}", hostInCluster, clusterId);
133149
return hostInCluster;
134150
}
135151
}
136152
}
153+
137154
// Try to find any Host in the zone
138-
return resourceManager.findOneRandomRunningHostByHypervisor(Hypervisor.HypervisorType.KVM, host.getDataCenterId());
155+
return resourceManager.findOneRandomRunningHostByHypervisor(Hypervisor.HypervisorType.KVM, vm.getDataCenterId());
139156
}
140157

141158
protected Host getVMHypervisorHost(VirtualMachine vm) {
@@ -249,16 +266,16 @@ private BackupVO createBackupObject(VirtualMachine vm, String backupPath) {
249266
}
250267

251268
@Override
252-
public boolean restoreBackupToVM(VirtualMachine vm, Backup backup, String hostIp, String dataStoreUuid) {
269+
public Pair<Boolean, String> restoreBackupToVM(VirtualMachine vm, Backup backup, String hostIp, String dataStoreUuid) {
253270
return restoreVMBackup(vm, backup);
254271
}
255272

256273
@Override
257274
public boolean restoreVMFromBackup(VirtualMachine vm, Backup backup) {
258-
return restoreVMBackup(vm, backup);
275+
return restoreVMBackup(vm, backup).first();
259276
}
260277

261-
private boolean restoreVMBackup(VirtualMachine vm, Backup backup) {
278+
private Pair<Boolean, String> restoreVMBackup(VirtualMachine vm, Backup backup) {
262279
List<String> backedVolumesUUIDs = backup.getBackedUpVolumes().stream()
263280
.sorted(Comparator.comparingLong(Backup.VolumeInfo::getDeviceId))
264281
.map(Backup.VolumeInfo::getUuid)
@@ -291,7 +308,7 @@ private boolean restoreVMBackup(VirtualMachine vm, Backup backup) {
291308
} catch (OperationTimedoutException e) {
292309
throw new CloudRuntimeException("Operation to restore backup timed out, please try again");
293310
}
294-
return answer.getResult();
311+
return new Pair<>(answer.getResult(), answer.getDetails());
295312
}
296313

297314
private List<String> getVolumePaths(List<VolumeVO> volumes) {

plugins/backup/networker/src/main/java/org/apache/cloudstack/backup/NetworkerBackupProvider.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -630,7 +630,7 @@ public void syncBackupStorageStats(Long zoneId) {
630630
public boolean willDeleteBackupsOnOfferingRemoval() { return false; }
631631

632632
@Override
633-
public boolean restoreBackupToVM(VirtualMachine vm, Backup backup, String hostIp, String dataStoreUuid) {
634-
return true;
633+
public Pair<Boolean, String> restoreBackupToVM(VirtualMachine vm, Backup backup, String hostIp, String dataStoreUuid) {
634+
return new Pair<>(true, null);
635635
}
636636
}

plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/VeeamBackupProvider.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -337,11 +337,11 @@ public List<Backup.RestorePoint> listRestorePoints(VirtualMachine vm) {
337337
}
338338

339339
@Override
340-
public boolean restoreBackupToVM(VirtualMachine vm, Backup backup, String hostIp, String dataStoreUuid) {
340+
public Pair<Boolean, String> restoreBackupToVM(VirtualMachine vm, Backup backup, String hostIp, String dataStoreUuid) {
341341
final Long zoneId = backup.getZoneId();
342342
final String restorePointId = backup.getExternalId();
343343
final String restoreLocation = vm.getInstanceName();
344-
return getClient(zoneId).restoreVMToDifferentLocation(restorePointId, restoreLocation, hostIp, dataStoreUuid).first();
344+
return getClient(zoneId).restoreVMToDifferentLocation(restorePointId, restoreLocation, hostIp, dataStoreUuid);
345345
}
346346

347347
@Override

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtRestoreBackupCommandWrapper.java

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -76,17 +76,22 @@ public Answer execute(RestoreBackupCommand command, LibvirtComputingResource ser
7676
restoreVolumesOfDestroyedVMs(restoreVolumePaths, vmName, backupPath, backupRepoType, backupRepoAddress, mountOptions);
7777
}
7878
} catch (CloudRuntimeException e) {
79-
String errorMessage = "Failed to restore backup for VM: " + vmName + ".";
80-
if (e.getMessage() != null && !e.getMessage().isEmpty()) {
81-
errorMessage += " Details: " + e.getMessage();
82-
}
83-
logger.error(errorMessage);
79+
String errorMessage = e.getMessage() != null ? e.getMessage() : "";
8480
return new BackupAnswer(command, false, errorMessage);
8581
}
8682

8783
return new BackupAnswer(command, true, newVolumeId);
8884
}
8985

86+
private void verifyBackupFile(String backupPath, String volUuid) {
87+
if (!checkBackupPathExists(backupPath)) {
88+
throw new CloudRuntimeException(String.format("Backup file for the volume [%s] does not exist.", volUuid));
89+
}
90+
if (!checkBackupFileImage(backupPath)) {
91+
throw new CloudRuntimeException(String.format("Backup qcow2 file for the volume [%s] is corrupt.", volUuid));
92+
}
93+
}
94+
9095
private void restoreVolumesOfExistingVM(List<String> restoreVolumePaths, List<String> backedVolumesUUIDs, String backupPath,
9196
String backupRepoType, String backupRepoAddress, String mountOptions) {
9297
String diskType = "root";
@@ -97,6 +102,7 @@ private void restoreVolumesOfExistingVM(List<String> restoreVolumePaths, List<St
97102
String backupVolumeUuid = backedVolumesUUIDs.get(idx);
98103
Pair<String, String> bkpPathAndVolUuid = getBackupPath(mountDirectory, null, backupPath, diskType, backupVolumeUuid);
99104
diskType = "datadisk";
105+
verifyBackupFile(bkpPathAndVolUuid.first(), bkpPathAndVolUuid.second());
100106
if (!replaceVolumeWithBackup(restoreVolumePath, bkpPathAndVolUuid.first())) {
101107
throw new CloudRuntimeException(String.format("Unable to restore contents from the backup volume [%s].", bkpPathAndVolUuid.second()));
102108
}
@@ -116,6 +122,7 @@ private void restoreVolumesOfDestroyedVMs(List<String> volumePaths, String vmNam
116122
String volumePath = volumePaths.get(i);
117123
Pair<String, String> bkpPathAndVolUuid = getBackupPath(mountDirectory, volumePath, backupPath, diskType, null);
118124
diskType = "datadisk";
125+
verifyBackupFile(bkpPathAndVolUuid.first(), bkpPathAndVolUuid.second());
119126
if (!replaceVolumeWithBackup(volumePath, bkpPathAndVolUuid.first())) {
120127
throw new CloudRuntimeException(String.format("Unable to restore contents from the backup volume [%s].", bkpPathAndVolUuid.second()));
121128
}
@@ -132,6 +139,7 @@ private void restoreVolume(String backupPath, String backupRepoType, String back
132139
Pair<String, String> bkpPathAndVolUuid;
133140
try {
134141
bkpPathAndVolUuid = getBackupPath(mountDirectory, volumePath, backupPath, diskType, volumeUUID);
142+
verifyBackupFile(bkpPathAndVolUuid.first(), bkpPathAndVolUuid.second());
135143
if (!replaceVolumeWithBackup(volumePath, bkpPathAndVolUuid.first())) {
136144
throw new CloudRuntimeException(String.format("Unable to restore contents from the backup volume [%s].", bkpPathAndVolUuid.second()));
137145
}
@@ -140,8 +148,6 @@ private void restoreVolume(String backupPath, String backupRepoType, String back
140148
throw new CloudRuntimeException(String.format("Failed to attach volume to VM: %s", vmNameAndState.first()));
141149
}
142150
}
143-
} catch (Exception e) {
144-
throw new CloudRuntimeException("Failed to restore volume", e);
145151
} finally {
146152
unmountBackupDirectory(mountDirectory);
147153
deleteTemporaryDirectory(mountDirectory);
@@ -167,7 +173,8 @@ private String mountBackupDirectory(String backupRepoAddress, String backupRepoT
167173
}
168174
Script.runSimpleBashScript(mount);
169175
} catch (Exception e) {
170-
throw new CloudRuntimeException(String.format("Failed to mount %s to %s", backupRepoType, backupRepoAddress), e);
176+
logger.error(String.format("Failed to mount repository {} of type {} to the directory {}", backupRepoAddress, backupRepoType, mountDirectory), e);
177+
throw new CloudRuntimeException("Failed to mount the backup repository on the KVM host");
171178
}
172179
return mountDirectory;
173180
}
@@ -177,15 +184,17 @@ private void unmountBackupDirectory(String backupDirectory) {
177184
String umountCmd = String.format(UMOUNT_COMMAND, backupDirectory);
178185
Script.runSimpleBashScript(umountCmd);
179186
} catch (Exception e) {
180-
throw new CloudRuntimeException(String.format("Failed to unmount backup directory: %s", backupDirectory), e);
187+
logger.error(String.format("Failed to unmount backup directory {}", backupDirectory), e);
188+
throw new CloudRuntimeException("Failed to unmount the backup directory");
181189
}
182190
}
183191

184192
private void deleteTemporaryDirectory(String backupDirectory) {
185193
try {
186194
Files.deleteIfExists(Paths.get(backupDirectory));
187195
} catch (IOException e) {
188-
throw new CloudRuntimeException(String.format("Failed to delete backup directory: %s", backupDirectory), e);
196+
logger.error(String.format("Failed to delete backup directory: %s", backupDirectory), e);
197+
throw new CloudRuntimeException("Failed to delete the backup directory");
189198
}
190199
}
191200

@@ -197,6 +206,16 @@ private Pair<String, String> getBackupPath(String mountDirectory, String volumeP
197206
return new Pair<>(bkpPath, volUuid);
198207
}
199208

209+
private boolean checkBackupFileImage(String backupPath) {
210+
int exitValue = Script.runSimpleBashScriptForExitValue(String.format("qemu-img check %s", backupPath));
211+
return exitValue == 0;
212+
}
213+
214+
private boolean checkBackupPathExists(String backupPath) {
215+
int exitValue = Script.runSimpleBashScriptForExitValue(String.format("ls %s", backupPath));
216+
return exitValue == 0;
217+
}
218+
200219
private boolean replaceVolumeWithBackup(String volumePath, String backupPath) {
201220
int exitValue = Script.runSimpleBashScriptForExitValue(String.format(RSYNC_COMMAND, backupPath, volumePath));
202221
return exitValue == 0;

server/src/main/java/com/cloud/vm/UserVmManagerImpl.java

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9473,9 +9473,6 @@ public UserVm allocateVMFromBackup(CreateVMFromBackupCmd cmd) throws Insufficien
94739473
if (backup == null) {
94749474
throw new InvalidParameterValueException("Backup " + cmd.getBackupId() + " does not exist");
94759475
}
9476-
if (backup.getZoneId() != cmd.getZoneId()) {
9477-
throw new InvalidParameterValueException("Instance should be created in the same zone as the backup");
9478-
}
94799476
backupManager.validateBackupForZone(backup.getZoneId());
94809477
backupDao.loadDetails(backup);
94819478

@@ -9612,20 +9609,15 @@ public UserVm allocateVMFromBackup(CreateVMFromBackupCmd cmd) throws Insufficien
96129609
@Override
96139610
public UserVm restoreVMFromBackup(CreateVMFromBackupCmd cmd) throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException {
96149611
long vmId = cmd.getEntityId();
9612+
UserVm vm;
96159613
Map<Long, DiskOffering> diskOfferingMap = cmd.getDataDiskTemplateToDiskOfferingMap();
96169614
Map<VirtualMachineProfile.Param, Object> additonalParams = new HashMap<>();
9617-
UserVm vm;
9615+
additonalParams.put(VirtualMachineProfile.Param.ReturnAfterVolumePrepare, true);
96189616

96199617
try {
9620-
vm = startVirtualMachine(vmId, null, null, null, diskOfferingMap, additonalParams, null);
9621-
9622-
boolean status = stopVirtualMachine(CallContext.current().getCallingUserId(), vm.getId()) ;
9623-
if (!status) {
9624-
UserVmVO vmVO = _vmDao.findById(vmId);
9625-
expunge(vmVO);
9626-
logger.debug("Successfully cleaned up Instance {} after create Instance from backup failed", vmId);
9627-
throw new CloudRuntimeException("Unable to stop the Instance before restore");
9628-
}
9618+
Pair<UserVmVO, Map<VirtualMachineProfile.Param, Object>> vmParamPair = null;
9619+
vmParamPair = startVirtualMachine(vmId, null, null, null, additonalParams, null);
9620+
vm = vmParamPair.first();
96299621

96309622
Long isoId = vm.getIsoId();
96319623
if (isoId != null) {
@@ -9663,6 +9655,7 @@ public UserVm restoreVMFromBackup(CreateVMFromBackupCmd cmd) throws ResourceUnav
96639655
podId = adminCmd.getPodId();
96649656
clusterId = adminCmd.getClusterId();
96659657
}
9658+
additonalParams.remove(VirtualMachineProfile.Param.ReturnAfterVolumePrepare);
96669659
vm = startVirtualMachine(vmId, podId, clusterId, cmd.getHostId(), diskOfferingMap, additonalParams, cmd.getDeploymentPlanner());
96679660
}
96689661
return vm;

0 commit comments

Comments
 (0)