⚠ This page is served via a proxy. Original site: https://github.com
This service does not collect credentials or authentication data.
Skip to content

Fixes issue with loading Capacity dashboard when mulitple backup providers configured#12550

Open
Pearl1594 wants to merge 8 commits into4.20from
ghi12449-capDashboard-multipleBackupProv
Open

Fixes issue with loading Capacity dashboard when mulitple backup providers configured#12550
Pearl1594 wants to merge 8 commits into4.20from
ghi12449-capDashboard-multipleBackupProv

Conversation

@Pearl1594
Copy link
Contributor

@Pearl1594 Pearl1594 commented Jan 29, 2026

Description

This PR fixed: #12449
The capacity dashboard issue is seen on 4.22 - but in general the getBackupProvider() is fixed to return the first provider set in the global settings when a comma separated list of providers is set.

94ac3ea - adds a validator, that prevents adding a comma separated list for backup provider setting. It also checks if the entered value is a valid one.

image image

Types of changes

  • Breaking change (fix or feature that would cause existing functionality to change)
  • New feature (non-breaking change which adds functionality)
  • Bug fix (non-breaking change which fixes an issue)
  • Enhancement (improves an existing feature and functionality)
  • Cleanup (Code refactoring and cleanup, that may add test cases)
  • Build/CI
  • Test (unit or integration test code)

Feature/Enhancement Scale or Bug Severity

Feature/Enhancement Scale

  • Major
  • Minor

Bug Severity

  • BLOCKER
  • Critical
  • Major
  • Minor
  • Trivial

Screenshots (if appropriate):

How Has This Been Tested?

How did you try to break this feature and the system with this change?

@codecov
Copy link

codecov bot commented Jan 29, 2026

Codecov Report

❌ Patch coverage is 18.51852% with 22 lines in your changes missing coverage. Please review.
✅ Project coverage is 16.37%. Comparing base (a6ccde4) to head (09c2399).
⚠️ Report is 9 commits behind head on 4.20.

Files with missing lines Patch % Lines
...loudstack/framework/config/ValidatedConfigKey.java 27.27% 8 Missing ⚠️
...va/org/apache/cloudstack/backup/BackupManager.java 22.22% 7 Missing ⚠️
.../cloud/configuration/ConfigurationManagerImpl.java 0.00% 4 Missing ⚠️
...rg/apache/cloudstack/backup/BackupManagerImpl.java 0.00% 3 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               4.20   #12550      +/-   ##
============================================
+ Coverage     16.26%   16.37%   +0.11%     
- Complexity    13428    13624     +196     
============================================
  Files          5660     5662       +2     
  Lines        499907   502494    +2587     
  Branches      60696    61844    +1148     
============================================
+ Hits          81316    82296     +980     
- Misses       409521   411072    +1551     
- Partials       9070     9126      +56     
Flag Coverage Δ
uitests 5.04% <ø> (+0.89%) ⬆️
unittests 17.17% <18.51%> (+0.05%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@Pearl1594
Copy link
Contributor Author

@blueorangutan package

@blueorangutan
Copy link

@Pearl1594 a [SL] Jenkins job has been kicked to build packages. It will be bundled with KVM, XenServer and VMware SystemVM templates. I'll keep you posted as I make progress.

@Pearl1594
Copy link
Contributor Author

@abh1sar @DaanHoogland I've added this bit : 94ac3ea to add a validator that can be used for any configuration. Do you think this is useful?

@blueorangutan
Copy link

Packaging result [SF]: ✔️ el8 ✔️ el9 ✔️ el10 ✔️ debian ✔️ suse15. SL-JID 16636

@Pearl1594
Copy link
Contributor Author

@blueorangutan package

@blueorangutan
Copy link

@Pearl1594 a [SL] Jenkins job has been kicked to build packages. It will be bundled with KVM, XenServer and VMware SystemVM templates. I'll keep you posted as I make progress.

@blueorangutan
Copy link

Packaging result [SF]: ✔️ el8 ✔️ el9 ✔️ el10 ✔️ debian ✔️ suse15. SL-JID 16639

Copy link
Collaborator

@abh1sar abh1sar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code LGTM.
just a few nitpicks.

"dummy",
"The backup and recovery provider plugin.", true, ConfigKey.Scope.Zone, BackupFrameworkEnabled.key());
"The backup and recovery provider plugin. Valid plugin values: dummy, veeam, networker and nas",
true, ConfigKey.Scope.Zone, BackupFrameworkEnabled.key(), value -> validateBackupProviderConfig((String)value));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
true, ConfigKey.Scope.Zone, BackupFrameworkEnabled.key(), value -> validateBackupProviderConfig((String)value));
true, ConfigKey.Scope.Zone, BackupFrameworkEnabled.key(), value -> validateBackupProviderConfig((String) value));

