cloudstack/engine/schema/src/main/java/com/cloud/dc/ClusterDetailsDaoImpl.java
Abhisar Sinha 2a4a1f73d0
Support multi-scope configuration settings (#10300)
This PR introduces the concept of multi-scope configuration settings. In addition to the Global level, currently all configurations can be set at a single scope level.
It will be useful if a configuration can be set at multiple scopes. For example, a configuration set at the domain level
will apply for all accounts, but it can be set for an account as well. In which case the account level setting will override the domain level setting.

This is done by changing the column `scope` of table `configuration` from string (single scope) to bitmask (multiple scopes).

```
public enum Scope {
    Global(null, 1),
    Zone(Global, 1 << 1),
    Cluster(Zone, 1 << 2),
    StoragePool(Cluster, 1 << 3),
    ManagementServer(Global, 1 << 4),
    ImageStore(Zone, 1 << 5),
    Domain(Global, 1 << 6),
    Account(Domain, 1 << 7);
```
Each scope is also assigned a parent scope. When a configuration for a given scope is not defined but is available for multiple scope types, the value will be retrieved from the parent scope. If there is no parent scope or if the configuration is defined for a single scope only, the value will fall back to the global level.

Hierarchy for different scopes is defined as below :
- Global
    - Zone
        - Cluster
            - Storage Pool
        - Image Store
    - Management Server
    - Domain
        - Account

This PR also updates the scope of the following configurations (Storage Pool scope is added in addition to the existing Zone scope):
- pool.storage.allocated.capacity.disablethreshold
- pool.storage.allocated.resize.capacity.disablethreshold
- pool.storage.capacity.disablethreshold

Doc PR : https://github.com/apache/cloudstack-documentation/pull/476

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
Co-authored-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
2025-02-14 11:25:01 +05:30

207 lines
7.7 KiB
Java

// 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 com.cloud.dc;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.inject.Inject;
import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.framework.config.ConfigKey.Scope;
import org.apache.cloudstack.framework.config.ScopedConfigStorage;
import org.apache.commons.collections.CollectionUtils;
import com.cloud.dc.dao.ClusterDao;
import com.cloud.org.Cluster;
import com.cloud.utils.Pair;
import com.cloud.utils.crypt.DBEncryptionUtil;
import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.db.TransactionLegacy;
import org.apache.cloudstack.resourcedetail.ResourceDetailsDaoBase;
public class ClusterDetailsDaoImpl extends ResourceDetailsDaoBase<ClusterDetailsVO> implements ClusterDetailsDao, ScopedConfigStorage {
@Inject
ClusterDao clusterDao;
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().getResourceId(), SearchCriteria.Op.EQ);
ClusterSearch.done();
DetailSearch = createSearchBuilder();
DetailSearch.and("clusterId", DetailSearch.entity().getResourceId(), SearchCriteria.Op.EQ);
DetailSearch.and("name", DetailSearch.entity().getName(), SearchCriteria.Op.EQ);
DetailSearch.done();
}
@Override
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
name = getCpuMemoryOvercommitRatio(name);
sc.setParameters("clusterId", clusterId);
sc.setParameters("name", name);
ClusterDetailsVO detail = findOneIncludingRemovedBy(sc);
if ("password".equals(name) && detail != null) {
detail.setValue(DBEncryptionUtil.decrypt(detail.getValue()));
}
return detail;
}
@Override
public void addDetail(long resourceId, String key, String value, boolean display) {
super.addDetail(new ClusterDetailsVO(resourceId, key, value));
}
@Override
public Map<String, String> findDetails(long clusterId) {
SearchCriteria<ClusterDetailsVO> sc = ClusterSearch.create();
sc.setParameters("clusterId", clusterId);
List<ClusterDetailsVO> results = search(sc, null);
Map<String, String> details = new HashMap<String, String>(results.size());
for (ClusterDetailsVO result : results) {
if ("password".equals(result.getName())) {
details.put(result.getName(), DBEncryptionUtil.decrypt(result.getValue()));
} else {
details.put(result.getName(), result.getValue());
}
}
return details;
}
@Override
public Map<String, String> findDetails(long clusterId, Collection<String> names) {
if (CollectionUtils.isEmpty(names)) {
return new HashMap<>();
}
SearchBuilder<ClusterDetailsVO> sb = createSearchBuilder();
sb.and("clusterId", sb.entity().getResourceId(), SearchCriteria.Op.EQ);
sb.and("name", sb.entity().getName(), SearchCriteria.Op.IN);
sb.done();
SearchCriteria<ClusterDetailsVO> sc = sb.create();
sc.setParameters("clusterId", clusterId);
sc.setParameters("name", names.toArray());
List<ClusterDetailsVO> results = search(sc, null);
return results.stream()
.collect(Collectors.toMap(ClusterDetailsVO::getName, ClusterDetailsVO::getValue));
}
@Override
public void deleteDetails(long clusterId) {
SearchCriteria<ClusterDetailsVO> sc = ClusterSearch.create();
sc.setParameters("clusterId", clusterId);
List<ClusterDetailsVO> results = search(sc, null);
for (ClusterDetailsVO result : results) {
remove(result.getId());
}
}
@Override
public void persist(long clusterId, Map<String, String> details) {
TransactionLegacy txn = TransactionLegacy.currentTxn();
txn.start();
SearchCriteria<ClusterDetailsVO> sc = ClusterSearch.create();
sc.setParameters("clusterId", clusterId);
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, name, value);
persist(vo);
}
txn.commit();
}
@Override
public void persist(long clusterId, String name, String value) {
name = getCpuMemoryOvercommitRatio(name);
TransactionLegacy txn = TransactionLegacy.currentTxn();
txn.start();
SearchCriteria<ClusterDetailsVO> sc = DetailSearch.create();
sc.setParameters("clusterId", clusterId);
sc.setParameters("name", name);
expunge(sc);
ClusterDetailsVO vo = new ClusterDetailsVO(clusterId, name, value);
persist(vo);
txn.commit();
}
@Override
public Scope getScope() {
return ConfigKey.Scope.Cluster;
}
@Override
public String getConfigValue(long id, String key) {
ClusterDetailsVO vo = findDetail(id, key);
return vo == null ? null : vo.getValue();
}
@Override
public String getVmwareDcName(Long clusterId) {
String dcName = null;
String url = findDetail(clusterId, "url").getValue();
String[] tokens = url.split("/"); // Cluster URL format is 'http://vcenter/dc/cluster'
if (tokens != null && tokens.length > 3)
dcName = tokens[3];
return dcName;
}
private String getCpuMemoryOvercommitRatio(String name) {
if (name.equalsIgnoreCase(CpuOverprovisioningFactor)) {
name = CpuOverCommitRatio;
}
if (name.equalsIgnoreCase(MemoryOverprovisioningFactor)) {
name = MemoryOverCommitRatio;
}
return name;
}
@Override
public Pair<Scope, Long> getParentScope(long id) {
Cluster cluster = clusterDao.findById(id);
if (cluster == null) {
return null;
}
return new Pair<>(getScope().getParent(), cluster.getDataCenterId());
}
}