Skip to content

Commit 87a027f

Browse files
abh1sarshwstppr
authored andcommitted
Support multi-scope configuration settings (apache#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 : apache/cloudstack-documentation#476 Signed-off-by: Abhishek Kumar <[email protected]> Co-authored-by: Abhishek Kumar <[email protected]>
1 parent 2db582d commit 87a027f

File tree

37 files changed

+877
-122
lines changed

37 files changed

+877
-122
lines changed

engine/components-api/src/main/java/com/cloud/capacity/CapacityManager.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
// under the License.
1717
package com.cloud.capacity;
1818

19+
import java.util.List;
20+
1921
import org.apache.cloudstack.framework.config.ConfigKey;
2022
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
2123

@@ -67,7 +69,7 @@ public interface CapacityManager {
6769
"0.85",
6870
"Percentage (as a value between 0 and 1) of storage utilization above which allocators will disable using the pool for low storage available.",
6971
true,
70-
ConfigKey.Scope.Zone);
72+
List.of(ConfigKey.Scope.StoragePool, ConfigKey.Scope.Zone));
7173
static final ConfigKey<Double> StorageOverprovisioningFactor =
7274
new ConfigKey<>(
7375
"Storage",
@@ -85,7 +87,7 @@ public interface CapacityManager {
8587
"0.85",
8688
"Percentage (as a value between 0 and 1) of allocated storage utilization above which allocators will disable using the pool for low allocated storage available.",
8789
true,
88-
ConfigKey.Scope.Zone);
90+
List.of(ConfigKey.Scope.StoragePool, ConfigKey.Scope.Zone));
8991
static final ConfigKey<Boolean> StorageOperationsExcludeCluster =
9092
new ConfigKey<>(
9193
Boolean.class,
@@ -125,7 +127,7 @@ public interface CapacityManager {
125127
"Percentage (as a value between 0 and 1) of allocated storage utilization above which allocators will disable using the pool for volume resize. " +
126128
"This is applicable only when volume.resize.allowed.beyond.allocation is set to true.",
127129
true,
128-
ConfigKey.Scope.Zone);
130+
List.of(ConfigKey.Scope.StoragePool, ConfigKey.Scope.Zone));
129131