throw new CloudRuntimeException("Invalid backup provider name provided");
}
if (!backupProvidersMap.containsKey(name)) {
String[] backupProviderNames = name.split(",");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not possible now, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so, currently, ACS allows taking any string as input, so if we provide a comma separated string, it would compare that with providers supported in ACS, and would fail and report Cannot find backup provider by name: dummy,nas (if both were set)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant that validator will not allow , in the name, so no need for splitting the name by comma. We can revert to old code.

Copy link
Contributor

@DaanHoogland DaanHoogland Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--@abh1sar , what do you mean by “the validator”? are you talking about the ui/service/manager…—

never mind, got it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, with the validator - this isn't required.

@DaanHoogland
Copy link
Contributor

@Pearl1594 , can you address @abh1sar ’s comments?

Copy link
Contributor

@DaanHoogland DaanHoogland left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clgtm

@DaanHoogland
Copy link
Contributor

@blueorangutan package

@blueorangutan
Copy link

@DaanHoogland a [SL] Jenkins job has been kicked to build packages. It will be bundled with KVM, XenServer and VMware SystemVM templates. I'll keep you posted as I make progress.

@blueorangutan
Copy link

Packaging result [SF]: ✔️ el8 ✔️ el9 ✔️ el10 ✔️ debian ✔️ suse15. SL-JID 16698

if (value != null && (value.contains(",") || value.trim().contains(" "))) {
throw new IllegalArgumentException("Multiple backup provider plugins are not supported. Please provide a single plugin value.");
}
List<String> validPlugins = List.of("dummy", "veeam", "networker", "nas");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can check these values with the enum?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't have enums - do you want me to create one?

@RosiKyu RosiKyu self-assigned this Feb 4, 2026
Copy link
Collaborator

@abh1sar abh1sar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code LGTM. Thanks @Pearl1594 .

@DaanHoogland
Copy link
Contributor

94ac3ea

@Pearl1594 , I never answerred this query; I think it is great, I wonder though if it shouldn’t just be part of ConfigKey instead of in a derived class??

Copy link
Collaborator

@RosiKyu RosiKyu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Pearl1594, I have tested the PR and the core fix works well. Two issues found:

1. Case-sensitivity mismatch between validator and runtime provider lookup
The validator accepts values like DUMMY, Veeam, nAS (due to .toLowerCase()) but backupProvidersMap lookup is case-sensitive, causing runtime failures. We can potentially normalize the value to lowercase before storing, or reject non-lowercase values.

2. Empty string accepted via API
Setting the value to empty string via API bypasses validation (value != null check doesn't catch it). Suggested fix: add an empty/blank check (unless this is a common api behavior in such cases)

Test Execution Summary

TC Name Result
TC1 Reject comma-separated multiple providers PASS
TC2 Reject space-separated multiple providers PASS
TC3 Reject invalid provider names FAIL (case-sensitivity bug)
TC4 Accept valid single providers PASS
TC5 Accept null/empty handling PASS with observation
TC6 Capacity dashboard loads with valid provider PASS
TC7 Capacity dashboard after MS restart PASS
TC9 Zone-scoped setting validation PASS
TC10 BackupManagerImpl indentation fix PASS

Detailed Test Report

TC1: Reject comma-separated multiple providers

Objective
Verify that setting backup.framework.provider.plugin to a comma-separated list of multiple providers is rejected with an appropriate error message, both via UI and API.

Test Steps

  1. Navigate to Configuration → Global Settings, search for backup.framework.provider.plugin
  2. Set the value to dummy,nas and click save — observe the error in UI
  3. Via CloudMonkey, run:
    • update configuration name=backup.framework.provider.plugin value=dummy,nas
    • update configuration name=backup.framework.provider.plugin value=veeam,networker
    • update configuration name=backup.framework.provider.plugin value=dummy,veeam,nas
  4. Check management server logs: grep -i "Multiple backup provider" /var/log/cloudstack/management/management-server.log | tail -5

Expected Result:
All attempts to set multiple comma-separated backup providers should be rejected with error: "Multiple backup provider plugins are not supported. Please provide a single plugin value." — both in UI and via API.

Actual Result:
All attempts were correctly rejected. The UI displayed an error dialog with the expected message and a red banner "There was an error saving this setting." The API returned HTTP 431 with error code 9999 and the same error message for all three comma-separated values. The management server log confirmed the validation was triggered.

Test Evidence:
UI: Error dialog displayed "Multiple backup provider plugins are not supported. Please provide a single plugin value." with red error banner. The setting value remained unchanged at dummy.

Screencast.from.2026-02-06.12-24-14.webm

CloudMonkey output:

(localcloud) 🐱 > update configuration name=backup.framework.provider.plugin value=dummy,nas
🙈 Error: (HTTP 431, error code 9999) Multiple backup provider plugins are not supported. Please provide a single plugin value.
(localcloud) 🐱 > update configuration name=backup.framework.provider.plugin value=veeam,networker
🙈 Error: (HTTP 431, error code 9999) Multiple backup provider plugins are not supported. Please provide a single plugin value.
(localcloud) 🐱 > update configuration name=backup.framework.provider.plugin value=dummy,veeam,nas
🙈 Error: (HTTP 431, error code 9999) Multiple backup provider plugins are not supported. Please provide a single plugin value.

Management server log:

2026-02-06 10:22:45,925 INFO  [c.c.a.ApiServer] (qtp2038105753-21:[ctx-996c2d83, ctx-eefc2ddd]) (logid:e070c0d6) Multiple backup provider plugins are not supported. Please provide a single plugin value.
2026-02-06 10:23:11,075 INFO  [c.c.a.ApiServer] (qtp2038105753-21:[ctx-347a8d8c, ctx-57f3836c]) (logid:22aaebb0) Multiple backup provider plugins are not supported. Please provide a single plugin value.
2026-02-06 10:23:41,775 INFO  [c.c.a.ApiServer] (qtp2038105753-23:[ctx-2a51a0f6, ctx-069f267d, ctx-34cfe07f]) (logid:aa1bb78b) Multiple backup provider plugins are not supported. Please provide a single plugin value.
2026-02-06 10:23:45,841 INFO  [c.c.a.ApiServer] (qtp2038105753-23:[ctx-79c5561e, ctx-2dde040d, ctx-60b040c1]) (logid:3ef84978) Multiple backup provider plugins are not supported. Please provide a single plugin value.
2026-02-06 10:23:47,790 INFO  [c.c.a.ApiServer] (qtp2038105753-21:[ctx-08b58f4a, ctx-79177dab, ctx-cfcbee6b]) (logid:24e48a85) Multiple backup provider plugins are not supported. Please provide a single plugin value.

Test Result: PASS

TC2: Reject space-separated multiple providers

Objective
Verify that setting backup.framework.provider.plugin to space-separated or comma-plus-space-separated values is rejected with an appropriate error message, both via UI and API.

Test Steps

  1. Via CloudMonkey, run:
    • update configuration name=backup.framework.provider.plugin value="dummy nas"
    • update configuration name=backup.framework.provider.plugin value="veeam networker"
    • update configuration name=backup.framework.provider.plugin value="dummy, nas"
  2. In the UI, navigate to Configuration → Global Settings, search for backup.framework.provider.plugin, set the value to dummy nas and click save
  3. Check management server logs: grep -i "Multiple backup provider" /var/log/cloudstack/management/management-server.log | tail -10

Expected Result:
All attempts to set space-separated or comma-plus-space-separated backup providers should be rejected with error: "Multiple backup provider plugins are not supported. Please provide a single plugin value."

Actual Result:
All attempts were correctly rejected. The API returned HTTP 431 with error code 9999 and the expected error message for all three variations. The UI also displayed an error when dummy nas was entered. Management server logs confirmed all validation rejections.

Test Evidence:
CloudMonkey output:

(localcloud) 🐱 > update configuration name=backup.framework.provider.plugin value="dummy nas"
🙈 Error: (HTTP 431, error code 9999) Multiple backup provider plugins are not supported. Please provide a single plugin value.
(localcloud) 🐱 > update configuration name=backup.framework.provider.plugin value="veeam networker"
🙈 Error: (HTTP 431, error code 9999) Multiple backup provider plugins are not supported. Please provide a single plugin value.
(localcloud) 🐱 > update configuration name=backup.framework.provider.plugin value="dummy, nas"
🙈 Error: (HTTP 431, error code 9999) Multiple backup provider plugins are not supported. Please provide a single plugin value.

UI: Setting dummy nas in the Global Settings UI resulted in an error.

Screencast.from.2026-02-06.12-26-01.webm

Management server log:

2026-02-06 10:24:17,864 INFO  [c.c.a.ApiServer] (qtp2038105753-21:[ctx-46bed86b, ctx-31a1a970]) (logid:429e3f21) Multiple backup provider plugins are not supported. Please provide a single plugin value.
2026-02-06 10:25:36,232 INFO  [c.c.a.ApiServer] (qtp2038105753-23:[ctx-81d96d87, ctx-ee7ee4d7, ctx-d7dfe3e1]) (logid:97150bbc) Multiple backup provider plugins are not supported. Please provide a single plugin value.
2026-02-06 10:25:39,685 INFO  [c.c.a.ApiServer] (qtp2038105753-23:[ctx-3dcf8de1, ctx-3d5104dc, ctx-801436da]) (logid:6b16d926) Multiple backup provider plugins are not supported. Please provide a single plugin value.
2026-02-06 10:25:41,472 INFO  [c.c.a.ApiServer] (qtp2038105753-21:[ctx-cfde9084, ctx-62e2fba0, ctx-b9c017a5]) (logid:afc9b8a5) Multiple backup provider plugins are not supported. Please provide a single plugin value.
2026-02-06 10:26:04,463 INFO  [c.c.a.ApiServer] (qtp2038105753-21:[ctx-9fb6652b, ctx-cddfc279]) (logid:013ee8ee) Multiple backup provider plugins are not supported. Please provide a single plugin value.

Test Result: PASS

TC3: Reject invalid provider names

Objective
Verify that setting backup.framework.provider.plugin to an invalid provider name is rejected with an appropriate error message listing the valid options, and verify behavior with case variations of valid names.

Test Steps

  1. Via CloudMonkey, attempt to set invalid provider names:
    • update configuration name=backup.framework.provider.plugin value=other
    • update configuration name=backup.framework.provider.plugin value=invalid
    • update configuration name=backup.framework.provider.plugin value=veeam2
  2. In the UI, navigate to Configuration → Global Settings, set backup.framework.provider.plugin to other and click save — observe the error
  3. Via CloudMonkey, test case variations of valid names:
    • update configuration name=backup.framework.provider.plugin value=DUMMY
    • update configuration name=backup.framework.provider.plugin value=Veeam
    • update configuration name=backup.framework.provider.plugin value=NaS
    • update configuration name=backup.framework.provider.plugin value=nAS
  4. Check management server logs: grep -i "Invalid backup provider" /var/log/cloudstack/management/management-server.log | tail -5
  5. Check for runtime backup provider errors: grep -i "Failed to find backup provider" /var/log/cloudstack/management/management-server.log | tail -5

Expected Result:
Invalid provider names should be rejected with error: "Invalid backup provider plugin: . Valid plugin values are: dummy, veeam, networker, nas". Case variations of valid names should either be rejected or normalized to lowercase before saving.

Actual Result:
All invalid provider names (other, invalid, veeam2) were correctly rejected with the expected error message both via API (HTTP 431, error code 9999) and via UI.

However, case variations of valid provider names (DUMMY, Veeam, NaS, nAS) were accepted and saved as-is (without normalizing to lowercase). This causes a runtime failure — the backupProvidersMap lookup in BackupManagerImpl.getBackupProvider() is case-sensitive, so a value like nAS passes validation but fails at runtime with: Failed to find backup provider by the name: nAS.

BUG: The validator uses .toLowerCase() to check validity but the value is stored in its original case. The runtime provider map lookup is case-sensitive. The fix should either:

  • Reject non-lowercase values in the validator, OR
  • Normalize the value to lowercase before storing it

Test Evidence:
CloudMonkey output — invalid values rejected:

(localcloud) 🐱 > update configuration name=backup.framework.provider.plugin value=other
🙈 Error: (HTTP 431, error code 9999) Invalid backup provider plugin: other. Valid plugin values are: dummy, veeam, networker, nas
(localcloud) 🐱 > update configuration name=backup.framework.provider.plugin value=invalid
🙈 Error: (HTTP 431, error code 9999) Invalid backup provider plugin: invalid. Valid plugin values are: dummy, veeam, networker, nas
(localcloud) 🐱 > update configuration name=backup.framework.provider.plugin value=veeam2
🙈 Error: (HTTP 431, error code 9999) Invalid backup provider plugin: veeam2. Valid plugin values are: dummy, veeam, networker, nas

Case variations accepted (should not have been): DUMMY, Veeam, NaS, nAS — all saved successfully without error.

UI: Setting other in the Global Settings UI resulted in an error dialog: "Invalid backup provider plugin: other. Valid plugin values are: dummy, veeam, networker, nas"

Screencast.from.2026-02-06.12-31-19.webm

Management server log — invalid values rejected:

2026-02-06 10:29:23,422 INFO  [c.c.a.ApiServer] (qtp2038105753-23:[ctx-a7998dda, ctx-2d139540, ctx-7365cbed]) (logid:c86be57f) Invalid backup provider plugin: other. Valid plugin values are: dummy, veeam, networker, nas
2026-02-06 10:29:28,660 INFO  [c.c.a.ApiServer] (qtp2038105753-23:[ctx-f668f843, ctx-1f45af7a, ctx-73ea5470]) (logid:9282842d) Invalid backup provider plugin: invalid. Valid plugin values are: dummy, veeam, networker, nas
2026-02-06 10:29:30,157 INFO  [c.c.a.ApiServer] (qtp2038105753-23:[ctx-7bd13b92, ctx-4c89683e, ctx-d3e84807]) (logid:4a304cea) Invalid backup provider plugin: veeam2. Valid plugin values are: dummy, veeam, networker, nas
2026-02-06 10:29:45,626 INFO  [c.c.a.ApiServer] (qtp2038105753-23:[ctx-c175c52d, ctx-d5292846]) (logid:de7422c0) Invalid backup provider plugin: other. Valid plugin values are: dummy, veeam, networker, nas
2026-02-06 10:30:04,994 INFO  [c.c.a.ApiServer] (qtp2038105753-23:[ctx-5d4c23e1, ctx-fdc786d3]) (logid:2cdd9dd8) Invalid backup provider plugin: other. Valid plugin values are: dummy, veeam, networker, nas

Runtime error from case-sensitive provider lookup (after nAS was set in TC3):

2026-02-06 10:32:59,600 ERROR [o.a.c.b.B.BackupSyncTask] (BackgroundTaskPollManager-6:[ctx-b9ae99bd]) (logid:7af270ee) Error trying to run backup-sync background task due to: [Failed to find backup provider by the name: nAS]. com.cloud.utils.exception.CloudRuntimeException: Failed to find backup provider by the name: nAS

Result: FAIL (case-insensitive validation allows values that cause runtime failures)

TC4: Accept valid single providers

Objective
Verify that each valid backup provider plugin value (dummy, veeam, networker, nas) can be successfully set via both API and UI.

Test Steps

  1. Via CloudMonkey, set each valid provider:
    • update configuration name=backup.framework.provider.plugin value=dummy
    • update configuration name=backup.framework.provider.plugin value=veeam
    • update configuration name=backup.framework.provider.plugin value=networker
    • update configuration name=backup.framework.provider.plugin value=nas
  2. Verify the current value: list configurations name=backup.framework.provider.plugin
  3. In the UI, navigate to Configuration → Global Settings, set backup.framework.provider.plugin to dummy and confirm it saves without error

Expected Result:
All four valid provider values should be accepted and saved successfully without any error, both via API and UI.

Actual Result:
All four valid provider values (dummy, veeam, networker, nas) were accepted and saved successfully via CloudMonkey. Each response returned the updated configuration with the correct value. The list configurations command confirmed the final value was set to dummy. The UI also saved the value without error.

Test Evidence:
CloudMonkey output:

(localcloud) 🐱 > update configuration name=backup.framework.provider.plugin value=dummy
{
  "configuration": {
    "category": "Advanced",
    "component": "BackupService",
    "defaultvalue": "dummy",
    "description": "The backup and recovery provider plugin. Valid plugin values: dummy, veeam, networker and nas",
    "displaytext": "Backup framework provider plugin",
    "group": "Miscellaneous",
    "isdynamic": true,
    "name": "backup.framework.provider.plugin",
    "parent": "backup.framework.enabled",
    "subgroup": "Backup & Recovery",
    "type": "String",
    "value": "dummy"
  }
}
(localcloud) 🐱 > update configuration name=backup.framework.provider.plugin value=veeam
{
  "configuration": {
    ...
    "value": "veeam"
  }
}
(localcloud) 🐱 > update configuration name=backup.framework.provider.plugin value=networker
{
  "configuration": {
    ...
    "value": "networker"
  }
}
(localcloud) 🐱 > update configuration name=backup.framework.provider.plugin value=nas
{
  "configuration": {
    ...
    "value": "nas"
  }
}
(localcloud) 🐱 > list configurations name=backup.framework.provider.plugin
{
  "configuration": [
    {
      "category": "Advanced",
      "component": "BackupService",
      "defaultvalue": "dummy",
      "description": "The backup and recovery provider plugin. Valid plugin values: dummy, veeam, networker and nas",
      "displaytext": "Backup framework provider plugin",
      "group": "Miscellaneous",
      "isdynamic": true,
      "name": "backup.framework.provider.plugin",
      "parent": "backup.framework.enabled",
      "subgroup": "Backup & Recovery",
      "type": "String",
      "value": "dummy"
    }
  ],
  "count": 1
}

UI: Setting value to dummy via Global Settings saved successfully without error.

Screencast.from.2026-02-06.12-33-55.webm

Test Result: PASS

TC5: Accept null/empty handling

Objective
Verify behavior when setting backup.framework.provider.plugin to an empty value via API and UI.

Test Steps

  1. Via CloudMonkey, set an empty value:
    • update configuration name=backup.framework.provider.plugin value=
  2. In the UI, navigate to Configuration → Global Settings, clear the backup.framework.provider.plugin field and click save
  3. Check management server logs: grep -i "backup.framework.provider.plugin" /var/log/cloudstack/management/management-server.log | tail -5

Expected Result:
Empty value should either be rejected with a validation error or should reset to the default value (dummy).

Actual Result:

  • Via CloudMonkey, the empty value was accepted and saved successfully — the configuration was set to value: "". The validator did not catch this because the code checks value != null but an empty string is not null, so it passes through both the multiple-provider check and the valid-name check.
    In the UI, clearing the field and saving resulted in the default value dummy being automatically placed back in the input field.
  • The management server log shows the range validation was skipped because the value was treated as null: "Not proceeding with configuration's range validation, as its provided value is null."

Test Evidence:
CloudMonkey output:

(localcloud) 🐱 > update configuration name=backup.framework.provider.plugin value=
{
  "configuration": {
    "category": "Advanced",
    "component": "BackupService",
    "defaultvalue": "dummy",
    "description": "The backup and recovery provider plugin. Valid plugin values: dummy, veeam, networker and nas",
    "displaytext": "Backup framework provider plugin",
    "group": "Miscellaneous",
    "isdynamic": true,
    "name": "backup.framework.provider.plugin",
    "parent": "backup.framework.enabled",
    "subgroup": "Backup & Recovery",
    "type": "String",
    "value": ""
  }
}

UI: Clearing the field and saving resulted in dummy being automatically restored in the input field.

Screencast.from.2026-02-06.12-35-20.webm

Management server log:

2026-02-06 10:35:23,779 WARN  [c.c.c.ConfigurationManagerImpl] (qtp2038105753-22:[ctx-cdecebe9, ctx-c11ee58b]) (logid:c4504fef) Did not find configuration [backup.framework.provider.plugin] in Config.java. Perhaps moved to ConfigDepot.
2026-02-06 10:35:23,780 WARN  [c.c.c.ConfigurationManagerImpl] (qtp2038105753-22:[ctx-cdecebe9, ctx-c11ee58b]) (logid:c4504fef) Did not find configuration [backup.framework.provider.plugin] in Config.java. Perhaps moved to ConfigDepot.
2026-02-06 10:35:23,780 DEBUG [c.c.c.ConfigurationManagerImpl] (qtp2038105753-22:[ctx-cdecebe9, ctx-c11ee58b]) (logid:c4504fef) Not proceeding with configuration [backup.framework.provider.plugin]'s range validation, as its provided value is null.
2026-02-06 10:35:23,790 WARN  [c.c.c.ConfigurationManagerImpl] (qtp2038105753-22:[ctx-cdecebe9, ctx-c11ee58b]) (logid:c4504fef) Did not find configuration [backup.framework.provider.plugin] in Config.java. Perhaps moved to ConfigDepot.
2026-02-06 10:35:23,791 DEBUG [c.c.a.ApiServlet] (qtp2038105753-22:[ctx-cdecebe9, ctx-c11ee58b]) (logid:c4504fef) ===END===  10.0.3.251 -- GET  name=backup.framework.provider.plugin&value=&command=updateConfiguration&response=json&sessionkey=9WULZifyoB7OwDN8hK-HzRNkjNI

Note: The API accepts an empty string value because the validator checks value != null but does not check for empty/blank strings. This could be flagged as a minor improvement — the validator could additionally reject empty or blank values.

Result: PASS (with minor API observation)

TC6: Capacity dashboard loads correctly with valid provider
Objective
Verify that the Capacity dashboard loads fully and displays all stats when a valid single backup provider is configured.

Test Steps

  1. Set provider to valid value: update configuration name=backup.framework.provider.plugin value=dummy
  2. Confirm the value: list configurations name=backup.framework.provider.plugin
  3. Navigate to the Dashboard in the CloudStack UI and verify all capacity sections load
  4. Check CapacityChecker logs: grep -i "CapacityChecker" /var/log/cloudstack/management/management-server.log | tail -10
  5. Check for backup provider errors: grep -i "Failed to find backup provider" /var/log/cloudstack/management/management-server.log | tail -5

Expected Result:
Dashboard should display all sections fully — Infrastructure, Compute (Memory, CPU, CPU cores, GPU), Storage, Network, Alerts, Events. No CapacityChecker exceptions in the logs.

Actual Result:
Dashboard loaded fully with all capacity sections displaying correctly: Memory (11.11%, 1.50 GiB / 13.50 GiB), CPU (3.97%, 1.00 GHz / 25.20 GHz), CPU cores (33.33%, 2/6), GPU (0%), Storage (55.36%), Network (VLAN/VNI, Public IPs, Management IPs). Infrastructure, Alerts, and Events sections all populated. CapacityChecker logs show successful execution with no exceptions.

One observation: the backup provider error log shows Failed to find backup provider by the name: nAS - this is a residual error from TC3 where the case variation nAS was accepted by the validator but rejected at runtime by the provider lookup (backupProvidersMap). This indicates a mismatch: the validator uses .toLowerCase() to accept case-insensitive values, but the provider map lookup is case-sensitive. This should be flagged as a bug in the PR review.

Test Evidence:
Dashboard : All sections loaded - Infrastructure (1 Pod, 1 Cluster, 2 Hosts, 0 Alerts, 2 Primary Storage, 2 System VMs, 0 Virtual Routers, 0 Instances), Compute capacity (Memory, CPU, CPU cores, GPU), Storage capacity (Primary used, Primary allocated, Secondary), Network (VLAN/VNI, Public IPs, Management IPs), Alerts, and Events all fully visible.

screencapture-10-0-34-75-8080-client-2026-02-06-12_38_45

CloudMonkey output:

(localcloud) 🐱 > update configuration name=backup.framework.provider.plugin value=dummy
{
  "configuration": {
    ...
    "value": "dummy"
  }
}
(localcloud) 🐱 > list configurations name=backup.framework.provider.plugin
{
  "configuration": [
    {
      ...
      "value": "dummy"
    }
  ],
  "count": 1
}

CapacityChecker log (no errors):

2026-02-06 10:33:23,280 DEBUG [c.c.a.AlertManagerImpl] (CapacityChecker:[ctx-d5751819]) (logid:d7b911cd) Executing cpu/ram capacity update
2026-02-06 10:33:23,299 DEBUG [c.c.a.AlertManagerImpl] (CapacityChecker:[ctx-d5751819]) (logid:d7b911cd) Done executing cpu/ram capacity update
2026-02-06 10:33:23,299 DEBUG [c.c.a.AlertManagerImpl] (CapacityChecker:[ctx-d5751819]) (logid:d7b911cd) Executing storage capacity update
2026-02-06 10:33:23,311 DEBUG [c.c.a.AlertManagerImpl] (CapacityChecker:[ctx-d5751819]) (logid:d7b911cd) Done executing storage capacity update
2026-02-06 10:33:23,311 DEBUG [c.c.a.AlertManagerImpl] (CapacityChecker:[ctx-d5751819]) (logid:d7b911cd) Executing capacity updates for public ip and Vlans
2026-02-06 10:33:23,317 DEBUG [c.c.a.AlertManagerImpl] (CapacityChecker:[ctx-d5751819]) (logid:d7b911cd) Done capacity updates for public ip and Vlans
2026-02-06 10:33:23,317 DEBUG [c.c.a.AlertManagerImpl] (CapacityChecker:[ctx-d5751819]) (logid:d7b911cd) Executing capacity updates for private ip
2026-02-06 10:33:23,320 DEBUG [c.c.a.AlertManagerImpl] (CapacityChecker:[ctx-d5751819]) (logid:d7b911cd) Done executing capacity updates for private ip
2026-02-06 10:33:23,320 DEBUG [c.c.a.AlertManagerImpl] (CapacityChecker:[ctx-d5751819]) (logid:d7b911cd) Done recalculating system capacity
2026-02-06 10:33:23,335 DEBUG [c.c.a.AlertManagerImpl] (CapacityChecker:[ctx-d5751819]) (logid:d7b911cd) Done running Capacity Checker ...

Backup provider error log (residual from TC3 case-sensitivity test):

2026-02-06 10:32:59,600 ERROR [o.a.c.b.B.BackupSyncTask] (BackgroundTaskPollManager-6:[ctx-b9ae99bd]) (logid:7af270ee) Error trying to run backup-sync background task due to: [Failed to find backup provider by the name: nAS]. com.cloud.utils.exception.CloudRuntimeException: Failed to find backup provider by the name: nAS

Result: PASS

TC7: Capacity dashboard after MS restart

Objective
Verify that the Capacity dashboard loads correctly after a management server restart with a valid backup provider configured.

Test Steps

  1. Confirm backup.framework.provider.plugin is set to dummy
  2. Restart the management server: systemctl restart cloudstack-management
  3. Wait for the management server to come back up
  4. Navigate to the Dashboard in the CloudStack UI and verify all capacity sections load
  5. Check CapacityChecker logs: grep -i "CapacityChecker" /var/log/cloudstack/management/management-server.log | tail -10
  6. Check for backup provider errors: grep -i "Failed to find backup provider" /var/log/cloudstack/management/management-server.log | tail -5

Expected Result:
After management server restart, the dashboard should load fully with all capacity sections displayed. No new CapacityChecker exceptions or backup provider errors in the logs.

Actual Result:
Dashboard loaded fully after MS restart with all sections displaying correctly: Memory (11.11%, 1.50 GiB / 13.50 GiB), CPU (3.97%, 1.00 GHz / 25.20 GHz), CPU cores (33.33%, 2/6), GPU (0%), Storage (Primary used 54.99%, Primary allocated 0.00%, Secondary 54.99%), Network (VLAN/VNI, Public IPs, Management IPs), Alerts, and Events. CapacityChecker logs show successful execution with no exceptions after restart. The only "Failed to find backup provider" error in the logs is from 10:32 (pre-restart, residual from TC3), with no new occurrences after the restart at 11:33.

Test Evidence:
Dashboard: All sections loaded correctly after MS restart — Infrastructure (1 Pod, 1 Cluster, 2 Hosts, 0 Alerts, 2 Primary Storage, 2 System VMs, 0 Virtual Routers, 0 Instances), Compute capacity, Storage capacity, Network, Alerts (showing "Management server node 10.0.34.75 is up" at 06 Feb 2026 11:33:16 confirming fresh restart), and Events.

screencapture-10-0-34-75-8080-client-2026-02-06-13_34_05

CapacityChecker log (post-restart, no errors):

2026-02-06 11:33:43,240 DEBUG [c.c.a.AlertManagerImpl] (CapacityChecker:[ctx-df4c02f9]) (logid:398dd079) Executing cpu/ram capacity update
2026-02-06 11:33:43,268 DEBUG [c.c.a.AlertManagerImpl] (CapacityChecker:[ctx-df4c02f9]) (logid:398dd079) Done executing cpu/ram capacity update
2026-02-06 11:33:43,268 DEBUG [c.c.a.AlertManagerImpl] (CapacityChecker:[ctx-df4c02f9]) (logid:398dd079) Executing storage capacity update
2026-02-06 11:33:43,297 DEBUG [c.c.a.AlertManagerImpl] (CapacityChecker:[ctx-df4c02f9]) (logid:398dd079) Done executing storage capacity update
2026-02-06 11:33:43,297 DEBUG [c.c.a.AlertManagerImpl] (CapacityChecker:[ctx-df4c02f9]) (logid:398dd079) Executing capacity updates for public ip and Vlans
2026-02-06 11:33:43,310 DEBUG [c.c.a.AlertManagerImpl] (CapacityChecker:[ctx-df4c02f9]) (logid:398dd079) Done capacity updates for public ip and Vlans
2026-02-06 11:33:43,310 DEBUG [c.c.a.AlertManagerImpl] (CapacityChecker:[ctx-df4c02f9]) (logid:398dd079) Executing capacity updates for private ip
2026-02-06 11:33:43,314 DEBUG [c.c.a.AlertManagerImpl] (CapacityChecker:[ctx-df4c02f9]) (logid:398dd079) Done executing capacity updates for private ip
2026-02-06 11:33:43,314 DEBUG [c.c.a.AlertManagerImpl] (CapacityChecker:[ctx-df4c02f9]) (logid:398dd079) Done recalculating system capacity
2026-02-06 11:33:43,345 DEBUG [c.c.a.AlertManagerImpl] (CapacityChecker:[ctx-df4c02f9]) (logid:398dd079) Done running Capacity Checker ...

Backup provider error log (only pre-restart residual from TC3, no new errors):

2026-02-06 10:32:59,600 ERROR [o.a.c.b.B.BackupSyncTask] (BackgroundTaskPollManager-6:[ctx-b9ae99bd]) (logid:7af270ee) Error trying to run backup-sync background task due to: [Failed to find backup provider by the name: nAS]. com.cloud.utils.exception.CloudRuntimeException: Failed to find backup provider by the name: nAS

Test Result: PASS

TC9: Zone-scoped setting

Objective
Verify that the backup provider plugin validation works correctly when setting the configuration at zone scope, accepting valid values and rejecting invalid and multiple provider values.

Test Steps

  1. Get the zone ID: list zones
  2. Set a valid provider at zone scope:
    • update configuration name=backup.framework.provider.plugin value=nas zoneid=75df9f33-2957-48ef-aea2-ee0c258a687d
  3. Attempt to set comma-separated providers at zone scope:
    • update configuration name=backup.framework.provider.plugin value=dummy,nas zoneid=75df9f33-2957-48ef-aea2-ee0c258a687d
  4. Attempt to set an invalid provider at zone scope:
    • update configuration name=backup.framework.provider.plugin value=invalid zoneid=75df9f33-2957-48ef-aea2-ee0c258a687d
  5. Verify the zone-level value:
    • list configurations name=backup.framework.provider.plugin zoneid=75df9f33-2957-48ef-aea2-ee0c258a687d

Expected Result:
Valid single provider should be accepted at zone scope. Multiple providers and invalid provider names should be rejected with the same error messages as global scope.

Actual Result:
Valid provider nas was accepted at zone scope and saved successfully with scope: zone. Comma-separated value dummy,nas was rejected with "Multiple backup provider plugins are not supported." Invalid value invalid was rejected with "Invalid backup provider plugin: invalid." The list configurations with zone ID confirmed the value was set to nas at zone scope. All validation behaves identically at zone scope as at global scope.

Test Evidence:
CloudMonkey output:

(localcloud) 🐱 > update configuration name=backup.framework.provider.plugin value=nas zoneid=75df9f33-2957-48ef-aea2-ee0c258a687d
{
  "configuration": {
    "category": "Advanced",
    "component": "BackupService",
    "defaultvalue": "dummy",
    "description": "The backup and recovery provider plugin. Valid plugin values: dummy, veeam, networker and nas",
    "displaytext": "Backup framework provider plugin",
    "group": "Miscellaneous",
    "isdynamic": true,
    "name": "backup.framework.provider.plugin",
    "parent": "backup.framework.enabled",
    "scope": "zone",
    "subgroup": "Backup & Recovery",
    "type": "String",
    "value": "nas"
  }
}
(localcloud) 🐱 > update configuration name=backup.framework.provider.plugin value=dummy,nas zoneid=75df9f33-2957-48ef-aea2-ee0c258a687d
🙈 Error: (HTTP 431, error code 9999) Multiple backup provider plugins are not supported. Please provide a single plugin value.
(localcloud) 🐱 > update configuration name=backup.framework.provider.plugin value=invalid zoneid=75df9f33-2957-48ef-aea2-ee0c258a687d
🙈 Error: (HTTP 431, error code 9999) Invalid backup provider plugin: invalid. Valid plugin values are: dummy, veeam, networker, nas
(localcloud) 🐱 > list configurations name=backup.framework.provider.plugin zoneid=75df9f33-2957-48ef-aea2-ee0c258a687d
{
  "configuration": [
    {
      ...
      "scope": "zone",
      "value": "nas"
    }
  ],
  "count": 1
}

Test Result: PASS

TC10: Verify BackupManagerImpl indentation fix

Objective
Verify that the whitespace-only change in BackupManagerImpl.getBackupProvider() did not break the backup provider lookup functionality.

Test Steps

  1. Set provider to dummy at zone scope:
    • update configuration name=backup.framework.provider.plugin value=dummy zoneid=75df9f33-2957-48ef-aea2-ee0c258a687d
  2. Execute a backup-related API call that triggers provider lookup:
    • list backupofferings zoneid=75df9f33-2957-48ef-aea2-ee0c258a687d
  3. Check for any new backup provider errors:
    • grep -i "Failed to find backup provider" /var/log/cloudstack/management/management-server.log | grep "$(date +%Y-%m-%d)" | grep -v "nAS" | tail -5

Expected Result:
Backup provider lookup should work without errors. Backup-related API calls should execute successfully (returning results or empty set, but no exceptions).

Actual Result:
The provider was set to dummy successfully. The list backupofferings command returned an empty set (no offerings configured) but executed without errors, confirming the provider lookup in getBackupProvider() worked correctly. No new "Failed to find backup provider" errors were found in the logs (excluding the pre-existing nAS error from TC3).

Test Evidence:
CloudMonkey output:

(localcloud) 🐱 > update configuration name=backup.framework.provider.plugin value=dummy zoneid=75df9f33-2957-48ef-aea2-ee0c258a687d
{
  "configuration": {
    "category": "Advanced",
    "component": "BackupService",
    "defaultvalue": "dummy",
    "description": "The backup and recovery provider plugin. Valid plugin values: dummy, veeam, networker and nas",
    "displaytext": "Backup framework provider plugin",
    "group": "Miscellaneous",
    "isdynamic": true,
    "name": "backup.framework.provider.plugin",
    "parent": "backup.framework.enabled",
    "scope": "zone",
    "subgroup": "Backup & Recovery",
    "type": "String",
    "value": "dummy"
  }
}
(localcloud) 🐱 > list backupofferings zoneid=75df9f33-2957-48ef-aea2-ee0c258a687d
(empty result — no offerings configured, no errors)

Log check (no new backup provider errors):

[root@ref-trl-10868-k-Mol9-rositsa-kyuchukova-mgmt1 ~]# grep -i "Failed to find backup provider" /var/log/cloudstack/management/management-server.log | grep "$(date +%Y-%m-%d)" | grep -v "nAS" | tail -5
(no output — no new errors)

Test Result: PASS

@Pearl1594 Pearl1594 requested a review from RosiKyu February 6, 2026 20:57
@sonarqubecloud
Copy link

sonarqubecloud bot commented Feb 6, 2026

Quality Gate Failed Quality Gate failed

Failed conditions
0.0% Coverage on New Code (required ≥ 40%)
C Maintainability Rating on New Code (required ≥ B)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Capacity dashboard doesn't load if incorrect backup.framework.provider.plugin value is given

7 participants