diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java index f5861c257a1d..0f61fe2addc8 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -63,6 +63,7 @@ public class ApiConstants { public static final String BACKUP_STORAGE_LIMIT = "backupstoragelimit"; public static final String BACKUP_STORAGE_TOTAL = "backupstoragetotal"; public static final String BACKUP_VM_OFFERING_REMOVED = "vmbackupofferingremoved"; + public static final String BACKUP_VM_EXPUNGED = "backupvmexpunged"; public static final String BACKUP_TOTAL = "backuptotal"; public static final String BASE64_IMAGE = "base64image"; public static final String BGP_PEERS = "bgppeers"; diff --git a/api/src/main/java/org/apache/cloudstack/api/response/BackupOfferingResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/BackupOfferingResponse.java index 0e895fa4e967..4120f68d9da3 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/BackupOfferingResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/BackupOfferingResponse.java @@ -61,6 +61,10 @@ public class BackupOfferingResponse extends BaseResponse { @Param(description = "zone name") private String zoneName; + @SerializedName(ApiConstants.CROSS_ZONE_INSTANCE_CREATION) + @Param(description = "the backups with this offering can be used to create Instances on all Zones", since = "4.22.0") + private Boolean crossZoneInstanceCreation; + @SerializedName(ApiConstants.CREATED) @Param(description = "the date this backup offering was created") private Date created; @@ -97,6 +101,10 @@ public void setZoneName(String zoneName) { this.zoneName = zoneName; } + public void setCrossZoneInstanceCreation(Boolean crossZoneInstanceCreation) { + this.crossZoneInstanceCreation = crossZoneInstanceCreation; + } + public void setCreated(Date created) { this.created = created; } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/BackupResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/BackupResponse.java index 0ae558ac8033..5c9b23c01bd4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/BackupResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/BackupResponse.java @@ -123,6 +123,10 @@ public class BackupResponse extends BaseResponse { @Param(description = "The backup offering corresponding to this backup was removed from the VM", since = "4.21.0") private Boolean vmOfferingRemoved; + @SerializedName(ApiConstants.BACKUP_VM_EXPUNGED) + @Param(description = "The VM from which the backup was taken is expunged", since = "4.22.0") + private Boolean vmExpunged; + public String getId() { return id; } @@ -306,4 +310,8 @@ public Boolean getVmOfferingRemoved() { public void setVmOfferingRemoved(Boolean vmOfferingRemoved) { this.vmOfferingRemoved = vmOfferingRemoved; } + + public void setVmExpunged(Boolean vmExpunged) { + this.vmExpunged = vmExpunged; + } } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupOfferingDao.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupOfferingDao.java index d001de8b6c67..d40e828121e0 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupOfferingDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupOfferingDao.java @@ -24,7 +24,7 @@ import com.cloud.utils.db.GenericDao; public interface BackupOfferingDao extends GenericDao { - BackupOfferingResponse newBackupOfferingResponse(BackupOffering policy); + BackupOfferingResponse newBackupOfferingResponse(BackupOffering policy, Boolean crossZoneInstanceCreation); BackupOffering findByExternalId(String externalId, Long zoneId); BackupOffering findByName(String name, Long zoneId); } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupOfferingDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupOfferingDaoImpl.java index 9d67d07fe5e3..a41e4e70d339 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupOfferingDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupOfferingDaoImpl.java @@ -50,7 +50,7 @@ protected void init() { } @Override - public BackupOfferingResponse newBackupOfferingResponse(BackupOffering offering) { + public BackupOfferingResponse newBackupOfferingResponse(BackupOffering offering, Boolean crossZoneInstanceCreation) { DataCenterVO zone = dataCenterDao.findById(offering.getZoneId()); BackupOfferingResponse response = new BackupOfferingResponse(); @@ -64,6 +64,9 @@ public BackupOfferingResponse newBackupOfferingResponse(BackupOffering offering) response.setZoneId(zone.getUuid()); response.setZoneName(zone.getName()); } + if (crossZoneInstanceCreation) { + response.setCrossZoneInstanceCreation(true); + } response.setCreated(offering.getCreated()); response.setObjectName("backupoffering"); return response; diff --git a/server/src/main/java/com/cloud/api/ApiDBUtils.java b/server/src/main/java/com/cloud/api/ApiDBUtils.java index 80043d0e2792..51ef4acdf671 100644 --- a/server/src/main/java/com/cloud/api/ApiDBUtils.java +++ b/server/src/main/java/com/cloud/api/ApiDBUtils.java @@ -75,9 +75,11 @@ import org.apache.cloudstack.api.response.VpcOfferingResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.backup.BackupOffering; +import org.apache.cloudstack.backup.BackupRepository; import org.apache.cloudstack.backup.BackupSchedule; import org.apache.cloudstack.backup.dao.BackupDao; import org.apache.cloudstack.backup.dao.BackupOfferingDao; +import org.apache.cloudstack.backup.dao.BackupRepositoryDao; import org.apache.cloudstack.backup.dao.BackupScheduleDao; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; @@ -493,6 +495,7 @@ public class ApiDBUtils { static BackupDao s_backupDao; static BackupScheduleDao s_backupScheduleDao; static BackupOfferingDao s_backupOfferingDao; + static BackupRepositoryDao s_backupRepositoryDao; static NicDao s_nicDao; static ResourceManagerUtil s_resourceManagerUtil; static SnapshotPolicyDetailsDao s_snapshotPolicyDetailsDao; @@ -751,6 +754,8 @@ public class ApiDBUtils { @Inject private BackupOfferingDao backupOfferingDao; @Inject + private BackupRepositoryDao backupRepositoryDao; + @Inject private BackupScheduleDao backupScheduleDao; @Inject private NicDao nicDao; @@ -899,6 +904,7 @@ void init() { s_backupDao = backupDao; s_backupScheduleDao = backupScheduleDao; s_backupOfferingDao = backupOfferingDao; + s_backupRepositoryDao = backupRepositoryDao; s_resourceIconDao = resourceIconDao; s_resourceManagerUtil = resourceManagerUtil; s_objectStoreDao = objectStoreDao; @@ -2297,8 +2303,10 @@ public static BackupScheduleResponse newBackupScheduleResponse(BackupSchedule sc return s_backupScheduleDao.newBackupScheduleResponse(schedule); } - public static BackupOfferingResponse newBackupOfferingResponse(BackupOffering policy) { - return s_backupOfferingDao.newBackupOfferingResponse(policy); + public static BackupOfferingResponse newBackupOfferingResponse(BackupOffering offering) { + BackupRepository repository = s_backupRepositoryDao.findByUuid(offering.getExternalId()); + Boolean crossZoneInstanceCreationEnabled = repository != null ? repository.crossZoneInstanceCreationEnabled() : false; + return s_backupOfferingDao.newBackupOfferingResponse(offering, crossZoneInstanceCreationEnabled); } public static NicVO findByIp4AddressAndNetworkId(String ip4Address, long networkId) { diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index c44239b4447b..dc72f5d7c469 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -5079,8 +5079,8 @@ public BackupScheduleResponse createBackupScheduleResponse(BackupSchedule schedu } @Override - public BackupOfferingResponse createBackupOfferingResponse(BackupOffering policy) { - return ApiDBUtils.newBackupOfferingResponse(policy); + public BackupOfferingResponse createBackupOfferingResponse(BackupOffering offering) { + return ApiDBUtils.newBackupOfferingResponse(offering); } public ManagementServerResponse createManagementResponse(ManagementServerHost mgmt) { diff --git a/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java b/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java index aff838a3cdfb..f94966e3383d 100644 --- a/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java @@ -379,7 +379,9 @@ public Map getBackupDetailsFromVM(VirtualMachine vm) { ServiceOffering serviceOffering = serviceOfferingDao.findById(vm.getServiceOfferingId()); details.put(ApiConstants.SERVICE_OFFERING_ID, serviceOffering.getUuid()); VirtualMachineTemplate template = vmTemplateDao.findById(vm.getTemplateId()); - details.put(ApiConstants.TEMPLATE_ID, template.getUuid()); + if (template != null) { + details.put(ApiConstants.TEMPLATE_ID, template.getUuid()); + } List vmDetails = vmInstanceDetailsDao.listDetails(vm.getId()); HashMap settings = new HashMap<>(); @@ -2140,10 +2142,15 @@ public BackupResponse createBackupResponse(Backup backup, Boolean listVmDetails) response.setId(backup.getUuid()); response.setName(backup.getName()); response.setDescription(backup.getDescription()); - response.setVmName(vm.getHostName()); - response.setVmId(vm.getUuid()); - if (vm.getBackupOfferingId() == null || vm.getBackupOfferingId() != backup.getBackupOfferingId()) { - response.setVmOfferingRemoved(true); + if (vm != null) { + response.setVmName(vm.getHostName()); + response.setVmId(vm.getUuid()); + if (vm.getBackupOfferingId() == null || vm.getBackupOfferingId() != backup.getBackupOfferingId()) { + response.setVmOfferingRemoved(true); + } + } + if (vm == null || VirtualMachine.State.Expunging.equals(vm.getState())) { + response.setVmExpunged(true); } response.setExternalId(backup.getExternalId()); response.setType(backup.getType()); @@ -2159,9 +2166,11 @@ public BackupResponse createBackupResponse(Backup backup, Boolean listVmDetails) } } // ACS 4.20: For backups taken prior this release the backup.backed_volumes column would be empty hence use vm_instance.backup_volumes - String backedUpVolumes; + String backedUpVolumes = ""; if (Objects.isNull(backup.getBackedUpVolumes())) { - backedUpVolumes = new Gson().toJson(vm.getBackupVolumeList().toArray(), Backup.VolumeInfo[].class); + if (vm != null) { + backedUpVolumes = new Gson().toJson(vm.getBackupVolumeList().toArray(), Backup.VolumeInfo[].class); + } } else { backedUpVolumes = new Gson().toJson(backup.getBackedUpVolumes().toArray(), Backup.VolumeInfo[].class); } diff --git a/ui/src/views/storage/CreateVMFromBackup.vue b/ui/src/views/storage/CreateVMFromBackup.vue index 0edee18ddd3a..e07e12a14951 100644 --- a/ui/src/views/storage/CreateVMFromBackup.vue +++ b/ui/src/views/storage/CreateVMFromBackup.vue @@ -29,7 +29,7 @@ - +