diff --git a/api/src/main/java/com/cloud/event/EventTypes.java b/api/src/main/java/com/cloud/event/EventTypes.java index 97d889bebcfa..b9c8f0b55a6d 100644 --- a/api/src/main/java/com/cloud/event/EventTypes.java +++ b/api/src/main/java/com/cloud/event/EventTypes.java @@ -799,7 +799,7 @@ public class EventTypes { // DISASTER RECOVERY public static final String EVENT_DISASTER_RECOVERY_CLUSTER = "DISASTER.RECOVERY.CLUSTER"; - + // Resource Limit public static final String EVENT_RESOURCE_LIMIT_UPDATE = "RESOURCE.LIMIT.UPDATE"; diff --git a/api/src/main/java/com/cloud/storage/VolumeApiService.java b/api/src/main/java/com/cloud/storage/VolumeApiService.java index 1239d37af26d..0698c09e0887 100644 --- a/api/src/main/java/com/cloud/storage/VolumeApiService.java +++ b/api/src/main/java/com/cloud/storage/VolumeApiService.java @@ -197,6 +197,6 @@ Snapshot takeSnapshot(Long volumeId, Long policyId, Long snapshotId, Account acc Volume cloneVolumeFromSnapshot(Volume volume, long snapshotId, Long vmId) throws StorageUnavailableException; Volume updateCompressDedupVolume(UpdateCompressDedupCmd cmd); - + Long getVolumePhysicalSize(Storage.ImageFormat format, String path, String chainInfo); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/DeleteBackupScheduleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/DeleteBackupScheduleCmd.java index 0245f228b895..fa721ef5c98e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/DeleteBackupScheduleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/DeleteBackupScheduleCmd.java @@ -26,6 +26,7 @@ import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.BackupScheduleResponse; import org.apache.cloudstack.api.response.SuccessResponse; import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.backup.BackupManager; @@ -51,13 +52,15 @@ public class DeleteBackupScheduleCmd extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, - type = CommandType.UUID, - entityType = UserVmResponse.class, - required = true, - description = "ID of the VM") + @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class, + description = "ID of the VM from which all backup schedules will be deleted.") private Long vmId; + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = BackupScheduleResponse.class, + since = "4.20.1", description = "ID of the backup schedule to be deleted. It has precedence over the 'virtualmachineid' parameter, " + + "i.e., when the 'id' parameter is specified, the 'virtualmachineid' parameter will be ignored.") + private Long id; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -66,6 +69,9 @@ public Long getVmId() { return vmId; } + public Long getId() { return id; } + + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -73,7 +79,7 @@ public Long getVmId() { @Override public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { try { - boolean result = backupManager.deleteBackupSchedule(getVmId()); + boolean result = backupManager.deleteBackupSchedule(this); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); response.setResponseName(getCommandName()); diff --git a/api/src/main/java/org/apache/cloudstack/api/response/BackupScheduleResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/BackupScheduleResponse.java index d7c6f96add5b..92f1bad54470 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/BackupScheduleResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/BackupScheduleResponse.java @@ -28,6 +28,9 @@ @EntityReference(value = BackupSchedule.class) public class BackupScheduleResponse extends BaseResponse { + @SerializedName(ApiConstants.ID) + @Param(description = "ID of the backup schedule.") + private String id; @SerializedName(ApiConstants.VIRTUAL_MACHINE_NAME) @Param(description = "name of the VM") @@ -51,7 +54,11 @@ public class BackupScheduleResponse extends BaseResponse { @SerializedName(ApiConstants.MAX_BACKUPS) @Param(description = "maximum number of backups retained") - private Integer maxBakups; + private Integer maxBackups; + + public void setId(String id) { + this.id = id; + } public String getVmName() { return vmName; @@ -93,7 +100,7 @@ public void setTimezone(String timezone) { this.timezone = timezone; } - public void setMaxBakups(Integer maxBakups) { - this.maxBakups = maxBakups; + public void setMaxBackups(Integer maxBackups) { + this.maxBackups = maxBackups; } } diff --git a/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java b/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java index cc99833c7ec1..4105406324e0 100644 --- a/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java +++ b/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java @@ -23,6 +23,7 @@ import org.apache.cloudstack.api.command.admin.backup.ImportBackupOfferingCmd; import org.apache.cloudstack.api.command.admin.backup.UpdateBackupOfferingCmd; import org.apache.cloudstack.api.command.user.backup.CreateBackupScheduleCmd; +import org.apache.cloudstack.api.command.user.backup.DeleteBackupScheduleCmd; import org.apache.cloudstack.api.command.user.backup.ListBackupOfferingsCmd; import org.apache.cloudstack.api.command.user.backup.ListBackupsCmd; import org.apache.cloudstack.framework.config.ConfigKey; @@ -159,12 +160,12 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer */ List listBackupSchedule(Long vmId); - /** + /** * Deletes VM backup schedule for a VM - * @param vmId + * @param cmd * @return */ - boolean deleteBackupSchedule(Long vmId); + boolean deleteBackupSchedule(DeleteBackupScheduleCmd cmd); /** * Creates backup of a VM diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupScheduleVO.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupScheduleVO.java index 0338d7f1c4e1..85573f5f207f 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupScheduleVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupScheduleVO.java @@ -18,6 +18,7 @@ package org.apache.cloudstack.backup; import java.util.Date; +import java.util.UUID; import javax.persistence.Column; import javax.persistence.Entity; @@ -42,6 +43,9 @@ public class BackupScheduleVO implements BackupSchedule { @Column(name = "vm_id") private Long vmId; + @Column(name = "uuid", nullable = false) + private String uuid = UUID.randomUUID().toString(); + @Column(name = "schedule_type") private Short scheduleType; @@ -84,6 +88,11 @@ public long getId() { return id; } + @Override + public String getUuid() { + return uuid; + } + public Long getVmId() { return vmId; } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupVO.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupVO.java index 81dd0fea68d3..ebe98b3dda53 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupVO.java @@ -22,12 +22,10 @@ import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; import org.apache.commons.lang3.StringUtils; -import java.beans.Transient; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.List; -import java.util.Map; import java.util.UUID; import javax.persistence.Column; diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupScheduleDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupScheduleDaoImpl.java index aac2e3bf2320..9374798dde32 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupScheduleDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupScheduleDaoImpl.java @@ -92,12 +92,13 @@ public List getSchedulesToExecute(Date currentTimestamp) { public BackupScheduleResponse newBackupScheduleResponse(BackupSchedule schedule) { VMInstanceVO vm = vmInstanceDao.findByIdIncludingRemoved(schedule.getVmId()); BackupScheduleResponse response = new BackupScheduleResponse(); + response.setId(schedule.getUuid()); response.setVmId(vm.getUuid()); response.setVmName(vm.getHostName()); response.setIntervalType(schedule.getScheduleType()); response.setSchedule(schedule.getSchedule()); response.setTimezone(schedule.getTimezone()); - response.setMaxBakups(schedule.getMaxBackups()); + response.setMaxBackups(schedule.getMaxBackups()); response.setObjectName("backupschedule"); return response; } diff --git a/engine/schema/src/main/resources/META-INF/db/schema-42010to42100.sql b/engine/schema/src/main/resources/META-INF/db/schema-42010to42100.sql index 31ffbae10cab..dfff847e47a9 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-42010to42100.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-42010to42100.sql @@ -27,3 +27,6 @@ CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.console_session', 'console_endpoint_ -- Add client_address column to cloud.console_session table CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.console_session', 'client_address', 'VARCHAR(45)'); + +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backup_schedule', 'uuid', 'VARCHAR(40) NOT NULL'); +UPDATE `cloud`.`backup_schedule` SET uuid = UUID(); diff --git a/framework/db/src/main/java/com/cloud/utils/db/GenericDaoBase.java b/framework/db/src/main/java/com/cloud/utils/db/GenericDaoBase.java index c7f2daadc518..28e182a624c5 100644 --- a/framework/db/src/main/java/com/cloud/utils/db/GenericDaoBase.java +++ b/framework/db/src/main/java/com/cloud/utils/db/GenericDaoBase.java @@ -2445,4 +2445,12 @@ private Optional> getConverter(Field field) { } } + public static class SumCount { + public long sum; + public long count; + + public SumCount() { + } + } + } diff --git a/plugins/backup/commvault/src/main/java/org/apache/cloudstack/backup/CommvaultBackupProvider.java b/plugins/backup/commvault/src/main/java/org/apache/cloudstack/backup/CommvaultBackupProvider.java index c5878f575de5..802d52d5861f 100644 --- a/plugins/backup/commvault/src/main/java/org/apache/cloudstack/backup/CommvaultBackupProvider.java +++ b/plugins/backup/commvault/src/main/java/org/apache/cloudstack/backup/CommvaultBackupProvider.java @@ -763,7 +763,7 @@ public Pair restoreBackedUpVolume(Backup backup, String volumeU } @Override - public boolean takeBackup(VirtualMachine vm) { + public Pair takeBackup(VirtualMachine vm) { String hostName = null; try { String commvaultServer = getUrlDomain(CommvaultUrl.value()); @@ -821,7 +821,7 @@ public boolean takeBackup(VirtualMachine vm) { } } LOG.error("Failed to request createSnapshot Mold-API."); - return false; + return new Pair<>(false, null); } else { JSONObject jsonObject = new JSONObject(createSnapResult); String jobId = jsonObject.get("jobid").toString(); @@ -843,7 +843,7 @@ public boolean takeBackup(VirtualMachine vm) { } } LOG.error("createSnapshot Mold-API async job resulted in failure."); - return false; + return new Pair<>(false, null); } checkResult.put(vol.getId(), snapId); SnapshotDataStoreVO snapshotStore = snapshotStoreDao.findLatestSnapshotForVolume(vol.getId(), DataStoreRole.Primary); @@ -1001,7 +1001,7 @@ public boolean takeBackup(VirtualMachine vm) { command = String.format(RM_COMMAND, storagePath + "/" + vm.getInstanceName()); executeDeleteXmlCommand(hostVO, credentials.first(), credentials.second(), sshPort, command); } - return true; + return new Pair<>(true, backup); } else { // 백업 실패 if (!checkResult.isEmpty()) { @@ -1018,7 +1018,7 @@ public boolean takeBackup(VirtualMachine vm) { executeDeleteXmlCommand(hostVO, credentials.first(), credentials.second(), sshPort, command); } LOG.error("createBackup commvault api resulted in " + jobStatus); - return false; + return new Pair<>(false, null); } } else { // 백업 실패 @@ -1036,7 +1036,7 @@ public boolean takeBackup(VirtualMachine vm) { executeDeleteXmlCommand(hostVO, credentials.first(), credentials.second(), sshPort, command); } LOG.error("createBackup commvault api resulted in " + jobStatus); - return false; + return new Pair<>(false, null); } } else { // 백업 실패 @@ -1054,7 +1054,7 @@ public boolean takeBackup(VirtualMachine vm) { executeDeleteXmlCommand(hostVO, credentials.first(), credentials.second(), sshPort, command); } LOG.error("failed request createBackup commvault api"); - return false; + return new Pair<>(false, null); } } else { // 백업 경로 업데이트 실패 @@ -1072,7 +1072,7 @@ public boolean takeBackup(VirtualMachine vm) { executeDeleteXmlCommand(hostVO, credentials.first(), credentials.second(), sshPort, command); } LOG.error("updateBackupSet commvault api resulted in failure."); - return false; + return new Pair<>(false, null); } } @@ -1559,4 +1559,16 @@ public static boolean versionCheck(String csVersionInfo) { } return true; } + + @Override + public List listRestorePoints(VirtualMachine vm) { + return null; + } + + + @Override + public Backup createNewBackupEntryForRestorePoint(Backup.RestorePoint restorePoint, VirtualMachine vm, Backup.Metric metric) { + return null; + } + } \ No newline at end of file diff --git a/plugins/backup/nas/src/main/java/org/apache/cloudstack/backup/NASBackupProvider.java b/plugins/backup/nas/src/main/java/org/apache/cloudstack/backup/NASBackupProvider.java index 1dd397bd7b3f..6113e2581008 100644 --- a/plugins/backup/nas/src/main/java/org/apache/cloudstack/backup/NASBackupProvider.java +++ b/plugins/backup/nas/src/main/java/org/apache/cloudstack/backup/NASBackupProvider.java @@ -27,10 +27,8 @@ import com.cloud.hypervisor.Hypervisor; import com.cloud.storage.ScopeType; import com.cloud.storage.Storage; -import com.cloud.storage.StoragePoolHostVO; import com.cloud.storage.Volume; import com.cloud.storage.VolumeVO; -import com.cloud.storage.dao.StoragePoolHostDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.utils.Pair; import com.cloud.utils.component.AdapterBase; @@ -84,9 +82,6 @@ public class NASBackupProvider extends AdapterBase implements BackupProvider, Co @Inject private VolumeDao volumeDao; - @Inject - private StoragePoolHostDao storagePoolHostDao; - @Inject private VMInstanceDao vmInstanceDao; @@ -268,12 +263,28 @@ private List getVolumePaths(List volumes) { return volumePaths; } + private String getVolumePathPrefix(StoragePoolVO storagePool) { + String volumePathPrefix; + if (ScopeType.HOST.equals(storagePool.getScope()) || + Storage.StoragePoolType.SharedMountPoint.equals(storagePool.getPoolType()) || + Storage.StoragePoolType.RBD.equals(storagePool.getPoolType())) { + volumePathPrefix = storagePool.getPath(); + } else { + // Should be Storage.StoragePoolType.NetworkFilesystem + volumePathPrefix = String.format("/mnt/%s", storagePool.getUuid()); + } + return volumePathPrefix; + } + @Override public Pair restoreBackedUpVolume(Backup backup, String volumeUuid, String hostIp, String dataStoreUuid, Pair vmNameAndState) { final VolumeVO volume = volumeDao.findByUuid(volumeUuid); final VirtualMachine backupSourceVm = vmInstanceDao.findById(backup.getVmId()); - final StoragePoolHostVO dataStore = storagePoolHostDao.findByUuid(dataStoreUuid); - final HostVO hostVO = hostDao.findByIp(hostIp); + final StoragePoolVO pool = primaryDataStoreDao.findByUuid(dataStoreUuid); + HostVO hostVO = hostDao.findByIp(hostIp); + if(hostVO == null) { + hostVO = hostDao.findByName(hostIp); + } Optional matchingVolume = getBackedUpVolumeInfo(backupSourceVm.getBackupVolumeList(), volumeUuid); Long backedUpVolumeSize = matchingVolume.isPresent() ? matchingVolume.get().getSize() : 0L; @@ -291,7 +302,7 @@ public Pair restoreBackedUpVolume(Backup backup, String volumeU restoredVolume.setUuid(volumeUUID); restoredVolume.setRemoved(null); restoredVolume.setDisplayVolume(true); - restoredVolume.setPoolId(dataStore.getPoolId()); + restoredVolume.setPoolId(pool.getId()); restoredVolume.setPath(restoredVolume.getUuid()); restoredVolume.setState(Volume.State.Copying); restoredVolume.setFormat(Storage.ImageFormat.QCOW2); @@ -303,7 +314,7 @@ public Pair restoreBackedUpVolume(Backup backup, String volumeU restoreCommand.setBackupRepoType(backupRepository.getType()); restoreCommand.setBackupRepoAddress(backupRepository.getAddress()); restoreCommand.setVmName(vmNameAndState.first()); - restoreCommand.setVolumePaths(Collections.singletonList(String.format("%s/%s", dataStore.getLocalPath(), volumeUUID))); + restoreCommand.setVolumePaths(Collections.singletonList(String.format("%s/%s", getVolumePathPrefix(pool), volumeUUID))); restoreCommand.setDiskType(volume.getVolumeType().name().toLowerCase(Locale.ROOT)); restoreCommand.setVmExists(null); restoreCommand.setVmState(vmNameAndState.second()); @@ -461,6 +472,10 @@ public String getConfigComponentName() { return BackupService.class.getSimpleName(); } + @Override + public void syncBackups(VirtualMachine vm, Backup.Metric metric) { + } + @Override public boolean checkBackupAgent(final Long zoneId) { return true; } diff --git a/plugins/backup/networker/src/main/java/org/apache/cloudstack/backup/NetworkerBackupProvider.java b/plugins/backup/networker/src/main/java/org/apache/cloudstack/backup/NetworkerBackupProvider.java index b89d82be540d..ae4beaa2c82e 100644 --- a/plugins/backup/networker/src/main/java/org/apache/cloudstack/backup/NetworkerBackupProvider.java +++ b/plugins/backup/networker/src/main/java/org/apache/cloudstack/backup/NetworkerBackupProvider.java @@ -34,6 +34,7 @@ import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.dao.VMInstanceDao; + import org.apache.cloudstack.backup.dao.BackupDao; import org.apache.cloudstack.backup.dao.BackupOfferingDaoImpl; import org.apache.cloudstack.backup.networker.NetworkerClient; @@ -624,4 +625,8 @@ public List listRestorePoints(VirtualMachine vm) { @Override public boolean updateBackupPlan(final Long zoneId, final String retentionPeriod, final String externalId) { return true; } + + @Override + public void syncBackups(VirtualMachine vm, Backup.Metric metric) { + } } diff --git a/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/VeeamBackupProvider.java b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/VeeamBackupProvider.java index ed9aaa4c638f..e6aa5872aa63 100644 --- a/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/VeeamBackupProvider.java +++ b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/VeeamBackupProvider.java @@ -370,6 +370,10 @@ public String getDescription() { return "Veeam Backup Plugin"; } + @Override + public void syncBackups(VirtualMachine vm, Backup.Metric metric) { + } + @Override public boolean checkBackupAgent(final Long zoneId) { return true; } diff --git a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java index 6d975e1809d4..4dbdb3cea3e2 100644 --- a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java @@ -136,6 +136,7 @@ import com.cloud.dc.DataCenterVO; import com.cloud.dc.Pod; import com.cloud.dc.dao.DataCenterDao; +import com.cloud.dc.dao.HostPodDao; import com.cloud.domain.Domain; import com.cloud.domain.dao.DomainDao; import com.cloud.event.ActionEvent; 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 5c1c7f9d955f..abf3b0e6c76f 100644 --- a/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java @@ -24,6 +24,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.TimeZone; import java.util.Timer; import java.util.TimerTask; @@ -33,7 +34,6 @@ import com.cloud.alert.AlertManager; import com.cloud.configuration.Resource; import com.cloud.exception.ResourceAllocationException; -import com.cloud.storage.Snapshot; import com.cloud.storage.VolumeApiService; import com.cloud.user.DomainManager; import com.cloud.user.ResourceLimitService; @@ -74,7 +74,6 @@ import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService; import org.apache.cloudstack.framework.config.ConfigKey; -import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.framework.jobs.AsyncJobDispatcher; import org.apache.cloudstack.framework.jobs.AsyncJobManager; import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO; @@ -87,6 +86,7 @@ import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; import org.apache.commons.lang.math.NumberUtils; import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import com.cloud.api.ApiDispatcher; @@ -589,16 +589,53 @@ public List listBackupSchedule(final Long vmId) { @Override @ActionEvent(eventType = EventTypes.EVENT_VM_BACKUP_SCHEDULE_DELETE, eventDescription = "deleting VM backup schedule") - public boolean deleteBackupSchedule(Long vmId) { - final VMInstanceVO vm = findVmById(vmId); + public boolean deleteBackupSchedule(DeleteBackupScheduleCmd cmd) { + Long vmId = cmd.getVmId(); + Long id = cmd.getId(); + if (ObjectUtils.allNull(vmId, id)) { + throw new InvalidParameterValueException("Either instance ID or ID of backup schedule needs to be specified."); + } + + if (Objects.nonNull(id)) { + BackupSchedule schedule = backupScheduleDao.findById(id); + if (schedule == null) { + throw new InvalidParameterValueException("Could not find the requested backup schedule."); + } + checkCallerAccessToBackupScheduleVm(schedule.getVmId()); + return backupScheduleDao.remove(schedule.getId()); + } + + checkCallerAccessToBackupScheduleVm(vmId); + return deleteAllVmBackupSchedules(vmId); + } + + /** + * Checks if the backup framework is enabled for the zone in which the VM with specified ID is allocated and + * if the caller has access to the VM. + * + * @param vmId The ID of the virtual machine to check access for + * @throws PermissionDeniedException if the caller doesn't have access to the VM + * @throws CloudRuntimeException if the backup framework is disabled + */ + protected void checkCallerAccessToBackupScheduleVm(long vmId) { + VMInstanceVO vm = findVmById(vmId); validateForZone(vm.getDataCenterId()); accountManager.checkAccess(CallContext.current().getCallingAccount(), null, true, vm); + } - final BackupSchedule schedule = backupScheduleDao.findByVM(vmId); - if (schedule == null) { - throw new CloudRuntimeException("VM has no backup schedule defined, no need to delete anything."); + /** + * Deletes all backup schedules associated with a specific VM. + * + * @param vmId The ID of the virtual machine whose backup schedules should be deleted + * @return true if all backup schedules were successfully deleted, false if any deletion failed + */ + protected boolean deleteAllVmBackupSchedules(long vmId) { + List vmBackupSchedules = backupScheduleDao.listByVM(vmId); + boolean success = true; + for (BackupScheduleVO vmBackupSchedule : vmBackupSchedules) { + success = success && backupScheduleDao.remove(vmBackupSchedule.getId()); } - return backupScheduleDao.remove(schedule.getId()); + return success; } @Override @@ -1613,6 +1650,7 @@ private void syncBackups(BackupProvider backupProvider, VirtualMachine vm, Backu Transaction.execute(new TransactionCallbackNoReturn() { @Override public void doInTransactionWithoutResult(TransactionStatus status) { + backupProvider.syncBackups(vm, metric); final List backupsInDb = backupDao.listByVmId(null, vm.getId()); List restorePoints = backupProvider.listRestorePoints(vm); if (restorePoints == null) { diff --git a/server/src/test/java/org/apache/cloudstack/backup/BackupManagerTest.java b/server/src/test/java/org/apache/cloudstack/backup/BackupManagerTest.java index 66fa2d33cb1e..88892d982d87 100644 --- a/server/src/test/java/org/apache/cloudstack/backup/BackupManagerTest.java +++ b/server/src/test/java/org/apache/cloudstack/backup/BackupManagerTest.java @@ -49,6 +49,7 @@ import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.command.admin.backup.UpdateBackupOfferingCmd; import org.apache.cloudstack.api.command.user.backup.CreateBackupScheduleCmd; +import org.apache.cloudstack.api.command.user.backup.DeleteBackupScheduleCmd; import org.apache.cloudstack.backup.dao.BackupDao; import org.apache.cloudstack.backup.dao.BackupOfferingDao; import org.apache.cloudstack.backup.dao.BackupScheduleDao; diff --git a/ui/public/locales/ko_KR.json b/ui/public/locales/ko_KR.json index 8c8acbac78bd..3f6977000bfb 100644 --- a/ui/public/locales/ko_KR.json +++ b/ui/public/locales/ko_KR.json @@ -856,8 +856,8 @@ "label.disksizeused": "\uc0ac\uc6a9\ub41c \ub514\uc2a4\ud06c \ud06c\uae30", "label.disksizeusedgb": "\uc0ac\uc6a9\ub41c", "label.display.text": "\ud14d\uc2a4\ud2b8 \ud45c\uc2dc", -"label.displayname": "\uc774\ub984 \ud45c\uc2dc", -"label.displaynetwork": "\ub124\ud2b8\uc6cc\ud06c \uc774\ub984 \ud45c\uc2dc", +"label.displayname": "\ud45c\uc2dc \uc774\ub984", +"label.displaynetwork": "\ub124\ud2b8\uc6cc\ud06c \ud45c\uc2dc \uc774\ub984", "label.displaytext": "\uc124\uba85", "label.distributedvpcrouter": "\ubd84\uc0b0 VPC \ub77c\uc6b0\ud130", "label.dns": "DNS", @@ -3517,7 +3517,7 @@ "message.resource.not.found": "\ub9ac\uc18c\uc2a4\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4.", "message.restart.mgmt.server": "\uc0c8\ub85c\uc6b4 \uc124\uc815\uc744 \uc0ac\uc6a9 \ud558\uae30 \uc704\ud574 \uad00\ub9ac \uc11c\ubc84\ub97c \uc7ac\uc2dc\uc791\ud574 \uc8fc\uc2ed\uc2dc\uc624.", "message.restart.network": "\ud604\uc7ac \ub124\ud2b8\uc6cc\ud06c\ub85c \uc81c\uacf5\ud558\ub294 \ubaa8\ub4e0 \uc11c\ube44\uc2a4\uac00 \uc911\ub2e8\ub429\ub2c8\ub2e4. \uc774 \ub124\ud2b8\uc6cc\ud06c\ub97c \uc7ac\uc2dc\uc791\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?", -"message.restart.vm.to.update.settings": "\uc774\ub984 \ubc0f \uc774\ub984 \ud45c\uc2dc \uc774\uc678\uc758 \ud56d\ubaa9\uc744 \uc5c5\ub370\uc774\ud2b8\ud558\ub824\uba74 VM\uc744 \ub2e4\uc2dc \uc2dc\uc791\ud574\uc57c \ud569\ub2c8\ub2e4.", +"message.restart.vm.to.update.settings": "\uc774\ub984 \ubc0f \ud45c\uc2dc \uc774\ub984 \uc774\uc678\uc758 \ud56d\ubaa9\uc744 \uc5c5\ub370\uc774\ud2b8\ud558\ub824\uba74 VM\uc744 \ub2e4\uc2dc \uc2dc\uc791\ud574\uc57c \ud569\ub2c8\ub2e4.", "message.restart.vpc": "VPC\ub97c \uc7ac\uc2dc\uc791\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?", "message.restart.vpc.remark": "VPC\ub97c \ub2e4\uc2dc \uc2dc\uc791\ud560 \uac83\uc778\uc9c0 \ud655\uc778\ud558\uc2ed\uc2dc\uc624.

\ucc38\uace0 : \ube44 \uc911\ubcf5 VPC\ub97c \uc774\uc911\ud654\ud558\uba74 \uac15\uc81c\ub85c \uc815\ub9ac\ub429\ub2c8\ub2e4. \uba87 \ubd84 \ub3d9\uc548 \ub124\ud2b8\uc6cc\ud06c\ub97c \uc0ac\uc6a9\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4 . ", "message.scale.processing": "\uc9c4\ud589\uc911", diff --git a/ui/src/config/section/compute.js b/ui/src/config/section/compute.js index 4eecbe3de076..597c86d52704 100644 --- a/ui/src/config/section/compute.js +++ b/ui/src/config/section/compute.js @@ -47,7 +47,7 @@ export default { return filters }, columns: () => { - const fields = ['name', 'state', 'qemuagentversion', 'ipaddress'] + const fields = ['name', 'displayname', 'state', 'qemuagentversion', 'ipaddress'] const metricsFields = ['cpunumber', 'cputotal', 'cpuused', 'memorytotal', { memoryused: (record) => { diff --git a/ui/src/style/dark-mode.less b/ui/src/style/dark-mode.less index 15462a3ef08a..ce5dc4b48683 100644 --- a/ui/src/style/dark-mode.less +++ b/ui/src/style/dark-mode.less @@ -1135,10 +1135,10 @@ .quickview-context-menu, .autogen-action-dropdown__content { - background: @dropdown-bgColor; - border-color: @dark-border-color; - box-shadow: 0 8px 24px @box-shadow-1; - color: @dark-text-color-4; + background: @dropdown-bgColor !important; + border-color: @dark-border-color !important; + box-shadow: 0 8px 24px @box-shadow-1 !important; + color: @dark-text-color-4 !important; } .quickview-context-menu .action-button-title, @@ -1163,10 +1163,17 @@ background: rgba(0, 0, 0, 0.50) } - .field-key, - .banner-label, - .banner-main { + .auto-alert-banner-container .banner-field { + background: rgba(0, 0, 0, 0.85); + } + + .banner-main, + .field-key { + color: #cfc9c9; + } + + .banner-label { color: #cfc9c9; - background-color: rgb(150 96 96 / 50%); + background-color: rgba(150, 96, 96, 0.5); } } diff --git a/ui/src/views/compute/backup/BackupSchedule.vue b/ui/src/views/compute/backup/BackupSchedule.vue index 7309068ea069..cdf62b84c0da 100644 --- a/ui/src/views/compute/backup/BackupSchedule.vue +++ b/ui/src/views/compute/backup/BackupSchedule.vue @@ -172,7 +172,7 @@ export default { methods: { handleClickDelete (record) { const params = {} - params.virtualmachineid = record.virtualmachineid + params.id = record.id this.actionLoading = true api('deleteBackupSchedule', params).then(json => { if (json.deletebackupscheduleresponse.success) {