Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,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 IS_BACKUP_VM_EXPUNGED = "isbackupvmexpunged";
public static final String BACKUP_TOTAL = "backuptotal";
public static final String BASE64_IMAGE = "base64image";
public static final String BGP_PEERS = "bgppeers";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.IS_BACKUP_VM_EXPUNGED)
@Param(description = "Indicates whether the VM from which the backup was taken is expunged or not", since = "4.22.0")
private Boolean isVmExpunged;

public String getId() {
return id;
}
Expand Down Expand Up @@ -306,4 +310,8 @@ public Boolean getVmOfferingRemoved() {
public void setVmOfferingRemoved(Boolean vmOfferingRemoved) {
this.vmOfferingRemoved = vmOfferingRemoved;
}

public void setVmExpunged(Boolean isVmExpunged) {
this.isVmExpunged = isVmExpunged;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
import com.cloud.utils.db.GenericDao;

public interface BackupOfferingDao extends GenericDao<BackupOfferingVO, Long> {
BackupOfferingResponse newBackupOfferingResponse(BackupOffering policy);
BackupOfferingResponse newBackupOfferingResponse(BackupOffering policy, Boolean crossZoneInstanceCreation);
BackupOffering findByExternalId(String externalId, Long zoneId);
BackupOffering findByName(String name, Long zoneId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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;
Expand Down
12 changes: 10 additions & 2 deletions server/src/main/java/com/cloud/api/ApiDBUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -751,6 +754,8 @@ public class ApiDBUtils {
@Inject
private BackupOfferingDao backupOfferingDao;
@Inject
private BackupRepositoryDao backupRepositoryDao;
@Inject
private BackupScheduleDao backupScheduleDao;
@Inject
private NicDao nicDao;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 ? Boolean.TRUE.equals(repository.crossZoneInstanceCreationEnabled()) : false;
return s_backupOfferingDao.newBackupOfferingResponse(offering, crossZoneInstanceCreationEnabled);
}

public static NicVO findByIp4AddressAndNetworkId(String ip4Address, long networkId) {
Expand Down
4 changes: 2 additions & 2 deletions server/src/main/java/com/cloud/api/ApiResponseHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
8 changes: 4 additions & 4 deletions server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -9513,11 +9513,11 @@ public UserVm allocateVMFromBackup(CreateVMFromBackupCmd cmd) throws Insufficien
} else {
String serviceOfferingUuid = backup.getDetail(ApiConstants.SERVICE_OFFERING_ID);
if (serviceOfferingUuid == null) {
throw new CloudRuntimeException("Backup doesn't contain service offering uuid. Please specify a valid service offering id while creating the instance");
throw new CloudRuntimeException("Backup doesn't contain a Service Offering UUID. Please specify a valid Service Offering while creating the Instance");
}
serviceOffering = serviceOfferingDao.findByUuid(serviceOfferingUuid);
if (serviceOffering == null) {
throw new CloudRuntimeException("Unable to find service offering with the uuid stored in backup. Please specify a valid service offering id while creating instance");
throw new CloudRuntimeException("Unable to find Service Offering with the UUID stored in the Backup. Please specify a valid Service Offering while creating the Instance");
}
}
verifyServiceOffering(cmd, serviceOffering);
Expand All @@ -9532,11 +9532,11 @@ public UserVm allocateVMFromBackup(CreateVMFromBackupCmd cmd) throws Insufficien
} else {
String templateUuid = backup.getDetail(ApiConstants.TEMPLATE_ID);
if (templateUuid == null) {
throw new CloudRuntimeException("Backup doesn't contain Template uuid. Please specify a valid Template/ISO while creating the instance");
throw new CloudRuntimeException("Backup doesn't contain a Template UUID. Please specify a valid Template/ISO while creating the Instance");
}
template = _templateDao.findByUuid(templateUuid);
if (template == null) {
throw new CloudRuntimeException("Unable to find template associated with the backup. Please specify a valid Template/ISO while creating instance");
throw new CloudRuntimeException("Unable to find Template with the UUID stored in the Backup. Please specify a valid Template/ISO while creating the Instance");
}
}
verifyTemplate(cmd, template, serviceOffering.getId());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,12 +122,14 @@
import com.cloud.serializer.GsonHelper;
import com.cloud.service.dao.ServiceOfferingDao;
import com.cloud.storage.DiskOfferingVO;
import com.cloud.storage.GuestOSVO;
import com.cloud.storage.ScopeType;
import com.cloud.storage.Storage;
import com.cloud.storage.Volume;
import com.cloud.storage.VolumeApiService;
import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.DiskOfferingDao;
import com.cloud.storage.dao.GuestOSDao;
import com.cloud.storage.dao.VMTemplateDao;
import com.cloud.storage.dao.VolumeDao;
import com.cloud.template.VirtualMachineTemplate;
Expand Down Expand Up @@ -232,6 +234,8 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager {
private ResourceLimitService resourceLimitMgr;
@Inject
private AlertManager alertManager;
@Inject
private GuestOSDao _guestOSDao;

private AsyncJobDispatcher asyncJobDispatcher;
private Timer backupTimer;
Expand Down Expand Up @@ -379,7 +383,15 @@ public Map<String, String> 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) {
long guestOSId = template.getGuestOSId();
details.put(ApiConstants.TEMPLATE_ID, template.getUuid());
GuestOSVO guestOS = _guestOSDao.findById(guestOSId);
if (guestOS != null) {
details.put(ApiConstants.OS_TYPE_ID, guestOS.getUuid());
details.put(ApiConstants.OS_NAME, guestOS.getDisplayName());
}
}

List<VMInstanceDetailVO> vmDetails = vmInstanceDetailsDao.listDetails(vm.getId());
HashMap<String, String> settings = new HashMap<>();
Expand Down Expand Up @@ -2097,15 +2109,13 @@ Map<String, String> getDetailsFromBackupDetails(Long backupId) {
if (details.containsKey(ApiConstants.TEMPLATE_ID)) {
VirtualMachineTemplate template = vmTemplateDao.findByUuid(details.get(ApiConstants.TEMPLATE_ID));
if (template != null) {
details.put(ApiConstants.TEMPLATE_ID, template.getUuid());
details.put(ApiConstants.TEMPLATE_NAME, template.getName());
details.put(ApiConstants.IS_ISO, String.valueOf(template.getFormat().equals(Storage.ImageFormat.ISO)));
}
}
if (details.containsKey(ApiConstants.SERVICE_OFFERING_ID)) {
ServiceOffering serviceOffering = serviceOfferingDao.findByUuid(details.get(ApiConstants.SERVICE_OFFERING_ID));
if (serviceOffering != null) {
details.put(ApiConstants.SERVICE_OFFERING_ID, serviceOffering.getUuid());
details.put(ApiConstants.SERVICE_OFFERING_NAME, serviceOffering.getName());
}
}
Expand Down Expand Up @@ -2140,10 +2150,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());
Expand All @@ -2159,9 +2174,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);
}
Expand All @@ -2177,7 +2194,9 @@ public BackupResponse createBackupResponse(Backup backup, Boolean listVmDetails)

if (Boolean.TRUE.equals(listVmDetails)) {
Map<String, String> vmDetails = new HashMap<>();
vmDetails.put(ApiConstants.HYPERVISOR, vm.getHypervisorType().toString());
if (vm != null) {
vmDetails.put(ApiConstants.HYPERVISOR, vm.getHypervisorType().toString());
}
Map<String, String> details = getDetailsFromBackupDetails(backup.getId());
vmDetails.putAll(details);
response.setVmDetails(vmDetails);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import com.cloud.storage.VolumeApiService;
import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.DiskOfferingDao;
import com.cloud.storage.dao.GuestOSDao;
import com.cloud.storage.dao.VMTemplateDao;
import com.cloud.storage.dao.VolumeDao;
import com.cloud.user.Account;
Expand Down Expand Up @@ -234,6 +235,9 @@ public class BackupManagerTest {
@Mock
DomainDao domainDao;

@Mock
private GuestOSDao _guestOSDao;

private Gson gson;

private String[] hostPossibleValues = {"127.0.0.1", "hostname"};
Expand Down Expand Up @@ -1569,14 +1573,12 @@ public void testNewBackupResponse() {

VMTemplateVO template = mock(VMTemplateVO.class);
when(template.getFormat()).thenReturn(Storage.ImageFormat.QCOW2);
when(template.getUuid()).thenReturn(templateUuid);
when(template.getName()).thenReturn("template1");
when(vmTemplateDao.findByUuid(templateUuid)).thenReturn(template);
Map<String, String> details = new HashMap<>();
details.put(ApiConstants.TEMPLATE_ID, templateUuid);

ServiceOfferingVO serviceOffering = mock(ServiceOfferingVO.class);
when(serviceOffering.getUuid()).thenReturn(serviceOfferingUuid);
when(serviceOffering.getName()).thenReturn("service-offering1");
when(serviceOfferingDao.findByUuid(serviceOfferingUuid)).thenReturn(serviceOffering);
details.put(ApiConstants.SERVICE_OFFERING_ID, serviceOfferingUuid);
Expand Down
2 changes: 2 additions & 0 deletions ui/public/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -2876,6 +2876,8 @@
"message.action.create.snapshot.from.vmsnapshot": "Please confirm that you want to create Snapshot from Instance Snapshot",
"message.action.create.instance.from.backup": "Please confirm that you want to create a new Instance from the given Backup.<br>Click on configure to edit the parameters for the new Instance before creation.",
"message.create.instance.from.backup.different.zone": "Creating Instance from Backup on a different Zone. Please ensure that the backup repository is accessible in the selected Zone.",
"message.template.ostype.different.from.backup": "Selected Template has a different OS type than the Backup. Please proceed with caution.",
"message.iso.ostype.different.from.backup": "Selected ISO has a different OS type than the Backup. Please proceed with caution.",
"message.action.delete.asnrange": "Please confirm the AS range that you want to delete",
"message.action.delete.autoscale.vmgroup": "Please confirm that you want to delete this autoscaling group.",
"message.action.delete.backup.offering": "Please confirm that you want to delete this backup offering?",
Expand Down
22 changes: 13 additions & 9 deletions ui/src/components/view/BackupMetadata.vue
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@
{{ getTemplateDisplayName() }}
</router-link>
</div>
<div v-else-if="item === 'ostypeid'">
<router-link :to="{ path: '/guestos' + '/' + backupMetadata[item] }">
{{ backupMetadata.osname }}
</router-link>
</div>
<div v-else-if="item === 'serviceofferingid'">
<router-link :to="{ path: '/computeoffering/' + backupMetadata[item] }">
{{ getServiceOfferingDisplayName() }}
Expand Down Expand Up @@ -84,15 +89,14 @@ export default {
if (!this.backupMetadata || Object.keys(this.backupMetadata).length === 0) {
return []
}
const fieldOrder = []
fieldOrder.push('templateid')
if (this.backupMetadata.isiso === 'true') {
fieldOrder.push('hypervisor')
}
fieldOrder.push('serviceofferingid')
fieldOrder.push('nics')
fieldOrder.push('vmsettings')

const fieldOrder = [
'templateid',
'ostypeid',
'hypervisor',
'serviceofferingid',
'nics',
'vmsettings'
]
return fieldOrder.filter(field => this.backupMetadata[field] !== undefined)
},
getNicEntities () {
Expand Down
Loading
Loading