CLOUDSTACK-741: Granular Global Parameters

Signed-off-by: Abhinandan Prateek <aprateek@apache.org>
This commit is contained in:
Harikrishna Patnala 2013-04-19 09:27:00 +05:30 committed by Abhinandan Prateek
parent 81e1ba3bb4
commit eae22d2ffa
17 changed files with 561 additions and 37 deletions

View File

@ -23,8 +23,7 @@ import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseListCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.response.ConfigurationResponse;
import org.apache.cloudstack.api.response.ListResponse;
import org.apache.cloudstack.api.response.*;
import org.apache.log4j.Logger;
import com.cloud.configuration.Configuration;
@ -46,6 +45,13 @@ public class ListCfgsByCmd extends BaseListCmd {
@Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "lists configuration by name")
private String configName;
@Parameter(name=ApiConstants.SCOPE, type = CommandType.STRING, description = "scope(zone/cluster/pool/account) of the parameter that needs to be updated")
private String scope;
@Parameter(name=ApiConstants.ID, type = CommandType.UUID, entityType = {ZoneResponse.class, ClusterResponse.class, StoragePoolResponse.class, AccountResponse.class}, description = "corresponding ID of the scope")
private Long id;
// ///////////////////////////////////////////////////
// ///////////////// Accessors ///////////////////////
// ///////////////////////////////////////////////////
@ -58,6 +64,15 @@ public class ListCfgsByCmd extends BaseListCmd {
return configName;
}
public String getScope() {
return scope;
}
public Long getId() {
return id;
}
@Override
public Long getPageSizeVal() {
Long pageSizeVal = 500L;
@ -85,6 +100,11 @@ public class ListCfgsByCmd extends BaseListCmd {
for (Configuration cfg : result.first()) {
ConfigurationResponse cfgResponse = _responseGenerator.createConfigurationResponse(cfg);
cfgResponse.setObjectName("configuration");
if (scope != null) {
cfgResponse.setScope(scope);
} else {
cfgResponse.setScope("global");
}
configResponses.add(cfgResponse);
}

View File

@ -22,7 +22,7 @@ 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.ConfigurationResponse;
import org.apache.cloudstack.api.response.*;
import org.apache.log4j.Logger;
import com.cloud.configuration.Configuration;
@ -43,6 +43,12 @@ public class UpdateCfgCmd extends BaseCmd {
@Parameter(name=ApiConstants.VALUE, type=CommandType.STRING, description="the value of the configuration", length=4095)
private String value;
@Parameter(name=ApiConstants.SCOPE, type = CommandType.STRING, description = "scope(zone/cluster/pool/account) of the parameter that needs to be updated")
private String scope;
@Parameter(name=ApiConstants.ID, type = CommandType.UUID, entityType = {ZoneResponse.class, ClusterResponse.class, StoragePoolResponse.class, AccountResponse.class}, description = "corresponding ID of the scope")
private Long id;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@ -55,6 +61,14 @@ public class UpdateCfgCmd extends BaseCmd {
return value;
}
public String getScope() {
return scope;
}
public Long getId() {
return id;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
@ -75,6 +89,12 @@ public class UpdateCfgCmd extends BaseCmd {
if (cfg != null) {
ConfigurationResponse response = _responseGenerator.createConfigurationResponse(cfg);
response.setResponseName(getCommandName());
if (scope != null) {
response.setScope(scope);
response.setValue(value);
} else {
response.setScope("global");
}
this.setResponseObject(response);
} else {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update config");

View File

@ -32,9 +32,13 @@ public class ConfigurationResponse extends BaseResponse {
@SerializedName(ApiConstants.VALUE) @Param(description="the value of the configuration")
private String value;
@SerializedName(ApiConstants.SCOPE) @Param(description="scope(zone/cluster/pool/account) of the parameter that needs to be updated")
private String scope;
@SerializedName(ApiConstants.DESCRIPTION) @Param(description="the description of the configuration")
private String description;
public String getCategory() {
return category;
}
@ -66,4 +70,12 @@ public class ConfigurationResponse extends BaseResponse {
public void setDescription(String description) {
this.description = description;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
}

View File

@ -0,0 +1,89 @@
// 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.test;
import com.cloud.configuration.Configuration;
import com.cloud.configuration.ConfigurationService;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.resource.ResourceService;
import com.cloud.server.ManagementService;
import com.cloud.utils.Pair;
import junit.framework.Assert;
import junit.framework.TestCase;
import org.apache.cloudstack.api.ResponseGenerator;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.command.admin.config.ListCfgsByCmd;
import org.apache.cloudstack.api.response.ConfigurationResponse;
import org.apache.cloudstack.api.response.ListResponse;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.Mockito;
import java.util.ArrayList;
import java.util.List;
public class ListCfgCmdTest extends TestCase{
private ListCfgsByCmd listCfgsByCmd;
private ManagementService mgr;
private ResponseGenerator responseGenerator;
@Rule
public ExpectedException expectedException = ExpectedException.none();
@Before
public void setUp() {
responseGenerator = Mockito.mock(ResponseGenerator.class);
mgr = Mockito.mock(ManagementService.class);
listCfgsByCmd = new ListCfgsByCmd();
}
@Test
public void testCreateSuccess() {
Configuration cfg = Mockito.mock(Configuration.class);
listCfgsByCmd._mgr = mgr;
listCfgsByCmd._responseGenerator = responseGenerator;
List<Configuration> configList = new ArrayList<Configuration>();
configList.add(cfg);
Pair<List<? extends Configuration>, Integer> result = new Pair<List<? extends Configuration>, Integer>(configList, 1);
try {
Mockito.when(
mgr.searchForConfigurations(listCfgsByCmd))
.thenReturn(result);
}catch (Exception e){
Assert.fail("Received exception when success expected " + e.getMessage());
}
ConfigurationResponse cfgResponse = new ConfigurationResponse();
cfgResponse.setName("Test case");
Mockito.when(responseGenerator.createConfigurationResponse(cfg)).thenReturn(cfgResponse);
listCfgsByCmd.execute();
Mockito.verify(responseGenerator).createConfigurationResponse(cfg);
ListResponse<ConfigurationResponse> actualResponse = (ListResponse<ConfigurationResponse>) listCfgsByCmd.getResponseObject();
Assert.assertEquals(cfgResponse, actualResponse.getResponses().get(0));
}
}

View File

@ -0,0 +1,116 @@
// 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.test;
import com.cloud.configuration.Configuration;
import com.cloud.configuration.ConfigurationService;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.resource.ResourceService;
import junit.framework.Assert;
import junit.framework.TestCase;
import org.apache.cloudstack.api.ResponseGenerator;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd;
import org.apache.cloudstack.api.response.ConfigurationResponse;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.Mockito;
public class UpdateCfgCmdTest extends TestCase{
private UpdateCfgCmd updateCfgCmd;
private ConfigurationService configService;
private ResponseGenerator responseGenerator;
@Rule
public ExpectedException expectedException = ExpectedException.none();
@Before
public void setUp() {
responseGenerator = Mockito.mock(ResponseGenerator.class);
configService = Mockito.mock(ConfigurationService.class);
updateCfgCmd = new UpdateCfgCmd();
}
@Test
public void testExecuteForEmptyResult() {
updateCfgCmd._configService = configService;
try {
updateCfgCmd.execute();
} catch (ServerApiException exception) {
Assert.assertEquals("Failed to update config",
exception.getDescription());
}
}
@Test
public void testExecuteForNullResult() {
updateCfgCmd._configService = configService;
try {
Mockito.when(
configService.updateConfiguration(updateCfgCmd))
.thenReturn(null);
} catch (InvalidParameterValueException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
updateCfgCmd.execute();
} catch (ServerApiException exception) {
Assert.assertEquals("Failed to update config",
exception.getDescription());
}
}
@Test
public void testCreateSuccess() {
Configuration cfg = Mockito.mock(Configuration.class);
updateCfgCmd._configService = configService;
updateCfgCmd._responseGenerator = responseGenerator;
try {
Mockito.when(
configService.updateConfiguration(updateCfgCmd))
.thenReturn(cfg);
}catch (Exception e){
Assert.fail("Received exception when success expected " + e.getMessage());
}
ConfigurationResponse response = new ConfigurationResponse();
response.setName("Test case");
Mockito.when(responseGenerator.createConfigurationResponse(cfg)).thenReturn(response);
updateCfgCmd.execute();
Mockito.verify(responseGenerator).createConfigurationResponse(cfg);
ConfigurationResponse actualResponse = (ConfigurationResponse) updateCfgCmd.getResponseObject();
Assert.assertEquals(response, actualResponse);
Assert.assertEquals("updateconfigurationresponse", response.getResponseName());
}
}

View File

@ -25,4 +25,5 @@ public interface StoragePoolDetailsDao extends GenericDao<StoragePoolDetailVO, L
void update(long poolId, Map<String, String> details);
Map<String, String> getDetails(long poolId);
StoragePoolDetailVO findDetail(long poolId, String name);
}

View File

@ -16,9 +16,7 @@
// under the License.
package com.cloud.configuration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.*;
import org.apache.cloudstack.engine.subsystem.api.storage.StoragePoolAllocator;
@ -339,7 +337,7 @@ public enum Config {
//disabling lb as cluster sync does not work with distributed cluster
AgentLbEnable("Advanced", ManagementServer.class, Boolean.class, "agent.lb.enabled", "false", "If agent load balancing enabled in cluster setup", null),
SubDomainNetworkAccess("Advanced", NetworkManager.class, Boolean.class, "allow.subdomain.network.access", "true", "Allow subdomains to use networks dedicated to their parent domain(s)", null),
UseExternalDnsServers("Advanced", NetworkManager.class, Boolean.class, "use.external.dns", "false", "Bypass internal dns, use external dns1 and dns2", null),
UseExternalDnsServers("Advanced", NetworkManager.class, Boolean.class, "use.external.dns", "false", "Bypass internal dns, use external dns1 and dns2", null, ConfigurationParameterScope.zone.toString()),
EncodeApiResponse("Advanced", ManagementServer.class, Boolean.class, "encode.api.response", "false", "Do URL encoding for the api response, false by default", null),
DnsBasicZoneUpdates("Advanced", NetworkManager.class, String.class, "network.dns.basiczone.updates", "all", "This parameter can take 2 values: all (default) and pod. It defines if DHCP/DNS requests have to be send to all dhcp servers in cloudstack, or only to the one in the same pod", "all,pod"),
@ -412,6 +410,35 @@ public enum Config {
private final String _defaultValue;
private final String _description;
private final String _range;
private final String _scope; // Parameter can be at different levels (Zone/cluster/pool/account), by default every parameter is at global
public static enum ConfigurationParameterScope {
global,
zone,
cluster,
pool,
account
}
private static final HashMap<String, List<Config>> _scopeLevelConfigsMap = new HashMap<String, List<Config>>();
static {
_scopeLevelConfigsMap.put(ConfigurationParameterScope.zone.toString(), new ArrayList<Config>());
_scopeLevelConfigsMap.put(ConfigurationParameterScope.cluster.toString(), new ArrayList<Config>());
_scopeLevelConfigsMap.put(ConfigurationParameterScope.pool.toString(), new ArrayList<Config>());
_scopeLevelConfigsMap.put(ConfigurationParameterScope.account.toString(), new ArrayList<Config>());
_scopeLevelConfigsMap.put(ConfigurationParameterScope.global.toString(), new ArrayList<Config>());
for (Config c : Config.values()) {
//Creating group of parameters per each level (zone/cluster/pool/account)
StringTokenizer tokens = new StringTokenizer(c.getScope(), ",");
while (tokens.hasMoreTokens()) {
String scope = tokens.nextToken().trim();
List<Config> currentConfigs = _scopeLevelConfigsMap.get(scope);
currentConfigs.add(c);
_scopeLevelConfigsMap.put(scope, currentConfigs);
}
}
}
private static final HashMap<String, List<Config>> _configs = new HashMap<String, List<Config>>();
static {
@ -447,6 +474,17 @@ public enum Config {
_defaultValue = defaultValue;
_description = description;
_range = range;
_scope = ConfigurationParameterScope.global.toString();
}
private Config(String category, Class<?> componentClass, Class<?> type, String name, String defaultValue, String description, String range, String scope) {
_category = category;
_componentClass = componentClass;
_type = type;
_name = name;
_defaultValue = defaultValue;
_description = description;
_range = range;
_scope = scope;
}
public String getCategory() {
@ -473,6 +511,10 @@ public enum Config {
return _componentClass;
}
public String getScope() {
return _scope;
}
public String getComponent() {
if (_componentClass == ManagementServer.class) {
return "management-server";
@ -530,4 +572,8 @@ public enum Config {
}
return categories;
}
public static List<Config> getConfigListByScope(String scope) {
return _scopeLevelConfigsMap.get(scope);
}
}

View File

@ -60,7 +60,7 @@ public interface ConfigurationManager extends ConfigurationService, Manager {
* @param name
* @param value
*/
void updateConfiguration(long userId, String name, String category, String value);
void updateConfiguration(long userId, String name, String category, String value, String scope, Long id);
/**
* Creates a new service offering

View File

@ -39,6 +39,7 @@ import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import com.cloud.dc.dao.*;
import org.apache.cloudstack.acl.SecurityChecker;
import org.apache.cloudstack.api.ApiConstants.LDAPParams;
import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd;
@ -78,20 +79,13 @@ import com.cloud.dc.DataCenter.NetworkType;
import com.cloud.dc.DataCenterIpAddressVO;
import com.cloud.dc.DataCenterLinkLocalIpAddressVO;
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.DcDetailVO;
import com.cloud.dc.HostPodVO;
import com.cloud.dc.Pod;
import com.cloud.dc.PodVlanMapVO;
import com.cloud.dc.Vlan;
import com.cloud.dc.Vlan.VlanType;
import com.cloud.dc.VlanVO;
import com.cloud.dc.dao.AccountVlanMapDao;
import com.cloud.dc.dao.ClusterDao;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.dc.dao.DataCenterIpAddressDao;
import com.cloud.dc.dao.DataCenterLinkLocalIpAddressDao;
import com.cloud.dc.dao.HostPodDao;
import com.cloud.dc.dao.PodVlanMapDao;
import com.cloud.dc.dao.VlanDao;
import com.cloud.deploy.DataCenterDeployment;
import com.cloud.domain.Domain;
import com.cloud.domain.DomainVO;
@ -188,6 +182,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
@Inject
DataCenterDao _zoneDao;
@Inject
DcDetailsDao _zoneDetailsDao;
@Inject
DomainDao _domainDao;
@Inject
SwiftDao _swiftDao;
@ -327,15 +323,37 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
@Override
@DB
public void updateConfiguration(long userId, String name, String category, String value) {
public void updateConfiguration(long userId, String name, String category, String value, String scope, Long resourceId) {
String validationMsg = validateConfigurationValue(name, value);
String validationMsg = validateConfigurationValue(name, value, scope);
if (validationMsg != null) {
s_logger.error("Invalid configuration option, name: " + name + ", value:" + value);
throw new InvalidParameterValueException(validationMsg);
}
// If scope of the parameter is given then it needs to be updated in the corresponding details table,
// if scope is mentioned as global or not mentioned then it is normal global parameter updation
if (scope != null && !scope.isEmpty() && !Config.ConfigurationParameterScope.global.toString().equalsIgnoreCase(scope)) {
if (Config.ConfigurationParameterScope.zone.toString().equalsIgnoreCase(scope)) {
DataCenterVO zone = _zoneDao.findById(resourceId);
if (zone == null) {
throw new InvalidParameterValueException("unable to find zone by id " + resourceId);
}
DcDetailVO dcDetailVO = _zoneDetailsDao.findDetail(resourceId, name.toLowerCase());
if (dcDetailVO == null) {
dcDetailVO = new DcDetailVO(dcDetailVO.getId(), name, value);
_zoneDetailsDao.persist(dcDetailVO);
} else {
dcDetailVO.setValue(value);
_zoneDetailsDao.update(resourceId, dcDetailVO);
}
} else {
s_logger.error("TO Do for the remaining levels (cluster/pool/account)");
throw new InvalidParameterValueException("The scope "+ scope +" yet to be implemented");
}
}
// Execute all updates in a single transaction
Transaction txn = Transaction.currentTxn();
txn.start();
@ -440,6 +458,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
Long userId = UserContext.current().getCallerUserId();
String name = cmd.getCfgName();
String value = cmd.getValue();
String scope = cmd.getScope();
Long id = cmd.getId();
UserContext.current().setEventDetails(" Name: " + name + " New Value: " + (((name.toLowerCase()).contains("password")) ? "*****" :
(((value == null) ? "" : value))));
// check if config value exists
@ -456,7 +476,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
value = null;
}
updateConfiguration(userId, name, config.getCategory(), value);
updateConfiguration(userId, name, config.getCategory(), value, scope, id);
String updatedValue = _configDao.getValue(name);
if ((value == null && updatedValue == null) || updatedValue.equalsIgnoreCase(value)) {
return _configDao.findByName(name);
@ -466,13 +486,20 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
}
}
private String validateConfigurationValue(String name, String value) {
private String validateConfigurationValue(String name, String value, String scope) {
Config c = Config.getConfig(name);
if (c == null) {
s_logger.error("Missing configuration variable " + name + " in configuration table");
return "Invalid configuration variable.";
}
String configScope = c.getScope();
if (scope != null && !scope.isEmpty()) {
if (!configScope.contains(scope)) {
s_logger.error("Invalid scope " + scope + " for the parameter " + name);
return "Invalid scope for the parameter.";
}
}
Class<?> type = c.getType();

View File

@ -17,6 +17,7 @@
package com.cloud.configuration.dao;
import java.util.Map;
import java.util.List;
import com.cloud.configuration.ConfigurationVO;
import com.cloud.utils.db.GenericDao;

View File

@ -40,6 +40,7 @@ import javax.ejb.Local;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import com.cloud.server.ConfigurationServer;
import org.apache.cloudstack.api.command.admin.router.UpgradeRouterCmd;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
@ -286,6 +287,8 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V
@Inject
ConfigurationManager _configMgr;
@Inject
ConfigurationServer _configServer;
@Inject
ServiceOfferingDao _serviceOfferingDao = null;
@Inject
UserVmDao _userVmDao;
@ -2094,7 +2097,7 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V
boolean useExtDns = !dnsProvided;
/* For backward compatibility */
String use_external_dns = _configDao.getValue(Config.UseExternalDnsServers.key());
String use_external_dns = _configServer.getConfigValue(Config.UseExternalDnsServers.key(), Config.ConfigurationParameterScope.zone.toString(), dc.getId());
if (use_external_dns != null && use_external_dns.equals("true")) {
useExtDns = true;
}

View File

@ -16,6 +16,9 @@
// under the License.
package com.cloud.server;
import java.util.List;
import com.cloud.configuration.ConfigurationVO;
import com.cloud.exception.InternalErrorException;
/**
@ -30,4 +33,6 @@ public interface ConfigurationServer {
*/
public void persistDefaultValues() throws InternalErrorException;
public void updateKeyPairs();
public String getConfigValue(String name, String scope, Long resourceId);
public List<ConfigurationVO> getConfigListByScope(String scope, Long resourceId);
}

View File

@ -36,28 +36,29 @@ import java.util.Map;
import java.util.Properties;
import java.util.UUID;
import java.util.regex.Pattern;
import java.util.StringTokenizer;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import com.cloud.configuration.*;
import com.cloud.dc.*;
import com.cloud.dc.dao.DcDetailsDao;
import com.cloud.user.*;
import com.cloud.utils.db.GenericDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO;
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
import org.apache.commons.codec.binary.Base64;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
import com.cloud.configuration.Config;
import com.cloud.configuration.ConfigurationVO;
import com.cloud.configuration.Resource;
import com.cloud.configuration.Resource.ResourceOwnerType;
import com.cloud.configuration.Resource.ResourceType;
import com.cloud.configuration.ResourceCountVO;
import com.cloud.configuration.dao.ConfigurationDao;
import com.cloud.configuration.dao.ResourceCountDao;
import com.cloud.dc.DataCenter.NetworkType;
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.HostPodVO;
import com.cloud.dc.VlanVO;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.dc.dao.HostPodDao;
import com.cloud.dc.dao.VlanDao;
@ -91,9 +92,6 @@ import com.cloud.service.dao.ServiceOfferingDao;
import com.cloud.storage.DiskOfferingVO;
import com.cloud.storage.dao.DiskOfferingDao;
import com.cloud.test.IPRangeConfig;
import com.cloud.user.Account;
import com.cloud.user.AccountVO;
import com.cloud.user.User;
import com.cloud.user.dao.AccountDao;
import com.cloud.utils.PasswordGenerator;
import com.cloud.utils.PropertiesUtil;
@ -127,6 +125,11 @@ public class ConfigurationServerImpl extends ManagerBase implements Configuratio
@Inject private ResourceCountDao _resourceCountDao;
@Inject private NetworkOfferingServiceMapDao _ntwkOfferingServiceMapDao;
@Inject private IdentityDao _identityDao;
@Inject private DcDetailsDao _dcDetailsDao;
@Inject private ClusterDetailsDao _clusterDetailsDao;
@Inject private StoragePoolDetailsDao _storagePoolDetailsDao;
@Inject private AccountDetailsDao _accountDetailsDao;
public ConfigurationServerImpl() {
setRunLevel(ComponentLifecycle.RUN_LEVEL_FRAMEWORK_BOOTSTRAP);
@ -672,6 +675,76 @@ public class ConfigurationServerImpl extends ManagerBase implements Configuratio
}
}
@Override
public String getConfigValue(String name, String scope, Long resourceId) {
// If either of scope or resourceId is null then return global config value otherwise return value at the scope
Config c = Config.getConfig(name);
if (c == null) {
throw new CloudRuntimeException("Missing configuration variable " + name + " in configuration table");
}
String configScope = c.getScope();
if (scope != null && !scope.isEmpty()) {
if (!configScope.contains(scope)) {
throw new CloudRuntimeException("Invalid scope " + scope + " for the parameter " + name );
}
if (resourceId != null) {
switch (Config.ConfigurationParameterScope.valueOf(scope)) {
case zone: DataCenterVO zone = _zoneDao.findById(resourceId);
if (zone == null) {
throw new InvalidParameterValueException("unable to find zone by id " + resourceId);
}
DcDetailVO dcDetailVO = _dcDetailsDao.findDetail(resourceId, name);
if (dcDetailVO != null && dcDetailVO.getValue() != null) {
return dcDetailVO.getValue();
} break;
case cluster: ClusterDetailsVO cluster = _clusterDetailsDao.findById(resourceId);
if (cluster == null) {
throw new InvalidParameterValueException("unable to find cluster by id " + resourceId);
}
ClusterDetailsVO clusterDetailsVO = _clusterDetailsDao.findDetail(resourceId, name);
if (clusterDetailsVO != null && clusterDetailsVO.getValue() != null) {
return clusterDetailsVO.getValue();
} break;
case pool: StoragePoolDetailVO pool = _storagePoolDetailsDao.findById(resourceId);
if (pool == null) {
throw new InvalidParameterValueException("unable to find storage pool by id " + resourceId);
}
StoragePoolDetailVO storagePoolDetailVO = _storagePoolDetailsDao.findDetail(resourceId, name);
if (storagePoolDetailVO != null && storagePoolDetailVO.getValue() != null) {
return storagePoolDetailVO.getValue();
} break;
case account: AccountDetailVO account = _accountDetailsDao.findById(resourceId);
if (account == null) {
throw new InvalidParameterValueException("unable to find account by id " + resourceId);
}
AccountDetailVO accountDetailVO = _accountDetailsDao.findDetail(resourceId, name);
if (accountDetailVO != null && accountDetailVO.getValue() != null) {
return accountDetailVO.getValue();
} break;
default:
}
}
}
return _configDao.getValue(name);
}
@Override
public List<ConfigurationVO> getConfigListByScope(String scope, Long resourceId) {
// Getting the list of parameters defined at the scope
List<Config> configList = Config.getConfigListByScope(scope);
List<ConfigurationVO> configVOList = new ArrayList<ConfigurationVO>();
for (Config param:configList){
ConfigurationVO configVo = _configDao.findByName(param.toString());
configVo.setValue(getConfigValue(param.toString(), scope, resourceId));
configVOList.add(configVo);
}
return configVOList;
}
private void writeKeyToDisk(String key, String keyPath) {
File keyfile = new File(keyPath);
if (!keyfile.exists()) {

View File

@ -44,6 +44,7 @@ import javax.crypto.spec.SecretKeySpec;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import com.cloud.configuration.*;
import com.cloud.storage.dao.*;
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
import org.apache.cloudstack.api.ApiConstants;
@ -106,10 +107,6 @@ import com.cloud.capacity.CapacityVO;
import com.cloud.capacity.dao.CapacityDao;
import com.cloud.capacity.dao.CapacityDaoImpl.SummedCapacity;
import com.cloud.cluster.ClusterManager;
import com.cloud.configuration.Config;
import com.cloud.configuration.Configuration;
import com.cloud.configuration.ConfigurationManager;
import com.cloud.configuration.ConfigurationVO;
import com.cloud.configuration.dao.ConfigurationDao;
import com.cloud.consoleproxy.ConsoleProxyManagementState;
import com.cloud.consoleproxy.ConsoleProxyManager;
@ -381,6 +378,9 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
@Inject
S3Manager _s3Mgr;
@Inject
ConfigurationServer _configServer;
private final ScheduledExecutorService _eventExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("EventChecker"));
private final ScheduledExecutorService _alertExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("AlertChecker"));
private KeystoreManager _ksMgr;
@ -1038,6 +1038,22 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
Object name = cmd.getConfigName();
Object category = cmd.getCategory();
Object keyword = cmd.getKeyword();
Long id = cmd.getId();
String scope = cmd.getScope();
if (scope!= null && !scope.isEmpty()) {
// getting the list of parameters at requested scope
try {
Config.ConfigurationParameterScope.valueOf(scope.toLowerCase());
} catch (Exception e ) {
throw new InvalidParameterValueException("Invalid scope " + scope + " while listing configuration parameters");
}
if (id == null) {
throw new InvalidParameterValueException("Invalid id null, id is needed corresponding to the scope");
}
List<ConfigurationVO> configList = _configServer.getConfigListByScope(scope, id);
return new Pair<List<? extends Configuration>, Integer>(configList, configList.size());
}
if (keyword != null) {
SearchCriteria<ConfigurationVO> ssc = _configDao.createSearchCriteria();

View File

@ -72,4 +72,13 @@ public class StoragePoolDetailsDaoImpl extends GenericDaoBase<StoragePoolDetailV
return detailsMap;
}
@Override
public StoragePoolDetailVO findDetail(long poolId, String name) {
SearchCriteria<StoragePoolDetailVO> sc = PoolSearch.create();
sc.setParameters("pool", poolId);
sc.setParameters("name", name);
return findOneIncludingRemovedBy(sc);
}
}

View File

@ -25,6 +25,7 @@ import javax.inject.Inject;
import javax.naming.ConfigurationException;
import javax.naming.NamingException;
import com.cloud.configuration.ConfigurationVO;
import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd;
import org.apache.cloudstack.api.command.admin.ldap.LDAPConfigCmd;
import org.apache.cloudstack.api.command.admin.ldap.LDAPRemoveCmd;
@ -425,9 +426,9 @@ public class MockConfigurationManagerImpl extends ManagerBase implements Configu
* @see com.cloud.configuration.ConfigurationManager#updateConfiguration(long, java.lang.String, java.lang.String, java.lang.String)
*/
@Override
public void updateConfiguration(long userId, String name, String category, String value) {
public void updateConfiguration(long userId, String name, String category, String value, String scope, Long resourceId) {
// TODO Auto-generated method stub
}
/* (non-Javadoc)

View File

@ -0,0 +1,85 @@
# 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.
""" P1 tests for updating the granular Configuration parameter with scope and resource id provided.
"""
#Import Local Modules
import marvin
from marvin.cloudstackTestCase import *
from marvin.cloudstackAPI import *
from marvin.remoteSSHClient import remoteSSHClient
from marvin.integration.lib.utils import *
from marvin.integration.lib.base import *
from marvin.integration.lib.common import *
from nose.plugins.attrib import attr
#Import System modules
import unittest
import hashlib
import random
class TestUpdateConfigWithScope(cloudstackTestCase):
"""
This test updates the value of a configuration parameter
which is at zone level(scope)
"""
def setUp(self):
"""
CloudStack internally saves its passwords in md5 form and that is how we
specify it in the API. Python's hashlib library helps us to quickly hash
strings as follows
"""
mdf = hashlib.md5()
mdf.update('password')
mdf_pass = mdf.hexdigest()
self.apiClient = self.testClient.getApiClient() #Get ourselves an API client
def test_UpdateConfigParamWithScope(self):
updateConfigurationCmd = updateConfiguration.updateConfigurationCmd()
updateConfigurationCmd.name = "use.external.dns"
updateConfigurationCmd.value = "true"
updateConfigurationCmd.scope = "zone"
updateConfigurationCmd.id = 1
updateConfigurationResponse = self.apiClient.updateConfiguration(updateConfigurationCmd)
self.debug("updated the parameter %s with value %s"%(updateConfigurationResponse.name, updateConfigurationResponse.value))
listConfigurationsCmd = listConfigurations.listConfigurationsCmd()
listConfigurationsCmd.cfgName = updateConfigurationResponse.name
listConfigurationsCmd.scope = "zone"
listConfigurationsCmd.id = 1
listConfigurationsResponse = self.apiClient.listConfigurations(listConfigurationsCmd)
self.assertNotEqual(len(listConfigurationsResponse), 0, "Check if the list API \
returns a non-empty response")
configParam = listConfigurationsResponse[0]
self.assertEqual(configParam.value, updateConfigurationResponse.value, "Check if the update API returned \
is the same as the one we got in the list API")
def tearDown(self):
updateConfigurationCmd = updateConfiguration.updateConfigurationCmd()
updateConfigurationCmd.name = "use.external.dns"
updateConfigurationCmd.value = "false"
updateConfigurationCmd.scope = "zone"
updateConfigurationCmd.id = 1
self.apiClient.updateConfiguration(updateConfigurationCmd)