mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
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:
parent
e06a66ba14
commit
2bd1dc1e14
@ -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
|
||||
*
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
367
test/integration/smoke/test_reset_configuration_settings.py
Normal file
367
test/integration/smoke/test_reset_configuration_settings.py
Normal 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")
|
||||
@ -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"""
|
||||
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user