From 5dfd760fe72b320b6f09a6bc12f1069f16c8bf41 Mon Sep 17 00:00:00 2001 From: Dajeong-Park Date: Tue, 12 May 2026 10:36:18 +0900 Subject: [PATCH 1/5] =?UTF-8?q?diplo=20=EB=B2=84=EC=A0=84=EC=9D=98=20?= =?UTF-8?q?=EB=B0=B1=EC=97=85=EB=B3=B8=EC=9D=98=20=EA=B2=BD=EC=9A=B0=20uui?= =?UTF-8?q?d=20=ED=98=95=EC=8B=9D=EC=9D=98=20=EB=B0=B1=EC=97=85=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=EC=9D=84=20=EC=9D=BD=EC=96=B4=EC=84=9C=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backup/AblestackNasBackupProvider.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/backup/ablestack-nas/src/main/java/org/apache/cloudstack/backup/AblestackNasBackupProvider.java b/plugins/backup/ablestack-nas/src/main/java/org/apache/cloudstack/backup/AblestackNasBackupProvider.java index ab13de00c9c7..222e156d78c6 100644 --- a/plugins/backup/ablestack-nas/src/main/java/org/apache/cloudstack/backup/AblestackNasBackupProvider.java +++ b/plugins/backup/ablestack-nas/src/main/java/org/apache/cloudstack/backup/AblestackNasBackupProvider.java @@ -877,14 +877,19 @@ private String getLegacyBackupFileName(Backup.VolumeInfo volumeInfo) { return volumePath; } String diskPrefix = Volume.Type.ROOT.equals(volumeInfo.getType()) ? "root" : "datadisk"; - return String.format("%s.%s.qcow2", diskPrefix, volumeInfo.getPath()); + return String.format("%s.%s.qcow2", diskPrefix, volumeInfo.getUuid()); } private List getLegacyBackupFileCandidates(Backup.VolumeInfo volumeInfo) { List candidates = new ArrayList<>(); + String legacyFileName = getLegacyBackupFileName(volumeInfo); + candidates.add(legacyFileName); + String volumePath = volumeInfo.getPath(); if (StringUtils.isNotBlank(volumePath)) { - candidates.add(volumePath); + if (!candidates.contains(volumePath)) { + candidates.add(volumePath); + } if (volumePath.contains("/")) { String baseName = volumePath.substring(volumePath.lastIndexOf('/') + 1); if (!Objects.equals(volumePath, baseName)) { @@ -893,11 +898,6 @@ private List getLegacyBackupFileCandidates(Backup.VolumeInfo volumeInfo) } } - String legacyFileName = getLegacyBackupFileName(volumeInfo); - if (!candidates.contains(legacyFileName)) { - candidates.add(legacyFileName); - } - if (volumePath != null && volumePath.contains("/")) { String baseName = volumePath.substring(volumePath.lastIndexOf('/') + 1); String diskPrefix = Volume.Type.ROOT.equals(volumeInfo.getType()) ? "root" : "datadisk"; From ad449e96ac8eb5feb9522c3360a32add968dd4ce Mon Sep 17 00:00:00 2001 From: Dajeong-Park Date: Tue, 12 May 2026 16:11:58 +0900 Subject: [PATCH 2/5] =?UTF-8?q?multi=20plugin=EC=9D=98=20=EA=B2=BD?= =?UTF-8?q?=EC=9A=B0=20=EB=8B=A4=EC=A4=91=20=EC=A1=B0=ED=9A=8C=EB=90=98?= =?UTF-8?q?=EB=8A=94=20=EB=B6=80=EB=B6=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cloudstack/backup/BackupManagerImpl.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) 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 cc7b471313b7..3fd26bd210a8 100644 --- a/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java @@ -2489,6 +2489,20 @@ private void syncOutOfBandBackups(final BackupProvider backupProvider, DataCente } for (final VMInstanceVO vm : vms) { try { + Long backupOfferingId = vm.getBackupOfferingId(); + if (backupOfferingId == null) { + logger.debug("Skipping VM [{}] because backup offering is not assigned.", vm); + continue; + } + BackupOfferingVO offering = backupOfferingDao.findById(vm.getBackupOfferingId()); + if (offering == null) { + logger.debug("Skipping VM [{}] because backup offering [{}] was not found.", vm, backupOfferingId); + continue; + } + if (!backupProvider.getName().equalsIgnoreCase(offering.getProvider())) { + logger.debug("Skipping VM [{}] because backup offering provider [{}] does not match current provider [{}].", vm, offering.getProvider(), backupProvider.getName()); + continue; + } logger.debug(String.format("Trying to sync backups of VM [%s] using backup provider [%s].", vm, backupProvider.getName())); // Sync out-of-band backups syncBackups(backupProvider, vm); From a4016f05e7b7bc828282fee6186a189682021dea Mon Sep 17 00:00:00 2001 From: Dajeong-Park Date: Tue, 12 May 2026 16:39:07 +0900 Subject: [PATCH 3/5] =?UTF-8?q?diplo=20=EB=B2=84=EC=A0=84=EC=9D=98=20?= =?UTF-8?q?=EB=B0=B1=EC=97=85=EB=B3=B8=EC=9D=98=20=EA=B2=BD=EC=9A=B0=20?= =?UTF-8?q?=EB=B0=B1=EC=97=85=20=ED=8C=8C=EC=9D=BC=EB=AA=85=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backup/AblestackNasBackupProvider.java | 37 +------------------ 1 file changed, 1 insertion(+), 36 deletions(-) diff --git a/plugins/backup/ablestack-nas/src/main/java/org/apache/cloudstack/backup/AblestackNasBackupProvider.java b/plugins/backup/ablestack-nas/src/main/java/org/apache/cloudstack/backup/AblestackNasBackupProvider.java index 222e156d78c6..0fd82a32e2ca 100644 --- a/plugins/backup/ablestack-nas/src/main/java/org/apache/cloudstack/backup/AblestackNasBackupProvider.java +++ b/plugins/backup/ablestack-nas/src/main/java/org/apache/cloudstack/backup/AblestackNasBackupProvider.java @@ -784,7 +784,7 @@ private List getBackupFileChain(String volumeUuid, Backup backup) { loadBackupDetailsIfNeeded(backup); if (isLegacyBackup(backup)) { Backup.VolumeInfo volumeInfo = getBackedUpVolumeInfo(backup.getBackedUpVolumes(), volumeUuid); - return volumeInfo != null ? getLegacyBackupFileCandidates(volumeInfo) : List.of(); + return volumeInfo != null ? List.of(getLegacyBackupFileName(volumeInfo)) : List.of(); } String backupEngine = getBackupDetail(backup, DETAIL_BACKUP_ENGINE); @@ -871,45 +871,10 @@ private boolean isLegacyBackup(Backup backup) { } private String getLegacyBackupFileName(Backup.VolumeInfo volumeInfo) { - String volumePath = volumeInfo.getPath(); - if (StringUtils.isNotBlank(volumePath) && - (volumePath.endsWith(".qcow2") || volumePath.endsWith(".raw") || volumePath.endsWith(".rbdiff"))) { - return volumePath; - } String diskPrefix = Volume.Type.ROOT.equals(volumeInfo.getType()) ? "root" : "datadisk"; return String.format("%s.%s.qcow2", diskPrefix, volumeInfo.getUuid()); } - private List getLegacyBackupFileCandidates(Backup.VolumeInfo volumeInfo) { - List candidates = new ArrayList<>(); - String legacyFileName = getLegacyBackupFileName(volumeInfo); - candidates.add(legacyFileName); - - String volumePath = volumeInfo.getPath(); - if (StringUtils.isNotBlank(volumePath)) { - if (!candidates.contains(volumePath)) { - candidates.add(volumePath); - } - if (volumePath.contains("/")) { - String baseName = volumePath.substring(volumePath.lastIndexOf('/') + 1); - if (!Objects.equals(volumePath, baseName)) { - candidates.add(baseName); - } - } - } - - if (volumePath != null && volumePath.contains("/")) { - String baseName = volumePath.substring(volumePath.lastIndexOf('/') + 1); - String diskPrefix = Volume.Type.ROOT.equals(volumeInfo.getType()) ? "root" : "datadisk"; - String baseNameLegacyFile = String.format("%s.%s.qcow2", diskPrefix, baseName); - if (!candidates.contains(baseNameLegacyFile)) { - candidates.add(baseNameLegacyFile); - } - } - - return candidates; - } - private List getVolumePaths(List volumes) { List volumePaths = new ArrayList<>(); for (VolumeVO volume : volumes) { From 3c93a92f5c444d44d774825d03067a10b19ebdec Mon Sep 17 00:00:00 2001 From: Dajeong-Park Date: Thu, 14 May 2026 15:36:54 +0900 Subject: [PATCH 4/5] =?UTF-8?q?=EB=B0=B1=EC=97=85=20=EA=B8=80=EB=A1=9C?= =?UTF-8?q?=EB=B2=8C=EC=84=A4=EC=A0=95=20=EC=84=A4=EB=AA=85=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/org/apache/cloudstack/backup/BackupManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 0d23442bab73..b3d2ab09a958 100644 --- a/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java +++ b/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java @@ -57,7 +57,7 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer ConfigKey BackupProviderPlugin = new ConfigKey<>("Advanced", String.class, "backup.framework.provider.plugin", "dummy", - "The backup and recovery provider plugin.", true, ConfigKey.Scope.Zone, BackupFrameworkEnabled.key()); + "The backup and recovery provider plugin (comma-separated). Example: dummy, veeam, networker, nas, commvault", true, ConfigKey.Scope.Zone, BackupFrameworkEnabled.key()); ConfigKey BackupSyncPollingInterval = new ConfigKey<>("Advanced", Long.class, "backup.framework.sync.interval", From 1406e192b27d84471df94c0ecfcbd8a7cefc7aa2 Mon Sep 17 00:00:00 2001 From: Dajeong-Park Date: Fri, 15 May 2026 16:32:05 +0900 Subject: [PATCH 5/5] =?UTF-8?q?UI=20=EB=B9=8C=EB=93=9C=EC=8B=9C=20?= =?UTF-8?q?=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../unit/components/view/ActionButton.spec.js | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/ui/tests/unit/components/view/ActionButton.spec.js b/ui/tests/unit/components/view/ActionButton.spec.js index 3c1844e6ddec..b7d80f799c39 100644 --- a/ui/tests/unit/components/view/ActionButton.spec.js +++ b/ui/tests/unit/components/view/ActionButton.spec.js @@ -131,11 +131,11 @@ describe('Components > View > ActionButton.vue', () => { it('API should be called and return not empty', async (done) => { mockAxios.mockResolvedValue({ testapinameresponse: { count: 2 } }) const wrapper = factory({ - props: { - actions: [ - { - command: 'test-api-case-3', - response: 'json', + props: { + actions: [ + { + command: 'test-api-case-3', + response: 'json', label: 'label.action', api: 'test-api-case-3', showBadge: true, @@ -169,10 +169,10 @@ describe('Components > View > ActionButton.vue', () => { it('API should be called and return empty', async (done) => { mockAxios.mockResolvedValue({ data: [] }) const wrapper = factory({ - props: { - actions: [ - { - command: 'test-api-case-4', + props: { + actions: [ + { + command: 'test-api-case-4', response: 'json', label: 'label.action', api: 'test-api-case-4', @@ -207,10 +207,10 @@ describe('Components > View > ActionButton.vue', () => { it('API should be called and throw eror', async (done) => { mockAxios.mockRejectedValue('errMethodMessage') const wrapper = factory({ - props: { - actions: [ - { - command: 'test-api-case-5', + props: { + actions: [ + { + command: 'test-api-case-5', response: 'json', label: 'label.action', api: 'test-api-case-5',