diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java index fd0dc7800d53..01f40b1ec547 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -135,6 +135,7 @@ public class ApiConstants { public static final String IP6_DNS1 = "ip6dns1"; public static final String IP6_DNS2 = "ip6dns2"; public static final String DOMAIN = "domain"; + public static final String DOMAIN_DETAILS = "domaindetails"; public static final String DOMAIN_PATH = "domainpath"; public static final String DOMAIN_ID = "domainid"; public static final String DOMAIN__ID = "domainId"; diff --git a/api/src/main/java/org/apache/cloudstack/api/response/DomainResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/DomainResponse.java index 6d5655269e96..e4e409a40ee1 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/DomainResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/DomainResponse.java @@ -26,6 +26,7 @@ import com.cloud.serializer.Param; import java.util.Date; +import java.util.Map; @EntityReference(value = Domain.class) public class DomainResponse extends BaseResponseWithAnnotations implements ResourceLimitAndCountResponse, SetResourceIconResponse { @@ -179,6 +180,10 @@ public class DomainResponse extends BaseResponseWithAnnotations implements Resou @Param(description = "Base64 string representation of the resource icon", since = "4.16.0.0") ResourceIconResponse icon; + @SerializedName(ApiConstants.DOMAIN_DETAILS) + @Param(description = "details for the domain") + private Map details; + public String getId() { return this.id; } @@ -438,4 +443,8 @@ public void setVmRunning(Integer vmRunning) { public void setResourceIconResponse(ResourceIconResponse icon) { this.icon = icon; } + + public void setDetails(Map details) { + this.details = details; + } } diff --git a/engine/schema/src/main/java/com/cloud/domain/dao/DomainDetailsDaoImpl.java b/engine/schema/src/main/java/com/cloud/domain/dao/DomainDetailsDaoImpl.java index ad7f70402074..dad3fe9ad1eb 100644 --- a/engine/schema/src/main/java/com/cloud/domain/dao/DomainDetailsDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/domain/dao/DomainDetailsDaoImpl.java @@ -20,7 +20,10 @@ import java.util.List; import java.util.Map; +import javax.inject.Inject; + import com.cloud.domain.DomainDetailVO; +import com.cloud.domain.DomainVO; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.QueryBuilder; import com.cloud.utils.db.SearchBuilder; @@ -30,10 +33,16 @@ 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.cloudstack.framework.config.dao.ConfigurationDao; public class DomainDetailsDaoImpl extends GenericDaoBase implements DomainDetailsDao, ScopedConfigStorage { protected final SearchBuilder domainSearch; + @Inject + protected DomainDao _domainDao; + @Inject + private ConfigurationDao _configDao; + protected DomainDetailsDaoImpl() { domainSearch = createSearchBuilder(); domainSearch.and("domainId", domainSearch.entity().getDomainId(), Op.EQ); @@ -98,7 +107,24 @@ public Scope getScope() { @Override public String getConfigValue(long id, ConfigKey key) { - DomainDetailVO vo = findDetail(id, key.key()); + DomainDetailVO vo = null; + String enableDomainSettingsForChildDomain = _configDao.getValue("enable.domain.settings.for.child.domain"); + if (!Boolean.parseBoolean(enableDomainSettingsForChildDomain)) { + vo = findDetail(id, key.key()); + return vo == null ? null : vo.getValue(); + } + DomainVO domain = _domainDao.findById(id); + // if value is not configured in domain then check its parent domain till ROOT + while (domain != null) { + vo = findDetail(domain.getId(), key.key()); + if (vo != null) { + break; + } else if (domain.getParent() != null) { + domain = _domainDao.findById(domain.getParent()); + } else { + break; + } + } return vo == null ? null : vo.getValue(); } } diff --git a/engine/schema/src/main/java/com/cloud/user/AccountDetailsDaoImpl.java b/engine/schema/src/main/java/com/cloud/user/AccountDetailsDaoImpl.java index c8675d874adc..5451192fc6d7 100644 --- a/engine/schema/src/main/java/com/cloud/user/AccountDetailsDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/user/AccountDetailsDaoImpl.java @@ -19,22 +19,40 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; +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 com.cloud.domain.DomainDetailVO; +import com.cloud.domain.DomainVO; +import com.cloud.domain.dao.DomainDetailsDao; +import com.cloud.domain.dao.DomainDao; +import com.cloud.user.dao.AccountDao; + import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.QueryBuilder; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria.Op; import com.cloud.utils.db.TransactionLegacy; +import org.apache.cloudstack.framework.config.dao.ConfigurationDao; public class AccountDetailsDaoImpl extends GenericDaoBase implements AccountDetailsDao, ScopedConfigStorage { protected final SearchBuilder accountSearch; + @Inject + protected AccountDao _accountDao; + @Inject + protected DomainDao _domainDao; + @Inject + protected DomainDetailsDao _domainDetailsDao; + @Inject + private ConfigurationDao _configDao; + protected AccountDetailsDaoImpl() { accountSearch = createSearchBuilder(); accountSearch.and("accountId", accountSearch.entity().getAccountId(), Op.EQ); @@ -99,7 +117,39 @@ public Scope getScope() { @Override public String getConfigValue(long id, ConfigKey key) { + // check if account level setting is configured AccountDetailVO vo = findDetail(id, key.key()); - return vo == null ? null : vo.getValue(); + String value = vo == null ? null : vo.getValue(); + if (value != null) { + return value; + } + + // if account level setting is not configured then check if + // we can take value from domain + String enableAccountSettingsForDomain = _configDao.getValue("enable.account.settings.for.domain"); + if (! Boolean.parseBoolean(enableAccountSettingsForDomain)) { + return null; + } + + // check if we can traverse till ROOT domain to get the value + String enableDomainSettingsForChildDomain = _configDao.getValue("enable.domain.settings.for.child.domain"); + if (Boolean.parseBoolean(enableDomainSettingsForChildDomain)) { + Optional account = Optional.ofNullable(_accountDao.findById(id)); + if (account.isPresent()) { + DomainVO domain = _domainDao.findById(account.get().getDomainId()); + while (domain != null) { + DomainDetailVO domainVO = _domainDetailsDao.findDetail(domain.getId(), key.key()); + if (domainVO != null) { + value = domainVO.getValue(); + break; + } else if (domain.getParent() != null) { + domain = _domainDao.findById(domain.getParent()); + } else { + break; + } + } + } + } + return value; } } diff --git a/framework/config/src/main/java/org/apache/cloudstack/framework/config/ConfigKey.java b/framework/config/src/main/java/org/apache/cloudstack/framework/config/ConfigKey.java index 2ace24dce5ac..d22fe1f1cd85 100644 --- a/framework/config/src/main/java/org/apache/cloudstack/framework/config/ConfigKey.java +++ b/framework/config/src/main/java/org/apache/cloudstack/framework/config/ConfigKey.java @@ -160,6 +160,19 @@ public T valueIn(Long id) { } } + public T valueInDomain(Long domainId) { + if (domainId == null) { + return value(); + } + + String value = s_depot != null ? s_depot.getDomainScope(this).getConfigValue(domainId, this) : null; + if (value == null) { + return value(); + } else { + return valueOf(value); + } + } + @SuppressWarnings("unchecked") protected T valueOf(String value) { Number multiplier = 1; diff --git a/framework/config/src/main/java/org/apache/cloudstack/framework/config/impl/ConfigDepotImpl.java b/framework/config/src/main/java/org/apache/cloudstack/framework/config/impl/ConfigDepotImpl.java index ddc06b652ebc..1cb93dc542a2 100644 --- a/framework/config/src/main/java/org/apache/cloudstack/framework/config/impl/ConfigDepotImpl.java +++ b/framework/config/src/main/java/org/apache/cloudstack/framework/config/impl/ConfigDepotImpl.java @@ -186,6 +186,16 @@ public ScopedConfigStorage findScopedConfigStorage(ConfigKey config) { 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 getScopedStorages() { return _scopedStorages; } diff --git a/server/src/main/java/com/cloud/api/ApiDBUtils.java b/server/src/main/java/com/cloud/api/ApiDBUtils.java index 36942444bea8..28bafe2cb8cc 100644 --- a/server/src/main/java/com/cloud/api/ApiDBUtils.java +++ b/server/src/main/java/com/cloud/api/ApiDBUtils.java @@ -16,77 +16,6 @@ // under the License. package com.cloud.api; -import java.util.ArrayList; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.List; -import java.util.ListIterator; -import java.util.Map; -import java.util.Set; - -import javax.annotation.PostConstruct; -import javax.inject.Inject; - -import com.cloud.resource.icon.ResourceIconVO; -import com.cloud.resource.icon.dao.ResourceIconDao; -import com.cloud.server.ResourceIcon; -import org.apache.cloudstack.acl.Role; -import org.apache.cloudstack.acl.RoleService; -import org.apache.cloudstack.affinity.AffinityGroup; -import org.apache.cloudstack.affinity.AffinityGroupResponse; -import org.apache.cloudstack.affinity.dao.AffinityGroupDao; -import org.apache.cloudstack.api.ApiCommandJobType; -import org.apache.cloudstack.api.ApiConstants.DomainDetails; -import org.apache.cloudstack.api.ApiConstants.HostDetails; -import org.apache.cloudstack.api.ApiConstants.VMDetails; -import org.apache.cloudstack.api.ResponseObject.ResponseView; -import org.apache.cloudstack.api.response.AccountResponse; -import org.apache.cloudstack.api.response.AsyncJobResponse; -import org.apache.cloudstack.api.response.BackupOfferingResponse; -import org.apache.cloudstack.api.response.BackupResponse; -import org.apache.cloudstack.api.response.BackupScheduleResponse; -import org.apache.cloudstack.api.response.DiskOfferingResponse; -import org.apache.cloudstack.api.response.DomainResponse; -import org.apache.cloudstack.api.response.DomainRouterResponse; -import org.apache.cloudstack.api.response.EventResponse; -import org.apache.cloudstack.api.response.HostForMigrationResponse; -import org.apache.cloudstack.api.response.HostResponse; -import org.apache.cloudstack.api.response.HostTagResponse; -import org.apache.cloudstack.api.response.ImageStoreResponse; -import org.apache.cloudstack.api.response.InstanceGroupResponse; -import org.apache.cloudstack.api.response.NetworkOfferingResponse; -import org.apache.cloudstack.api.response.ProjectAccountResponse; -import org.apache.cloudstack.api.response.ProjectInvitationResponse; -import org.apache.cloudstack.api.response.ProjectResponse; -import org.apache.cloudstack.api.response.ResourceIconResponse; -import org.apache.cloudstack.api.response.ResourceTagResponse; -import org.apache.cloudstack.api.response.SecurityGroupResponse; -import org.apache.cloudstack.api.response.ServiceOfferingResponse; -import org.apache.cloudstack.api.response.StoragePoolResponse; -import org.apache.cloudstack.api.response.StorageTagResponse; -import org.apache.cloudstack.api.response.TemplateResponse; -import org.apache.cloudstack.api.response.UserResponse; -import org.apache.cloudstack.api.response.UserVmResponse; -import org.apache.cloudstack.api.response.VolumeResponse; -import org.apache.cloudstack.api.response.VpcOfferingResponse; -import org.apache.cloudstack.api.response.ZoneResponse; -import org.apache.cloudstack.backup.Backup; -import org.apache.cloudstack.backup.BackupOffering; -import org.apache.cloudstack.backup.BackupSchedule; -import org.apache.cloudstack.backup.dao.BackupDao; -import org.apache.cloudstack.backup.dao.BackupOfferingDao; -import org.apache.cloudstack.backup.dao.BackupScheduleDao; -import org.apache.cloudstack.context.CallContext; -import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; -import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService; -import org.apache.cloudstack.framework.config.dao.ConfigurationDao; -import org.apache.cloudstack.framework.jobs.AsyncJob; -import org.apache.cloudstack.framework.jobs.AsyncJobManager; -import org.apache.cloudstack.framework.jobs.dao.AsyncJobDao; -import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao; -import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; -import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; - import com.cloud.agent.api.VgpuTypesInfo; import com.cloud.api.query.dao.AccountJoinDao; import com.cloud.api.query.dao.AffinityGroupJoinDao; @@ -163,6 +92,7 @@ import com.cloud.domain.Domain; import com.cloud.domain.DomainVO; import com.cloud.domain.dao.DomainDao; +import com.cloud.domain.dao.DomainDetailsDao; import com.cloud.event.Event; import com.cloud.event.dao.EventJoinDao; import com.cloud.exception.InvalidParameterValueException; @@ -256,13 +186,16 @@ import com.cloud.projects.ProjectService; import com.cloud.region.ha.GlobalLoadBalancingRulesService; import com.cloud.resource.ResourceManager; +import com.cloud.resource.icon.ResourceIconVO; +import com.cloud.resource.icon.dao.ResourceIconDao; import com.cloud.server.ManagementServer; +import com.cloud.server.ResourceIcon; +import com.cloud.server.ResourceManagerUtil; import com.cloud.server.ResourceMetaDataService; import com.cloud.server.ResourceTag; import com.cloud.server.ResourceTag.ResourceObjectType; import com.cloud.server.StatsCollector; import com.cloud.server.TaggedResourceService; -import com.cloud.server.ResourceManagerUtil; import com.cloud.service.ServiceOfferingDetailsVO; import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; @@ -340,6 +273,72 @@ import com.cloud.vm.dao.VMInstanceDao; import com.cloud.vm.snapshot.VMSnapshot; import com.cloud.vm.snapshot.dao.VMSnapshotDao; +import org.apache.cloudstack.acl.Role; +import org.apache.cloudstack.acl.RoleService; +import org.apache.cloudstack.affinity.AffinityGroup; +import org.apache.cloudstack.affinity.AffinityGroupResponse; +import org.apache.cloudstack.affinity.dao.AffinityGroupDao; +import org.apache.cloudstack.api.ApiCommandJobType; +import org.apache.cloudstack.api.ApiConstants.DomainDetails; +import org.apache.cloudstack.api.ApiConstants.HostDetails; +import org.apache.cloudstack.api.ApiConstants.VMDetails; +import org.apache.cloudstack.api.ResponseObject.ResponseView; +import org.apache.cloudstack.api.response.AccountResponse; +import org.apache.cloudstack.api.response.AsyncJobResponse; +import org.apache.cloudstack.api.response.BackupOfferingResponse; +import org.apache.cloudstack.api.response.BackupResponse; +import org.apache.cloudstack.api.response.BackupScheduleResponse; +import org.apache.cloudstack.api.response.DiskOfferingResponse; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.DomainRouterResponse; +import org.apache.cloudstack.api.response.EventResponse; +import org.apache.cloudstack.api.response.HostForMigrationResponse; +import org.apache.cloudstack.api.response.HostResponse; +import org.apache.cloudstack.api.response.HostTagResponse; +import org.apache.cloudstack.api.response.ImageStoreResponse; +import org.apache.cloudstack.api.response.InstanceGroupResponse; +import org.apache.cloudstack.api.response.NetworkOfferingResponse; +import org.apache.cloudstack.api.response.ProjectAccountResponse; +import org.apache.cloudstack.api.response.ProjectInvitationResponse; +import org.apache.cloudstack.api.response.ProjectResponse; +import org.apache.cloudstack.api.response.ResourceIconResponse; +import org.apache.cloudstack.api.response.ResourceTagResponse; +import org.apache.cloudstack.api.response.SecurityGroupResponse; +import org.apache.cloudstack.api.response.ServiceOfferingResponse; +import org.apache.cloudstack.api.response.StoragePoolResponse; +import org.apache.cloudstack.api.response.StorageTagResponse; +import org.apache.cloudstack.api.response.TemplateResponse; +import org.apache.cloudstack.api.response.UserResponse; +import org.apache.cloudstack.api.response.UserVmResponse; +import org.apache.cloudstack.api.response.VolumeResponse; +import org.apache.cloudstack.api.response.VpcOfferingResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.backup.Backup; +import org.apache.cloudstack.backup.BackupOffering; +import org.apache.cloudstack.backup.BackupSchedule; +import org.apache.cloudstack.backup.dao.BackupDao; +import org.apache.cloudstack.backup.dao.BackupOfferingDao; +import org.apache.cloudstack.backup.dao.BackupScheduleDao; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; +import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService; +import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.framework.jobs.AsyncJob; +import org.apache.cloudstack.framework.jobs.AsyncJobManager; +import org.apache.cloudstack.framework.jobs.dao.AsyncJobDao; +import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao; +import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; + +import javax.annotation.PostConstruct; +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Set; public class ApiDBUtils { private static ManagementServer s_ms; @@ -407,6 +406,7 @@ public class ApiDBUtils { static ResourceLimitService s_resourceLimitMgr; static ProjectService s_projectMgr; static ResourceManager s_resourceMgr; + static DomainDetailsDao s_domainDetailsDao; static AccountDetailsDao s_accountDetailsDao; static NetworkDomainDao s_networkDomainDao; static HighAvailabilityManager s_haMgr; @@ -596,6 +596,8 @@ public class ApiDBUtils { @Inject private ResourceManager resourceMgr; @Inject + private DomainDetailsDao domainDetailsDao; + @Inject private AccountDetailsDao accountDetailsDao; @Inject private NetworkDomainDao networkDomainDao; @@ -784,6 +786,7 @@ void init() { s_resourceLimitMgr = resourceLimitMgr; s_projectMgr = projectMgr; s_resourceMgr = resourceMgr; + s_domainDetailsDao = domainDetailsDao; s_accountDetailsDao = accountDetailsDao; s_networkDomainDao = networkDomainDao; s_haMgr = haMgr; @@ -1436,6 +1439,11 @@ public static long getProjectOwnwerId(long projectId) { return s_projectMgr.getProjectOwner(projectId).getId(); } + public static Map getDomainDetails(long domainId) { + Map details = s_domainDetailsDao.findDetails(domainId); + return details.isEmpty() ? null : details; + } + public static Map getAccountDetails(long accountId) { Map details = s_accountDetailsDao.findDetails(accountId); return details.isEmpty() ? null : details; diff --git a/server/src/main/java/com/cloud/api/query/dao/DomainJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/DomainJoinDaoImpl.java index ff0736e933e6..529761a95628 100644 --- a/server/src/main/java/com/cloud/api/query/dao/DomainJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/DomainJoinDaoImpl.java @@ -102,6 +102,7 @@ public DomainResponse newDomainResponse(ResponseView view, EnumSet SET_HOST_DOWN_TO_MAINTENANCE = new ConfigKey(Boolean.class, "set.host.down.to.maintenance", "Advanced", "false", "Indicates whether the host in down state can be put into maintenance state so thats its not enabled after it comes back.", true, ConfigKey.Scope.Zone, null); + public static final ConfigKey ENABLE_ACCOUNT_SETTINGS_FOR_DOMAIN = new ConfigKey(Boolean.class, "enable.account.settings.for.domain", "Advanced", "false", + "Indicates whether to add account settings for domain. If true, account settings will be added to domain settings, all accounts in the domain will inherit the domain setting if account setting is not set.", true, ConfigKey.Scope.Global, null); + public static final ConfigKey ENABLE_DOMAIN_SETTINGS_FOR_CHILD_DOMAIN = new ConfigKey(Boolean.class, "enable.domain.settings.for.child.domain", "Advanced", "false", + "Indicates whether the settings of parent domain should be applied for child domain. If true, the child domain will get value from parent domain if its not configured in child domain else global value is taken.", + true, ConfigKey.Scope.Global, null); public static ConfigKey VM_SERVICE_OFFERING_MAX_CPU_CORES = new ConfigKey("Advanced", Integer.class, "vm.serviceoffering.cpu.cores.max", "0", "Maximum CPU cores " + "for vm service offering. If 0 - no limitation", true); @@ -883,7 +888,9 @@ private String validateConfigurationValue(final String name, String value, final final String configScope = cfg.getScope(); if (scope != null) { - if (!configScope.contains(scope)) { + if (!configScope.contains(scope) && + !(ENABLE_ACCOUNT_SETTINGS_FOR_DOMAIN.value() && configScope.contains(ConfigKey.Scope.Account.toString()) && + scope.equals(ConfigKey.Scope.Domain.toString()))) { s_logger.error("Invalid scope id provided for the parameter " + name); return "Invalid scope id provided for the parameter " + name; } @@ -6867,6 +6874,8 @@ public String getConfigComponentName() { public ConfigKey[] getConfigKeys() { return new ConfigKey[] {SystemVMUseLocalStorage, IOPS_MAX_READ_LENGTH, IOPS_MAX_WRITE_LENGTH, BYTES_MAX_READ_LENGTH, BYTES_MAX_WRITE_LENGTH, ADD_HOST_ON_SERVICE_RESTART_KVM, SET_HOST_DOWN_TO_MAINTENANCE, VM_SERVICE_OFFERING_MAX_CPU_CORES, - VM_SERVICE_OFFERING_MAX_RAM_SIZE, VM_USERDATA_MAX_LENGTH, MIGRATE_VM_ACROSS_CLUSTERS}; + VM_SERVICE_OFFERING_MAX_RAM_SIZE, VM_USERDATA_MAX_LENGTH, MIGRATE_VM_ACROSS_CLUSTERS, + ENABLE_ACCOUNT_SETTINGS_FOR_DOMAIN, ENABLE_DOMAIN_SETTINGS_FOR_CHILD_DOMAIN + }; } } diff --git a/server/src/main/java/com/cloud/server/ManagementServerImpl.java b/server/src/main/java/com/cloud/server/ManagementServerImpl.java index 075b338f1bba..13f6440e5a83 100644 --- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java +++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java @@ -597,6 +597,7 @@ import com.cloud.capacity.dao.CapacityDaoImpl.SummedCapacity; import com.cloud.cluster.ClusterManager; import com.cloud.configuration.Config; +import com.cloud.configuration.ConfigurationManagerImpl; import com.cloud.consoleproxy.ConsoleProxyManagementState; import com.cloud.consoleproxy.ConsoleProxyManager; import com.cloud.dc.AccountVlanMapVO; @@ -2040,7 +2041,12 @@ public Pair, Integer> searchForConfigurations(fina if (scope != null && !scope.isEmpty()) { // getting the list of parameters at requested scope - sc.addAnd("scope", SearchCriteria.Op.EQ, scope); + if (ConfigurationManagerImpl.ENABLE_ACCOUNT_SETTINGS_FOR_DOMAIN.value() + && scope.equals(ConfigKey.Scope.Domain.toString())) { + sc.addAnd("scope", SearchCriteria.Op.IN, ConfigKey.Scope.Domain.toString(), ConfigKey.Scope.Account.toString()); + } else { + sc.addAnd("scope", SearchCriteria.Op.EQ, scope); + } } final Pair, Integer> result = _configDao.searchAndCount(sc, searchFilter); @@ -2053,7 +2059,13 @@ public Pair, Integer> searchForConfigurations(fina if (configVo != null) { final ConfigKey key = _configDepot.get(param.getName()); if (key != null) { - configVo.setValue(key.valueIn(id) == null ? null : key.valueIn(id).toString()); + if (scope.equals(ConfigKey.Scope.Domain.toString())) { + Object value = key.valueInDomain(id); + configVo.setValue(value == null ? null : value.toString()); + } else { + Object value = key.valueIn(id); + configVo.setValue(value == null ? null : value.toString()); + } configVOList.add(configVo); } else { s_logger.warn("ConfigDepot could not find parameter " + param.getName() + " for scope " + scope); diff --git a/test/integration/smoke/test_enable_account_settings_for_domain.py b/test/integration/smoke/test_enable_account_settings_for_domain.py new file mode 100644 index 000000000000..09550ed1e27b --- /dev/null +++ b/test/integration/smoke/test_enable_account_settings_for_domain.py @@ -0,0 +1,514 @@ +# 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) +from marvin.lib.common import (get_domain, + get_zone) + +class TestDedicatePublicIPRange(cloudstackTestCase): + + @classmethod + def setUpClass(self): + self.testClient = super( + TestDedicatePublicIPRange, + self).getClsTestClient() + self.apiclient = self.testClient.getApiClient() + self.testdata = self.testClient.getParsedTestDataConfig() + self.hypervisor = self.testClient.getHypervisorInfo() + # Get Zone, Domain + self.domain = get_domain(self.apiclient) + self.zone = get_zone(self.apiclient, self.testClient.getZoneForTests()) + self._cleanup = [] + return + + @classmethod + def tearDownClass(self): + try: + # Cleanup resources used + cleanup_resources(self.apiclient, self._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + + return + + def tearDown(self): + try: + # Clean up + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @attr(tags=["advanced", "advancedsg"], required_hardware="false") + def test_01_disable_account_settings_for_domain(self): + """Disable account settings for domain + + # Validate the following: + # (1) Change global setting enable.account.settings.for.domain to false (default value) + # (2) create domain/account + # (3) list global settings vmsnapshot.expire.interval and get value (original value) + # (4) list account settings with name=(3), value should be same as (3) + # (5) list domain settings with name=(3), should return null/empty + # (6) change global settings (3) to original +10 + # (7) list account settings with name=(3), value should be same as (6) + # (8) change account settings (4) to original +20 + # (9) list account settings with name=(3), value should be same as (8) + # (10) update domain settings with name=(3), should get exception + # (11) reset vmsnapshot.expire.interval to original value (3) + """ + + config_name = "vmsnapshot.expire.interval" + + # (1) Change global setting enable.account.settings.for.domain to false (default value) + Configurations.update( + self.apiclient, + name="enable.account.settings.for.domain", + value="false" + ) + Configurations.update( + self.apiclient, + name="enable.domain.settings.for.child.domain", + value="false" + ) + + # (2) create domain/account + user_domain = Domain.create( + self.apiclient, + services=self.testdata["acl"]["domain2"], + parentdomainid=self.domain.id) + account = Account.create( + self.apiclient, + self.testdata["acl"]["accountD2"], + domainid=user_domain.id + ) + self.cleanup.append(account) + self.cleanup.append(user_domain) + + # (3) list global settings vmsnapshot.expire.interval and get value (original value) + configs = Configurations.list( + self.apiclient, + name=config_name) + self.assertIsNotNone(configs, "Fail to get global setting %s " % config_name) + orig_value = int(configs[0].value) + + # (4) list account settings with name=(3), value should be same as (3) + configs = Configurations.list( + self.apiclient, + accountid=account.id, + name=config_name) + self.assertIsNotNone(configs, "Fail to get account setting %s " % config_name) + account_value = int(configs[0].value) + self.assertEqual(orig_value, account_value, "Account setting is not equal to global setting") + + # (5) list domain settings with name=(3), should return null/empty + configs = Configurations.list( + self.apiclient, + domainid=user_domain.id, + name=config_name) + self.assertIsNone(configs, "Domain setting %s should not exist" % config_name) + + # (6) change global settings (3) to original +10 + new_value = orig_value + 10 + Configurations.update( + self.apiclient, + name=config_name, + value=new_value + ) + + # (7) list account settings with name=(3), value should be same as (6) + configs = Configurations.list( + self.apiclient, + accountid=account.id, + name=config_name) + self.assertIsNotNone(configs, "Fail to get account setting %s " % config_name) + account_value = int(configs[0].value) + self.assertEqual(new_value, account_value, "Account setting is not equal to new value of global setting") + + # (8) change account settings (4) to original +20 + new_value = orig_value + 20 + Configurations.update( + self.apiclient, + accountid=account.id, + name=config_name, + value=new_value + ) + + # (9) list account settings with name=(3), value should be same as (8) + configs = Configurations.list( + self.apiclient, + accountid=account.id, + name=config_name) + self.assertIsNotNone(configs, "Fail to get account setting %s " % config_name) + account_value = int(configs[0].value) + self.assertEqual(new_value, account_value, "Account setting is not equal to new value of account setting") + + # (10) update domain settings with name=(3), should get exception + try: + Configurations.update( + self.apiclient, + domainid=user_domain.id, + name=config_name, + value=new_value + ) + self.fail("Updating domain setting should fail") + except Exception as e: + self.debug("Updating domain setting failed as expected with Exception %s" % e) + + # (11) reset vmsnapshot.expire.interval to original value (3) + Configurations.update( + self.apiclient, + name=config_name, + value=orig_value + ) + + return + + @attr(tags=["advanced", "advancedsg"], required_hardware="false") + def test_02_enable_account_settings_for_domain(self): + """Enable account settings for domain + + # Validate the following: + # (1) Change global setting enable.account.settings.for.domain to true + # (2) create domain/account + # (3) list global settings vmsnapshot.expire.interval and get value (original value) + # (4) list domain settings with name=(3), value should be same as (3) + # (5) list account settings with name=(3), value should be same as (4) = (5) + # (6) change global settings (3) to original +10 + # (7) list domain settings with name=(3), value should be same as (6) + # (8) list account settings with name=(3), value should be same as (6)=(7) + # (9) change ROOT domain settings (4) to original +20 + # (10) list domain settings with name=(3), value should be same as (9) + # (11) list account settings with name=(3), value should be same as (9)=(10) + # (12) change domain settings (4) to original +30 + # (13) list domain settings with name=(3), value should be same as (12) + # (14) list account settings with name=(3), value should be same as (12)=(13) + # (15) change account settings (4) to original +40 + # (16) list account settings with name=(3), value should be same as (15) + # (17) reset vmsnapshot.expire.interval to original value (3) + """ + + config_name = "vmsnapshot.expire.interval" + + # (1) Change global setting enable.account.settings.for.domain to true + Configurations.update( + self.apiclient, + name="enable.account.settings.for.domain", + value="true" + ) + + # (2) create domain/account + user_domain = Domain.create( + self.apiclient, + services=self.testdata["acl"]["domain2"], + parentdomainid=self.domain.id) + account = Account.create( + self.apiclient, + self.testdata["acl"]["accountD2"], + domainid=user_domain.id + ) + self.cleanup.append(account) + self.cleanup.append(user_domain) + + # (3) list global settings vmsnapshot.expire.interval and get value (original value) + configs = Configurations.list( + self.apiclient, + name=config_name) + self.assertIsNotNone(configs, "Fail to get global setting %s " % config_name) + orig_value = int(configs[0].value) + + # (4) list domain settings with name=(3), value should be same as (3) + configs = Configurations.list( + self.apiclient, + domainid=user_domain.id, + name=config_name) + self.assertIsNotNone(configs, "Fail to get domain setting %s " % config_name) + domain_value = int(configs[0].value) + self.assertEqual(orig_value, domain_value, "Domain setting is not equal to global setting") + + # (5) list account settings with name=(3), value should be same as (4) = (5) + configs = Configurations.list( + self.apiclient, + accountid=account.id, + name=config_name) + self.assertIsNotNone(configs, "Fail to get account setting %s " % config_name) + account_value = int(configs[0].value) + self.assertEqual(orig_value, account_value, "Account setting is not equal to global setting") + + # (6) change global settings (3) to original +10 + new_value = orig_value + 10 + Configurations.update( + self.apiclient, + name=config_name, + value=new_value + ) + + # (7) list domain settings with name=(3), value should be same as (6) + configs = Configurations.list( + self.apiclient, + domainid=user_domain.id, + name=config_name) + self.assertIsNotNone(configs, "Fail to get domain setting %s " % config_name) + domain_value = int(configs[0].value) + self.assertEqual(new_value, domain_value, "Domain setting is not equal to new value of global setting") + + # (8) list account settings with name=(3), value should be same as (6)=(7) + configs = Configurations.list( + self.apiclient, + accountid=account.id, + name=config_name) + self.assertIsNotNone(configs, "Fail to get account setting %s " % config_name) + account_value = int(configs[0].value) + self.assertEqual(new_value, account_value, "Account setting is not equal to new value of global setting") + + old_domain_value = new_value + + # (9) change ROOT domain settings (4) to original +20 + new_value = orig_value + 20 + Configurations.update( + self.apiclient, + domainid=self.domain.id, + name=config_name, + value=new_value + ) + + # (10) list domain settings with name=(3), value should be same as (9) + configs = Configurations.list( + self.apiclient, + domainid=user_domain.id, + name=config_name) + self.assertIsNotNone(configs, "Fail to get domain setting %s " % config_name) + domain_value = int(configs[0].value) + self.assertEqual(old_domain_value, domain_value, "Domain setting is not equal to new value of ROOT domain setting") + + # (11) list account settings with name=(3), value should be same as (9)=(10) + configs = Configurations.list( + self.apiclient, + accountid=account.id, + name=config_name) + self.assertIsNotNone(configs, "Fail to get account setting %s " % config_name) + account_value = int(configs[0].value) + self.assertEqual(old_domain_value, account_value, "Account setting is not equal to new value of ROOT domain setting") + + # (12) change domain settings (4) to original +30 + new_value = orig_value + 30 + Configurations.update( + self.apiclient, + domainid=user_domain.id, + name=config_name, + value=new_value + ) + + # (13) list domain settings with name=(3), value should be same as (12) + configs = Configurations.list( + self.apiclient, + domainid=user_domain.id, + name=config_name) + self.assertIsNotNone(configs, "Fail to get domain setting %s " % config_name) + domain_value = int(configs[0].value) + self.assertEqual(new_value, domain_value, "Domain setting is not equal to new value of domain setting") + + # (14) list account settings with name=(3), value should be same as (12)=(13) + configs = Configurations.list( + self.apiclient, + accountid=account.id, + name=config_name) + self.assertIsNotNone(configs, "Fail to get account setting %s " % config_name) + account_value = int(configs[0].value) + self.assertEqual(old_domain_value, account_value, "Account setting is not equal to new value of domain setting") + + # (15) change account settings (4) to original +40 + new_value = orig_value + 40 + Configurations.update( + self.apiclient, + accountid=account.id, + name=config_name, + value=new_value + ) + + # (16) list account settings with name=(3), value should be same as (15) + configs = Configurations.list( + self.apiclient, + accountid=account.id, + name=config_name) + self.assertIsNotNone(configs, "Fail to get account setting %s " % config_name) + account_value = int(configs[0].value) + self.assertEqual(new_value, account_value, "Account setting is not equal to new value of account setting") + + # (17) reset vmsnapshot.expire.interval to original value (3) + Configurations.update( + self.apiclient, + name=config_name, + value=orig_value + ) + + Configurations.update( + self.apiclient, + name="enable.account.settings.for.domain", + value="false" + ) + + return + + @attr(tags=["advanced", "advancedsg"], required_hardware="false") + def test_03_enable_account_settings_for_domain(self): + """Enable account settings for domain + + # Validate the following: + # (1) Change global setting enable.account.settings.for.domain to true + # (2) create domain/account + # (3) list global settings vmsnapshot.expire.interval and get value (original value) + # (4) list domain settings with name=(3), value should be same as (3) + # (5) list account settings with name=(3), value should be same as (4) = (5) + # (6) change global settings (3) to original +10 + # (7) change global setting enable.domain.settings.for.child.domain to true + # (8) change domain setting (3) to original +30 + # (9) list domain settings with name=(3), value should be same as (8) + # (10) list account settings with name=(3), value should be same as (9)=(8) + # (11) change acount setting (3) to original +50 + # (12) list account settings with name=(3), value should be same as (10) + """ + + config_name = "vmsnapshot.expire.interval" + + # (1) Change global setting enable.account.settings.for.domain to true + Configurations.update( + self.apiclient, + name="enable.account.settings.for.domain", + value="true" + ) + + # (2) create domain/account + user_domain = Domain.create( + self.apiclient, + services=self.testdata["acl"]["domain2"], + parentdomainid=self.domain.id) + account = Account.create( + self.apiclient, + self.testdata["acl"]["accountD2"], + domainid=user_domain.id + ) + self.cleanup.append(account) + self.cleanup.append(user_domain) + + # (3) list global settings vmsnapshot.expire.interval and get value (original value) + configs = Configurations.list( + self.apiclient, + name=config_name) + self.assertIsNotNone(configs, "Fail to get global setting %s " % config_name) + orig_value = int(configs[0].value) + + # (4) list domain settings with name=(3), value should be same as (3) + configs = Configurations.list( + self.apiclient, + domainid=user_domain.id, + name=config_name) + self.assertIsNotNone(configs, "Fail to get domain setting %s " % config_name) + domain_value = int(configs[0].value) + self.assertEqual(orig_value, domain_value, "Domain setting is not equal to global setting") + + # (5) list account settings with name=(3), value should be same as (4) = (5) + configs = Configurations.list( + self.apiclient, + accountid=account.id, + name=config_name) + self.assertIsNotNone(configs, "Fail to get account setting %s " % config_name) + account_value = int(configs[0].value) + self.assertEqual(orig_value, account_value, "Account setting is not equal to global setting") + + # (6) change global settings (3) to original +10 + new_value = orig_value + 10 + Configurations.update( + self.apiclient, + name=config_name, + value=new_value + ) + + # (7) change global setting enable.domain.settings.for.child.domain to true + Configurations.update( + self.apiclient, + name="enable.domain.settings.for.child.domain", + value="true" + ) + + # (8) change domain setting (3) to original +30 + new_domain_value = domain_value + 30 + Configurations.update( + self.apiclient, + name=config_name, + domainid=user_domain.id, + value=new_domain_value + ) + + # (9) list domain settings with name=(3), value should be same as (8) + configs = Configurations.list( + self.apiclient, + domainid=user_domain.id, + name=config_name) + self.assertIsNotNone(configs, "Fail to get domain setting %s " % config_name) + domain_value = int(configs[0].value) + self.assertEqual(new_domain_value, domain_value, "Domain setting is not equal to new value of global setting") + + # (10) list account settings with name=(3), value should be same as (9)=(8) + configs = Configurations.list( + self.apiclient, + accountid=account.id, + name=config_name) + self.assertIsNotNone(configs, "Fail to get account setting %s " % config_name) + account_value = int(configs[0].value) + self.assertEqual(new_domain_value, account_value, "Account setting is not equal to new value of global setting") + + # (11) change acount setting (3) to original +50 + new_account_value = account_value + 50 + Configurations.update( + self.apiclient, + accountid=account.id, + name=config_name, + value=new_account_value + ) + + # (12) list account settings with name=(3), value should be same as (10) + configs = Configurations.list( + self.apiclient, + accountid=account.id, + name=config_name) + self.assertIsNotNone(configs, "Fail to get account setting %s " % config_name) + account_value = int(configs[0].value) + self.assertEqual(new_account_value, account_value, "Account setting is not equal to new value of global setting") + + Configurations.update( + self.apiclient, + name="enable.account.settings.for.domain", + value="false" + ) + Configurations.update( + self.apiclient, + name="enable.domain.settings.for.child.domain", + value="false" + )