From 0bb161612273b892b720d94a3e73ebdbcda054bb Mon Sep 17 00:00:00 2001 From: Pearl Dsilva Date: Thu, 29 Jan 2026 13:19:19 -0500 Subject: [PATCH 1/6] Fixes issue with loading Capacity dashboard when mulitple backup providers configured --- .../java/org/apache/cloudstack/backup/BackupManagerImpl.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 dc2677a507f0..49fdc4e68814 100644 --- a/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java @@ -972,10 +972,11 @@ public BackupProvider getBackupProvider(final String name) { if (StringUtils.isEmpty(name)) { throw new CloudRuntimeException("Invalid backup provider name provided"); } - if (!backupProvidersMap.containsKey(name)) { + String[] backupProviderNames = name.split(","); + if (!backupProvidersMap.containsKey(backupProviderNames[0])) { throw new CloudRuntimeException("Failed to find backup provider by the name: " + name); } - return backupProvidersMap.get(name); + return backupProvidersMap.get(backupProviderNames[0]); } @Override From 94ac3eae32825b4f72736dc04debaec13c1e75a4 Mon Sep 17 00:00:00 2001 From: Pearl Dsilva Date: Thu, 29 Jan 2026 16:24:10 -0500 Subject: [PATCH 2/6] Add validator for config key --- .../cloudstack/backup/BackupManager.java | 15 +++++++- .../framework/config/ValidatedConfigKey.java | 38 +++++++++++++++++++ .../ConfigurationManagerImpl.java | 7 ++++ 3 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 framework/config/src/main/java/org/apache/cloudstack/framework/config/ValidatedConfigKey.java 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 78d189c3bf1d..a92275c56ec1 100644 --- a/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java +++ b/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java @@ -26,6 +26,7 @@ import org.apache.cloudstack.api.command.user.backup.ListBackupOfferingsCmd; import org.apache.cloudstack.api.command.user.backup.ListBackupsCmd; import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.framework.config.ValidatedConfigKey; import org.apache.cloudstack.framework.config.Configurable; import com.cloud.utils.Pair; @@ -42,10 +43,20 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer "false", "Is backup and recovery framework enabled.", false, ConfigKey.Scope.Zone); - ConfigKey BackupProviderPlugin = new ConfigKey<>("Advanced", String.class, + ConfigKey BackupProviderPlugin = new ValidatedConfigKey<>("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. Valid plugin values: dummy, veeam, networker and nas", + true, ConfigKey.Scope.Zone, BackupFrameworkEnabled.key(), + (value) -> { + if (value != null && ((String)value).contains(",") || ((String)value).contains(" ")) { + throw new IllegalArgumentException("Multiple backup provider plugins are not supported. Please provide a single plugin value."); + } + List validPlugins = List.of("dummy", "veeam", "networker", "nas"); + if (!validPlugins.contains(((String) value).toLowerCase())) { + throw new IllegalArgumentException("Invalid backup provider plugin: " + value + ". Valid plugin values are: " + String.join(", ", validPlugins)); + } + }); ConfigKey BackupSyncPollingInterval = new ConfigKey<>("Advanced", Long.class, "backup.framework.sync.interval", diff --git a/framework/config/src/main/java/org/apache/cloudstack/framework/config/ValidatedConfigKey.java b/framework/config/src/main/java/org/apache/cloudstack/framework/config/ValidatedConfigKey.java new file mode 100644 index 000000000000..a5921b1a5dce --- /dev/null +++ b/framework/config/src/main/java/org/apache/cloudstack/framework/config/ValidatedConfigKey.java @@ -0,0 +1,38 @@ +// 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.framework.config; + +import java.util.function.Consumer; + +public class ValidatedConfigKey extends ConfigKey { + private final Consumer validator; + + public ValidatedConfigKey(String category, Class type, String name, String defaultValue, String description, boolean dynamic, Scope scope, String parent, Consumer validator) { + super(category, type, name, defaultValue, description, dynamic, scope); + this.validator = validator; + } + + public Consumer getValidator() { + return validator; + } + + public void validateValue(String value) { + if (validator != null) { + validator.accept((T) value); + } + } +} diff --git a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java index 3a4bdf8afec5..eb138bb10b00 100644 --- a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java @@ -107,6 +107,7 @@ import org.apache.cloudstack.framework.config.ConfigDepot; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; +import org.apache.cloudstack.framework.config.ValidatedConfigKey; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.framework.config.dao.ConfigurationGroupDao; import org.apache.cloudstack.framework.config.dao.ConfigurationSubGroupDao; @@ -716,6 +717,12 @@ public String updateConfiguration(final long userId, final String name, final St throw new InvalidParameterValueException(validationMsg); } + ConfigKey configKey = _configDepot.get(name); + if (configKey instanceof ValidatedConfigKey) { + ValidatedConfigKey validatedConfigKey = (ValidatedConfigKey) configKey; + validatedConfigKey.validateValue(value); + } + // If scope of the parameter is given then it needs to be updated in the // corresponding details table, // if scope is mentioned as global or not mentioned then it is normal From 2d146fa869f6dc753bbab93e1621d5d51e19665d Mon Sep 17 00:00:00 2001 From: Pearl Dsilva Date: Thu, 29 Jan 2026 17:29:53 -0500 Subject: [PATCH 3/6] cleanup --- .../main/java/org/apache/cloudstack/backup/BackupManager.java | 2 +- .../apache/cloudstack/framework/config/ValidatedConfigKey.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 a92275c56ec1..f9820c4818ab 100644 --- a/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java +++ b/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java @@ -48,7 +48,7 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer "dummy", "The backup and recovery provider plugin. Valid plugin values: dummy, veeam, networker and nas", true, ConfigKey.Scope.Zone, BackupFrameworkEnabled.key(), - (value) -> { + value -> { if (value != null && ((String)value).contains(",") || ((String)value).contains(" ")) { throw new IllegalArgumentException("Multiple backup provider plugins are not supported. Please provide a single plugin value."); } diff --git a/framework/config/src/main/java/org/apache/cloudstack/framework/config/ValidatedConfigKey.java b/framework/config/src/main/java/org/apache/cloudstack/framework/config/ValidatedConfigKey.java index a5921b1a5dce..6b2d3bdaa25f 100644 --- a/framework/config/src/main/java/org/apache/cloudstack/framework/config/ValidatedConfigKey.java +++ b/framework/config/src/main/java/org/apache/cloudstack/framework/config/ValidatedConfigKey.java @@ -22,7 +22,7 @@ public class ValidatedConfigKey extends ConfigKey { private final Consumer validator; public ValidatedConfigKey(String category, Class type, String name, String defaultValue, String description, boolean dynamic, Scope scope, String parent, Consumer validator) { - super(category, type, name, defaultValue, description, dynamic, scope); + super(category, type, name, defaultValue, description, dynamic, scope, parent); this.validator = validator; } From de2305222ce331f78374391dbcecce6d5971933d Mon Sep 17 00:00:00 2001 From: Pearl Dsilva Date: Thu, 29 Jan 2026 23:33:16 -0500 Subject: [PATCH 4/6] extract code --- .../cloudstack/backup/BackupManager.java | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) 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 f9820c4818ab..a56ed791f607 100644 --- a/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java +++ b/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java @@ -47,16 +47,7 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer "backup.framework.provider.plugin", "dummy", "The backup and recovery provider plugin. Valid plugin values: dummy, veeam, networker and nas", - true, ConfigKey.Scope.Zone, BackupFrameworkEnabled.key(), - value -> { - if (value != null && ((String)value).contains(",") || ((String)value).contains(" ")) { - throw new IllegalArgumentException("Multiple backup provider plugins are not supported. Please provide a single plugin value."); - } - List validPlugins = List.of("dummy", "veeam", "networker", "nas"); - if (!validPlugins.contains(((String) value).toLowerCase())) { - throw new IllegalArgumentException("Invalid backup provider plugin: " + value + ". Valid plugin values are: " + String.join(", ", validPlugins)); - } - }); + true, ConfigKey.Scope.Zone, BackupFrameworkEnabled.key(), value -> validateBackupProviderConfig((String)value)); ConfigKey BackupSyncPollingInterval = new ConfigKey<>("Advanced", Long.class, "backup.framework.sync.interval", @@ -159,4 +150,14 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer boolean deleteBackup(final Long backupId, final Boolean forced); BackupOffering updateBackupOffering(UpdateBackupOfferingCmd updateBackupOfferingCmd); + + static void validateBackupProviderConfig(String value) { + if (value != null && (value.contains(",") || value.contains(" "))) { + throw new IllegalArgumentException("Multiple backup provider plugins are not supported. Please provide a single plugin value."); + } + List validPlugins = List.of("dummy", "veeam", "networker", "nas"); + if (value != null && !validPlugins.contains(value.toLowerCase())) { + throw new IllegalArgumentException("Invalid backup provider plugin: " + value + ". Valid plugin values are: " + String.join(", ", validPlugins)); + } + } } From 174f92d6798419ade8e59a2685c8ece260a3377b Mon Sep 17 00:00:00 2001 From: Pearl Dsilva Date: Thu, 29 Jan 2026 23:41:12 -0500 Subject: [PATCH 5/6] trim spaces when checking for commas --- .../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 a56ed791f607..683027baa2fb 100644 --- a/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java +++ b/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java @@ -152,7 +152,7 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer BackupOffering updateBackupOffering(UpdateBackupOfferingCmd updateBackupOfferingCmd); static void validateBackupProviderConfig(String value) { - if (value != null && (value.contains(",") || value.contains(" "))) { + if (value != null && (value.trim().contains(",") || value.contains(" "))) { throw new IllegalArgumentException("Multiple backup provider plugins are not supported. Please provide a single plugin value."); } List validPlugins = List.of("dummy", "veeam", "networker", "nas"); From 0669ac3455e7f15d80d23aa2501347a463897a12 Mon Sep 17 00:00:00 2001 From: Pearl Dsilva Date: Fri, 30 Jan 2026 09:45:48 -0500 Subject: [PATCH 6/6] address comments --- .../main/java/org/apache/cloudstack/backup/BackupManager.java | 2 +- .../cloudstack/framework/config/ValidatedConfigKey.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) 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 683027baa2fb..9ed2df1d79b8 100644 --- a/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java +++ b/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java @@ -152,7 +152,7 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer BackupOffering updateBackupOffering(UpdateBackupOfferingCmd updateBackupOfferingCmd); static void validateBackupProviderConfig(String value) { - if (value != null && (value.trim().contains(",") || value.contains(" "))) { + 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 validPlugins = List.of("dummy", "veeam", "networker", "nas"); diff --git a/framework/config/src/main/java/org/apache/cloudstack/framework/config/ValidatedConfigKey.java b/framework/config/src/main/java/org/apache/cloudstack/framework/config/ValidatedConfigKey.java index 6b2d3bdaa25f..4fcbe4151d30 100644 --- a/framework/config/src/main/java/org/apache/cloudstack/framework/config/ValidatedConfigKey.java +++ b/framework/config/src/main/java/org/apache/cloudstack/framework/config/ValidatedConfigKey.java @@ -18,10 +18,10 @@ import java.util.function.Consumer; -public class ValidatedConfigKey extends ConfigKey { +public class ValidatedConfigKey extends ConfigKey { private final Consumer validator; - public ValidatedConfigKey(String category, Class type, String name, String defaultValue, String description, boolean dynamic, Scope scope, String parent, Consumer validator) { + public ValidatedConfigKey(String category, Class type, String name, String defaultValue, String description, boolean dynamic, Scope scope, String parent, Consumer validator) { super(category, type, name, defaultValue, description, dynamic, scope, parent); this.validator = validator; }