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 <rakeshv@apache.org>
Co-authored-by: dahn <daan.hoogland@gmail.com>
This commit is contained in:
Rakesh 2022-01-03 21:55:30 +01:00 committed by GitHub
parent e06a66ba14
commit 2bd1dc1e14
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 849 additions and 9 deletions

View File

@ -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<Configuration, String> resetConfiguration(ResetCfgCmd cmd) throws InvalidParameterValueException;
/**
* Create a service offering through the API
*

View File

@ -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";

View File

@ -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<Configuration, String> 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");
}
}
}

View File

@ -35,6 +35,11 @@ public class ClusterDetailsDaoImpl extends GenericDaoBase<ClusterDetailsVO, Long
protected final SearchBuilder<ClusterDetailsVO> ClusterSearch;
protected final SearchBuilder<ClusterDetailsVO> 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<ClusterDetailsVO, Long
public ClusterDetailsVO findDetail(long clusterId, String name) {
SearchCriteria<ClusterDetailsVO> 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<ClusterDetailsVO, Long
expunge(sc);
for (Map.Entry<String, String> 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<ClusterDetailsVO, Long
@Override
public void persist(long clusterId, String name, String value) {
name = getCpuMemoryOvercommitRatio(name);
TransactionLegacy txn = TransactionLegacy.currentTxn();
txn.start();
SearchCriteria<ClusterDetailsVO> sc = DetailSearch.create();
@ -147,4 +150,15 @@ public class ClusterDetailsDaoImpl extends GenericDaoBase<ClusterDetailsVO, Long
dcName = tokens[3];
return dcName;
}
private String getCpuMemoryOvercommitRatio(String name) {
if (name.equalsIgnoreCase(CpuOverprovisioningFactor)) {
name = CpuOverCommitRatio;
}
if (name.equalsIgnoreCase(MemoryOverprovisioningFactor)) {
name = MemoryOverCommitRatio;
}
return name;
}
}

View File

@ -30,10 +30,12 @@ import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.Vector;
@ -51,6 +53,7 @@ import org.apache.cloudstack.agent.lb.IndirectAgentLBServiceImpl;
import org.apache.cloudstack.annotation.AnnotationService;
import org.apache.cloudstack.annotation.dao.AnnotationDao;
import org.apache.cloudstack.api.ApiConstants;
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;
@ -105,6 +108,7 @@ import org.apache.cloudstack.region.dao.RegionDao;
import org.apache.cloudstack.resourcedetail.DiskOfferingDetailVO;
import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao;
import org.apache.cloudstack.storage.datastore.db.ImageStoreDao;
import org.apache.cloudstack.storage.datastore.db.ImageStoreDetailVO;
import org.apache.cloudstack.storage.datastore.db.ImageStoreDetailsDao;
import org.apache.cloudstack.storage.datastore.db.ImageStoreVO;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
@ -630,9 +634,16 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
if (cluster == null) {
throw new InvalidParameterValueException("unable to find cluster by id " + resourceId);
}
ClusterDetailsVO clusterDetailsVO = _clusterDetailsDao.findDetail(resourceId, name);
String newName = name;
if (name.equalsIgnoreCase("cpu.overprovisioning.factor")) {
newName = "cpuOvercommitRatio";
}
if (name.equalsIgnoreCase("mem.overprovisioning.factor")) {
newName = "memoryOvercommitRatio";
}
ClusterDetailsVO clusterDetailsVO = _clusterDetailsDao.findDetail(resourceId, newName);
if (clusterDetailsVO == null) {
clusterDetailsVO = new ClusterDetailsVO(resourceId, name, value);
clusterDetailsVO = new ClusterDetailsVO(resourceId, newName, value);
_clusterDetailsDao.persist(clusterDetailsVO);
} else {
clusterDetailsVO.setValue(value);
@ -879,6 +890,159 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
}
}
private ParamCountPair getParamCount(Map<String, Long> 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<Configuration, String> 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<String, Long> 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<Configuration, String>(_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;
}
}
}

View File

@ -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);

View File

@ -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<Configuration, String> resetConfiguration(ResetCfgCmd cmd) throws InvalidParameterValueException {
// TODO Auto-generated method stub
return null;
}
}

View File

@ -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")

View File

@ -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"""

View File

@ -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",

View File

@ -348,6 +348,12 @@
v-if="editableValueKey === record.key"
iconType="check-circle"
iconTwoToneColor="#52c41a" />
<tooltip-button
:tooltip="$t('label.reset.config.value')"
@click="resetConfig(record)"
v-if="editableValueKey !== record.key"
icon="reload"
:disabled="!('updateConfiguration' in $store.getters.apis)" />
</template>
<template slot="tariffActions" slot-scope="text, record">
<tooltip-button
@ -526,6 +532,23 @@ export default {
this.$emit('refresh')
})
},
resetConfig (item) {
api('resetConfiguration', {
name: item.name
}).then(() => {
const message = `${this.$t('label.setting')} ${item.name} ${this.$t('label.reset.config.value')}`
this.$message.success(message)
}).catch(error => {
console.error(error)
this.$message.error(this.$t('message.error.reset.config'))
this.$notification.error({
message: this.$t('label.error'),
description: this.$t('message.error.reset.config')
})
}).finally(() => {
this.$emit('refresh')
})
},
editValue (record) {
this.editableValueKey = record.key
this.editableValue = record.value

View File

@ -64,6 +64,12 @@
v-if="editableValueKey === index"
iconType="check-circle"
iconTwoToneColor="#52c41a" />
<tooltip-button
:tooltip="$t('label.reset.config.value')"
@click="resetConfig(item)"
v-if="editableValueKey !== index"
icon="reload"
:disabled="!('updateConfiguration' in $store.getters.apis)" />
</div>
</a-list-item>
</a-list>
@ -182,6 +188,28 @@ export default {
handleSearch (value) {
this.filter = value
this.fetchData()
},
resetConfig (item) {
this.tabLoading = true
api('resetConfiguration', {
[this.scopeKey]: this.resource.id,
name: item.name
}).then(() => {
const message = `${this.$t('label.setting')} ${item.name} ${this.$t('label.reset.config.value')}`
this.$message.success(message)
}).catch(error => {
console.error(error)
this.$message.error(this.$t('message.error.reset.config'))
this.$notification.error({
message: this.$t('label.error'),
description: this.$t('message.error.reset.config')
})
}).finally(() => {
this.tabLoading = false
this.fetchData(() => {
this.editableValueKey = null
})
})
}
}
}