130132
ConfigKey<Integer> CapacityCalculateWorkers = new ConfigKey<>(ConfigKey.CATEGORY_ADVANCED, Integer.class,
131133
"capacity.calculate.workers", "1",

engine/components-api/src/main/java/com/cloud/storage/StorageManager.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ public interface StorageManager extends StorageService {
214214
ConfigKey<Boolean> AllowVolumeReSizeBeyondAllocation = new ConfigKey<Boolean>("Advanced", Boolean.class, "volume.resize.allowed.beyond.allocation", "false",
215215
"Determines whether volume size can exceed the pool capacity allocation disable threshold (pool.storage.allocated.capacity.disablethreshold) " +
216216
"when resize a volume upto resize capacity disable threshold (pool.storage.allocated.resize.capacity.disablethreshold)",
217-
true, ConfigKey.Scope.Zone);
217+
true, List.of(ConfigKey.Scope.StoragePool, ConfigKey.Scope.Zone));
218218

219219
ConfigKey<Integer> StoragePoolHostConnectWorkers = new ConfigKey<>("Storage", Integer.class,
220220
"storage.pool.host.connect.workers", "1",

engine/schema/src/main/java/com/cloud/dc/ClusterDetailsDaoImpl.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,16 @@
2222
import java.util.Map;
2323
import java.util.stream.Collectors;
2424

25+
import javax.inject.Inject;
26+
2527
import org.apache.cloudstack.framework.config.ConfigKey;
2628
import org.apache.cloudstack.framework.config.ConfigKey.Scope;
2729
import org.apache.cloudstack.framework.config.ScopedConfigStorage;
2830
import org.apache.commons.collections.CollectionUtils;
2931

32+
import com.cloud.dc.dao.ClusterDao;
33+
import com.cloud.org.Cluster;
34+
import com.cloud.utils.Pair;
3035
import com.cloud.utils.crypt.DBEncryptionUtil;
3136
import com.cloud.utils.db.SearchBuilder;
3237
import com.cloud.utils.db.SearchCriteria;
@@ -35,6 +40,9 @@
3540

3641
public class ClusterDetailsDaoImpl extends ResourceDetailsDaoBase<ClusterDetailsVO> implements ClusterDetailsDao, ScopedConfigStorage {
3742

43+
@Inject
44+
ClusterDao clusterDao;
45+
3846
protected final SearchBuilder<ClusterDetailsVO> ClusterSearch;
3947
protected final SearchBuilder<ClusterDetailsVO> DetailSearch;
4048

@@ -186,4 +194,13 @@ private String getCpuMemoryOvercommitRatio(String name) {
186194

187195
return name;
188196
}
197+
198+
@Override
199+
public Pair<Scope, Long> getParentScope(long id) {
200+
Cluster cluster = clusterDao.findById(id);
201+
if (cluster == null) {
202+
return null;
203+
}
204+
return new Pair<>(getScope().getParent(), cluster.getDataCenterId());
205+
}
189206
}

engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolDetailsDaoImpl.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
3131
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
3232

33+
import com.cloud.utils.Pair;
34+
3335
public class StoragePoolDetailsDaoImpl extends ResourceDetailsDaoBase<StoragePoolDetailVO> implements StoragePoolDetailsDao, ScopedConfigStorage {
3436

3537
@Inject
@@ -57,4 +59,17 @@ public void addDetail(long resourceId, String key, String value, boolean display
5759
}
5860
super.addDetail(new StoragePoolDetailVO(resourceId, key, value, display));
5961
}
62+
63+
@Override
64+
public Pair<Scope, Long> getParentScope(long id) {
65+
StoragePoolVO pool = _storagePoolDao.findById(id);
66+
if (pool != null) {
67+
if (pool.getClusterId() != null) {
68+
return new Pair<>(getScope().getParent(), pool.getClusterId());
69+
} else {
70+
return new Pair<>(ConfigKey.Scope.Zone, pool.getDataCenterId());
71+
}
72+
}
73+
return null;
74+
}
6075
}

engine/schema/src/main/java/com/cloud/upgrade/ConfigurationGroupsAggregator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public ConfigurationGroupsAggregator() {
5454

5555
public void updateConfigurationGroups() {
5656
LOG.debug("Updating configuration groups");
57-
List<ConfigurationVO> configs = configDao.listAllIncludingRemoved();
57+
List<ConfigurationVO> configs = configDao.searchPartialConfigurations();
5858
if (CollectionUtils.isEmpty(configs)) {
5959
return;
6060
}

engine/schema/src/main/java/com/cloud/upgrade/dao/DatabaseAccessObject.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,36 @@ public boolean columnExists(Connection conn, String tableName, String columnName
8787
return columnExists;
8888
}
8989

90+
public String getColumnType(Connection conn, String tableName, String columnName) {
91+
try (PreparedStatement pstmt = conn.prepareStatement(String.format("DESCRIBE %s %s", tableName, columnName));){
92+
ResultSet rs = pstmt.executeQuery();
93+
if (rs.next()) {
94+
return rs.getString("Type");
95+
}
96+
} catch (SQLException e) {
97+
logger.warn("Type for column {} can not be retrieved in {} ignoring exception: {}", columnName, tableName, e.getMessage());
98+
}
99+
return null;
100+
}
101+
102+
public void addColumn(Connection conn, String tableName, String columnName, String columnDefinition) {
103+
try (PreparedStatement pstmt = conn.prepareStatement(String.format("ALTER TABLE %s ADD COLUMN %s %s", tableName, columnName, columnDefinition));){
104+
pstmt.executeUpdate();
105+
logger.debug("Column {} is added successfully from the table {}", columnName, tableName);
106+
} catch (SQLException e) {
107+
logger.warn("Unable to add column {} to table {} due to exception", columnName, tableName, e);
108+
}
109+
}
110+
111+
public void changeColumn(Connection conn, String tableName, String oldColumnName, String newColumnName, String columnDefinition) {
112+
try (PreparedStatement pstmt = conn.prepareStatement(String.format("ALTER TABLE %s CHANGE COLUMN %s %s %s", tableName, oldColumnName, newColumnName, columnDefinition));){
113+
pstmt.executeUpdate();
114+
logger.debug("Column {} is changed successfully to {} from the table {}", oldColumnName, newColumnName, tableName);
115+
} catch (SQLException e) {
116+
logger.warn("Unable to add column {} to {} from the table {} due to exception", oldColumnName, newColumnName, tableName, e);
117+
}
118+
}
119+
90120
public String generateIndexName(String tableName, String... columnName) {
91121
return String.format("i_%s__%s", tableName, StringUtils.join(columnName, "__"));
92122
}

engine/schema/src/main/java/com/cloud/upgrade/dao/DbUpgradeUtils.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,20 @@ public static void dropTableColumnsIfExist(Connection conn, String tableName, Li
5858
}
5959
}
6060

61+
public static String getTableColumnType(Connection conn, String tableName, String columnName) {
62+
return dao.getColumnType(conn, tableName, columnName);
63+
}
64+
65+
public static void addTableColumnIfNotExist(Connection conn, String tableName, String columnName, String columnDefinition) {
66+
if (!dao.columnExists(conn, tableName, columnName)) {
67+
dao.addColumn(conn, tableName, columnName, columnDefinition);
68+
}
69+
}
70+
71+
public static void changeTableColumnIfNotExist(Connection conn, String tableName, String oldColumnName, String newColumnName, String columnDefinition) {
72+
if (dao.columnExists(conn, tableName, oldColumnName)) {
73+
dao.changeColumn(conn, tableName, oldColumnName, newColumnName, columnDefinition);
74+
}
75+
}
76+
6177
}

engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42010to42100.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,16 @@
1717
package com.cloud.upgrade.dao;
1818

1919
import com.cloud.upgrade.SystemVmTemplateRegistration;
20+
import com.cloud.utils.db.TransactionLegacy;
2021
import com.cloud.utils.exception.CloudRuntimeException;
2122

2223
import java.io.InputStream;
2324
import java.sql.Connection;
25+
import java.sql.PreparedStatement;
26+
import java.sql.SQLException;
27+
import java.util.List;
28+
29+
import org.apache.cloudstack.framework.config.ConfigKey;
2430

2531
public class Upgrade42010to42100 extends DbUpgradeAbstractImpl implements DbUpgrade, DbUpgradeSystemVmTemplate {
2632
private SystemVmTemplateRegistration systemVmTemplateRegistration;
@@ -53,6 +59,7 @@ public InputStream[] getPrepareScripts() {
5359

5460
@Override
5561
public void performDataMigration(Connection conn) {
62+
migrateConfigurationScopeToBitmask(conn);
5663
}
5764

5865
@Override
@@ -80,4 +87,35 @@ public void updateSystemVmTemplates(Connection conn) {
8087
throw new CloudRuntimeException("Failed to find / register SystemVM template(s)");
8188
}
8289
}
90+
91+
protected void migrateConfigurationScopeToBitmask(Connection conn) {
92+
String scopeDataType = DbUpgradeUtils.getTableColumnType(conn, "configuration", "scope");
93+
logger.info("Data type of the column scope of table configuration is {}", scopeDataType);
94+
if (!"varchar(255)".equals(scopeDataType)) {
95+
return;
96+
}
97+
DbUpgradeUtils.addTableColumnIfNotExist(conn, "configuration", "new_scope", "BIGINT DEFAULT 0");
98+
migrateExistingConfigurationScopeValues(conn);
99+
DbUpgradeUtils.dropTableColumnsIfExist(conn, "configuration", List.of("scope"));
100+
DbUpgradeUtils.changeTableColumnIfNotExist(conn, "configuration", "new_scope", "scope", "BIGINT NOT NULL DEFAULT 0 COMMENT 'Bitmask for scope(s) of this parameter'");
101+
}
102+
103+
protected void migrateExistingConfigurationScopeValues(Connection conn) {
104+
StringBuilder sql = new StringBuilder("UPDATE configuration\n" +
105+
"SET new_scope = " +
106+
" CASE ");
107+
for (ConfigKey.Scope scope : ConfigKey.Scope.values()) {
108+
sql.append(" WHEN scope = '").append(scope.name()).append("' THEN ").append(scope.getBitValue()).append(" ");
109+
}
110+
sql.append(" ELSE 0 " +
111+
" END " +
112+
"WHERE scope IS NOT NULL;");
113+
TransactionLegacy txn = TransactionLegacy.currentTxn();
114+
try (PreparedStatement pstmt = txn.prepareAutoCloseStatement(sql.toString())) {
115+
pstmt.executeUpdate();
116+
} catch (SQLException e) {
117+
logger.error("Failed to migrate existing configuration scope values to bitmask", e);
118+
throw new CloudRuntimeException(String.format("Failed to migrate existing configuration scope values to bitmask due to: %s", e.getMessage()));
119+
}
120+
}
83121
}

engine/schema/src/main/java/com/cloud/user/AccountDetailsDaoImpl.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import com.cloud.domain.dao.DomainDao;
3434
import com.cloud.domain.dao.DomainDetailsDao;
3535
import com.cloud.user.dao.AccountDao;
36+
import com.cloud.utils.Pair;
3637
import com.cloud.utils.db.QueryBuilder;
3738
import com.cloud.utils.db.SearchBuilder;
3839
import com.cloud.utils.db.SearchCriteria;
@@ -156,4 +157,13 @@ public String getConfigValue(long id, String key) {
156157
}
157158
return value;
158159
}
160+
161+
@Override
162+
public Pair<Scope, Long> getParentScope(long id) {
163+
Account account = _accountDao.findById(id);
164+
if (account == null) {
165+
return null;
166+
}
167+
return new Pair<>(getScope().getParent(), account.getDomainId());
168+
}
159169
}

engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ImageStoreDetailsDaoImpl.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,17 @@
2020
import java.util.List;
2121
import java.util.Map;
2222

23+
import javax.inject.Inject;
24+
2325
import org.apache.cloudstack.api.ApiConstants;
2426
import org.apache.cloudstack.framework.config.ConfigKey;
2527
import org.apache.cloudstack.framework.config.ConfigKey.Scope;
2628
import org.apache.cloudstack.framework.config.ScopedConfigStorage;
2729
import org.apache.cloudstack.resourcedetail.ResourceDetailsDaoBase;
2830
import org.springframework.stereotype.Component;
2931

32+
import com.cloud.storage.ImageStore;
33+
import com.cloud.utils.Pair;
3034
import com.cloud.utils.crypt.DBEncryptionUtil;
3135
import com.cloud.utils.db.QueryBuilder;
3236
import com.cloud.utils.db.SearchBuilder;
@@ -36,6 +40,9 @@
3640

3741
@Component
3842
public class ImageStoreDetailsDaoImpl extends ResourceDetailsDaoBase<ImageStoreDetailVO> implements ImageStoreDetailsDao, ScopedConfigStorage {
43+
@Inject
44+
ImageStoreDao imageStoreDao;
45+
3946
protected final SearchBuilder<ImageStoreDetailVO> storeSearch;
4047

4148
public ImageStoreDetailsDaoImpl() {
@@ -118,4 +125,14 @@ public String getConfigValue(long id, ConfigKey<?> key) {
118125
public void addDetail(long resourceId, String key, String value, boolean display) {
119126
super.addDetail(new ImageStoreDetailVO(resourceId, key, value, display));
120127
}
128+
129+
@Override
130+
public Pair<Scope, Long> getParentScope(long id) {
131+
ImageStore store = imageStoreDao.findById(id);
132+
if (store == null) {
133+
return null;
134+
}
135+
return new Pair<>(getScope().getParent(), store.getDataCenterId());
136+
}
137+
121138
}

0 commit comments

Comments
 (0)