From 2bd1dc1e149777ac42fb077bffb64bfa56c68ac4 Mon Sep 17 00:00:00 2001 From: Rakesh Date: Mon, 3 Jan 2022 21:55:30 +0100 Subject: [PATCH] Enable resetting config values to default value (#4230) * Enable resetting config values to default value Provide reset button to zone,cluster,domain,account, primary and secondary storage so that config values can be reset to default value * fix ui issue * Update test/integration/smoke/test_reset_configuration_settings.py * Update test/integration/smoke/test_reset_configuration_settings.py Co-authored-by: Rakesh Venkatesh Co-authored-by: dahn --- .../configuration/ConfigurationService.java | 10 + .../apache/cloudstack/api/ApiConstants.java | 1 + .../api/command/admin/config/ResetCfgCmd.java | 166 ++++++++ .../com/cloud/dc/ClusterDetailsDaoImpl.java | 28 +- .../ConfigurationManagerImpl.java | 204 +++++++++- .../cloud/server/ManagementServerImpl.java | 2 + .../vpc/MockConfigurationManagerImpl.java | 7 + .../test_reset_configuration_settings.py | 367 ++++++++++++++++++ tools/marvin/marvin/lib/base.py | 20 + ui/public/locales/en.json | 2 + ui/src/components/view/ListView.vue | 23 ++ ui/src/components/view/SettingsTab.vue | 28 ++ 12 files changed, 849 insertions(+), 9 deletions(-) create mode 100644 api/src/main/java/org/apache/cloudstack/api/command/admin/config/ResetCfgCmd.java create mode 100644 test/integration/smoke/test_reset_configuration_settings.py diff --git a/api/src/main/java/com/cloud/configuration/ConfigurationService.java b/api/src/main/java/com/cloud/configuration/ConfigurationService.java index 38d9d3243cf..87d2d2feaf1 100644 --- a/api/src/main/java/com/cloud/configuration/ConfigurationService.java +++ b/api/src/main/java/com/cloud/configuration/ConfigurationService.java @@ -18,6 +18,7 @@ package com.cloud.configuration; import java.util.List; +import org.apache.cloudstack.api.command.admin.config.ResetCfgCmd; import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd; import org.apache.cloudstack.api.command.admin.network.CreateManagementNetworkIpRangeCmd; import org.apache.cloudstack.api.command.admin.network.CreateNetworkOfferingCmd; @@ -76,6 +77,15 @@ public interface ConfigurationService { */ Configuration updateConfiguration(UpdateCfgCmd cmd) throws InvalidParameterValueException; + /** + * Resets a configuration entry with default value + * + * @param cmd + * - the command wrapping name parameter + * @return updated configuration object if successful + */ + Pair resetConfiguration(ResetCfgCmd cmd) throws InvalidParameterValueException; + /** * Create a service offering through the API * 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 b19819c36f1..b9a809f9956 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -714,6 +714,7 @@ public class ApiConstants { public static final String VM_SNAPSHOT_MEMORY = "snapshotmemory"; public static final String VM_SNAPSHOT_QUIESCEVM = "quiescevm"; public static final String IMAGE_STORE_UUID = "imagestoreuuid"; + public static final String IMAGE_STORE_ID = "imagestoreid"; public static final String GUEST_VM_CIDR = "guestvmcidr"; public static final String NETWORK_CIDR = "networkcidr"; public static final String RESERVED_IP_RANGE = "reservediprange"; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/config/ResetCfgCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/config/ResetCfgCmd.java new file mode 100644 index 00000000000..28a69cebc1e --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/config/ResetCfgCmd.java @@ -0,0 +1,166 @@ +// 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.command.admin.config; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiArgValidator; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.ImageStoreResponse; +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.response.AccountResponse; +import org.apache.cloudstack.api.response.ClusterResponse; +import org.apache.cloudstack.api.response.ConfigurationResponse; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.StoragePoolResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.config.Configuration; + +import com.cloud.user.Account; +import com.cloud.utils.Pair; + +@APICommand(name = "resetConfiguration", description = "Resets a configuration. The configuration will be set to default value for global setting, and removed from account_details or domain_details for Account/Domain settings", responseObject = ConfigurationResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.16.0") +public class ResetCfgCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(ResetCfgCmd.class.getName()); + private static final String s_name = "resetconfigurationresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "the name of the configuration", validations = {ApiArgValidator.NotNullOrEmpty}) + private String cfgName; + + @Parameter(name = ApiConstants.ZONE_ID, + type = CommandType.UUID, + entityType = ZoneResponse.class, + description = "the ID of the Zone to reset the parameter value for corresponding zone") + private Long zoneId; + + @Parameter(name = ApiConstants.CLUSTER_ID, + type = CommandType.UUID, + entityType = ClusterResponse.class, + description = "the ID of the Cluster to reset the parameter value for corresponding cluster") + private Long clusterId; + + @Parameter(name = ApiConstants.STORAGE_ID, + type = CommandType.UUID, + entityType = StoragePoolResponse.class, + description = "the ID of the Storage pool to reset the parameter value for corresponding storage pool") + private Long storagePoolId; + + @Parameter(name = ApiConstants.DOMAIN_ID, + type = CommandType.UUID, + entityType = DomainResponse.class, + description = "the ID of the Domain to reset the parameter value for corresponding domain") + private Long domainId; + + @Parameter(name = ApiConstants.ACCOUNT_ID, + type = CommandType.UUID, + entityType = AccountResponse.class, + description = "the ID of the Account to reset the parameter value for corresponding account") + private Long accountId; + + @Parameter(name = ApiConstants.IMAGE_STORE_ID, + type = CommandType.UUID, + entityType = ImageStoreResponse.class, + description = "the ID of the Image Store to reset the parameter value for corresponding image store") + private Long imageStoreId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public String getCfgName() { + return cfgName; + } + + public Long getZoneId() { + return zoneId; + } + + public Long getClusterId() { + return clusterId; + } + + public Long getStoragepoolId() { + return storagePoolId; + } + + public Long getDomainId() { + return domainId; + } + + public Long getAccountId() { + return accountId; + } + + public Long getImageStoreId() { + return imageStoreId; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute() { + Pair cfg = _configService.resetConfiguration(this); + if (cfg != null) { + ConfigurationResponse response = _responseGenerator.createConfigurationResponse(cfg.first()); + response.setResponseName(getCommandName()); + if (getZoneId() != null) { + response.setScope(ConfigKey.Scope.Zone.name()); + } + if (getClusterId() != null) { + response.setScope(ConfigKey.Scope.Cluster.name()); + } + if (getStoragepoolId() != null) { + response.setScope(ConfigKey.Scope.StoragePool.name()); + } + if (getDomainId() != null) { + response.setScope(ConfigKey.Scope.Domain.name()); + } + if (getAccountId() != null) { + response.setScope(ConfigKey.Scope.Account.name()); + } + if (getImageStoreId() != null) { + response.setScope(ConfigKey.Scope.ImageStore.name()); + } + response.setValue(cfg.second()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to reset config"); + } + } +} diff --git a/engine/schema/src/main/java/com/cloud/dc/ClusterDetailsDaoImpl.java b/engine/schema/src/main/java/com/cloud/dc/ClusterDetailsDaoImpl.java index a9323229594..c2058ad5644 100644 --- a/engine/schema/src/main/java/com/cloud/dc/ClusterDetailsDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/dc/ClusterDetailsDaoImpl.java @@ -35,6 +35,11 @@ public class ClusterDetailsDaoImpl extends GenericDaoBase ClusterSearch; protected final SearchBuilder DetailSearch; + private final String CpuOverprovisioningFactor = "cpu.overprovisioning.factor"; + private final String MemoryOverprovisioningFactor = "mem.overprovisioning.factor"; + private final String CpuOverCommitRatio = "cpuOvercommitRatio"; + private final String MemoryOverCommitRatio = "memoryOvercommitRatio"; + protected ClusterDetailsDaoImpl() { ClusterSearch = createSearchBuilder(); ClusterSearch.and("clusterId", ClusterSearch.entity().getClusterId(), SearchCriteria.Op.EQ); @@ -50,12 +55,7 @@ public class ClusterDetailsDaoImpl extends GenericDaoBase sc = DetailSearch.create(); // This is temporary fix to support list/update configuration api for cpu and memory overprovisioning ratios - if (name.equalsIgnoreCase("cpu.overprovisioning.factor")) { - name = "cpuOvercommitRatio"; - } - if (name.equalsIgnoreCase("mem.overprovisioning.factor")) { - name = "memoryOvercommitRatio"; - } + name = getCpuMemoryOvercommitRatio(name); sc.setParameters("clusterId", clusterId); sc.setParameters("name", name); @@ -103,11 +103,13 @@ public class ClusterDetailsDaoImpl extends GenericDaoBase detail : details.entrySet()) { + String name = detail.getKey(); + name = getCpuMemoryOvercommitRatio(name); String value = detail.getValue(); if ("password".equals(detail.getKey())) { value = DBEncryptionUtil.encrypt(value); } - ClusterDetailsVO vo = new ClusterDetailsVO(clusterId, detail.getKey(), value); + ClusterDetailsVO vo = new ClusterDetailsVO(clusterId, name, value); persist(vo); } txn.commit(); @@ -115,6 +117,7 @@ public class ClusterDetailsDaoImpl extends GenericDaoBase sc = DetailSearch.create(); @@ -147,4 +150,15 @@ public class ClusterDetailsDaoImpl extends GenericDaoBase scopeMap) { + Long id = null; + int paramCount = 0; + String scope = ConfigKey.Scope.Global.toString(); + + for (var entry : scopeMap.entrySet()) { + if (entry.getValue() != null) { + id = entry.getValue(); + scope = entry.getKey(); + paramCount++; + } + } + + return new ParamCountPair(id, paramCount, scope); + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_CONFIGURATION_VALUE_EDIT, eventDescription = "resetting configuration") + public Pair resetConfiguration(final ResetCfgCmd cmd) throws InvalidParameterValueException { + final Long userId = CallContext.current().getCallingUserId(); + final String name = cmd.getCfgName(); + final Long zoneId = cmd.getZoneId(); + final Long clusterId = cmd.getClusterId(); + final Long storagepoolId = cmd.getStoragepoolId(); + final Long accountId = cmd.getAccountId(); + final Long domainId = cmd.getDomainId(); + final Long imageStoreId = cmd.getImageStoreId(); + Optional optionalValue; + final ConfigKey configKey = _configDepot.get(name); + if (configKey == null) { + s_logger.warn("Probably the component manager where configuration variable " + name + " is defined needs to implement Configurable interface"); + throw new InvalidParameterValueException("Config parameter with name " + name + " doesn't exist"); + } + String defaultValue = configKey.defaultValue(); + String category = configKey.category(); + String configScope = configKey.scope().toString(); + + String scope = ""; + Map scopeMap = new LinkedHashMap<>(); + + Long id = null; + int paramCountCheck = 0; + + scopeMap.put(ConfigKey.Scope.Zone.toString(), zoneId); + scopeMap.put(ConfigKey.Scope.Cluster.toString(), clusterId); + scopeMap.put(ConfigKey.Scope.Domain.toString(), domainId); + scopeMap.put(ConfigKey.Scope.Account.toString(), accountId); + scopeMap.put(ConfigKey.Scope.StoragePool.toString(), storagepoolId); + scopeMap.put(ConfigKey.Scope.ImageStore.toString(), imageStoreId); + + ParamCountPair paramCountPair = getParamCount(scopeMap); + id = paramCountPair.getId(); + paramCountCheck = paramCountPair.getParamCount(); + scope = paramCountPair.getScope(); + + if (paramCountCheck > 1) { + throw new InvalidParameterValueException("cannot handle multiple IDs, provide only one ID corresponding to the scope"); + } + + if (scope != null && !scope.equals(ConfigKey.Scope.Global.toString()) && !configScope.contains(scope)) { + throw new InvalidParameterValueException("Invalid scope id provided for the parameter " + name); + } + + String newValue = null; + switch (ConfigKey.Scope.valueOf(scope)) { + case Zone: + final DataCenterVO zone = _zoneDao.findById(id); + if (zone == null) { + throw new InvalidParameterValueException("unable to find zone by id " + id); + } + _dcDetailsDao.removeDetail(id, name); + optionalValue = Optional.ofNullable(configKey.valueIn(id)); + newValue = optionalValue.isPresent() ? optionalValue.get().toString() : defaultValue; + break; + + case Cluster: + final ClusterVO cluster = _clusterDao.findById(id); + if (cluster == null) { + throw new InvalidParameterValueException("unable to find cluster by id " + id); + } + ClusterDetailsVO clusterDetailsVO = _clusterDetailsDao.findDetail(id, name); + newValue = configKey.value().toString(); + if (name.equalsIgnoreCase("cpu.overprovisioning.factor") || name.equalsIgnoreCase("mem.overprovisioning.factor")) { + _clusterDetailsDao.persist(id, name, newValue); + } else if (clusterDetailsVO != null) { + _clusterDetailsDao.remove(clusterDetailsVO.getId()); + } + optionalValue = Optional.ofNullable(configKey.valueIn(id)); + newValue = optionalValue.isPresent() ? optionalValue.get().toString() : defaultValue; + break; + + case StoragePool: + final StoragePoolVO pool = _storagePoolDao.findById(id); + if (pool == null) { + throw new InvalidParameterValueException("unable to find storage pool by id " + id); + } + _storagePoolDetailsDao.removeDetail(id, name); + optionalValue = Optional.ofNullable(configKey.valueIn(id)); + newValue = optionalValue.isPresent() ? optionalValue.get().toString() : defaultValue; + break; + + case Domain: + final DomainVO domain = _domainDao.findById(id); + if (domain == null) { + throw new InvalidParameterValueException("unable to find domain by id " + id); + } + DomainDetailVO domainDetailVO = _domainDetailsDao.findDetail(id, name); + if (domainDetailVO != null) { + _domainDetailsDao.remove(domainDetailVO.getId()); + } + optionalValue = Optional.ofNullable(configKey.valueIn(id)); + newValue = optionalValue.isPresent() ? optionalValue.get().toString() : defaultValue; + break; + + case Account: + final AccountVO account = _accountDao.findById(id); + if (account == null) { + throw new InvalidParameterValueException("unable to find account by id " + id); + } + AccountDetailVO accountDetailVO = _accountDetailsDao.findDetail(id, name); + if (accountDetailVO != null) { + _accountDetailsDao.remove(accountDetailVO.getId()); + } + optionalValue = Optional.ofNullable(configKey.valueIn(id)); + newValue = optionalValue.isPresent() ? optionalValue.get().toString() : defaultValue; + break; + + case ImageStore: + final ImageStoreVO imageStoreVO = _imageStoreDao.findById(id); + if (imageStoreVO == null) { + throw new InvalidParameterValueException("unable to find the image store by id " + id); + } + ImageStoreDetailVO imageStoreDetailVO = _imageStoreDetailsDao.findDetail(id, name); + if (imageStoreDetailVO != null) { + _imageStoreDetailsDao.remove(imageStoreDetailVO.getId()); + } + optionalValue = Optional.ofNullable(configKey.valueIn(id)); + newValue = optionalValue.isPresent() ? optionalValue.get().toString() : defaultValue; + break; + + default: + if (!_configDao.update(name, category, defaultValue)) { + s_logger.error("Failed to reset configuration option, name: " + name + ", defaultValue:" + defaultValue); + throw new CloudRuntimeException("Failed to reset configuration value. Please contact Cloud Support."); + } + optionalValue = Optional.ofNullable(configKey.value()); + newValue = optionalValue.isPresent() ? optionalValue.get().toString() : defaultValue; + } + + CallContext.current().setEventDetails(" Name: " + name + " New Value: " + (name.toLowerCase().contains("password") ? "*****" : defaultValue == null ? "" : defaultValue)); + return new Pair(_configDao.findByName(name), newValue); + } + private String validateConfigurationValue(final String name, String value, final String scope) { final ConfigurationVO cfg = _configDao.findByName(name); @@ -7056,4 +7220,40 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati ENABLE_ACCOUNT_SETTINGS_FOR_DOMAIN, ENABLE_DOMAIN_SETTINGS_FOR_CHILD_DOMAIN }; } + + static class ParamCountPair { + private Long id; + private int paramCount; + private String scope; + + public ParamCountPair(Long id, int paramCount, String scope) { + this.id = id; + this.paramCount = paramCount; + this.scope = scope; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public int getParamCount() { + return paramCount; + } + + public void setParamCount(int paramCount) { + this.paramCount = paramCount; + } + + public String getScope() { + return scope; + } + + public void setScope(String scope) { + this.scope = scope; + } + } } diff --git a/server/src/main/java/com/cloud/server/ManagementServerImpl.java b/server/src/main/java/com/cloud/server/ManagementServerImpl.java index 2b79ff00be0..790a5709b0f 100644 --- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java +++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java @@ -74,6 +74,7 @@ import org.apache.cloudstack.api.command.admin.cluster.UpdateClusterCmd; import org.apache.cloudstack.api.command.admin.config.ListCfgsByCmd; import org.apache.cloudstack.api.command.admin.config.ListDeploymentPlannersCmd; import org.apache.cloudstack.api.command.admin.config.ListHypervisorCapabilitiesCmd; +import org.apache.cloudstack.api.command.admin.config.ResetCfgCmd; import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd; import org.apache.cloudstack.api.command.admin.config.UpdateHypervisorCapabilitiesCmd; import org.apache.cloudstack.api.command.admin.direct.download.RevokeTemplateDirectDownloadCertificateCmd; @@ -3009,6 +3010,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe cmdList.add(ListHypervisorCapabilitiesCmd.class); cmdList.add(UpdateCfgCmd.class); cmdList.add(UpdateHypervisorCapabilitiesCmd.class); + cmdList.add(ResetCfgCmd.class); cmdList.add(CreateDomainCmd.class); cmdList.add(DeleteDomainCmd.class); cmdList.add(ListDomainChildrenCmd.class); diff --git a/server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java b/server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java index 112d20ba847..81831e3667a 100644 --- a/server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java +++ b/server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java @@ -23,6 +23,7 @@ import java.util.Set; import javax.inject.Inject; import javax.naming.ConfigurationException; +import org.apache.cloudstack.api.command.admin.config.ResetCfgCmd; import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd; import org.apache.cloudstack.api.command.admin.network.CreateManagementNetworkIpRangeCmd; import org.apache.cloudstack.api.command.admin.network.CreateNetworkOfferingCmd; @@ -615,4 +616,10 @@ public class MockConfigurationManagerImpl extends ManagerBase implements Configu return null; } + @Override + public Pair resetConfiguration(ResetCfgCmd cmd) throws InvalidParameterValueException { + // TODO Auto-generated method stub + return null; + } + } diff --git a/test/integration/smoke/test_reset_configuration_settings.py b/test/integration/smoke/test_reset_configuration_settings.py new file mode 100644 index 00000000000..edec6e4f511 --- /dev/null +++ b/test/integration/smoke/test_reset_configuration_settings.py @@ -0,0 +1,367 @@ +# 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. + +# Import Local Modules +from nose.plugins.attrib import attr +from marvin.cloudstackTestCase import cloudstackTestCase, unittest +from marvin.lib.utils import (validateList, + cleanup_resources) +from marvin.lib.base import (Account, + Configurations, + Domain, + Cluster, + StoragePool) +from marvin.lib.common import (get_domain, + get_zone) + +class TestRestConfigurationSettings(cloudstackTestCase): + @classmethod + def setUpClass(self): + self.testClient = super( + TestRestConfigurationSettings, + self).getClsTestClient() + self.apiclient = self.testClient.getApiClient() + self.testdata = self.testClient.getParsedTestDataConfig() + + # Get Zone, Domain + self.domain = get_domain(self.apiclient) + self.zone = get_zone(self.apiclient, self.testClient.getZoneForTests()) + self._cleanup = [] + return + + @classmethod + def tearDownClass(cls): + super(TestRestConfigurationSettings, cls).tearDownClass() + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + + return + + def tearDown(self): + super(TestRestConfigurationSettings, self).tearDown() + return + + @attr(tags=["advanced", "advancedsg"], required_hardware="false") + def test_01_test_settings_for_domain(self): + """ + 1. Get the default value for the setting in domain scope + 2. Change the default value to new value + 3. Make sure updated value is same as new value + 4. Reset the config value + 5. Make sure that current value is same as default value + :return: + """ + config_name="ldap.basedn" + #1. Get default value + configs = Configurations.list( + self.apiclient, + name=config_name + ) + self.assertIsNotNone(configs, "Fail to get domain setting %s " % config_name) + + orig_value = str(configs[0].value) + new_value = "testing" + + #2. Update to new value + Configurations.update( + self.apiclient, + name=config_name, + value=new_value, + domainid=self.domain.id + ) + + configs = Configurations.list( + self.apiclient, + name=config_name, + domainid=self.domain.id + ) + self.assertIsNotNone(configs, "Fail to get domain setting %s " % config_name) + + #3. validate they are same + self.assertEqual(new_value, + str(configs[0].value), + "Failed to set new config value") + + #4. Reset the value + Configurations.reset( + self.apiclient, + name=config_name, + domainid=self.domain.id + ) + + #5. Make sure its same as original value + configs = Configurations.list( + self.apiclient, + name=config_name, + domainid=self.domain.id + ) + self.assertIsNotNone(configs, "Fail to get domain setting %s " % config_name) + + self.assertEqual(orig_value, + str(configs[0].value), + "Failed to reset the value") + + @attr(tags=["advanced", "advancedsg"], required_hardware="false") + def test_02_test_settings_for_account(self): + """ + 1. Get the default value for the setting in account scope + 2. Change the default value to new value + 3. Make sure updated value is same as new value + 4. Reset the config value + 5. Make sure that current value is same as default value + :return: + """ + accounts = Account.list( + self.apiclient, + domainid=self.domain.id, + listall=True + ) + + self.assertIsNotNone(accounts[0], + "There should be atleast 1 account in the zone") + + config_name = "enable.additional.vm.configuration" + #1. Get the default value + configs = Configurations.list( + self.apiclient, + name=config_name + ) + self.assertIsNotNone(configs, "Fail to get account setting %s " % config_name) + + orig_value = str(configs[0].value) + new_value = "true" + + Configurations.update( + self.apiclient, + name=config_name, + value=new_value, + accountid=accounts[0].id + ) + + configs = Configurations.list( + self.apiclient, + name=config_name, + accountid=accounts[0].id + ) + self.assertIsNotNone(configs, "Fail to get account setting %s " % config_name) + + self.assertEqual(new_value, + str(configs[0].value), + "Failed to set new config value") + + Configurations.reset( + self.apiclient, + name=config_name, + accountid=accounts[0].id + ) + + configs = Configurations.list( + self.apiclient, + name=config_name, + accountid=accounts[0].id + ) + self.assertIsNotNone(configs, "Fail to get account setting %s " % config_name) + + self.assertEqual(orig_value, + str(configs[0].value), + "Failed to reset the value") + + @attr(tags=["advanced", "advancedsg"], required_hardware="false") + def test_03_test_settings_for_cluster(self): + """ + 1. Get the default value for the setting in cluster scope + 2. Change the default value to new value + 3. Make sure updated value is same as new value + 4. Reset the config value + 5. Make sure that current value is same as default value + :return: + """ + cluster = Cluster.list( + self.apiclient + ) + + self.assertIsNotNone(cluster[0], + "There should be atleast 1 cluster in the zone") + + config_name = "cluster.storage.operations.exclude" + configs = Configurations.list( + self.apiclient, + name=config_name, + clusterid=cluster[0].id + ) + self.assertIsNotNone(configs, "Fail to get cluster setting %s " % config_name) + + orig_value = str(configs[0].value) + new_value = "true" + + Configurations.update( + self.apiclient, + name=config_name, + value=new_value, + clusterid=cluster[0].id + ) + + configs = Configurations.list( + self.apiclient, + name=config_name, + clusterid=cluster[0].id + ) + self.assertIsNotNone(configs, "Fail to get cluster setting %s " % config_name) + + self.assertEqual(new_value, + str(configs[0].value), + "Failed to set new config value") + + Configurations.reset( + self.apiclient, + name=config_name, + clusterid=cluster[0].id + ) + + configs = Configurations.list( + self.apiclient, + name=config_name, + clusterid=cluster[0].id + ) + self.assertIsNotNone(configs, "Fail to get cluster setting %s " % config_name) + + self.assertEqual(orig_value, + str(configs[0].value), + "Failed to reset the value") + + @attr(tags=["advanced", "advancedsg"], required_hardware="false") + def test_04_test_settings_for_storage(self): + """ + 1. Get the default value for the setting in storage scope + 2. Change the default value to new value + 3. Make sure updated value is same as new value + 4. Reset the config value + 5. Make sure that current value is same as default value + :return: + """ + storage = StoragePool.list( + self.apiclient + ) + + self.assertIsNotNone(storage[0], + "There should be atleast 1 primary storage pool in the zone") + + config_name = "vmware.create.full.clone" + configs = Configurations.list( + self.apiclient, + name=config_name, + storageid=storage[0].id + ) + self.assertIsNotNone(configs, "Fail to get storage pool setting %s " % config_name) + + orig_value = str(configs[0].value) + new_value = 'false' + + Configurations.update( + self.apiclient, + name=config_name, + value=new_value, + storageid=storage[0].id + ) + + configs = Configurations.list( + self.apiclient, + name=config_name, + storageid=storage[0].id + ) + self.assertIsNotNone(configs, "Fail to get storage pool setting %s " % config_name) + + self.assertEqual(new_value, + (configs[0].value), + "Failed to set new config value") + + Configurations.reset( + self.apiclient, + name=config_name, + storageid=storage[0].id + ) + + configs = Configurations.list( + self.apiclient, + name=config_name, + storage=storage[0].id + ) + self.assertIsNotNone(configs, "Fail to get storage pool setting %s " % config_name) + + self.assertEqual(orig_value, + (configs[0].value), + "Failed to reset the value for storage pool") + + @attr(tags=["advanced", "advancedsg"], required_hardware="false") + def test_05_test_settings_for_zone(self): + """ + 1. Get the default value for the setting in zone scope + 2. Change the default value to new value + 3. Make sure updated value is same as new value + 4. Reset the config value + 5. Make sure that current value is same as default value + :return: + """ + config_name = "enable.dynamic.scale.vm" + configs = Configurations.list( + self.apiclient, + name=config_name, + zoneid=self.zone.id + ) + self.assertIsNotNone(configs, "Fail to get zone setting %s " % config_name) + + orig_value = str(configs[0].value) + new_value = 'true' + + Configurations.update( + self.apiclient, + name=config_name, + value=new_value, + zoneid=self.zone.id + ) + + configs = Configurations.list( + self.apiclient, + name=config_name, + zoneid=self.zone.id + ) + self.assertIsNotNone(configs, "Fail to get ol setting %s " % config_name) + + self.assertEqual(new_value, + (configs[0].value), + "Failed to set new config value") + + Configurations.reset( + self.apiclient, + name=config_name, + zoneid=self.zone.id + ) + + configs = Configurations.list( + self.apiclient, + name=config_name, + zoneid=self.zone.id + ) + self.assertIsNotNone(configs, "Fail to get zone setting %s " % config_name) + + self.assertEqual(orig_value, + (configs[0].value), + "Failed to reset the value for zone") diff --git a/tools/marvin/marvin/lib/base.py b/tools/marvin/marvin/lib/base.py index b8c1138e846..910e8bd7c04 100755 --- a/tools/marvin/marvin/lib/base.py +++ b/tools/marvin/marvin/lib/base.py @@ -4273,6 +4273,26 @@ class Configurations: return (apiclient.listCapabilities(cmd)) + @classmethod + def reset(cls, apiclient, name, zoneid=None, clusterid=None, storageid=None, domainid=None, accountid=None): + """Resets the specified configuration to original value""" + + cmd = resetConfiguration.resetConfigurationCmd() + cmd.name = name + + if zoneid: + cmd.zoneid = zoneid + if clusterid: + cmd.clusterid = clusterid + if storageid: + cmd.storageid = storageid + if domainid: + cmd.domainid = domainid + if accountid: + cmd.accountid = accountid + + apiclient.resetConfiguration(cmd) + class NetScaler: """Manage external netscaler device""" diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json index 135630e8167..9dbbd854a1d 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -1863,6 +1863,7 @@ "label.reservedsystemnetmask": "Reserved system netmask", "label.reservedsystemstartip": "Start Reserved system IP", "label.reset": "Reset", +"label.reset.config.value": "Reset to default value", "label.reset.ssh.key.pair": "Reset SSH Key Pair", "label.reset.ssh.key.pair.on.vm": "Reset SSH Key Pair on VM", "label.reset.to.default": "Reset to default", @@ -2943,6 +2944,7 @@ "message.error.rados.user": "Please enter RADOS User", "message.error.remove.nic": "There was an error", "message.error.remove.secondary.ipaddress": "There was an error removing the secondary IP Address", +"message.error.reset.config": "Unable to reset config to default value", "message.error.required.input": "Please enter input", "message.error.retrieve.kubeconfig": "Unable to retrieve Kubernetes cluster config", "message.error.s3nfs.path": "Please enter S3 NFS Path", diff --git a/ui/src/components/view/ListView.vue b/ui/src/components/view/ListView.vue index 7ea3e39a13d..2665035aa89 100644 --- a/ui/src/components/view/ListView.vue +++ b/ui/src/components/view/ListView.vue @@ -348,6 +348,12 @@ v-if="editableValueKey === record.key" iconType="check-circle" iconTwoToneColor="#52c41a" /> +