JoaoJandre commented on code in PR #10140:
URL: https://github.com/apache/cloudstack/pull/10140#discussion_r2229202350


##########
server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java:
##########
@@ -926,6 +1058,224 @@ private Backup.VolumeInfo 
getVolumeInfo(List<Backup.VolumeInfo> backedUpVolumes,
         return null;
     }
 
+    @Override
+    public void checkVmDisksSizeAgainstBackup(List<VmDiskInfo> vmDiskInfoList, 
Backup backup) {
+        List<VmDiskInfo> vmDiskInfoListFromBackup = 
getDataDiskInfoListFromBackup(backup);
+        int index = 0;
+        if (vmDiskInfoList.size() != vmDiskInfoListFromBackup.size()) {
+            throw new InvalidParameterValueException("Unable to create 
Instance from Backup " +
+                    "as the backup has a different number of disks than the 
Instance.");
+        }
+        for (VmDiskInfo vmDiskInfo : vmDiskInfoList) {
+            if (index < vmDiskInfoListFromBackup.size()) {
+                if (vmDiskInfo.getSize() < 
vmDiskInfoListFromBackup.get(index).getSize()) {
+                    throw new InvalidParameterValueException(
+                            String.format("Instance volume size %d[GiB] cannot 
be less than the backed-up volume size %d[GiB].",
+                            vmDiskInfo.getSize(), 
vmDiskInfoListFromBackup.get(index).getSize()));
+                }
+            }
+            index++;
+        }
+    }
+
+    @Override
+    public VmDiskInfo getRootDiskInfoFromBackup(Backup backup) {
+        List<Backup.VolumeInfo> volumes = backup.getBackedUpVolumes();
+        VmDiskInfo rootDiskOffering = null;
+        if (volumes == null || volumes.isEmpty()) {
+            throw new CloudRuntimeException("Failed to get backed-up volumes 
info from backup");
+        }
+        for (Backup.VolumeInfo volume : volumes) {
+            if (volume.getType() == Volume.Type.ROOT) {
+                DiskOfferingVO diskOffering = 
diskOfferingDao.findByUuid(volume.getDiskOfferingId());
+                if (diskOffering == null) {
+                    throw new CloudRuntimeException(String.format("Unable to 
find the root disk offering with uuid (%s) " +
+                            "stored in backup. Please specify a valid root 
disk offering id while creating the instance",
+                            volume.getDiskOfferingId()));
+                }
+                Long size = volume.getSize() / (1024 * 1024 * 1024);
+                rootDiskOffering = new VmDiskInfo(diskOffering, size, 
volume.getMinIops(), volume.getMaxIops());
+            }
+        }
+        if (rootDiskOffering == null) {
+            throw new CloudRuntimeException("Failed to get the root disk in 
backed-up volumes info from backup");
+        }
+        return rootDiskOffering;
+    }
+
+    @Override
+    public List<VmDiskInfo> getDataDiskInfoListFromBackup(Backup backup) {
+        List<VmDiskInfo> vmDiskInfoList = new ArrayList<>();
+        List<Backup.VolumeInfo> volumes = backup.getBackedUpVolumes();
+        if (volumes == null || volumes.isEmpty()) {
+            throw new CloudRuntimeException("Failed to get backed-up Volumes 
info from backup");
+        }
+        for (Backup.VolumeInfo volume : volumes) {
+            if (volume.getType() == Volume.Type.DATADISK) {
+                DiskOfferingVO diskOffering = 
diskOfferingDao.findByUuid(volume.getDiskOfferingId());
+                if (diskOffering == null || 
diskOffering.getState().equals(DiskOffering.State.Inactive)) {
+                    throw new CloudRuntimeException("Unable to find the disk 
offering with uuid (" + volume.getDiskOfferingId() + ") stored in backup. " +
+                            "Please specify a valid disk offering id while 
creating the instance");
+                }
+                Long size = volume.getSize() / (1024 * 1024 * 1024);
+                vmDiskInfoList.add(new VmDiskInfo(diskOffering, size, 
volume.getMinIops(), volume.getMaxIops(), volume.getDeviceId()));
+            }
+        }
+        return vmDiskInfoList;
+    }
+
+    private List<String> parseAddressString(String input) {
+        if (input == null) return null;
+        return Arrays.stream(input.split(","))
+                .map(s -> "null".equalsIgnoreCase(s.trim()) ? null : s.trim())
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    public Map<Long, Network.IpAddresses> getIpToNetworkMapFromBackup(Backup 
backup, boolean preserveIps, List<Long> networkIds)
+    {
+        Map<Long, Network.IpAddresses> ipToNetworkMap = new 
LinkedHashMap<Long, Network.IpAddresses>();
+
+        String nicsJson = backup.getDetail(ApiConstants.NICS);
+        if (nicsJson == null) {
+            throw new CloudRuntimeException("Backup doesn't contain network 
information. " +
+                    "Please specify at least one valid network while creating 
instance");
+        }
+
+        Type type = new TypeToken<List<Map<String, String>>>(){}.getType();
+        List<Map<String, String>> nics = new Gson().fromJson(nicsJson, type);
+
+        for (Map<String, String> nic : nics) {
+            String networkUuid = nic.get(ApiConstants.NETWORK_ID);
+            if (networkUuid == null) {
+                throw new CloudRuntimeException("Backup doesn't contain 
network information. " +
+                        "Please specify at least one valid network while 
creating instance");
+            }
+
+            Network network = networkDao.findByUuid(networkUuid);
+            if (network == null) {
+                throw new CloudRuntimeException("Unable to find network with 
the uuid " + networkUuid + " stored in backup. " +
+                        "Please specify a valid network id while creating the 
instance");
+            }
+
+            Long networkId = network.getId();
+            Network.IpAddresses ipAddresses = null;
+
+            if (preserveIps) {
+                String ip = nic.get(ApiConstants.IP_ADDRESS);
+                String ipv6 = nic.get(ApiConstants.IP6_ADDRESS);
+                String mac = nic.get(ApiConstants.MAC_ADDRESS);
+                ipAddresses = networkService.getIpAddressesFromIps(ip, ipv6, 
mac);
+            }
+
+            ipToNetworkMap.put(networkId, ipAddresses);
+            networkIds.add(networkId);
+        }
+        return ipToNetworkMap;
+    }
+
+    @Override
+    public Boolean canCreateInstanceFromBackup(final Long backupId) {
+        final BackupVO backup = backupDao.findById(backupId);
+        BackupOffering offering = 
backupOfferingDao.findByIdIncludingRemoved(backup.getBackupOfferingId());
+        if (offering == null) {
+            throw new CloudRuntimeException("Failed to find backup offering");
+        }
+        final BackupProvider backupProvider = 
getBackupProvider(offering.getProvider());
+        return backupProvider.supportsInstanceFromBackup();
+    }
+
+    @Override
+    public boolean restoreBackupToVM(final Long backupId, final Long vmId) 
throws ResourceUnavailableException {
+        final BackupVO backup = backupDao.findById(backupId);
+        if (backup == null) {
+            throw new CloudRuntimeException("Backup " + backupId + " does not 
exist");
+        }
+        if (backup.getStatus() != Backup.Status.BackedUp) {
+            throw new CloudRuntimeException("Backup should be in BackedUp 
state");
+        }
+        validateBackupForZone(backup.getZoneId());
+
+        VMInstanceVO vm = vmInstanceDao.findByIdIncludingRemoved(vmId);
+        if (vm == null) {
+            throw new CloudRuntimeException("Instance with ID " + 
backup.getVmId() + " couldn't be found.");
+        }
+        accountManager.checkAccess(CallContext.current().getCallingAccount(), 
null, true, vm);
+
+        if (vm.getRemoved() != null) {
+            throw new CloudRuntimeException("Instance with ID " + 
backup.getVmId() + " couldn't be found.");
+        }
+        if (!vm.getState().equals(VirtualMachine.State.Stopped)) {
+            throw new CloudRuntimeException("The VM should be in stopped 
state");
+        }
+
+        List<Backup.VolumeInfo> backupVolumes = backup.getBackedUpVolumes();
+        if (backupVolumes == null) {
+            throw new CloudRuntimeException("Backed up volumes info not found 
in the backup");
+        }
+
+        List<VolumeVO> vmVolumes = volumeDao.findByInstance(vmId);
+        if (vmVolumes.size() != backupVolumes.size()) {
+            throw new CloudRuntimeException("Unable to create Instance from 
backup as the backup has a different number of disks than the Instance");
+        }
+
+        int index = 0;
+        for (VolumeVO vmVolume: vmVolumes) {
+            Backup.VolumeInfo backupVolume = backupVolumes.get(index);
+            if (vmVolume.getSize() < backupVolume.getSize()) {
+                throw new CloudRuntimeException(String.format(
+                    "Instance volume size %d[GiB] for volume (%s) is less than 
the backed-up volume size %d[GiB] for backed-up volume (%s).",
+                    vmVolume.getSize(), vmVolume.getUuid(), 
backupVolume.getSize(), backupVolume.getUuid()));
+            }
+            index++;
+        }
+
+        BackupOffering offering = 
backupOfferingDao.findByIdIncludingRemoved(backup.getBackupOfferingId());
+        if (offering == null) {
+            throw new CloudRuntimeException("Failed to find backup offering");
+        }
+        final BackupProvider backupProvider = 
getBackupProvider(offering.getProvider());
+        if (!backupProvider.supportsInstanceFromBackup()) {
+            throw new CloudRuntimeException("Create instance from backup is 
not supported by the " + offering.getProvider() + " provider.");
+        }
+
+        String backupDetailsInMessage = 
ReflectionToStringBuilderUtils.reflectOnlySelectedFields(backup, "uuid", 
"externalId", "newVMId", "type", "status", "date");
+        Long eventId = null;
+        try {
+            updateVmState(vm, VirtualMachine.Event.RestoringRequested, 
VirtualMachine.State.Restoring);
+            updateVolumeState(vm, Volume.Event.RestoreRequested, 
Volume.State.Restoring);
+            eventId = ActionEventUtils.onStartedActionEvent(User.UID_SYSTEM, 
vm.getAccountId(), EventTypes.EVENT_VM_CREATE_FROM_BACKUP,
+                    String.format("Creating Instance %s from backup %s", 
vm.getInstanceName(), backup.getUuid()),
+                    vm.getId(), 
ApiCommandResourceType.VirtualMachine.toString(),
+                    true, 0);
+
+            String host = null;
+            String dataStore = null;
+            if (!"nas".equals(offering.getProvider())) {
+                Pair<HostVO, StoragePoolVO> restoreInfo = 
getRestoreVolumeHostAndDatastore(vm);
+                host = restoreInfo.first().getPrivateIpAddress();
+                dataStore = restoreInfo.second().getUuid();
+            }
+            if (!backupProvider.restoreBackupToVM(vm, backup, host, 
dataStore)) {
+                throw new CloudRuntimeException(String.format("Error restoring 
backup [%s] to VM %s.", backupDetailsInMessage, vm.getUuid()));
+            }
+        } catch (Exception e) {
+            updateVolumeState(vm, Volume.Event.RestoreFailed, 
Volume.State.Ready);
+            updateVmState(vm, VirtualMachine.Event.RestoringFailed, 
VirtualMachine.State.Stopped);

Review Comment:
   So if an exception happens the new VM is automatically expunged?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscr...@cloudstack.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org

Reply via email to