blob: 46a1de950ddfdd4b06a54756a71ec5b131e6289e [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.apache.cloudstack.framework.config.impl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import org.apache.cloudstack.framework.config.ConfigDepot;
import org.apache.cloudstack.framework.config.ConfigDepotAdmin;
import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.framework.config.Configurable;
import org.apache.cloudstack.framework.config.ScopedConfigStorage;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.framework.config.dao.ConfigurationGroupDao;
import org.apache.cloudstack.framework.config.dao.ConfigurationSubGroupDao;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import com.cloud.utils.Pair;
import com.cloud.utils.Ternary;
import com.cloud.utils.exception.CloudRuntimeException;
/**
* ConfigDepotImpl implements the ConfigDepot and ConfigDepotAdmin interface.
* Its functionalities include:
* - Control how dynamic config values are cached and refreshed.
* - Control how scoped config values are stored.
* - Gather all of the Configurable interfaces and insert their config
* variables into the config table.
* - Hide the data source where configs are stored and retrieved.
*
* When dealing with this class, we must be very careful on cluster situations.
*
* TODO:
* - Move the rest of the changes to the config table to here.
* - Add the code to mark the rows in configuration table without
* the corresponding keys to be null.
* - Move all of the configurations to using ConfigDepot
* - Completely eliminate Config.java
* - Figure out the correct categories.
* - Add a scope for management server, where if the scope is management server
* then the override is retrieved from a properties file. Imagine adding a
* new management server node and it is much more capable system than previous
* management servers, you want the adjustments to thread pools etc to be
* very different than other management serves.
* - Add validation methods to ConfigKey<?>. If a validation class is declared
* when constructing a ConfigKey then configuration server should use the
* validation class to validate the value the admin input for the key.
*/
public class ConfigDepotImpl implements ConfigDepot, ConfigDepotAdmin {
private final static Logger s_logger = Logger.getLogger(ConfigDepotImpl.class);
@Inject
ConfigurationDao _configDao;
@Inject
ConfigurationGroupDao _configGroupDao;
@Inject
ConfigurationSubGroupDao _configSubGroupDao;
List<Configurable> _configurables;
List<ScopedConfigStorage> _scopedStorages;
Set<Configurable> _configured = Collections.synchronizedSet(new HashSet<Configurable>());
Set<String> newConfigs = Collections.synchronizedSet(new HashSet<>());
private HashMap<String, Pair<String, ConfigKey<?>>> _allKeys = new HashMap<String, Pair<String, ConfigKey<?>>>(1007);
HashMap<ConfigKey.Scope, Set<ConfigKey<?>>> _scopeLevelConfigsMap = new HashMap<ConfigKey.Scope, Set<ConfigKey<?>>>();
public ConfigDepotImpl() {
ConfigKey.init(this);
createEmptyScopeLevelMappings();
}
/**
* Create an empty map of ConfigKey.Scope values, setting the _scopeLevelConfigsMap with the created map
* This map must contain all ConfigKey.Scope values, except the ConfigKey.Scope.Global.
*/
protected void createEmptyScopeLevelMappings() {
_scopeLevelConfigsMap = new HashMap<ConfigKey.Scope, Set<ConfigKey<?>>>();
_scopeLevelConfigsMap.put(ConfigKey.Scope.Zone, new HashSet<ConfigKey<?>>());
_scopeLevelConfigsMap.put(ConfigKey.Scope.Cluster, new HashSet<ConfigKey<?>>());
_scopeLevelConfigsMap.put(ConfigKey.Scope.StoragePool, new HashSet<ConfigKey<?>>());
_scopeLevelConfigsMap.put(ConfigKey.Scope.Account, new HashSet<ConfigKey<?>>());
_scopeLevelConfigsMap.put(ConfigKey.Scope.ImageStore, new HashSet<ConfigKey<?>>());
_scopeLevelConfigsMap.put(ConfigKey.Scope.Domain, new HashSet<ConfigKey<?>>());
_scopeLevelConfigsMap.put(ConfigKey.Scope.ManagementServer, new HashSet<ConfigKey<?>>());
}
@Override
public ConfigKey<?> get(String key) {
Pair<String, ConfigKey<?>> value = _allKeys.get(key);
return value != null ? value.second() : null;
}
@PostConstruct
@Override
public void populateConfigurations() {
Date date = new Date();
for (Configurable configurable : _configurables) {
populateConfiguration(date, configurable);
}
}
protected void populateConfiguration(Date date, Configurable configurable) {
if (_configured.contains(configurable))
return;
s_logger.debug("Retrieving keys from " + configurable.getClass().getSimpleName());
for (ConfigKey<?> key : configurable.getConfigKeys()) {
Pair<String, ConfigKey<?>> previous = _allKeys.get(key.key());
if (previous != null && !previous.first().equals(configurable.getConfigComponentName())) {
throw new CloudRuntimeException("Configurable " + configurable.getConfigComponentName() + " is adding a key that has been added before by " +
previous.first() + ": " + key.toString());
}
_allKeys.put(key.key(), new Pair<String, ConfigKey<?>>(configurable.getConfigComponentName(), key));
createOrupdateConfigObject(date, configurable.getConfigComponentName(), key, null);
if ((key.scope() != null) && (key.scope() != ConfigKey.Scope.Global)) {
Set<ConfigKey<?>> currentConfigs = _scopeLevelConfigsMap.get(key.scope());
currentConfigs.add(key);
}
}
_configured.add(configurable);
}
private void createOrupdateConfigObject(Date date, String componentName, ConfigKey<?> key, String value) {
Long groupId = 1L;
Long subGroupId = 1L;
if (key.group() != null) {
Ternary<String, String, Long> group = key.group();
ConfigurationGroupVO groupVO = _configGroupDao.findByName(group.first());
if (groupVO == null) {
groupVO = new ConfigurationGroupVO(group.first(), group.second(), group.third());
groupVO = _configGroupDao.persist(groupVO);
}
groupId = groupVO.getId();
}
if (key.subGroup() != null) {
Pair<String, Long> subGroup = key.subGroup();
ConfigurationSubGroupVO subGroupVO = _configSubGroupDao.findByNameAndGroup(subGroup.first(), groupId);
if (subGroupVO == null) {
subGroupVO = new ConfigurationSubGroupVO();
subGroupVO = _configSubGroupDao.persist(subGroupVO);
}
subGroupId = subGroupVO.getId();
}
ConfigurationVO vo = _configDao.findById(key.key());
if (vo == null) {
vo = new ConfigurationVO(componentName, key);
vo.setUpdated(date);
if (value != null) {
vo.setValue(value);
}
if (key.group() == null && key.subGroup() == null ) {
Pair<Long, Long> configGroupAndSubGroup = getConfigurationGroupAndSubGroupByName(key.key());
vo.setGroupId(configGroupAndSubGroup.first());
vo.setSubGroupId(configGroupAndSubGroup.second());
} else {
vo.setGroupId(groupId);
vo.setSubGroupId(subGroupId);
}
if (key.kind() != null) {
vo.setKind(key.kind().toString());
}
if (key.options() != null) {
vo.setOptions(key.options());
}
_configDao.persist(vo);
newConfigs.add(vo.getName());
} else {
boolean configUpdated = false;
if (vo.isDynamic() != key.isDynamic() || !ObjectUtils.equals(vo.getDescription(), key.description()) || !ObjectUtils.equals(vo.getDefaultValue(), key.defaultValue()) ||
!ObjectUtils.equals(vo.getScope(), key.scope().toString()) ||
!ObjectUtils.equals(vo.getComponent(), componentName)) {
vo.setDynamic(key.isDynamic());
vo.setDescription(key.description());
vo.setDefaultValue(key.defaultValue());
vo.setScope(key.scope().toString());
vo.setComponent(componentName);
vo.setUpdated(date);
configUpdated = true;
}
if (key.displayText() != null && !ObjectUtils.equals(vo.getDisplayText(), key.displayText())) {
vo.setDisplayText(key.displayText());
configUpdated = true;
}
if (key.parent() != null && !ObjectUtils.equals(vo.getParent(), key.parent())) {
vo.setParent(key.parent());
configUpdated = true;
}
if (key.group() == null && key.subGroup() == null ) {
Pair<Long, Long> configGroupAndSubGroup = getConfigurationGroupAndSubGroupByName(key.key());
if (configGroupAndSubGroup.first() != 1 && configGroupAndSubGroup.second() != 1) {
vo.setGroupId(configGroupAndSubGroup.first());
vo.setSubGroupId(configGroupAndSubGroup.second());
configUpdated = true;
}
}
if (key.group() != null && !ObjectUtils.equals(vo.getGroupId(), groupId)) {
vo.setGroupId(groupId);
configUpdated = true;
}
if (key.subGroup() != null && !ObjectUtils.equals(vo.getSubGroupId(), subGroupId)) {
vo.setSubGroupId(subGroupId);
configUpdated = true;
}
if (key.kind() != null) {
vo.setKind(key.kind().toString());
configUpdated = true;
}
if (key.options() != null) {
vo.setOptions(key.options());
configUpdated = true;
}
if (configUpdated) {
_configDao.persist(vo);
}
}
}
@Override
public void populateConfiguration(Configurable configurable) {
populateConfiguration(new Date(), configurable);
}
@Override
public List<String> getComponentsInDepot() {
return new ArrayList<String>();
}
public ConfigurationDao global() {
return _configDao;
}
public ScopedConfigStorage findScopedConfigStorage(ConfigKey<?> config) {
for (ScopedConfigStorage storage : _scopedStorages) {
if (storage.getScope() == config.scope()) {
return storage;
}
}
throw new CloudRuntimeException("Unable to find config storage for this scope: " + config.scope() + " for " + config.key());
}
public ScopedConfigStorage getDomainScope(ConfigKey<?> config) {
for (ScopedConfigStorage storage : _scopedStorages) {
if (storage.getScope() == ConfigKey.Scope.Domain) {
return storage;
}
}
throw new CloudRuntimeException("Unable to find config storage for this scope: " + ConfigKey.Scope.Domain + " for " + config.key());
}
public List<ScopedConfigStorage> getScopedStorages() {
return _scopedStorages;
}
@Inject
public void setScopedStorages(List<ScopedConfigStorage> scopedStorages) {
_scopedStorages = scopedStorages;
}
public List<Configurable> getConfigurables() {
return _configurables;
}
@Inject
public void setConfigurables(List<Configurable> configurables) {
_configurables = configurables;
}
@Override
public Set<ConfigKey<?>> getConfigListByScope(String scope) {
return _scopeLevelConfigsMap.get(ConfigKey.Scope.valueOf(scope));
}
@Override
public <T> void set(ConfigKey<T> key, T value) {
_configDao.update(key.key(), value.toString());
}
@Override
public <T> void createOrUpdateConfigObject(String componentName, ConfigKey<T> key, String value) {
createOrupdateConfigObject(new Date(), componentName, key, value);
}
@Override
public Pair<Long, Long> getConfigurationGroupAndSubGroupByName(String configName) {
Long subGroupId = 1L;
Long groupId = 1L;
if (StringUtils.isNotBlank(configName)) {
String[] nameWords = configName.split("\\.");
if (nameWords.length > 0) {
for (int index = 0; index < nameWords.length; index++) {
ConfigurationSubGroupVO configSubGroup = _configSubGroupDao.findByName(nameWords[index]);
if (configSubGroup == null) {
configSubGroup = _configSubGroupDao.findByKeyword(nameWords[index]);
}
if (configSubGroup != null) {
subGroupId = configSubGroup.getId();
groupId = configSubGroup.getGroupId();
break;
}
}
}
}
return new Pair<>(groupId, subGroupId);
}
@Override
public boolean isNewConfig(ConfigKey<?> configKey) {
return newConfigs.contains(configKey.key());
}
}