From 2b3b323769444f0c5e503f3ce138c79a4986d606 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20B=C3=B6ck?= Date: Mon, 28 Sep 2026 15:21:47 -0300 Subject: [PATCH 1/6] Secondary storage backup path GUI addition --- .../org/apache/cloudstack/api/ApiConstants.java | 1 + .../command/admin/backup/FinalizeBackupCmd.java | 4 +++- .../api/command/admin/backup/StartBackupCmd.java | 3 ++- .../api/command/user/backup/ListBackupsCmd.java | 3 ++- .../cloudstack/api/response/BackupResponse.java | 13 +++++++++++++ .../apache/cloudstack/backup/BackupManager.java | 2 +- .../cloudstack/backup/InternalBackupJoinVO.java | 7 +++++++ .../db/views/cloud.internal_backup_view.sql | 2 ++ .../cloudstack/backup/BackupManagerImpl.java | 16 +++++++++++++++- .../cloudstack/backup/BackupManagerTest.java | 14 +++++++++++++- ui/public/locales/en.json | 1 + ui/public/locales/pt_BR.json | 1 + ui/src/components/view/DetailsTab.vue | 5 +++++ ui/src/config/section/storage.js | 2 +- 14 files changed, 67 insertions(+), 7 deletions(-) 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 f74c46161180..11ca219041fc 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -61,6 +61,7 @@ public class ApiConstants { public static final String BACKUP_AVAILABLE = "backupavailable"; public static final String BACKUP_ID = "backupid"; public static final String BACKUP_LIMIT = "backuplimit"; + public static final String BACKUP_PATHS = "backuppaths"; public static final String BACKUP_OFFERING_NAME = "backupofferingname"; public static final String BACKUP_OFFERING_ID = "backupofferingid"; public static final String BACKUP_OFFERING_DETAILS = "backupofferingdetails"; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/FinalizeBackupCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/FinalizeBackupCmd.java index 45173f8668ee..d0ddd0ec75f6 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/FinalizeBackupCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/FinalizeBackupCmd.java @@ -79,7 +79,9 @@ public void execute() { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create Backup"); } - BackupResponse response = backupManager.createBackupResponse(backup, null); + boolean isCallerRootAdmin = _accountService.isRootAdmin(CallContext.current().getCallingAccountId()); + + BackupResponse response = backupManager.createBackupResponse(backup, null, isCallerRootAdmin); response.setResponseName(getCommandName()); setResponseObject(response); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/StartBackupCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/StartBackupCmd.java index 1bf6d45db049..3fd41180b900 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/StartBackupCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/StartBackupCmd.java @@ -82,7 +82,8 @@ public String getDescription() { public void execute() { try { Backup backup = kvmBackupExportService.startBackup(this); - BackupResponse response = backupManager.createBackupResponse(backup, null); + boolean isCallerRootAdmin = _accountService.isRootAdmin(CallContext.current().getCallingAccountId()); + BackupResponse response = backupManager.createBackupResponse(backup, null, isCallerRootAdmin); response.setResponseName(getCommandName()); setResponseObject(response); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupsCmd.java index 703fd9cd4358..a94151b9344a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupsCmd.java @@ -148,12 +148,13 @@ public Boolean getListVmDetails() { ///////////////////////////////////////////////////// protected void setupResponseBackupList(final List backups, final Integer count) { + boolean isCallerRootAdmin = _accountService.isRootAdmin(CallContext.current().getCallingAccountId()); final List responses = new ArrayList<>(); for (Backup backup : backups) { if (backup == null) { continue; } - BackupResponse backupResponse = backupManager.createBackupResponse(backup, this.getListVmDetails()); + BackupResponse backupResponse = backupManager.createBackupResponse(backup, this.getListVmDetails(), isCallerRootAdmin); responses.add(backupResponse); } final ListResponse response = new ListResponse<>(); 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 70db01445edd..0d6fd08c82e8 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 @@ -99,6 +99,10 @@ public class BackupResponse extends BaseResponse { @Param(description = "Backup offering name") private String backupOfferingName; + @SerializedName(ApiConstants.BACKUP_PATHS) + @Param(description = "The backups paths in the secondary storage") + private Map backupPaths; + @SerializedName(ApiConstants.ACCOUNT_ID) @Param(description = "Account id") private String accountId; @@ -386,4 +390,13 @@ public void setHostId(String hostId) { public String getHostId() { return this.hostId; } + + public Map getBackupPaths() { + return backupPaths; + } + + public void setBackupPaths(Map backupPaths) { + this.backupPaths = backupPaths; + } + } 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 30fc2bbec40d..b5dfe0d8e818 100644 --- a/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java +++ b/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java @@ -259,7 +259,7 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer String getBackupNameFromVM(VirtualMachine vm); - BackupResponse createBackupResponse(Backup backup, Boolean listVmDetails); + BackupResponse createBackupResponse(Backup backup, Boolean listVmDetails, boolean isCallerRootAdmin); Capacity getBackupStorageUsedStats(Long zoneId); diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/InternalBackupJoinVO.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/InternalBackupJoinVO.java index acc54ccd41fa..d39d436db990 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/backup/InternalBackupJoinVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/InternalBackupJoinVO.java @@ -98,6 +98,9 @@ public class InternalBackupJoinVO { @Column(name = "volume_id") private long volumeId; + @Column(name = "volume_name") + private String volumeName; + @Column(name = "isolated") private Boolean isolated; @@ -175,6 +178,10 @@ public long getVolumeId() { return volumeId; } + public String getVolumeName() { + return volumeName; + } + public Boolean getIsolated() { return BooleanUtils.isTrue(isolated); } diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.internal_backup_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.internal_backup_view.sql index a1fbd102630b..02a1622f501a 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.internal_backup_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.internal_backup_view.sql @@ -37,11 +37,13 @@ SELECT b.id, MAX(CASE WHEN bd.name = 'current' THEN bd.value END) current, COALESCE(MAX(CASE WHEN bd.name = 'isolated' THEN bd.value END), 'false') isolated, nbpr.volume_id, + v.name as volume_name, nbsr.path image_store_path FROM backups b LEFT JOIN backup_details bd ON b.id = bd.backup_id LEFT JOIN backup_offering bo ON b.backup_offering_id = bo.id LEFT JOIN internal_backup_store_ref nbsr ON b.id = nbsr.backup_id LEFT JOIN internal_backup_pool_ref nbpr ON nbpr.volume_id = nbsr.volume_id +LEFT JOIN volumes v ON v.id = nbsr.volume_id WHERE bo.provider='kboss' GROUP BY b.id, nbsr.volume_id; 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 782862df6d83..879c4dc36596 100644 --- a/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java @@ -85,6 +85,7 @@ import org.apache.cloudstack.backup.dao.BackupOfferingDao; import org.apache.cloudstack.backup.dao.BackupOfferingDetailsDao; import org.apache.cloudstack.backup.dao.BackupScheduleDao; +import org.apache.cloudstack.backup.dao.InternalBackupJoinDao; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.jobs.AsyncJobDispatcher; @@ -255,6 +256,8 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager { private DomainHelper domainHelper; @Inject ReservationDao reservationDao; + @Inject + private InternalBackupJoinDao internalBackupJoinDao; private AsyncJobDispatcher asyncJobDispatcher; private Timer backupTimer; @@ -2595,7 +2598,7 @@ Map getDetailsFromBackupDetails(Long backupId) { } @Override - public BackupResponse createBackupResponse(Backup backup, Boolean listVmDetails) { + public BackupResponse createBackupResponse(Backup backup, Boolean listVmDetails, boolean isCallerRootAdmin) { VMInstanceVO vm = vmInstanceDao.findByIdIncludingRemoved(backup.getVmId()); AccountVO account = accountDao.findByIdIncludingRemoved(backup.getAccountId()); DomainVO domain = domainDao.findByIdIncludingRemoved(backup.getDomainId()); @@ -2677,6 +2680,17 @@ public BackupResponse createBackupResponse(Backup backup, Boolean listVmDetails) response.setToCheckpointId(backup.getToCheckpointId()); } + if (KBOSS_BACKUP_PROVIDER.equals(offering.getProvider()) && isCallerRootAdmin) { + List backupJoins = internalBackupJoinDao.listById(backup.getId()); + Map backupVolumePaths = new HashMap<>(); + + backupJoins.forEach(b -> { + backupVolumePaths.put(b.getVolumeName(), b.getImageStorePath()); + }); + + response.setBackupPaths(backupVolumePaths); + } + response.setObjectName("backup"); return response; } 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 04bd03670079..94113811034a 100644 --- a/server/src/test/java/org/apache/cloudstack/backup/BackupManagerTest.java +++ b/server/src/test/java/org/apache/cloudstack/backup/BackupManagerTest.java @@ -59,6 +59,7 @@ import org.apache.cloudstack.backup.dao.BackupOfferingDao; import org.apache.cloudstack.backup.dao.BackupOfferingDetailsDao; import org.apache.cloudstack.backup.dao.BackupScheduleDao; +import org.apache.cloudstack.backup.dao.InternalBackupJoinDao; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.impl.ConfigDepotImpl; @@ -196,6 +197,9 @@ public class BackupManagerTest { @Mock private AlertManager alertManagerMock; + @Mock + private InternalBackupJoinDao internalBackupJoinDaoMock; + @Mock private Domain domainMock; @@ -1713,7 +1717,14 @@ public void testNewBackupResponse() { Mockito.when(backupDetailsDao.listDetailsKeyPairs(backup.getId(), true)).thenReturn(details); - BackupResponse response = backupManager.createBackupResponse(backup, true); + String backupPath = "/backup/path"; + String volumeName = "ROOT"; + InternalBackupJoinVO backupJoin = new InternalBackupJoinVO(); + ReflectionTestUtils.setField(backupJoin, "imageStorePath", backupPath); + ReflectionTestUtils.setField(backupJoin, "volumeName", volumeName); + Mockito.when(internalBackupJoinDaoMock.listById(Mockito.anyLong())).thenReturn(Collections.singletonList(backupJoin)); + + BackupResponse response = backupManager.createBackupResponse(backup, true, true); Assert.assertEquals("backup-uuid", response.getId()); Assert.assertEquals("test-vm", response.getVmName()); @@ -1730,6 +1741,7 @@ public void testNewBackupResponse() { "nics=[{\"networkid\":\"network-uuid1\",\"networkname\":\"network1\"}], serviceofferingname=service-offering1, " + "templatename=template1, templateid=template-uuid1}", response.getVmDetails().toString()); Assert.assertEquals(true, response.getVmOfferingRemoved()); + Assert.assertEquals(backupPath, response.getBackupPaths().get(volumeName)); } @Test diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json index 99bf2cf7aef9..47ee6fe569c8 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -489,6 +489,7 @@ "label.backup.restore": "Restore Instance backup", "label.backup.schedule.create.failed": "Failed to create Backup Schedule", "label.backuplimit": "Backup Limits", +"label.backuppaths": "Backup secondary storage paths", "label.backup.schedules": "Backup Schedules", "label.backup.storage": "Backup Storage", "label.backupstoragelimit": "Backup Storage Limits (GiB)", diff --git a/ui/public/locales/pt_BR.json b/ui/public/locales/pt_BR.json index 8fba779b1d12..f180518074fa 100644 --- a/ui/public/locales/pt_BR.json +++ b/ui/public/locales/pt_BR.json @@ -442,6 +442,7 @@ "label.backingup": "Salvando", "label.backup.attach.restore": "Restaurar e anexar volume de backup", "label.backuplimit": "Limite de backups", +"label.backuppaths": "Caminho dos backups no armazenamento secund\u00e1rio", "label.backup.storage": "Armazenamento de backup", "label.backupstoragelimit": "Limite de armazenamento de backup (GiB)", "label.backup.configure.schedule": "Configurar Agendamento de Backup", diff --git a/ui/src/components/view/DetailsTab.vue b/ui/src/components/view/DetailsTab.vue index d2aabacb10c9..b6b742434645 100644 --- a/ui/src/components/view/DetailsTab.vue +++ b/ui/src/components/view/DetailsTab.vue @@ -68,6 +68,11 @@ {{ volume.type }} - {{ volume.path }} ({{ parseFloat(volume.size / (1024.0 * 1024.0 * 1024.0)).toFixed(1) }} GiB) +
+
+ {{ volumeName }} - {{ backupPath }} +
+
{{ dataResource.rootdisksize }} GB diff --git a/ui/src/config/section/storage.js b/ui/src/config/section/storage.js index 75bdfd4d5fa6..5b8625a2c6ce 100644 --- a/ui/src/config/section/storage.js +++ b/ui/src/config/section/storage.js @@ -494,7 +494,7 @@ export default { permission: ['listBackups'], params: { listvmdetails: 'true' }, columns: ['name', 'status', 'compressionstatus', 'validationstatus', 'size', 'virtualsize', 'virtualmachinename', 'backupofferingname', 'intervaltype', 'type', 'created', 'account', 'domain', 'zone'], - details: ['name', 'description', 'virtualmachinename', 'id', 'intervaltype', 'type', 'externalid', 'size', 'virtualsize', 'volumes', 'backupofferingname', 'zone', 'account', 'domain', 'created'], + details: ['name', 'description', 'virtualmachinename', 'id', 'intervaltype', 'type', 'externalid', 'size', 'virtualsize', 'volumes', 'backupofferingname', 'backuppaths', 'zone', 'account', 'domain', 'created'], searchFilters: ['name', 'zoneid', 'domainid', 'account', 'backupofferingid', 'status', 'type', 'virtualmachineid'], tabs: [ { From bb9ccb76553dc6beccaadb9d74b6f523da1231f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20B=C3=B6ck?= Date: Mon, 19 Oct 2026 10:52:38 -0300 Subject: [PATCH 2/6] Add null check to offering --- .../java/org/apache/cloudstack/backup/BackupManagerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 879c4dc36596..4a5afffcc018 100644 --- a/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java @@ -2680,7 +2680,7 @@ public BackupResponse createBackupResponse(Backup backup, Boolean listVmDetails, response.setToCheckpointId(backup.getToCheckpointId()); } - if (KBOSS_BACKUP_PROVIDER.equals(offering.getProvider()) && isCallerRootAdmin) { + if (offering != null && KBOSS_BACKUP_PROVIDER.equals(offering.getProvider()) && isCallerRootAdmin) { List backupJoins = internalBackupJoinDao.listById(backup.getId()); Map backupVolumePaths = new HashMap<>(); From a8ec02752e786484005f1380a5c35efefba202c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20B=C3=B6ck?= Date: Mon, 19 Oct 2026 13:53:07 -0300 Subject: [PATCH 3/6] Store volume UUID as map key and use dedicated structure with volume name and path as value --- .../api/response/BackupResponse.java | 8 ++-- .../helper/BackupVolumeNameAndPath.java | 45 +++++++++++++++++++ .../backup/InternalBackupJoinVO.java | 8 ++++ .../db/views/cloud.internal_backup_view.sql | 1 + .../cloudstack/backup/BackupManagerImpl.java | 5 ++- ui/src/components/view/DetailsTab.vue | 4 +- 6 files changed, 63 insertions(+), 8 deletions(-) create mode 100644 api/src/main/java/org/apache/cloudstack/api/response/helper/BackupVolumeNameAndPath.java 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 0d6fd08c82e8..4fccdb176e14 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 @@ -20,6 +20,7 @@ import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseResponse; import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.api.response.helper.BackupVolumeNameAndPath; import org.apache.cloudstack.backup.Backup; import com.cloud.serializer.Param; @@ -101,7 +102,7 @@ public class BackupResponse extends BaseResponse { @SerializedName(ApiConstants.BACKUP_PATHS) @Param(description = "The backups paths in the secondary storage") - private Map backupPaths; + private Map backupPaths; @SerializedName(ApiConstants.ACCOUNT_ID) @Param(description = "Account id") @@ -391,12 +392,11 @@ public String getHostId() { return this.hostId; } - public Map getBackupPaths() { + public Map getBackupPaths() { return backupPaths; } - public void setBackupPaths(Map backupPaths) { + public void setBackupPaths(Map backupPaths) { this.backupPaths = backupPaths; } - } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/helper/BackupVolumeNameAndPath.java b/api/src/main/java/org/apache/cloudstack/api/response/helper/BackupVolumeNameAndPath.java new file mode 100644 index 000000000000..00a7b97564b8 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/helper/BackupVolumeNameAndPath.java @@ -0,0 +1,45 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.response.helper; + +public class BackupVolumeNameAndPath { + private String volumeName; + private String backupPath; + + public BackupVolumeNameAndPath(String volumeName, String backupPath) { + this.volumeName = volumeName; + this.backupPath = backupPath; + } + + public String getVolumeName() { + return volumeName; + } + + public void setVolumeName(String volumeName) { + this.volumeName = volumeName; + } + + public String getBackupPath() { + return backupPath; + } + + public void setBackupPath(String backupPath) { + this.backupPath = backupPath; + } +} + diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/InternalBackupJoinVO.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/InternalBackupJoinVO.java index d39d436db990..b79ac477cee8 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/backup/InternalBackupJoinVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/InternalBackupJoinVO.java @@ -98,6 +98,9 @@ public class InternalBackupJoinVO { @Column(name = "volume_id") private long volumeId; + @Column(name = "volume_uuid") + private String volumeUuid; + @Column(name = "volume_name") private String volumeName; @@ -190,6 +193,11 @@ public Backup.CompressionStatus getCompressionStatus() { return compressionStatus; } + + public String getVolumeUuid() { + return volumeUuid; + } + @Override public String toString() { return ReflectionToStringBuilder.toString(this, ToStringStyle.JSON_STYLE); diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.internal_backup_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.internal_backup_view.sql index 02a1622f501a..fb8bed4478cb 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.internal_backup_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.internal_backup_view.sql @@ -38,6 +38,7 @@ SELECT b.id, COALESCE(MAX(CASE WHEN bd.name = 'isolated' THEN bd.value END), 'false') isolated, nbpr.volume_id, v.name as volume_name, + v.uuid as volume_uuid, nbsr.path image_store_path FROM backups b LEFT JOIN backup_details bd ON b.id = bd.backup_id 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 4a5afffcc018..da359b3ef11e 100644 --- a/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java @@ -80,6 +80,7 @@ import org.apache.cloudstack.api.command.user.backup.repository.UpdateBackupRepositoryCmd; import org.apache.cloudstack.api.command.user.vm.CreateVMFromBackupCmd; import org.apache.cloudstack.api.response.BackupResponse; +import org.apache.cloudstack.api.response.helper.BackupVolumeNameAndPath; import org.apache.cloudstack.backup.dao.BackupDao; import org.apache.cloudstack.backup.dao.BackupDetailsDao; import org.apache.cloudstack.backup.dao.BackupOfferingDao; @@ -2682,10 +2683,10 @@ public BackupResponse createBackupResponse(Backup backup, Boolean listVmDetails, if (offering != null && KBOSS_BACKUP_PROVIDER.equals(offering.getProvider()) && isCallerRootAdmin) { List backupJoins = internalBackupJoinDao.listById(backup.getId()); - Map backupVolumePaths = new HashMap<>(); + Map backupVolumePaths = new HashMap<>(); backupJoins.forEach(b -> { - backupVolumePaths.put(b.getVolumeName(), b.getImageStorePath()); + backupVolumePaths.put(b.getVolumeUuid(), new BackupVolumeNameAndPath(b.getVolumeName(), b.getImageStorePath())); }); response.setBackupPaths(backupVolumePaths); diff --git a/ui/src/components/view/DetailsTab.vue b/ui/src/components/view/DetailsTab.vue index b6b742434645..ec21a94949d2 100644 --- a/ui/src/components/view/DetailsTab.vue +++ b/ui/src/components/view/DetailsTab.vue @@ -69,8 +69,8 @@
-
- {{ volumeName }} - {{ backupPath }} +
+ {{ backupVolumeInfo.volumeName }} - {{ backupVolumeInfo.backupPath }}
From 24a2c2abca1898a9586a8d2f3c022094beacee11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20B=C3=B6ck?= Date: Mon, 19 Oct 2026 15:39:08 -0300 Subject: [PATCH 4/6] Improve backup response tests --- .../backup/BackupManagerResponsesTest.java | 262 ++++++++++++++++++ .../cloudstack/backup/BackupManagerTest.java | 104 ------- 2 files changed, 262 insertions(+), 104 deletions(-) create mode 100644 server/src/test/java/org/apache/cloudstack/backup/BackupManagerResponsesTest.java diff --git a/server/src/test/java/org/apache/cloudstack/backup/BackupManagerResponsesTest.java b/server/src/test/java/org/apache/cloudstack/backup/BackupManagerResponsesTest.java new file mode 100644 index 000000000000..2600c2e8ef26 --- /dev/null +++ b/server/src/test/java/org/apache/cloudstack/backup/BackupManagerResponsesTest.java @@ -0,0 +1,262 @@ +package org.apache.cloudstack.backup; + +import com.cloud.dc.DataCenter; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.domain.DomainVO; +import com.cloud.domain.dao.DomainDao; +import com.cloud.hypervisor.Hypervisor; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.NetworkVO; +import com.cloud.service.ServiceOfferingVO; +import com.cloud.service.dao.ServiceOfferingDao; +import com.cloud.storage.Storage; +import com.cloud.storage.VMTemplateVO; +import com.cloud.storage.dao.VMTemplateDao; +import com.cloud.user.AccountVO; +import com.cloud.user.dao.AccountDao; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.dao.VMInstanceDao; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.response.BackupResponse; +import org.apache.cloudstack.backup.dao.BackupDetailsDao; +import org.apache.cloudstack.backup.dao.BackupOfferingDao; +import org.apache.cloudstack.backup.dao.InternalBackupJoinDao; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class BackupManagerResponsesTest { + @Spy + @InjectMocks + private BackupManagerImpl backupManager = new BackupManagerImpl(); + + @Mock + private VMInstanceDao vmInstanceDao; + @Mock + private AccountDao accountDao; + @Mock + private DomainDao domainDao; + @Mock + private DataCenterDao dataCenterDao; + @Mock + private BackupOfferingDao backupOfferingDao; + @Mock + private VMTemplateDao vmTemplateDao; + @Mock + private ServiceOfferingDao serviceOfferingDao; + @Mock + private NetworkDao networkDao; + @Mock + private BackupDetailsDao backupDetailsDao; + @Mock + private InternalBackupJoinDao internalBackupJoinDaoMock; + + // Entities + private BackupVO backup; + private VMInstanceVO virtualMachine; + private AccountVO account; + private DomainVO domain; + private DataCenterVO zone; + private BackupOfferingVO backupOffering; + private VMTemplateVO template; + private ServiceOfferingVO serviceOffering; + private NetworkVO network; + + @Before + public void setup() { + Long vmId = 1L; + Long accountId = 2L; + Long domainId = 3L; + Long zoneId = 4L; + Long vmOfferingId = 5L; + Long backupOfferingId = 6L; + Long backupId = 7L; + Long templateId = 8L; + String templateUuid = "template-uuid1"; + String serviceOfferingUuid = "service-offering-uuid1"; + + backup = new BackupVO(); + ReflectionTestUtils.setField(backup, "id", backupId); + ReflectionTestUtils.setField(backup, "uuid", "backup-uuid"); + backup.setVmId(vmId); + backup.setAccountId(accountId); + backup.setDomainId(domainId); + backup.setZoneId(zoneId); + backup.setBackupOfferingId(backupOfferingId); + backup.setType("Full"); + backup.setBackupScheduleId(null); + + virtualMachine = new VMInstanceVO(vmId, 0L, "test-vm", "test-vm", VirtualMachine.Type.User, + 0L, Hypervisor.HypervisorType.Simulator, 0L, domainId, accountId, 0L, false); + virtualMachine.setDataCenterId(zoneId); + virtualMachine.setBackupOfferingId(vmOfferingId); + virtualMachine.setTemplateId(templateId); + + account = new AccountVO(); + account.setUuid("account-uuid"); + account.setAccountName("test-account"); + + domain = new DomainVO(); + domain.setUuid("domain-uuid"); + domain.setName("test-domain"); + + zone = new DataCenterVO(1L, "test-zone", null, null, null, null, null, null, null, null, DataCenter.NetworkType.Advanced, null, null); + zone.setUuid("zone-uuid"); + + backupOffering = new BackupOfferingVO(); + ReflectionTestUtils.setField(backupOffering, "id", backupOfferingId); + ReflectionTestUtils.setField(backupOffering, "uuid", "offering-uuid"); + backupOffering.setName("test-offering"); + + Mockito.when(vmInstanceDao.findByIdIncludingRemoved(vmId)).thenReturn(virtualMachine); + Mockito.when(accountDao.findByIdIncludingRemoved(accountId)).thenReturn(account); + Mockito.when(domainDao.findByIdIncludingRemoved(domainId)).thenReturn(domain); + Mockito.when(dataCenterDao.findByIdIncludingRemoved(zoneId)).thenReturn(zone); + Mockito.when(backupOfferingDao.findByIdIncludingRemoved(backupOfferingId)).thenReturn(backupOffering); + + template = new VMTemplateVO(); + template.setFormat(Storage.ImageFormat.QCOW2); + template.setName("template1"); + when(vmTemplateDao.findByUuid(templateUuid)).thenReturn(template); + + + serviceOffering = mock(ServiceOfferingVO.class); + when(serviceOffering.getName()).thenReturn("service-offering1"); + when(serviceOfferingDao.findByUuid(serviceOfferingUuid)).thenReturn(serviceOffering); + + network = new NetworkVO(); + network.setName("network1"); + when(networkDao.findByUuid("network-uuid1")).thenReturn(network); + + + Map details = new HashMap<>(); + details.put(ApiConstants.TEMPLATE_ID, templateUuid); + details.put(ApiConstants.SERVICE_OFFERING_ID, serviceOfferingUuid); + details.put(ApiConstants.NICS, "[{\"networkid\":\"network-uuid1\"}]"); + Mockito.when(backupDetailsDao.listDetailsKeyPairs(backup.getId(), true)).thenReturn(details); + } + + @Test + public void createBackupResponseTestBasicResponse() { + BackupResponse response = backupManager.createBackupResponse(backup, true, true); + + assertBasicBackupResponseFields(response); + Assert.assertNull(response.getBackupPaths()); + } + + @Test + public void createBackupResponseTestKBOSSResponseAdmin() { + ReflectionTestUtils.setField(backupOffering, "provider", BackupManagerImpl.KBOSS_BACKUP_PROVIDER); + + InternalBackupJoinVO backupJoin = createBackupJoin(); + Mockito.when(internalBackupJoinDaoMock.listById(Mockito.anyLong())).thenReturn(Collections.singletonList(backupJoin)); + + BackupResponse response = backupManager.createBackupResponse(backup, true, true); + + assertBasicBackupResponseFields(response); + Assert.assertFalse(response.getBackupPaths().isEmpty()); + Assert.assertTrue(response.getBackupPaths().containsKey(backupJoin.getVolumeUuid())); + Assert.assertEquals(backupJoin.getImageStorePath(), response.getBackupPaths().get(backupJoin.getVolumeUuid()).getBackupPath()); + Assert.assertEquals(backupJoin.getVolumeName(), response.getBackupPaths().get(backupJoin.getVolumeUuid()).getVolumeName()); + } + + @Test + public void createBackupResponseTestKBOSSResponseMultipleEqualNamedVolumes() { + ReflectionTestUtils.setField(backupOffering, "provider", BackupManagerImpl.KBOSS_BACKUP_PROVIDER); + + int joinsAmount = 3; + List backupJoins = getListOfBackupJoins(joinsAmount); + Mockito.when(internalBackupJoinDaoMock.listById(Mockito.anyLong())).thenReturn(backupJoins); + + BackupResponse response = backupManager.createBackupResponse(backup, true, true); + + assertBasicBackupResponseFields(response); + Assert.assertFalse(response.getBackupPaths().isEmpty()); + Assert.assertEquals(joinsAmount, response.getBackupPaths().size()); + + for (InternalBackupJoinVO backupJoin : backupJoins) { + Assert.assertTrue(response.getBackupPaths().containsKey(backupJoin.getVolumeUuid())); + Assert.assertEquals(backupJoin.getImageStorePath(), response.getBackupPaths().get(backupJoin.getVolumeUuid()).getBackupPath()); + Assert.assertEquals(backupJoin.getVolumeName(), response.getBackupPaths().get(backupJoin.getVolumeUuid()).getVolumeName()); + } + } + + @Test + public void createBackupResponseTestKBOSSResponseNormalUser() { + ReflectionTestUtils.setField(backupOffering, "provider", BackupManagerImpl.KBOSS_BACKUP_PROVIDER); + + BackupResponse response = backupManager.createBackupResponse(backup, true, false); + + assertBasicBackupResponseFields(response); + Assert.assertNull(response.getBackupPaths()); + } + + @Test + public void createBackupResponseTestKBOSSResponseNullOffering() { + ReflectionTestUtils.setField(backupOffering, "provider", null); + + BackupResponse response = backupManager.createBackupResponse(backup, true, false); + + assertBasicBackupResponseFields(response); + Assert.assertNull(response.getBackupPaths()); + } + + private void assertBasicBackupResponseFields(BackupResponse response) { + Assert.assertEquals("backup-uuid", response.getId()); + Assert.assertEquals("test-vm", response.getVmName()); + Assert.assertEquals("account-uuid", response.getAccountId()); + Assert.assertEquals("test-account", response.getAccount()); + Assert.assertEquals("domain-uuid", response.getDomainId()); + Assert.assertEquals("test-domain", response.getDomain()); + Assert.assertEquals("zone-uuid", response.getZoneId()); + Assert.assertEquals("test-zone", response.getZone()); + Assert.assertEquals("offering-uuid", response.getBackupOfferingId()); + Assert.assertEquals("test-offering", response.getBackupOffering()); + Assert.assertEquals("MANUAL", response.getIntervalType()); + Assert.assertEquals("{serviceofferingid=service-offering-uuid1, isiso=false, hypervisor=Simulator, " + + "nics=[{\"networkid\":\"network-uuid1\",\"networkname\":\"network1\"}], serviceofferingname=service-offering1, " + + "templatename=template1, templateid=template-uuid1}", response.getVmDetails().toString()); + Assert.assertEquals(true, response.getVmOfferingRemoved()); + } + + private List getListOfBackupJoins(int listSize) { + List backupJoins = new java.util.ArrayList<>(); + + for (int i = 0; i < listSize; i++) { + backupJoins.add(createBackupJoin()); + } + + return backupJoins; + } + + private InternalBackupJoinVO createBackupJoin() { + String backupPath = "/backup/path"; + String volumeName = "ROOT"; + String volumeUuid = UUID.randomUUID().toString(); + InternalBackupJoinVO backupJoin = new InternalBackupJoinVO(); + ReflectionTestUtils.setField(backupJoin, "imageStorePath", backupPath); + ReflectionTestUtils.setField(backupJoin, "volumeName", volumeName); + ReflectionTestUtils.setField(backupJoin, "volumeUuid", volumeUuid); + + return backupJoin; + } +} 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 94113811034a..b770bdd452ae 100644 --- a/server/src/test/java/org/apache/cloudstack/backup/BackupManagerTest.java +++ b/server/src/test/java/org/apache/cloudstack/backup/BackupManagerTest.java @@ -53,13 +53,11 @@ 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.ListBackupScheduleCmd; -import org.apache.cloudstack.api.response.BackupResponse; import org.apache.cloudstack.backup.dao.BackupDao; import org.apache.cloudstack.backup.dao.BackupDetailsDao; import org.apache.cloudstack.backup.dao.BackupOfferingDao; import org.apache.cloudstack.backup.dao.BackupOfferingDetailsDao; import org.apache.cloudstack.backup.dao.BackupScheduleDao; -import org.apache.cloudstack.backup.dao.InternalBackupJoinDao; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.impl.ConfigDepotImpl; @@ -92,7 +90,6 @@ import com.cloud.api.query.vo.UserVmJoinVO; import com.cloud.capacity.CapacityVO; import com.cloud.configuration.Resource; -import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenterVO; import com.cloud.dc.dao.DataCenterDao; import com.cloud.domain.Domain; @@ -114,7 +111,6 @@ import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.storage.DiskOfferingVO; -import com.cloud.storage.Storage; import com.cloud.storage.VMTemplateVO; import com.cloud.storage.Volume; import com.cloud.storage.VolumeApiService; @@ -197,9 +193,6 @@ public class BackupManagerTest { @Mock private AlertManager alertManagerMock; - @Mock - private InternalBackupJoinDao internalBackupJoinDaoMock; - @Mock private Domain domainMock; @@ -1647,103 +1640,6 @@ public void testDeleteBackupBlockedByPendingJobs() throws ResourceAllocationExce backupManager.deleteBackup(backupId, false); } - @Test - public void testNewBackupResponse() { - Long vmId = 1L; - Long accountId = 2L; - Long domainId = 3L; - Long zoneId = 4L; - Long vmOfferingId = 5L; - Long backupOfferingId = 6L; - Long backupId = 7L; - Long templateId = 8L; - String templateUuid = "template-uuid1"; - String serviceOfferingUuid = "service-offering-uuid1"; - - BackupVO backup = new BackupVO(); - ReflectionTestUtils.setField(backup, "id", backupId); - ReflectionTestUtils.setField(backup, "uuid", "backup-uuid"); - backup.setVmId(vmId); - backup.setAccountId(accountId); - backup.setDomainId(domainId); - backup.setZoneId(zoneId); - backup.setBackupOfferingId(backupOfferingId); - backup.setType("Full"); - backup.setBackupScheduleId(null); - - VMInstanceVO vm = new VMInstanceVO(vmId, 0L, "test-vm", "test-vm", VirtualMachine.Type.User, - 0L, Hypervisor.HypervisorType.Simulator, 0L, domainId, accountId, 0L, false); - vm.setDataCenterId(zoneId); - vm.setBackupOfferingId(vmOfferingId); - vm.setTemplateId(templateId); - - AccountVO account = new AccountVO(); - account.setUuid("account-uuid"); - account.setAccountName("test-account"); - - DomainVO domain = new DomainVO(); - domain.setUuid("domain-uuid"); - domain.setName("test-domain"); - - DataCenterVO zone = new DataCenterVO(1L, "test-zone", null, null, null, null, null, null, null, null, DataCenter.NetworkType.Advanced, null, null); - zone.setUuid("zone-uuid"); - - BackupOfferingVO offering = Mockito.mock(BackupOfferingVO.class); - Mockito.when(offering.getUuid()).thenReturn("offering-uuid"); - Mockito.when(offering.getName()).thenReturn("test-offering"); - - Mockito.when(vmInstanceDao.findByIdIncludingRemoved(vmId)).thenReturn(vm); - Mockito.when(accountDao.findByIdIncludingRemoved(accountId)).thenReturn(account); - Mockito.when(domainDao.findByIdIncludingRemoved(domainId)).thenReturn(domain); - Mockito.when(dataCenterDao.findByIdIncludingRemoved(zoneId)).thenReturn(zone); - Mockito.when(backupOfferingDao.findByIdIncludingRemoved(backupOfferingId)).thenReturn(offering); - - VMTemplateVO template = mock(VMTemplateVO.class); - when(template.getFormat()).thenReturn(Storage.ImageFormat.QCOW2); - when(template.getName()).thenReturn("template1"); - when(vmTemplateDao.findByUuid(templateUuid)).thenReturn(template); - Map details = new HashMap<>(); - details.put(ApiConstants.TEMPLATE_ID, templateUuid); - - ServiceOfferingVO serviceOffering = mock(ServiceOfferingVO.class); - when(serviceOffering.getName()).thenReturn("service-offering1"); - when(serviceOfferingDao.findByUuid(serviceOfferingUuid)).thenReturn(serviceOffering); - details.put(ApiConstants.SERVICE_OFFERING_ID, serviceOfferingUuid); - - NetworkVO network = mock(NetworkVO.class); - when(network.getName()).thenReturn("network1"); - when(networkDao.findByUuid("network-uuid1")).thenReturn(network); - details.put(ApiConstants.NICS, "[{\"networkid\":\"network-uuid1\"}]"); - - Mockito.when(backupDetailsDao.listDetailsKeyPairs(backup.getId(), true)).thenReturn(details); - - String backupPath = "/backup/path"; - String volumeName = "ROOT"; - InternalBackupJoinVO backupJoin = new InternalBackupJoinVO(); - ReflectionTestUtils.setField(backupJoin, "imageStorePath", backupPath); - ReflectionTestUtils.setField(backupJoin, "volumeName", volumeName); - Mockito.when(internalBackupJoinDaoMock.listById(Mockito.anyLong())).thenReturn(Collections.singletonList(backupJoin)); - - BackupResponse response = backupManager.createBackupResponse(backup, true, true); - - Assert.assertEquals("backup-uuid", response.getId()); - Assert.assertEquals("test-vm", response.getVmName()); - Assert.assertEquals("account-uuid", response.getAccountId()); - Assert.assertEquals("test-account", response.getAccount()); - Assert.assertEquals("domain-uuid", response.getDomainId()); - Assert.assertEquals("test-domain", response.getDomain()); - Assert.assertEquals("zone-uuid", response.getZoneId()); - Assert.assertEquals("test-zone", response.getZone()); - Assert.assertEquals("offering-uuid", response.getBackupOfferingId()); - Assert.assertEquals("test-offering", response.getBackupOffering()); - Assert.assertEquals("MANUAL", response.getIntervalType()); - Assert.assertEquals("{serviceofferingid=service-offering-uuid1, isiso=false, hypervisor=Simulator, " + - "nics=[{\"networkid\":\"network-uuid1\",\"networkname\":\"network1\"}], serviceofferingname=service-offering1, " + - "templatename=template1, templateid=template-uuid1}", response.getVmDetails().toString()); - Assert.assertEquals(true, response.getVmOfferingRemoved()); - Assert.assertEquals(backupPath, response.getBackupPaths().get(volumeName)); - } - @Test public void validateAndGetDefaultBackupRetentionIfRequiredTestReturnZeroAsDefaultValue() { int retention = backupManager.validateAndGetDefaultBackupRetentionIfRequired(null, backupOfferingVOMock, null); From e43d5a58eb2e207d6b32297a11233778c445b019 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20B=C3=B6ck?= Date: Mon, 19 Oct 2026 15:59:15 -0300 Subject: [PATCH 5/6] Separate offering related assertions and fix null offering test --- .../backup/BackupManagerResponsesTest.java | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/server/src/test/java/org/apache/cloudstack/backup/BackupManagerResponsesTest.java b/server/src/test/java/org/apache/cloudstack/backup/BackupManagerResponsesTest.java index 2600c2e8ef26..38b671919bbd 100644 --- a/server/src/test/java/org/apache/cloudstack/backup/BackupManagerResponsesTest.java +++ b/server/src/test/java/org/apache/cloudstack/backup/BackupManagerResponsesTest.java @@ -159,7 +159,7 @@ public void setup() { public void createBackupResponseTestBasicResponse() { BackupResponse response = backupManager.createBackupResponse(backup, true, true); - assertBasicBackupResponseFields(response); + assertFullBackupResponseFields(response); Assert.assertNull(response.getBackupPaths()); } @@ -172,7 +172,7 @@ public void createBackupResponseTestKBOSSResponseAdmin() { BackupResponse response = backupManager.createBackupResponse(backup, true, true); - assertBasicBackupResponseFields(response); + assertFullBackupResponseFields(response); Assert.assertFalse(response.getBackupPaths().isEmpty()); Assert.assertTrue(response.getBackupPaths().containsKey(backupJoin.getVolumeUuid())); Assert.assertEquals(backupJoin.getImageStorePath(), response.getBackupPaths().get(backupJoin.getVolumeUuid()).getBackupPath()); @@ -189,7 +189,7 @@ public void createBackupResponseTestKBOSSResponseMultipleEqualNamedVolumes() { BackupResponse response = backupManager.createBackupResponse(backup, true, true); - assertBasicBackupResponseFields(response); + assertFullBackupResponseFields(response); Assert.assertFalse(response.getBackupPaths().isEmpty()); Assert.assertEquals(joinsAmount, response.getBackupPaths().size()); @@ -206,20 +206,25 @@ public void createBackupResponseTestKBOSSResponseNormalUser() { BackupResponse response = backupManager.createBackupResponse(backup, true, false); - assertBasicBackupResponseFields(response); + assertFullBackupResponseFields(response); Assert.assertNull(response.getBackupPaths()); } @Test public void createBackupResponseTestKBOSSResponseNullOffering() { - ReflectionTestUtils.setField(backupOffering, "provider", null); + Mockito.when(backupOfferingDao.findByIdIncludingRemoved(Mockito.anyLong())).thenReturn(null); - BackupResponse response = backupManager.createBackupResponse(backup, true, false); + BackupResponse response = backupManager.createBackupResponse(backup, true, true); assertBasicBackupResponseFields(response); Assert.assertNull(response.getBackupPaths()); } + private void assertFullBackupResponseFields(BackupResponse response) { + assertBasicBackupResponseFields(response); + assertBackupResponseOfferingFields(response); + } + private void assertBasicBackupResponseFields(BackupResponse response) { Assert.assertEquals("backup-uuid", response.getId()); Assert.assertEquals("test-vm", response.getVmName()); @@ -229,8 +234,6 @@ private void assertBasicBackupResponseFields(BackupResponse response) { Assert.assertEquals("test-domain", response.getDomain()); Assert.assertEquals("zone-uuid", response.getZoneId()); Assert.assertEquals("test-zone", response.getZone()); - Assert.assertEquals("offering-uuid", response.getBackupOfferingId()); - Assert.assertEquals("test-offering", response.getBackupOffering()); Assert.assertEquals("MANUAL", response.getIntervalType()); Assert.assertEquals("{serviceofferingid=service-offering-uuid1, isiso=false, hypervisor=Simulator, " + "nics=[{\"networkid\":\"network-uuid1\",\"networkname\":\"network1\"}], serviceofferingname=service-offering1, " + @@ -238,6 +241,11 @@ private void assertBasicBackupResponseFields(BackupResponse response) { Assert.assertEquals(true, response.getVmOfferingRemoved()); } + private void assertBackupResponseOfferingFields(BackupResponse response) { + Assert.assertEquals("offering-uuid", response.getBackupOfferingId()); + Assert.assertEquals("test-offering", response.getBackupOffering()); + } + private List getListOfBackupJoins(int listSize) { List backupJoins = new java.util.ArrayList<>(); From ae4431a25e9d6e196ba9e4867d087a92cf07faeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20B=C3=B6ck?= Date: Mon, 19 Oct 2026 16:12:54 -0300 Subject: [PATCH 6/6] Add license to file --- .../backup/BackupManagerResponsesTest.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/server/src/test/java/org/apache/cloudstack/backup/BackupManagerResponsesTest.java b/server/src/test/java/org/apache/cloudstack/backup/BackupManagerResponsesTest.java index 38b671919bbd..daac8469eab8 100644 --- a/server/src/test/java/org/apache/cloudstack/backup/BackupManagerResponsesTest.java +++ b/server/src/test/java/org/apache/cloudstack/backup/BackupManagerResponsesTest.java @@ -1,3 +1,20 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + package org.apache.cloudstack.backup; import com.cloud.dc.DataCenter;