Improve global settings UI to be more intuitive/logical (#5797)

Co-authored-by: Suresh Kumar Anaparti <suresh.anaparti@shapeblue.com>
Co-authored-by: nvazquez <nicovazquez90@gmail.com>
Co-authored-by: davidjumani <dj.davidjumani1994@gmail.com>
Co-authored-by: dahn <daan.hoogland@gmail.com>
Co-authored-by: dahn <daan@onecht.net>
This commit is contained in:
Suresh Kumar Anaparti 2023-01-31 15:53:43 +05:30 committed by GitHub
parent 3b6ce97097
commit d8c7e34b38
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
81 changed files with 3249 additions and 172 deletions

View File

@ -22,6 +22,7 @@ import java.util.Map;
import com.cloud.user.UserData;
import org.apache.cloudstack.api.command.admin.cluster.ListClustersCmd;
import org.apache.cloudstack.api.command.admin.config.ListCfgGroupsByCmd;
import org.apache.cloudstack.api.command.admin.config.ListCfgsByCmd;
import org.apache.cloudstack.api.command.admin.config.UpdateHypervisorCapabilitiesCmd;
import org.apache.cloudstack.api.command.admin.guest.AddGuestOsCmd;
@ -63,6 +64,7 @@ import org.apache.cloudstack.api.command.user.userdata.RegisterUserDataCmd;
import org.apache.cloudstack.api.command.user.vm.GetVMPasswordCmd;
import org.apache.cloudstack.api.command.user.vmgroup.UpdateVMGroupCmd;
import org.apache.cloudstack.config.Configuration;
import org.apache.cloudstack.config.ConfigurationGroup;
import com.cloud.alert.Alert;
import com.cloud.capacity.Capacity;
@ -102,6 +104,13 @@ public interface ManagementService {
*/
Pair<List<? extends Configuration>, Integer> searchForConfigurations(ListCfgsByCmd c);
/**
* returns the the configuration groups
*
* @return list of configuration groups
*/
Pair<List<? extends ConfigurationGroup>, Integer> listConfigurationGroups(ListCfgGroupsByCmd cmd);
/**
* Searches for Clusters by the specified search criteria
*

View File

@ -118,6 +118,7 @@ public class ApiConstants {
public static final String MAX_IOPS = "maxiops";
public static final String HYPERVISOR_SNAPSHOT_RESERVE = "hypervisorsnapshotreserve";
public static final String DATADISK_OFFERING_LIST = "datadiskofferinglist";
public static final String DEFAULT_VALUE = "defaultvalue";
public static final String DESCRIPTION = "description";
public static final String DESTINATION = "destination";
public static final String DESTINATION_ZONE_ID = "destzoneid";
@ -187,6 +188,7 @@ public class ApiConstants {
public static final String GATEWAY = "gateway";
public static final String IP6_GATEWAY = "ip6gateway";
public static final String GROUP = "group";
public static final String SUBGROUP = "subgroup";
public static final String GROUP_ID = "groupid";
public static final String GSLB_LB_METHOD = "gslblbmethod";
public static final String GSLB_SERVICE_DOMAIN_NAME = "gslbdomainname";
@ -301,6 +303,7 @@ public class ApiConstants {
public static final String IS_DEFAULT_USE = "defaultuse";
public static final String OLD_FORMAT = "oldformat";
public static final String OP = "op";
public static final String OPTIONS = "options";
public static final String OS_CATEGORY_ID = "oscategoryid";
public static final String OS_CATEGORY_NAME = "oscategoryname";
public static final String OS_ID = "osid";
@ -312,6 +315,7 @@ public class ApiConstants {
public static final String OUTPUT = "output";
public static final String PROPERTIES = "properties";
public static final String PARAMS = "params";
public static final String PARENT = "parent";
public static final String PARENT_ID = "parentid";
public static final String PARENT_DOMAIN_ID = "parentdomainid";
public static final String PARENT_TEMPLATE_ID = "parenttemplateid";
@ -333,6 +337,7 @@ public class ApiConstants {
public static final String POSITION = "position";
public static final String POST_URL = "postURL";
public static final String POWER_STATE = "powerstate";
public static final String PRECEDENCE = "precedence";
public static final String PRIVATE_INTERFACE = "privateinterface";
public static final String PRIVATE_IP = "privateip";
public static final String PRIVATE_PORT = "privateport";

View File

@ -40,6 +40,7 @@ import org.apache.cloudstack.api.response.BackupScheduleResponse;
import org.apache.cloudstack.api.response.CapacityResponse;
import org.apache.cloudstack.api.response.ClusterResponse;
import org.apache.cloudstack.api.response.ConditionResponse;
import org.apache.cloudstack.api.response.ConfigurationGroupResponse;
import org.apache.cloudstack.api.response.ConfigurationResponse;
import org.apache.cloudstack.api.response.CounterResponse;
import org.apache.cloudstack.api.response.CreateCmdResponse;
@ -133,6 +134,7 @@ import org.apache.cloudstack.backup.Backup;
import org.apache.cloudstack.backup.BackupOffering;
import org.apache.cloudstack.backup.BackupSchedule;
import org.apache.cloudstack.config.Configuration;
import org.apache.cloudstack.config.ConfigurationGroup;
import org.apache.cloudstack.direct.download.DirectDownloadCertificate;
import org.apache.cloudstack.direct.download.DirectDownloadCertificateHostMap;
import org.apache.cloudstack.direct.download.DirectDownloadManager;
@ -244,6 +246,8 @@ public interface ResponseGenerator {
ConfigurationResponse createConfigurationResponse(Configuration cfg);
ConfigurationGroupResponse createConfigurationGroupResponse(ConfigurationGroup cfgGroup);
SnapshotResponse createSnapshotResponse(Snapshot snapshot);
SnapshotPolicyResponse createSnapshotPolicyResponse(SnapshotPolicy policy);

View File

@ -0,0 +1,79 @@
// 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.admin.config;
import java.util.ArrayList;
import java.util.List;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.BaseListCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.response.ConfigurationGroupResponse;
import org.apache.cloudstack.api.response.ListResponse;
import org.apache.cloudstack.config.ConfigurationGroup;
import org.apache.log4j.Logger;
import com.cloud.utils.Pair;
@APICommand(name = ListCfgGroupsByCmd.APINAME, description = "Lists all configuration groups (primarily used for UI).", responseObject = ConfigurationGroupResponse.class,
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.18.0")
public class ListCfgGroupsByCmd extends BaseListCmd {
public static final Logger s_logger = Logger.getLogger(ListCfgGroupsByCmd.class.getName());
public static final String APINAME = "listConfigurationGroups";
// ///////////////////////////////////////////////////
// ////////////// API parameters /////////////////////
// ///////////////////////////////////////////////////
@Parameter(name = ApiConstants.GROUP, type = CommandType.STRING, description = "lists configuration group by group name")
private String groupName;
// ///////////////////////////////////////////////////
// ///////////////// Accessors ///////////////////////
// ///////////////////////////////////////////////////
public String getGroupName() {
return groupName;
}
// ///////////////////////////////////////////////////
// ///////////// API Implementation///////////////////
// ///////////////////////////////////////////////////
@Override
public String getCommandName() {
return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
}
@Override
public void execute() {
Pair<List<? extends ConfigurationGroup>, Integer> result = _mgr.listConfigurationGroups(this);
ListResponse<ConfigurationGroupResponse> response = new ListResponse<>();
List<ConfigurationGroupResponse> configGroupResponses = new ArrayList<>();
for (ConfigurationGroup cfgGroup : result.first()) {
ConfigurationGroupResponse cfgGroupResponse = _responseGenerator.createConfigurationGroupResponse(cfgGroup);
configGroupResponses.add(cfgGroupResponse);
}
response.setResponses(configGroupResponses, result.second());
response.setResponseName(getCommandName());
setResponseObject(response);
}
}

View File

@ -19,7 +19,10 @@ package org.apache.cloudstack.api.command.admin.config;
import java.util.ArrayList;
import java.util.List;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.response.DomainResponse;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.apache.cloudstack.api.APICommand;
@ -35,7 +38,9 @@ import org.apache.cloudstack.api.response.StoragePoolResponse;
import org.apache.cloudstack.api.response.ZoneResponse;
import org.apache.cloudstack.config.Configuration;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.utils.Pair;
import com.cloud.utils.exception.CloudRuntimeException;
@APICommand(name = "listConfigurations", description = "Lists all configurations.", responseObject = ConfigurationResponse.class,
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
@ -89,6 +94,15 @@ public class ListCfgsByCmd extends BaseListCmd {
description = "the ID of the Image Store to update the parameter value for corresponding image store")
private Long imageStoreId;
@Parameter(name = ApiConstants.GROUP, type = CommandType.STRING, description = "lists configuration by group name (primarily used for UI)", since = "4.18.0")
private String groupName;
@Parameter(name = ApiConstants.SUBGROUP, type = CommandType.STRING, description = "lists configuration by subgroup name (primarily used for UI)", since = "4.18.0")
private String subGroupName;
@Parameter(name = ApiConstants.PARENT, type = CommandType.STRING, description = "lists configuration by parent name (primarily used for UI)", since = "4.18.0")
private String parentName;
// ///////////////////////////////////////////////////
// ///////////////// Accessors ///////////////////////
// ///////////////////////////////////////////////////
@ -125,6 +139,26 @@ public class ListCfgsByCmd extends BaseListCmd {
return imageStoreId;
}
public String getGroupName() {
return groupName;
}
public String getSubGroupName() {
return subGroupName;
}
public String getParentName() {
return parentName;
}
@Override
public Integer getPageSize() {
if (StringUtils.isNotEmpty(getGroupName())) {
return Integer.valueOf(s_pageSizeUnlimited.intValue());
}
return super.getPageSize();
}
@Override
public Long getPageSizeVal() {
Long defaultPageSize = 500L;
@ -143,37 +177,74 @@ public class ListCfgsByCmd extends BaseListCmd {
// ///////////// API Implementation///////////////////
// ///////////////////////////////////////////////////
private void setScope(ConfigurationResponse cfgResponse) {
if (!matchesConfigurationGroup(cfgResponse)) {
return;
}
cfgResponse.setObjectName("configuration");
if (getZoneId() != null) {
cfgResponse.setScope("zone");
}
if (getClusterId() != null) {
cfgResponse.setScope("cluster");
}
if (getStoragepoolId() != null) {
cfgResponse.setScope("storagepool");
}
if (getAccountId() != null) {
cfgResponse.setScope("account");
}
if (getDomainId() != null) {
cfgResponse.setScope("domain");
}
if (getImageStoreId() != null){
cfgResponse.setScope("imagestore");
}
}
@Override
public void execute() {
Pair<List<? extends Configuration>, Integer> result = _mgr.searchForConfigurations(this);
ListResponse<ConfigurationResponse> response = new ListResponse<ConfigurationResponse>();
List<ConfigurationResponse> configResponses = new ArrayList<ConfigurationResponse>();
for (Configuration cfg : result.first()) {
ConfigurationResponse cfgResponse = _responseGenerator.createConfigurationResponse(cfg);
cfgResponse.setObjectName("configuration");
if (getZoneId() != null) {
cfgResponse.setScope("zone");
validateParameters();
try {
Pair<List<? extends Configuration>, Integer> result = _mgr.searchForConfigurations(this);
ListResponse<ConfigurationResponse> response = new ListResponse<>();
List<ConfigurationResponse> configResponses = new ArrayList<>();
for (Configuration cfg : result.first()) {
ConfigurationResponse cfgResponse = _responseGenerator.createConfigurationResponse(cfg);
setScope(cfgResponse);
configResponses.add(cfgResponse);
}
if (getClusterId() != null) {
cfgResponse.setScope("cluster");
}
if (getStoragepoolId() != null) {
cfgResponse.setScope("storagepool");
}
if (getAccountId() != null) {
cfgResponse.setScope("account");
}
if (getDomainId() != null) {
cfgResponse.setScope("domain");
}
if (getImageStoreId() != null){
cfgResponse.setScope("imagestore");
}
configResponses.add(cfgResponse);
}
response.setResponses(configResponses, result.second());
response.setResponseName(getCommandName());
setResponseObject(response);
if (StringUtils.isNotEmpty(getGroupName())) {
response.setResponses(configResponses, configResponses.size());
} else {
response.setResponses(configResponses, result.second());
}
response.setResponseName(getCommandName());
setResponseObject(response);
} catch (InvalidParameterValueException e) {
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, e.getMessage());
} catch (CloudRuntimeException e) {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
}
}
private void validateParameters() {
if (StringUtils.isNotEmpty(getSubGroupName()) && StringUtils.isEmpty(getGroupName())) {
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Configuration group name must be specified with the subgroup name");
}
}
private boolean matchesConfigurationGroup(ConfigurationResponse cfgResponse) {
if (StringUtils.isNotEmpty(getGroupName())) {
if (!(getGroupName().equalsIgnoreCase(cfgResponse.getGroup()))) {
return false;
}
if (StringUtils.isNotEmpty(getSubGroupName()) &&
!getSubGroupName().equalsIgnoreCase(cfgResponse.getSubGroup())) {
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,76 @@
// 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.response;
import java.util.List;
import com.google.gson.annotations.SerializedName;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseResponse;
import com.cloud.serializer.Param;
public class ConfigurationGroupResponse extends BaseResponse {
@SerializedName(ApiConstants.NAME)
@Param(description = "the name of the configuration group")
private String groupName;
@SerializedName(ApiConstants.SUBGROUP)
@Param(description = "the subgroups of the configuration group", responseObject = ConfigurationSubGroupResponse.class)
private List<ConfigurationSubGroupResponse> subGroups;
@SerializedName(ApiConstants.DESCRIPTION)
@Param(description = "the description of the configuration group")
private String description;
@SerializedName(ApiConstants.PRECEDENCE)
@Param(description = "the precedence of the configuration group")
private Long precedence;
public String getGroupName() {
return groupName;
}
public void setGroupName(String groupName) {
this.groupName = groupName;
}
public List<ConfigurationSubGroupResponse> getSubGroups() {
return subGroups;
}
public void setSubGroups(List<ConfigurationSubGroupResponse> subGroups) {
this.subGroups = subGroups;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Long getPrecedence() {
return precedence;
}
public void setPrecedence(Long precedence) {
this.precedence = precedence;
}
}

View File

@ -28,6 +28,14 @@ public class ConfigurationResponse extends BaseResponse {
@Param(description = "the category of the configuration")
private String category;
@SerializedName(ApiConstants.GROUP)
@Param(description = "the group of the configuration", since = "4.18.0")
private String group;
@SerializedName(ApiConstants.SUBGROUP)
@Param(description = "the subgroup of the configuration", since = "4.18.0")
private String subGroup;
@SerializedName(ApiConstants.NAME)
@Param(description = "the name of the configuration")
private String name;
@ -36,6 +44,10 @@ public class ConfigurationResponse extends BaseResponse {
@Param(description = "the value of the configuration")
private String value;
@SerializedName(ApiConstants.DEFAULT_VALUE)
@Param(description = "the default value of the configuration", since = "4.18.0")
private String defaultValue;
@SerializedName(ApiConstants.SCOPE)
@Param(description = "scope(zone/cluster/pool/account) of the parameter that needs to be updated")
private String scope;
@ -52,6 +64,26 @@ public class ConfigurationResponse extends BaseResponse {
@Param(description = "true if the configuration is dynamic")
private boolean isDynamic;
@SerializedName(ApiConstants.COMPONENT)
@Param(description = "the component of the configuration", since = "4.18.0")
private String component;
@SerializedName(ApiConstants.PARENT)
@Param(description = "the name of the parent configuration", since = "4.18.0")
private String parent;
@SerializedName(ApiConstants.DISPLAY_TEXT)
@Param(description = "the display text of the configuration", since = "4.18.0")
private String displayText;
@SerializedName(ApiConstants.TYPE)
@Param(description = "the type of the configuration value", since = "4.18.0")
private String type;
@SerializedName(ApiConstants.OPTIONS)
@Param(description = "the possible options of the configuration value", since = "4.18.0")
private String options;
public String getCategory() {
return category;
}
@ -60,6 +92,22 @@ public class ConfigurationResponse extends BaseResponse {
this.category = category;
}
public String getGroup() {
return group;
}
public void setGroup(String group) {
this.group = group;
}
public String getSubGroup() {
return subGroup;
}
public void setSubGroup(String subGroup) {
this.subGroup = subGroup;
}
public String getName() {
return name;
}
@ -76,6 +124,14 @@ public class ConfigurationResponse extends BaseResponse {
this.value = value;
}
public String getDefaultValue() {
return defaultValue;
}
public void setDefaultValue(String defaultValue) {
this.defaultValue = defaultValue;
}
public String getDescription() {
return description;
}
@ -100,4 +156,44 @@ public class ConfigurationResponse extends BaseResponse {
this.isDynamic = isDynamic;
}
public String getComponent() {
return component;
}
public void setComponent(String component) {
this.component = component;
}
public String getParent() {
return parent;
}
public void setParent(String parent) {
this.parent = parent;
}
public String getDisplayText() {
return displayText;
}
public void setDisplayText(String displayText) {
this.displayText = displayText;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getOptions() {
return options;
}
public void setOptions(String options) {
this.options = options;
}
}

View File

@ -0,0 +1,50 @@
// 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.response;
import com.google.gson.annotations.SerializedName;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseResponse;
import com.cloud.serializer.Param;
public class ConfigurationSubGroupResponse extends BaseResponse {
@SerializedName(ApiConstants.NAME)
@Param(description = "the name of the configuration subgroup")
private String subGroupName;
@SerializedName(ApiConstants.PRECEDENCE)
@Param(description = "the precedence of the configuration subgroup")
private Long precedence;
public String getSubGroupName() {
return subGroupName;
}
public void setSubGroupName(String subGroupName) {
this.subGroupName = subGroupName;
}
public Long getPrecedence() {
return precedence;
}
public void setPrecedence(Long precedence) {
this.precedence = precedence;
}
}

View File

@ -44,12 +44,12 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer
ConfigKey<String> BackupProviderPlugin = new ConfigKey<>("Advanced", String.class,
"backup.framework.provider.plugin",
"dummy",
"The backup and recovery provider plugin.", true, ConfigKey.Scope.Zone);
"The backup and recovery provider plugin.", true, ConfigKey.Scope.Zone, BackupFrameworkEnabled.key());
ConfigKey<Long> BackupSyncPollingInterval = new ConfigKey<>("Advanced", Long.class,
"backup.framework.sync.interval",
"300",
"The backup and recovery background sync task polling interval in seconds.", true);
"The backup and recovery background sync task polling interval in seconds.", true, BackupFrameworkEnabled.key());
ConfigKey<Boolean> BackupEnableAttachDetachVolumes = new ConfigKey<>("Advanced", Boolean.class,
"backup.enable.attach.detach.of.volumes",

View File

@ -46,10 +46,10 @@ public interface CAManager extends CAService, Configurable, PluggableService {
"2048",
"The key size to be used for random certificate keypair generation.", true);
ConfigKey<String> CertSignatureAlgorithm = new ConfigKey<>("Advanced", String.class,
"ca.framework.cert.signature.algorithm",
ConfigKey<String> CertSignatureAlgorithm = new ConfigKey<>(String.class,
"ca.framework.cert.signature.algorithm", "Advanced",
"SHA256withRSA",
"The default signature algorithm to use for certificate generation.", true);
"The default signature algorithm to use for certificate generation.", true, ConfigKey.Scope.Global, null, null, null, null, null, ConfigKey.Kind.Select, "SHA256withRSA");
ConfigKey<Integer> CertValidityPeriod = new ConfigKey<>("Advanced", Integer.class,

View File

@ -20,15 +20,15 @@ import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.framework.config.Configurable;
public class ApiServiceConfiguration implements Configurable {
public static final ConfigKey<String> ManagementServerAddresses = new ConfigKey<String>("Advanced", String.class, "host", "localhost", "The ip address of management server. This can also accept comma separated addresses.", true);
public static final ConfigKey<String> ManagementServerAddresses = new ConfigKey<>(String.class, "host", "Advanced", "localhost", "The ip address of management server. This can also accept comma separated addresses.", true, ConfigKey.Scope.Global, null, null, null, null, null, ConfigKey.Kind.CSV, null);
public static final ConfigKey<String> ApiServletPath = new ConfigKey<String>("Advanced", String.class, "endpoint.url", "http://localhost:8080/client/api",
"API end point. Can be used by CS components/services deployed remotely, for sending CS API requests", true);
public static final ConfigKey<Long> DefaultUIPageSize = new ConfigKey<Long>("Advanced", Long.class, "default.ui.page.size", "20",
"The default pagesize to be used by UI and other clients when making list* API calls", true, ConfigKey.Scope.Global);
public static final ConfigKey<Boolean> ApiSourceCidrChecksEnabled = new ConfigKey<>("Advanced", Boolean.class, "api.source.cidr.checks.enabled",
"true", "Are the source checks on API calls enabled (true) or not (false)? See api.allowed.source.cidr.list", true, ConfigKey.Scope.Global);
public static final ConfigKey<String> ApiAllowedSourceCidrList = new ConfigKey<String>("Advanced", String.class, "api.allowed.source.cidr.list",
"0.0.0.0/0,::/0", "Comma separated list of IPv4/IPv6 CIDRs from which API calls can be performed. Can be set on Global and Account levels.", true, ConfigKey.Scope.Account);
public static final ConfigKey<String> ApiAllowedSourceCidrList = new ConfigKey<>(String.class, "api.allowed.source.cidr.list", "Advanced",
"0.0.0.0/0,::/0", "Comma separated list of IPv4/IPv6 CIDRs from which API calls can be performed. Can be set on Global and Account levels.", true, ConfigKey.Scope.Account, null, null, null, null, null, ConfigKey.Kind.CSV, null);
@Override
public String getConfigComponentName() {
return ApiServiceConfiguration.class.getSimpleName();

View File

@ -94,13 +94,13 @@ public interface QueryService {
ConfigKey<Boolean> AllowUserViewDestroyedVM = new ConfigKey<>("Advanced", Boolean.class, "allow.user.view.destroyed.vm", "false",
"Determines whether users can view their destroyed or expunging vm ", true, ConfigKey.Scope.Account);
static final ConfigKey<String> UserVMDeniedDetails = new ConfigKey<String>("Advanced", String.class,
"user.vm.denied.details", "rootdisksize, cpuOvercommitRatio, memoryOvercommitRatio, Message.ReservedCapacityFreed.Flag",
"Determines whether users can view certain VM settings. When set to empty, default value used is: rootdisksize, cpuOvercommitRatio, memoryOvercommitRatio, Message.ReservedCapacityFreed.Flag.", true);
static final ConfigKey<String> UserVMDeniedDetails = new ConfigKey<>(String.class,
"user.vm.denied.details", "Advanced", "rootdisksize, cpuOvercommitRatio, memoryOvercommitRatio, Message.ReservedCapacityFreed.Flag",
"Determines whether users can view certain VM settings. When set to empty, default value used is: rootdisksize, cpuOvercommitRatio, memoryOvercommitRatio, Message.ReservedCapacityFreed.Flag.", true, ConfigKey.Scope.Global, null, null, null, null, null, ConfigKey.Kind.CSV, null);
static final ConfigKey<String> UserVMReadOnlyDetails = new ConfigKey<String>("Advanced", String.class,
"user.vm.readonly.details", "dataDiskController, rootDiskController",
"List of read-only VM settings/details as comma separated string", true);
static final ConfigKey<String> UserVMReadOnlyDetails = new ConfigKey<>(String.class,
"user.vm.readonly.details", "Advanced", "dataDiskController, rootDiskController",
"List of read-only VM settings/details as comma separated string", true, ConfigKey.Scope.Global, null, null, null, null, null, ConfigKey.Kind.CSV, null);
ConfigKey<Boolean> SortKeyAscending = new ConfigKey<>("Advanced", Boolean.class, "sortkey.algorithm", "true",
"Sort algorithm - ascending or descending - to use. For entities that use sort key(template, disk offering, service offering, " +

View File

@ -66,7 +66,7 @@ public interface VirtualMachineManager extends Manager {
"If config drive need to be created and hosted on primary storage pool. Currently only supported for KVM.", true, ConfigKey.Scope.Zone);
ConfigKey<Boolean> VmConfigDriveUseHostCacheOnUnsupportedPool = new ConfigKey<>("Advanced", Boolean.class, "vm.configdrive.use.host.cache.on.unsupported.pool", "true",
"If true, config drive is created on the host cache storage when vm.configdrive.primarypool.enabled is true and the primary pool type doesn't support config drive.", true, ConfigKey.Scope.Zone);
"If true, config drive is created on the host cache storage when vm.configdrive.primarypool.enabled is true and the primary pool type doesn't support config drive.", true, ConfigKey.Scope.Zone, VmConfigDriveOnPrimaryPool.key());
ConfigKey<Boolean> VmConfigDriveForceHostCacheUse = new ConfigKey<>("Advanced", Boolean.class, "vm.configdrive.force.host.cache.use", "false",
"If true, config drive is forced to create on the host cache storage. Currently only supported for KVM.", true, ConfigKey.Scope.Zone);

View File

@ -20,6 +20,8 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.cloudstack.framework.config.impl.ConfigurationSubGroupVO;
import com.cloud.dc.ClusterVO;
import com.cloud.dc.DataCenter;
import com.cloud.dc.DataCenter.NetworkType;
@ -42,6 +44,7 @@ import com.cloud.offering.NetworkOffering.Availability;
import com.cloud.offerings.NetworkOfferingVO;
import com.cloud.org.Grouping.AllocationState;
import com.cloud.user.Account;
import com.cloud.utils.Pair;
import com.cloud.utils.net.NetUtils;
/**
@ -261,4 +264,10 @@ public interface ConfigurationManager {
AllocationState findPodAllocationState(HostPodVO pod);
AllocationState findClusterAllocationState(ClusterVO cluster);
String getConfigurationType(String configName);
Pair<String, String> getConfigurationGroupAndSubGroup(String configName);
List<ConfigurationSubGroupVO> getConfigurationSubGroups(Long groupId);
}

View File

@ -49,11 +49,12 @@ public interface IpAddressManager {
"When true, ip address delete (ipassoc) failures are ignored", true);
ConfigKey<String> VrouterRedundantTiersPlacement = new ConfigKey<String>(
"Advanced", String.class,
String.class,
"vrouter.redundant.tiers.placement",
"Advanced",
"random",
"Set placement of vrouter ips in redundant mode in vpc tiers, this can be 3 value: `first` to use first ips in tiers, `last` to use last ips in tiers and `random` to take random ips in tiers.",
true, ConfigKey.Scope.Account);
true, ConfigKey.Scope.Account, null, null, null, null, null, ConfigKey.Kind.Select, "first,last,random");
/**
* Assigns a new public ip address.

View File

@ -53,13 +53,13 @@ public interface ResourceManager extends ResourceService, Configurable {
"Number of retries when preparing a host into Maintenance Mode is faulty before failing",
false);
ConfigKey<String> HOST_MAINTENANCE_LOCAL_STRATEGY = new ConfigKey<>("Advanced", String.class,
"host.maintenance.local.storage.strategy", "Error",
ConfigKey<String> HOST_MAINTENANCE_LOCAL_STRATEGY = new ConfigKey<>(String.class,
"host.maintenance.local.storage.strategy", "Advanced","Error",
"Defines the strategy towards VMs with volumes on local storage when putting a host in maintenance. "
+ "The default strategy is 'Error', preventing maintenance in such a case. "
+ "Choose 'Migration' strategy to migrate away VMs running on local storage. "
+ "To force-stop VMs, choose 'ForceStop' strategy",
true, ConfigKey.Scope.Global);
true, ConfigKey.Scope.Global, null, null, null, null, null, ConfigKey.Kind.Select, "Error,Migration,ForceStop");
/**
* Register a listener for different types of resource life cycle events.

View File

@ -45,22 +45,6 @@ import com.cloud.vm.DiskProfile;
import com.cloud.vm.VMInstanceVO;
public interface StorageManager extends StorageService {
ConfigKey<Integer> StorageCleanupInterval = new ConfigKey<>(Integer.class,
"storage.cleanup.interval",
"Advanced",
"86400",
"The interval (in seconds) to wait before running the storage cleanup thread.",
false,
ConfigKey.Scope.Global,
null);
ConfigKey<Integer> StorageCleanupDelay = new ConfigKey<>(Integer.class,
"storage.cleanup.delay",
"Advanced",
"86400",
"Determines how long (in seconds) to wait before actually expunging destroyed volumes. The default value = the default value of storage.cleanup.interval.",
false,
ConfigKey.Scope.Global,
null);
ConfigKey<Boolean> StorageCleanupEnabled = new ConfigKey<>(Boolean.class,
"storage.cleanup.enabled",
"Advanced",
@ -69,6 +53,24 @@ public interface StorageManager extends StorageService {
false,
ConfigKey.Scope.Global,
null);
ConfigKey<Integer> StorageCleanupInterval = new ConfigKey<>(Integer.class,
"storage.cleanup.interval",
"Advanced",
"86400",
"The interval (in seconds) to wait before running the storage cleanup thread.",
false,
ConfigKey.Scope.Global,
null,
StorageCleanupEnabled.key());
ConfigKey<Integer> StorageCleanupDelay = new ConfigKey<>(Integer.class,
"storage.cleanup.delay",
"Advanced",
"86400",
"Determines how long (in seconds) to wait before actually expunging destroyed volumes. The default value = the default value of storage.cleanup.interval.",
false,
ConfigKey.Scope.Global,
null,
StorageCleanupEnabled.key());
ConfigKey<Boolean> TemplateCleanupEnabled = new ConfigKey<>(Boolean.class,
"storage.template.cleanup.enabled",
"Storage",
@ -76,7 +78,8 @@ public interface StorageManager extends StorageService {
"Enable/disable template cleanup activity, only take effect when overall storage cleanup is enabled",
false,
ConfigKey.Scope.Global,
null);
null,
StorageCleanupEnabled.key());
ConfigKey<Integer> KvmStorageOfflineMigrationWait = new ConfigKey<>(Integer.class,
"kvm.storage.offline.migration.wait",
"Storage",

View File

@ -136,7 +136,7 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust
protected final ConfigKey<Boolean> EnableLB = new ConfigKey<Boolean>(Boolean.class, "agent.lb.enabled", "Advanced", "false", "Enable agent load balancing between management server nodes", true);
protected final ConfigKey<Double> ConnectedAgentThreshold = new ConfigKey<Double>(Double.class, "agent.load.threshold", "Advanced", "0.7",
"What percentage of the agents can be held by one management server before load balancing happens", true);
"What percentage of the agents can be held by one management server before load balancing happens", true, EnableLB.key());
protected final ConfigKey<Integer> LoadSize = new ConfigKey<Integer>(Integer.class, "direct.agent.load.size", "Advanced", "16", "How many agents to connect to in each round", true);
protected final ConfigKey<Integer> ScanInterval = new ConfigKey<Integer>(Integer.class, "direct.agent.scan.interval", "Advanced", "90", "Interval between scans to load agents", false,
ConfigKey.Scope.Global, 1000);

View File

@ -0,0 +1,106 @@
// 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.upgrade;
import java.util.List;
import javax.inject.Inject;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.framework.config.dao.ConfigurationDaoImpl;
import org.apache.cloudstack.framework.config.dao.ConfigurationGroupDao;
import org.apache.cloudstack.framework.config.dao.ConfigurationGroupDaoImpl;
import org.apache.cloudstack.framework.config.dao.ConfigurationSubGroupDao;
import org.apache.cloudstack.framework.config.dao.ConfigurationSubGroupDaoImpl;
import org.apache.cloudstack.framework.config.impl.ConfigurationSubGroupVO;
import org.apache.cloudstack.framework.config.impl.ConfigurationVO;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import com.cloud.utils.Pair;
public class ConfigurationGroupsAggregator {
static final Logger LOG = Logger.getLogger(ConfigurationGroupsAggregator.class);
@Inject
ConfigurationDao configDao;
@Inject
ConfigurationGroupDao configGroupDao;
@Inject
ConfigurationSubGroupDao configSubGroupDao;
public ConfigurationGroupsAggregator() {
configDao = new ConfigurationDaoImpl();
configGroupDao = new ConfigurationGroupDaoImpl();
configSubGroupDao = new ConfigurationSubGroupDaoImpl();
}
public void updateConfigurationGroups() {
LOG.debug("Updating configuration groups");
List<ConfigurationVO> configs = configDao.listAllIncludingRemoved();
if (CollectionUtils.isEmpty(configs)) {
return;
}
for (final ConfigurationVO config : configs) {
String configName = config.getName();
if (StringUtils.isBlank(configName)) {
continue;
}
try {
Pair<Long, Long> configGroupAndSubGroup = getConfigurationGroupAndSubGroupByName(configName);
if (configGroupAndSubGroup.first() != 1 && configGroupAndSubGroup.second() != 1) {
config.setGroupId(configGroupAndSubGroup.first());
config.setSubGroupId(configGroupAndSubGroup.second());
configDao.persist(config);
}
} catch (Exception e) {
LOG.error("Failed to update group for configuration " + configName + " due to " + e.getMessage(), e);
}
}
LOG.debug("Successfully updated configuration groups.");
}
private Pair<Long, Long> getConfigurationGroupAndSubGroupByName(String configName) {
Long subGroupId = 1L;
Long groupId = 1L;
if (StringUtils.isNotBlank(configName)) {
// Get words from the dot notation in the configuration
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);
}
}

View File

@ -16,11 +16,6 @@
// under the License.
package com.cloud.upgrade.dao;
import com.cloud.upgrade.SystemVmTemplateRegistration;
import com.cloud.utils.exception.CloudRuntimeException;
import org.apache.log4j.Logger;
import java.io.InputStream;
import java.math.BigInteger;
import java.sql.Connection;
@ -29,6 +24,11 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.UUID;
import org.apache.log4j.Logger;
import com.cloud.upgrade.SystemVmTemplateRegistration;
import com.cloud.utils.exception.CloudRuntimeException;
public class Upgrade41610to41700 implements DbUpgrade, DbUpgradeSystemVmTemplate {
final static Logger LOG = Logger.getLogger(Upgrade41700to41710.class);

View File

@ -17,6 +17,7 @@
package com.cloud.upgrade.dao;
import com.cloud.storage.GuestOSHypervisorMapping;
import com.cloud.upgrade.ConfigurationGroupsAggregator;
import com.cloud.upgrade.GuestOsMapper;
import com.cloud.upgrade.SystemVmTemplateRegistration;
import com.cloud.utils.exception.CloudRuntimeException;
@ -45,6 +46,7 @@ public class Upgrade41720to41800 implements DbUpgrade, DbUpgradeSystemVmTemplate
private GuestOsMapper guestOsMapper = new GuestOsMapper();
private SystemVmTemplateRegistration systemVmTemplateRegistration;
private ConfigurationGroupsAggregator configGroupsAggregator = new ConfigurationGroupsAggregator();
@Override
public String[] getUpgradableVersionRange() {
@ -79,6 +81,7 @@ public class Upgrade41720to41800 implements DbUpgrade, DbUpgradeSystemVmTemplate
correctGuestOsNames();
updateGuestOsMappings();
correctGuestOsIdsInHypervisorMapping(conn);
updateConfigurationGroups();
}
@Override
@ -702,4 +705,8 @@ public class Upgrade41720to41800 implements DbUpgrade, DbUpgradeSystemVmTemplate
LOG.debug("Correcting guest OS ids in hypervisor mappings");
guestOsMapper.updateGuestOsIdInHypervisorMapping(conn, 10, "Ubuntu 20.04 LTS", new GuestOSHypervisorMapping("Xenserver", "8.2.0", "Ubuntu Focal Fossa 20.04"));
}
private void updateConfigurationGroups() {
configGroupsAggregator.updateConfigurationGroups();
}
}

View File

@ -61,6 +61,8 @@
<bean id="commandExecLogDaoImpl" class="com.cloud.secstorage.CommandExecLogDaoImpl" />
<bean id="conditionDaoImpl" class="com.cloud.network.as.dao.ConditionDaoImpl" />
<bean id="consoleProxyDaoImpl" class="com.cloud.vm.dao.ConsoleProxyDaoImpl" />
<bean id="configurationGroupDaoImpl" class="org.apache.cloudstack.framework.config.dao.ConfigurationGroupDaoImpl" />
<bean id="configurationSubGroupDaoImpl" class="org.apache.cloudstack.framework.config.dao.ConfigurationSubGroupDaoImpl" />
<bean id="counterDaoImpl" class="com.cloud.network.as.dao.CounterDaoImpl" />
<bean id="dataCenterJoinDaoImpl" class="com.cloud.api.query.dao.DataCenterJoinDaoImpl" />
<bean id="domainVlanMapDaoImpl" class="com.cloud.dc.dao.DomainVlanMapDaoImpl" />

View File

@ -26,8 +26,6 @@ ALTER TABLE cloud.remote_access_vpn MODIFY ipsec_psk text NOT NULL;
-- PR#5832 Fix 'endpointe.url' global settings configuration typo.
UPDATE `cloud`.`configuration` SET name='endpoint.url' WHERE name='endpointe.url';
ALTER TABLE `cloud`.`service_offering` ADD COLUMN `uuid` varchar(40) UNIQUE DEFAULT NULL;
ALTER TABLE `cloud`.`service_offering` ADD COLUMN `name` varchar(255) NOT NULL;
ALTER TABLE `cloud`.`service_offering` ADD COLUMN `display_text` varchar(4096) DEFAULT NULL ;
@ -592,7 +590,7 @@ CREATE TABLE `cloud`.`vm_stats` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- PR#5984 Update name for global configuration vm.stats.increment.metrics
Update configuration set name='vm.stats.increment.metrics' where name='vm.stats.increment.metrics.in.memory';
UPDATE `cloud`.`configuration` SET name = 'vm.stats.increment.metrics' WHERE name = 'vm.stats.increment.metrics.in.memory';
ALTER TABLE `cloud`.`domain_router` ADD COLUMN `software_version` varchar(100) COMMENT 'Software version';
@ -970,4 +968,4 @@ WHERE not exists( SELECT 1
;END;
CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (2, 'Debian GNU/Linux 11 (64-bit)', 'XenServer', '8.2.1', 'Debian Bullseye 11');
CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (2, 'Debian GNU/Linux 11 (32-bit)', 'XenServer', '8.2.1', 'Debian Bullseye 11');
CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (2, 'Debian GNU/Linux 11 (32-bit)', 'XenServer', '8.2.1', 'Debian Bullseye 11');

View File

@ -219,12 +219,12 @@ CREATE TABLE IF NOT EXISTS `cloud`.`passphrase` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- Add passphrase column to volumes table
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.volumes', 'passphrase_id', 'bigint unsigned DEFAULT NULL COMMENT ''encryption passphrase id'' ');
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.volumes', 'passphrase_id', 'bigint unsigned DEFAULT NULL COMMENT "encryption passphrase id" ');
CALL `cloud`.`IDEMPOTENT_ADD_FOREIGN_KEY`('cloud.volumes', 'passphrase', 'id');
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.volumes', 'encrypt_format', 'varchar(64) DEFAULT NULL COMMENT ''encryption format'' ');
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.volumes', 'encrypt_format', 'varchar(64) DEFAULT NULL COMMENT "encryption format" ');
-- Add encrypt column to disk_offering
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.disk_offering', 'encrypt', 'tinyint(1) DEFAULT 0 COMMENT ''volume encrypt requested'' ');
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.disk_offering', 'encrypt', 'tinyint(1) DEFAULT 0 COMMENT "volume encrypt requested" ');
-- add encryption support to disk offering view
DROP VIEW IF EXISTS `cloud`.`disk_offering_view`;
@ -1101,6 +1101,249 @@ INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`)
SELECT UUID(), `roles`.`id`, 'isAccountAllowedToCreateOfferingsWithTags', 'ALLOW'
FROM `cloud`.`roles` WHERE `role_type` = 'DomainAdmin';
--
-- Update Configuration Groups and Subgroups
--
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.configuration', 'group_id', 'bigint unsigned DEFAULT 1 COMMENT "group id this configuration belongs to" ');
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.configuration', 'subgroup_id', 'bigint unsigned DEFAULT 1 COMMENT "subgroup id this configuration belongs to" ');
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.configuration', 'parent', 'VARCHAR(255) DEFAULT NULL COMMENT "name of the parent configuration if this depends on it" ');
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.configuration', 'display_text', 'VARCHAR(255) DEFAULT NULL COMMENT "Short text about configuration to display to the users" ');
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.configuration', 'kind', 'VARCHAR(255) DEFAULT NULL COMMENT "kind of the value such as order, csv, etc" ');
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.configuration', 'options', 'VARCHAR(255) DEFAULT NULL COMMENT "possible options for the value" ');
CREATE TABLE `cloud`.`configuration_group` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
`name` varchar(255) NOT NULL COMMENT 'name of the configuration group',
`description` varchar(1024) DEFAULT NULL COMMENT 'description of the configuration group',
`precedence` bigint(20) unsigned DEFAULT '999' COMMENT 'precedence for the configuration group',
PRIMARY KEY (`id`),
UNIQUE KEY (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `cloud`.`configuration_subgroup` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
`name` varchar(255) NOT NULL COMMENT 'name of the configuration subgroup',
`keywords` varchar(4096) DEFAULT NULL COMMENT 'comma-separated keywords for the configuration subgroup',
`precedence` bigint(20) unsigned DEFAULT '999' COMMENT 'precedence for the configuration subgroup',
`group_id` bigint(20) unsigned NOT NULL COMMENT 'configuration group id',
PRIMARY KEY (`id`),
UNIQUE KEY (`name`, `group_id`),
CONSTRAINT `fk_configuration_subgroup__group_id` FOREIGN KEY (`group_id`) REFERENCES `configuration_group` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `cloud`.`configuration_group` AUTO_INCREMENT=1;
INSERT INTO `cloud`.`configuration_group` (`name`, `description`, `precedence`) VALUES ('Miscellaneous', 'Miscellaneous configuration', 999);
INSERT INTO `cloud`.`configuration_group` (`name`, `description`, `precedence`) VALUES ('Access', 'Identity and Access management configuration', 1);
INSERT INTO `cloud`.`configuration_group` (`name`, `description`, `precedence`) VALUES ('Compute', 'Compute configuration', 2);
INSERT INTO `cloud`.`configuration_group` (`name`, `description`, `precedence`) VALUES ('Storage', 'Storage configuration', 3);
INSERT INTO `cloud`.`configuration_group` (`name`, `description`, `precedence`) VALUES ('Network', 'Network configuration', 4);
INSERT INTO `cloud`.`configuration_group` (`name`, `description`, `precedence`) VALUES ('Hypervisor', 'Hypervisor specific configuration', 5);
INSERT INTO `cloud`.`configuration_group` (`name`, `description`, `precedence`) VALUES ('Management Server', 'Management Server configuration', 6);
INSERT INTO `cloud`.`configuration_group` (`name`, `description`, `precedence`) VALUES ('System VMs', 'System VMs related configuration', 7);
INSERT INTO `cloud`.`configuration_group` (`name`, `description`, `precedence`) VALUES ('Infrastructure', 'Infrastructure configuration', 8);
ALTER TABLE `cloud`.`configuration_subgroup` AUTO_INCREMENT=1;
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('Others', NULL, 999, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'Miscellaneous'));
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('Account', NULL, 1, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'Access'));
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('Domain', NULL, 2, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'Access'));
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('Project', NULL, 3, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'Access'));
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('LDAP', NULL, 4, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'Access'));
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('SAML', 'saml2', 5, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'Access'));
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('Virtual Machine', 'vm,instance,cpu,ssh,affinity', 1, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'Compute'));
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('Kubernetes', 'kubernetes', 2, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'Compute'));
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('High Availability', 'ha', 3, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'Compute'));
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('Images', 'template,iso', 1, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'Storage'));
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('Volume', 'disk,diskoffering', 2, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'Storage'));
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('Snapshot', NULL, 3, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'Storage'));
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('VM Snapshot', 'vmsnapshot', 4, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'Storage'));
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('Network', 'firewall,vlan,dns,globodns,ipaddress,cidr', 1, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'Network'));
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('DHCP', 'externaldhcp', 2, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'Network'));
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('VPC', NULL, 3, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'Network'));
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('LoadBalancer', 'lb,gslb', 4, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'Network'));
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('API', NULL, 1, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'Management Server'));
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('Alerts', 'alert', 2, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'Management Server'));
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('Events', 'event', 3, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'Management Server'));
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('Security', 'secure,password,authenticators', 4, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'Management Server'));
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('Usage', NULL, 5, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'Management Server'));
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('Limits', 'capacity,delay,interval,workers', 6, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'Management Server'));
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('Jobs', 'job', 7, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'Management Server'));
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('Agent', NULL, 8, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'Management Server'));
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('Hypervisor', 'host', 1, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'Hypervisor'));
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('KVM', 'libvirt', 2, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'Hypervisor'));
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('VMware', 'vcenter', 3, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'Hypervisor'));
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('XenServer', 'xen,xapi,XCP', 4, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'Hypervisor'));
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('OVM', 'ovm3', 5, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'Hypervisor'));
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('Baremetal', NULL, 6, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'Hypervisor'));
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('ConsoleProxyVM', 'cpvm,consoleproxy,novnc', 1, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'System VMs'));
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('SecStorageVM', 'ssvm,secondary', 2, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'System VMs'));
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('VirtualRouter', 'vr,router,vrouter', 3, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'System VMs'));
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('Diagnostics', NULL, 4, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'System VMs'));
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('Primary Storage', 'storage,pool,primary', 1, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'Infrastructure'));
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('Secondary Storage', 'image,secstorage', 2, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'Infrastructure'));
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('Backup & Recovery', 'backup,recovery,veeam', 1, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'Miscellaneous'));
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('Certificate Authority', 'CA', 2, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'Miscellaneous'));
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('Quota', NULL, 3, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'Miscellaneous'));
INSERT INTO `cloud`.`configuration_subgroup` (`name`, `keywords`, `precedence`, `group_id`) VALUES ('Cloudian', NULL, 4, (SELECT id FROM `cloud`.`configuration_group` WHERE `name` = 'Miscellaneous'));
UPDATE `cloud`.`configuration` SET parent = 'agent.lb.enabled' WHERE name IN ('agent.load.threshold');
UPDATE `cloud`.`configuration` SET parent = 'indirect.agent.lb.check.interval' WHERE name IN ('indirect.agent.lb.algorithm');
UPDATE `cloud`.`configuration` SET parent = 'alert.purge.delay' WHERE name IN ('alert.purge.interval');
UPDATE `cloud`.`configuration` SET parent = 'api.throttling.enabled' WHERE name IN ('api.throttling.cachesize', 'api.throttling.interval', 'api.throttling.max');
UPDATE `cloud`.`configuration` SET parent = 'backup.framework.enabled' WHERE name IN ('backup.framework.provider.plugin', 'backup.framework.sync.interval');
UPDATE `cloud`.`configuration` SET parent = 'cloud.kubernetes.service.enabled' WHERE name IN ('cloud.kubernetes.cluster.max.size', 'cloud.kubernetes.cluster.network.offering', 'cloud.kubernetes.cluster.scale.timeout', 'cloud.kubernetes.cluster.start.timeout', 'cloud.kubernetes.cluster.upgrade.timeout', 'cloud.kubernetes.cluster.experimental.features.enabled');
UPDATE `cloud`.`configuration` SET parent = 'diagnostics.data.gc.enable' WHERE name IN ('diagnostics.data.gc.interval', 'diagnostics.data.max.file.age');
UPDATE `cloud`.`configuration` SET parent = 'enable.additional.vm.configuration' WHERE name IN ('allow.additional.vm.configuration.list.kvm', 'allow.additional.vm.configuration.list.vmware', 'allow.additional.vm.configuration.list.xenserver');
UPDATE `cloud`.`configuration` SET parent = 'event.purge.delay' WHERE name IN ('event.purge.interval');
UPDATE `cloud`.`configuration` SET parent = 'network.loadbalancer.basiczone.elb.enabled' WHERE name IN ('network.loadbalancer.basiczone.elb.network', 'network.loadbalancer.basiczone.elb.vm.cpu.mhz', 'network.loadbalancer.basiczone.elb.vm.ram.size', 'network.loadbalancer.basiczone.elb.vm.vcpu.num', 'network.loadbalancer.basiczone.elb.gc.interval.minutes');
UPDATE `cloud`.`configuration` SET parent = 'prometheus.exporter.enable' WHERE name IN ('prometheus.exporter.port', 'prometheus.exporter.allowed.ips');
UPDATE `cloud`.`configuration` SET parent = 'router.health.checks.enable' WHERE name IN ('router.health.checks.basic.interval', 'router.health.checks.advanced.interval', 'router.health.checks.config.refresh.interval', 'router.health.checks.results.fetch.interval', 'router.health.checks.to.exclude', 'router.health.checks.failures.to.recreate.vr', 'router.health.checks.free.disk.space.threshold', 'router.health.checks.max.cpu.usage.threshold', 'router.health.checks.max.memory.usage.threshold');
UPDATE `cloud`.`configuration` SET parent = 'storage.cache.replacement.enabled' WHERE name IN ('storage.cache.replacement.interval', 'storage.cache.replacement.lru.interval');
UPDATE `cloud`.`configuration` SET parent = 'storage.cleanup.enabled' WHERE name IN ('storage.cleanup.interval', 'storage.cleanup.delay', 'storage.template.cleanup.enabled');
UPDATE `cloud`.`configuration` SET parent = 'vm.configdrive.primarypool.enabled' WHERE name IN ('vm.configdrive.use.host.cache.on.unsupported.pool');
UPDATE `cloud`.`configuration` SET display_text = CONCAT(UCASE(LEFT(REPLACE(name, ".", " "), 1)), LCASE(SUBSTRING(REPLACE(name, ".", " "), 2)));
UPDATE `cloud`.`configuration` SET
`kind` = 'Order',
`options` = 'HostAntiAffinityProcessor,ExplicitDedicationProcessor,HostAffinityProcessor'
where `name` = 'affinity.processors.order' ;
UPDATE `cloud`.`configuration` SET
`kind` = 'Order',
`options` = 'FirstFitPlanner,UserDispersingPlanner,UserConcentratedPodPlanner,ImplicitDedicationPlanner,BareMetalPlanner'
where `name` = 'deployment.planners.order' ;
UPDATE `cloud`.`configuration` SET
`kind` = 'Order',
`options` = 'SimpleInvestigator,XenServerInvestigator,KVMInvestigator,HypervInvestigator,VMwareInvestigator,PingInvestigator,ManagementIPSysVMInvestigator,Ovm3Investigator'
where `name` = 'ha.investigators.order' ;
UPDATE `cloud`.`configuration` SET
`kind` = 'Order',
`options` = 'FirstFitRouting'
where `name` = 'host.allocators.order' ;
UPDATE `cloud`.`configuration` SET
`kind` = 'Order',
`options` = 'SAML2Auth'
where `name` = 'pluggableApi.authenticators.order' ;
UPDATE `cloud`.`configuration` SET
`kind` = 'Order',
`options` = 'AffinityGroupAccessChecker,DomainChecker'
where `name` = 'security.checkers.order' ;
UPDATE `cloud`.`configuration` SET
`kind` = 'Order',
`options` = 'LocalStorage,ClusterScopeStoragePoolAllocator,ZoneWideStoragePoolAllocator'
where `name` = 'storage.pool.allocators.order' ;
UPDATE `cloud`.`configuration` SET
`kind` = 'Order',
`options` = 'PBKDF2,SHA256SALT,MD5,LDAP,SAML2,PLAINTEXT'
where `name` = 'user.authenticators.order' ;
UPDATE `cloud`.`configuration` SET
`kind` = 'Order',
`options` = 'PBKDF2,SHA256SALT,MD5,LDAP,SAML2,PLAINTEXT'
where `name` = 'user.password.encoders.order' ;
UPDATE `cloud`.`configuration` SET
`kind` = 'CSV'
where `name` like "%.list" ;
UPDATE `cloud`.`configuration` SET
`kind` = 'CSV'
where `name` like "%.defaults" ;
UPDATE `cloud`.`configuration` SET
`kind` = 'CSV'
where `name` like "%.details" ;
UPDATE `cloud`.`configuration` SET
`kind` = 'CSV'
where `name` like "%.exclude" ;
UPDATE `cloud`.`configuration` SET
`kind` = 'CSV'
where `name` IN ("alert.email.addresses", "allow.additional.vm.configuration.list.kvm", "allow.additional.vm.configuration.list.xenserver", "host",
"network.dhcp.nondefaultnetwork.setgateway.guestos", "router.health.checks.failures.to.recreate.vr", "router.health.checks.to.exclude") ;
UPDATE `cloud`.`configuration` SET
`kind` = 'Select',
`options` = 'Error,Migration,ForceStop'
where `name` = 'host.maintenance.local.storage.strategy' ;
UPDATE `cloud`.`configuration` SET
`kind` = 'Select',
`options` = 'SHA256withRSA'
where `name` = 'ca.framework.cert.signature.algorithm' ;
UPDATE `cloud`.`configuration` SET
`kind` = 'Select',
`options` = 'firstfitleastconsumed,random'
where `name` = 'image.store.allocation.algorithm' ;
UPDATE `cloud`.`configuration` SET
`kind` = 'Select',
`options` = 'static,roundrobin,shuffle'
where `name` = 'indirect.agent.lb.algorithm' ;
UPDATE `cloud`.`configuration` SET
`kind` = 'Select',
`options` = 'random,firstfit,userdispersing,userconcentratedpod_random,userconcentratedpod_firstfit,firstfitleastconsumed'
where `name` = 'vm.allocation.algorithm' ;
UPDATE `cloud`.`configuration` SET
`kind` = 'Select',
`options` = 'all,pod'
where `name` = 'network.dns.basiczone.updates' ;
UPDATE `cloud`.`configuration` SET
`kind` = 'Select',
`options` = 'global,guest-network,link-local,disabled,all,default'
where `name` = 'network.loadbalancer.haproxy.stats.visibility' ;
UPDATE `cloud`.`configuration` SET
`kind` = 'Select',
`options` = 'SHA1,SHA256,SHA384,SHA512'
where `name` = 'saml2.sigalg' ;
UPDATE `cloud`.`configuration` SET
`kind` = 'Select',
`options` = 'FirstFitPlanner,UserDispersingPlanner,UserConcentratedPodPlanner'
where `name` = 'vm.deployment.planner' ;
UPDATE `cloud`.`configuration` SET
`kind` = 'Select',
`options` = 'scsi,ide,osdefault'
where `name` = 'vmware.root.disk.controller' ;
UPDATE `cloud`.`configuration` SET
`kind` = 'Select',
`options` = 'E1000,PCNet32,Vmxnet2,Vmxnet3'
where `name` = 'vmware.systemvm.nic.device.type' ;
UPDATE `cloud`.`configuration` SET
`kind` = 'Select',
`options` = 'first,last,random'
where `name` = 'vrouter.redundant.tiers.placement' ;
UPDATE `cloud`.`configuration` SET
`kind` = 'Select',
`options` = 'xenserver56,xenserver61'
where `name` = 'xenserver.pvdriver.version' ;
--- Create table for handling console sessions #7094
CREATE TABLE IF NOT EXISTS `cloud`.`console_session` (

View File

@ -65,8 +65,8 @@ public class ImageStoreProviderManagerImpl implements ImageStoreProviderManager,
Map<String, ImageStoreDriver> driverMaps;
static final ConfigKey<String> ImageStoreAllocationAlgorithm = new ConfigKey<String>("Advanced", String.class, "image.store.allocation.algorithm", "firstfitleastconsumed",
"firstfitleastconsumed','random' : Order in which hosts within a cluster will be considered for VM/volume allocation", true, ConfigKey.Scope.Global );
static final ConfigKey<String> ImageStoreAllocationAlgorithm = new ConfigKey<>(String.class, "image.store.allocation.algorithm", "Advanced", "firstfitleastconsumed",
"firstfitleastconsumed','random' : Order in which hosts within a cluster will be considered for VM/volume allocation", true, ConfigKey.Scope.Global, null, null, null, null, null, ConfigKey.Kind.Select, "firstfitleastconsumed,random" );
@PostConstruct
public void config() {

View File

@ -33,6 +33,8 @@ import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationSer
import org.apache.cloudstack.engine.service.api.OrchestrationService;
import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector;
import org.apache.cloudstack.framework.config.dao.ConfigurationDaoImpl;
import org.apache.cloudstack.framework.config.dao.ConfigurationGroupDaoImpl;
import org.apache.cloudstack.framework.config.dao.ConfigurationSubGroupDaoImpl;
import org.apache.cloudstack.framework.rpc.RpcProvider;
import org.apache.cloudstack.storage.cache.manager.StorageCacheManagerImpl;
import org.apache.cloudstack.storage.test.ChildTestConfiguration.Library;
@ -96,12 +98,12 @@ import com.cloud.vm.snapshot.dao.VMSnapshotDaoImpl;
@Configuration
@ComponentScan(basePackageClasses = {NicDaoImpl.class, VMInstanceDaoImpl.class, VolumeDaoImpl.class,
VMTemplatePoolDaoImpl.class, ResourceTagsDaoImpl.class, VMTemplateDaoImpl.class, MockStorageMotionStrategy.class, ConfigurationDaoImpl.class,
ClusterDaoImpl.class, HostPodDaoImpl.class, VMTemplateZoneDaoImpl.class, VMTemplateDetailsDaoImpl.class, HostDetailsDaoImpl.class,
HostTagsDaoImpl.class, HostTransferMapDaoImpl.class, DataCenterIpAddressDaoImpl.class, DataCenterLinkLocalIpAddressDaoImpl.class,
DataCenterVnetDaoImpl.class, PodVlanDaoImpl.class, DataCenterDetailsDaoImpl.class, DiskOfferingDaoImpl.class, StoragePoolHostDaoImpl.class,
UserVmDaoImpl.class, UserVmDetailsDaoImpl.class, ServiceOfferingDaoImpl.class, CapacityDaoImpl.class, SnapshotDaoImpl.class, VMSnapshotDaoImpl.class,
OCFS2ManagerImpl.class, ClusterDetailsDaoImpl.class, SecondaryStorageVmDaoImpl.class, ConsoleProxyDaoImpl.class, StoragePoolWorkDaoImpl.class,
StorageCacheManagerImpl.class, UserDaoImpl.class, DataCenterDaoImpl.class, StoragePoolDetailsDaoImpl.class, DomainDaoImpl.class,
ConfigurationGroupDaoImpl.class, ConfigurationSubGroupDaoImpl.class, ClusterDaoImpl.class, HostPodDaoImpl.class, VMTemplateZoneDaoImpl.class,
VMTemplateDetailsDaoImpl.class, HostDetailsDaoImpl.class, HostTagsDaoImpl.class, HostTransferMapDaoImpl.class, DataCenterIpAddressDaoImpl.class,
DataCenterLinkLocalIpAddressDaoImpl.class, DataCenterVnetDaoImpl.class, PodVlanDaoImpl.class, DataCenterDetailsDaoImpl.class, DiskOfferingDaoImpl.class,
StoragePoolHostDaoImpl.class, UserVmDaoImpl.class, UserVmDetailsDaoImpl.class, ServiceOfferingDaoImpl.class, CapacityDaoImpl.class, SnapshotDaoImpl.class,
VMSnapshotDaoImpl.class, OCFS2ManagerImpl.class, ClusterDetailsDaoImpl.class, SecondaryStorageVmDaoImpl.class, ConsoleProxyDaoImpl.class,
StoragePoolWorkDaoImpl.class, StorageCacheManagerImpl.class, UserDaoImpl.class, DataCenterDaoImpl.class, StoragePoolDetailsDaoImpl.class, DomainDaoImpl.class,
DownloadMonitorImpl.class, AccountDaoImpl.class, ActionEventUtils.class, EventDaoImpl.class},
includeFilters = {@Filter(value = Library.class, type = FilterType.CUSTOM)},
useDefaultFilters = false)

View File

@ -188,5 +188,9 @@
-->
<bean id="configurationDaoImpl" class="com.cloud.configuration.dao.ConfigurationDaoImpl">
</bean>
<bean id="configurationGroupDaoImpl" class="com.cloud.configuration.dao.ConfigurationGroupDaoImpl">
</bean>
<bean id="configurationSubGroupDaoImpl" class="com.cloud.configuration.dao.ConfigurationSubGroupDaoImpl">
</bean>
</beans>

View File

@ -22,9 +22,17 @@ import java.util.Date;
* Configuration represents one global configuration parameter for CloudStack.
* Its scope should indicate whether this parameter can be set at different
* organization levels in CloudStack.
*
*/
public interface Configuration {
enum ValueType {
Boolean,
Number,
Decimal,
Range, // Min and Max (Percentage - 0 to 100)
String,
List, // Set of values
Date;
}
/**
* @return Category of the parameter.
@ -83,8 +91,25 @@ public interface Configuration {
Date getUpdated();
/**
*
* @return returns true if the configuration is encrypted else false.
*/
boolean isEncrypted();
/**
* @return Parent of the configuration.
*/
String getParent();
/**
* @return Display text of the configuration.
*/
String getDisplayText();
Long getGroupId();
Long getSubGroupId();
String getKind();
String getOptions();
}

View File

@ -0,0 +1,28 @@
// 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.config;
public interface ConfigurationGroup {
long getId();
String getName();
String getDescription();
Long getPrecedence();
}

View File

@ -0,0 +1,30 @@
// 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.config;
public interface ConfigurationSubGroup {
public long getId();
public String getName();
public String getKeywords();
public Long getPrecedence();
public Long getGroupId();
}

View File

@ -18,6 +18,8 @@ package org.apache.cloudstack.framework.config;
import java.util.List;
import com.cloud.utils.Pair;
/**
* Administrative interface to ConfigDepot
*
@ -35,4 +37,6 @@ public interface ConfigDepotAdmin {
void populateConfiguration(Configurable configurable);
List<String> getComponentsInDepot();
Pair<Long, Long> getConfigurationGroupAndSubGroupByName(String configName);
}

View File

@ -21,6 +21,8 @@ import java.sql.Date;
import org.apache.cloudstack.framework.config.impl.ConfigDepotImpl;
import org.apache.cloudstack.framework.config.impl.ConfigurationVO;
import com.cloud.utils.Pair;
import com.cloud.utils.Ternary;
import com.cloud.utils.exception.CloudRuntimeException;
/**
@ -33,10 +35,14 @@ public class ConfigKey<T> {
public static final String CATEGORY_ADVANCED = "Advanced";
public static final String CATEGORY_ALERT = "Alert";
public static enum Scope {
public enum Scope {
Global, Zone, Cluster, StoragePool, Account, ManagementServer, ImageStore, Domain
}
public enum Kind {
CSV, Order, Select
}
private final String _category;
public String category() {
@ -59,6 +65,10 @@ public class ConfigKey<T> {
return _description;
}
public String displayText() {
return _displayText;
}
public Scope scope() {
return _scope;
}
@ -67,6 +77,26 @@ public class ConfigKey<T> {
return _isDynamic;
}
public Ternary<String, String, Long> group() {
return _group;
}
public Pair<String, Long> subGroup() {
return _subGroup;
}
public final String parent() {
return _parent;
}
public final Kind kind() {
return _kind;
}
public final String options() {
return _options;
}
@Override
public String toString() {
return _name;
@ -76,8 +106,14 @@ public class ConfigKey<T> {
private final String _name;
private final String _defaultValue;
private final String _description;
private final String _displayText;
private final Scope _scope; // Parameter can be at different levels (Zone/cluster/pool/account), by default every parameter is at global
private final boolean _isDynamic;
private final String _parent;
private final Ternary<String, String, Long> _group; // Group name, description with precedence
private final Pair<String, Long> _subGroup; // SubGroup name with precedence
private final Kind _kind; // Kind such as order, csv, etc
private final String _options; // list of possible options in case of order, list, etc
private final T _multiplier;
T _value = null;
@ -91,19 +127,47 @@ public class ConfigKey<T> {
this(type, name, category, defaultValue, description, isDynamic, scope, null);
}
public ConfigKey(String category, Class<T> type, String name, String defaultValue, String description, boolean isDynamic, Scope scope, String parent) {
this(type, name, category, defaultValue, description, isDynamic, scope, null, null, parent, null, null, null, null);
}
public ConfigKey(String category, Class<T> type, String name, String defaultValue, String description, boolean isDynamic) {
this(type, name, category, defaultValue, description, isDynamic, Scope.Global, null);
}
public ConfigKey(String category, Class<T> type, String name, String defaultValue, String description, boolean isDynamic, String parent) {
this(type, name, category, defaultValue, description, isDynamic, Scope.Global, null, null, parent, null, null, null, null);
}
public ConfigKey(Class<T> type, String name, String category, String defaultValue, String description, boolean isDynamic, Scope scope, T multiplier) {
this(type, name, category, defaultValue, description, isDynamic, scope, multiplier, null, null, null, null, null, null);
}
public ConfigKey(Class<T> type, String name, String category, String defaultValue, String description, boolean isDynamic, Scope scope, T multiplier, String parent) {
this(type, name, category, defaultValue, description, isDynamic, scope, multiplier, null, parent, null, null, null, null);
}
public ConfigKey(Class<T> type, String name, String category, String defaultValue, String description, boolean isDynamic, Scope scope, T multiplier,
String displayText, String parent, Ternary<String, String, Long> group, Pair<String, Long> subGroup) {
this(type, name, category, defaultValue, description, isDynamic, scope, multiplier, null, parent, null, null, null, null);
}
public ConfigKey(Class<T> type, String name, String category, String defaultValue, String description, boolean isDynamic, Scope scope, T multiplier,
String displayText, String parent, Ternary<String, String, Long> group, Pair<String, Long> subGroup, Kind kind, String options) {
_category = category;
_type = type;
_name = name;
_defaultValue = defaultValue;
_description = description;
_displayText = displayText;
_scope = scope;
_isDynamic = isDynamic;
_multiplier = multiplier;
_parent = parent;
_group = group;
_subGroup = subGroup;
_kind = kind;
_options = options;
}
@Deprecated
@ -111,6 +175,10 @@ public class ConfigKey<T> {
this(type, name, category, defaultValue, description, isDynamic, Scope.Global, null);
}
public ConfigKey(Class<T> type, String name, String category, String defaultValue, String description, boolean isDynamic, String parent) {
this(type, name, category, defaultValue, description, isDynamic, Scope.Global, null, null, parent, null, null);
}
public T multiplier() {
return _multiplier;
}

View File

@ -0,0 +1,25 @@
// 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.dao;
import org.apache.cloudstack.framework.config.impl.ConfigurationGroupVO;
import com.cloud.utils.db.GenericDao;
public interface ConfigurationGroupDao extends GenericDao<ConfigurationGroupVO, Long> {
ConfigurationGroupVO findByName(String name);
}

View File

@ -0,0 +1,44 @@
// 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.dao;
import org.apache.cloudstack.framework.config.impl.ConfigurationGroupVO;
import org.springframework.stereotype.Component;
import com.cloud.utils.db.GenericDaoBase;
import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
@Component
public class ConfigurationGroupDaoImpl extends GenericDaoBase<ConfigurationGroupVO, Long> implements ConfigurationGroupDao {
final SearchBuilder<ConfigurationGroupVO> nameSearch;
public ConfigurationGroupDaoImpl() {
super();
nameSearch = createSearchBuilder();
nameSearch.and("name", nameSearch.entity().getName(), SearchCriteria.Op.EQ);
}
@Override
public ConfigurationGroupVO findByName(String name) {
SearchCriteria<ConfigurationGroupVO> sc = nameSearch.create();
sc.setParameters("name", name);
return findOneIncludingRemovedBy(sc);
}
}

View File

@ -0,0 +1,31 @@
// 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.dao;
import java.util.List;
import org.apache.cloudstack.framework.config.impl.ConfigurationSubGroupVO;
import com.cloud.utils.db.GenericDao;
public interface ConfigurationSubGroupDao extends GenericDao<ConfigurationSubGroupVO, Long> {
ConfigurationSubGroupVO findByName(String name);
ConfigurationSubGroupVO startsWithName(String name);
ConfigurationSubGroupVO findByKeyword(String keyword);
ConfigurationSubGroupVO findByNameAndGroup(String name, Long groupId);
List<ConfigurationSubGroupVO> findByGroup(Long groupId);
}

View File

@ -0,0 +1,131 @@
// 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.dao;
import java.util.Arrays;
import java.util.List;
import java.util.function.BiPredicate;
import org.apache.cloudstack.framework.config.impl.ConfigurationSubGroupVO;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import com.cloud.utils.db.Filter;
import com.cloud.utils.db.GenericDaoBase;
import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
@Component
public class ConfigurationSubGroupDaoImpl extends GenericDaoBase<ConfigurationSubGroupVO, Long> implements ConfigurationSubGroupDao {
final SearchBuilder<ConfigurationSubGroupVO> nameSearch;
final SearchBuilder<ConfigurationSubGroupVO> groupSearch;
final SearchBuilder<ConfigurationSubGroupVO> nameAndGroupSearch;
final SearchBuilder<ConfigurationSubGroupVO> keywordSearch;
public ConfigurationSubGroupDaoImpl() {
super();
nameSearch = createSearchBuilder();
nameSearch.and("name", nameSearch.entity().getName(), SearchCriteria.Op.LIKE);
nameSearch.done();
groupSearch = createSearchBuilder();
groupSearch.and("groupId", groupSearch.entity().getGroupId(), SearchCriteria.Op.EQ);
groupSearch.done();
nameAndGroupSearch = createSearchBuilder();
nameAndGroupSearch.and("name", nameAndGroupSearch.entity().getName(), SearchCriteria.Op.EQ);
nameAndGroupSearch.and("groupId", nameAndGroupSearch.entity().getGroupId(), SearchCriteria.Op.EQ);
nameAndGroupSearch.done();
keywordSearch = createSearchBuilder();
keywordSearch.and("keywords", keywordSearch.entity().getKeywords(), SearchCriteria.Op.NNULL);
keywordSearch.done();
}
@Override
public ConfigurationSubGroupVO findByName(String name) {
SearchCriteria<ConfigurationSubGroupVO> sc = nameSearch.create();
sc.setParameters("name", name);
return findOneIncludingRemovedBy(sc);
}
@Override
public ConfigurationSubGroupVO startsWithName(String name) {
SearchCriteria<ConfigurationSubGroupVO> sc = nameSearch.create();
sc.setParameters("name", name + "%");
return findOneIncludingRemovedBy(sc);
}
private ConfigurationSubGroupVO matchKeywordBy(BiPredicate<String, String> matcher, List<ConfigurationSubGroupVO> configurationSubGroups, String keyword) {
for (ConfigurationSubGroupVO configurationSubGroup : configurationSubGroups) {
if (StringUtils.isBlank(configurationSubGroup.getKeywords())) {
continue;
}
String[] configKeywords = configurationSubGroup.getKeywords().split(",");
if (configKeywords.length <= 0) {
continue;
}
List<String> keywords = Arrays.asList(configKeywords);
for (String configKeyword : keywords) {
if (StringUtils.isNotBlank(configKeyword)) {
configKeyword = configKeyword.strip().toLowerCase();
if (matcher.test(keyword, configKeyword)) {
return configurationSubGroup;
}
}
}
}
return null;
}
@Override
public ConfigurationSubGroupVO findByKeyword(String keyword) {
if (StringUtils.isBlank(keyword)) {
return null;
}
SearchCriteria<ConfigurationSubGroupVO> sc = keywordSearch.create();
List<ConfigurationSubGroupVO> configurationSubGroups = listBy(sc);
BiPredicate<String, String> equals = (a, b) -> { return a.equalsIgnoreCase(b); };
ConfigurationSubGroupVO configSubGroup = matchKeywordBy(equals, configurationSubGroups, keyword);
if (configSubGroup == null) {
BiPredicate<String, String> startsWith = (a, b) -> { return a.startsWith(b); };
configSubGroup = matchKeywordBy(startsWith, configurationSubGroups, keyword.toLowerCase());
}
return configSubGroup;
}
@Override
public ConfigurationSubGroupVO findByNameAndGroup(String name, Long groupId) {
SearchCriteria<ConfigurationSubGroupVO> sc = nameAndGroupSearch.create();
sc.setParameters("name", name);
sc.setParameters("groupId", groupId);
return findOneIncludingRemovedBy(sc);
}
@Override
public List<ConfigurationSubGroupVO> findByGroup(Long groupId) {
SearchCriteria<ConfigurationSubGroupVO> sc = groupSearch.create();
sc.setParameters("groupId", groupId);
final Filter filter = new Filter(ConfigurationSubGroupVO.class, "precedence", true, null, null);
return listIncludingRemovedBy(sc, filter);
}
}

View File

@ -33,10 +33,14 @@ 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;
/**
@ -70,6 +74,10 @@ 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>());
@ -139,6 +147,28 @@ public class ConfigDepotImpl implements ConfigDepot, ConfigDepotAdmin {
}
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);
@ -146,8 +176,25 @@ public class ConfigDepotImpl implements ConfigDepot, ConfigDepotAdmin {
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);
} 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)) {
@ -157,6 +204,48 @@ public class ConfigDepotImpl implements ConfigDepot, ConfigDepotAdmin {
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);
}
}
@ -227,6 +316,31 @@ public class ConfigDepotImpl implements ConfigDepot, ConfigDepotAdmin {
@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);
}
}

View File

@ -0,0 +1,91 @@
// 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 javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import org.apache.cloudstack.config.ConfigurationGroup;
@Entity
@Table(name = "configuration_group")
public class ConfigurationGroupVO implements ConfigurationGroup {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private long id = 1;
@Column(name = "name")
private String name;
@Column(name = "description", length = 1024)
private String description = null;
@Column(name = "precedence")
private Long precedence = 999L;
protected ConfigurationGroupVO() {
}
public ConfigurationGroupVO(String name, String description) {
this.name = name;
this.description = description;
this.precedence = 999L;
}
public ConfigurationGroupVO(String name, String description, Long precedence) {
this.name = name;
this.description = description;
this.precedence = precedence;
}
@Override
public long getId() {
return id;
}
@Override
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public Long getPrecedence() {
return precedence;
}
public void setPrecedence(Long precedence) {
this.precedence = precedence;
}
}

View File

@ -0,0 +1,103 @@
// 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 javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import org.apache.cloudstack.config.ConfigurationSubGroup;
@Entity
@Table(name = "configuration_subgroup")
public class ConfigurationSubGroupVO implements ConfigurationSubGroup {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private long id = 1;
@Column(name = "name")
private String name;
@Column(name = "keywords")
private String keywords = null;
@Column(name = "precedence")
private Long precedence = 999L;
@Column(name = "group_id")
private Long groupId;
protected ConfigurationSubGroupVO() {
}
public ConfigurationSubGroupVO(String name, String keywords) {
this.name = name;
this.keywords = keywords;
this.precedence = 999L;
}
public ConfigurationSubGroupVO(String name, String keywords, Long precedence) {
this.name = name;
this.keywords = keywords;
this.precedence = precedence;
}
@Override
public long getId() {
return id;
}
@Override
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String getKeywords() {
return keywords;
}
public void setKeywords(String keywords) {
this.keywords = keywords;
}
@Override
public Long getPrecedence() {
return precedence;
}
public void setPrecedence(Long precedence) {
this.precedence = precedence;
}
@Override
public Long getGroupId() {
return groupId;
}
public void setGroupId(Long groupId) {
this.groupId = groupId;
}
}

View File

@ -27,6 +27,7 @@ import javax.persistence.TemporalType;
import org.apache.cloudstack.config.Configuration;
import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.commons.lang3.StringUtils;
import com.cloud.utils.crypt.DBEncryptionUtil;
@ -65,20 +66,50 @@ public class ConfigurationVO implements Configuration {
@Temporal(value = TemporalType.TIMESTAMP)
private Date updated;
@Column(name = "group_id")
private Long groupId = 1L;
@Column(name = "subgroup_id")
private Long subGroupId = 1L;
@Column(name = "parent")
private String parent;
@Column(name = "display_text", length = 255)
private String displayText;
@Column(name = "kind")
private String kind;
@Column(name = "options")
private String options;
protected ConfigurationVO() {
}
public ConfigurationVO(String category, String instance, String component, String name, String value, String description) {
this(category, instance, component, name, value, description, null, null);
}
public ConfigurationVO(String category, String instance, String component, String name, String value, String description, String displayText, String parentConfigName) {
this(category, instance, component, name, value, description, displayText, parentConfigName, null, null);
}
public ConfigurationVO(String category, String instance, String component, String name, String value, String description, String displayText, String parentConfigName, Long groupId, Long subGroupId) {
this.category = category;
this.instance = instance;
this.component = component;
this.name = name;
this.description = description;
this.parent = parentConfigName;
setValue(value);
setDisplayText(displayText);
setGroupId(groupId);
setSubGroupId(subGroupId);
}
public ConfigurationVO(String component, ConfigKey<?> key) {
this(key.category(), "DEFAULT", component, key.key(), key.defaultValue(), key.description());
this(key.category(), "DEFAULT", component, key.key(), key.defaultValue(), key.description(), key.displayText(), key.parent());
defaultValue = key.defaultValue();
dynamic = key.isDynamic();
scope = key.scope() != null ? key.scope().toString() : null;
@ -186,4 +217,83 @@ public class ConfigurationVO implements Configuration {
public void setUpdated(Date updated) {
this.updated = updated;
}
@Override
public Long getGroupId() {
return groupId;
}
public void setGroupId(Long groupId) {
if (groupId != null) {
this.groupId = groupId;
} else {
this.groupId = 1L;
}
}
@Override
public Long getSubGroupId() {
return subGroupId;
}
public void setSubGroupId(Long subGroupId) {
if (subGroupId != null) {
this.subGroupId = subGroupId;
} else {
this.subGroupId = 1L;
}
}
@Override
public String getParent() {
return parent;
}
public void setParent(String parent) {
this.parent = parent;
}
@Override
public String getDisplayText() {
if (StringUtils.isNotBlank(displayText)) {
return displayText;
}
String displayText = "";
String name = getName();
if (StringUtils.isNotBlank(name)) {
name = name.replace(".", " ");
displayText = name.substring(0, 1).toUpperCase() + name.substring(1);
}
return displayText;
}
public void setDisplayText(String displayText) {
if (StringUtils.isBlank(displayText)) {
String name = getName();
if (StringUtils.isNotBlank(name)) {
name = name.replace(".", " ");
displayText = name.substring(0, 1).toUpperCase() + name.substring(1);
}
}
this.displayText = displayText;
}
public String getKind() {
return kind;
}
public void setKind(String kind) {
this.kind = kind;
}
public String getOptions() {
return options;
}
public void setOptions(String options) {
this.options = options;
}
}

View File

@ -45,6 +45,12 @@
</property>
</bean>
<bean id="configurationGroupDaoImpl"
class="org.apache.cloudstack.framework.config.dao.ConfigurationGroupDaoImpl" />
<bean id="configurationSubGroupDaoImpl"
class="org.apache.cloudstack.framework.config.dao.ConfigurationSubGroupDaoImpl" />
<bean id="scopedConfigStorageRegistry"
class="org.apache.cloudstack.spring.lifecycle.registry.ExtensionRegistry" />

View File

@ -39,12 +39,12 @@ public class ConfigKeyTest {
@Test
public void testIsSameKeyAs() {
ConfigKey key = new ConfigKey("cat", String.class, "naam", "nick", "bijnaam", true, Scope.Cluster);
Assert.assertTrue("1 and one should be considdered the same address", key.isSameKeyAs("naam"));
Assert.assertTrue("1 and one should be considered the same address", key.isSameKeyAs("naam"));
}
@Test(expected = CloudRuntimeException.class)
public void testIsSameKeyAsThrowingCloudRuntimeException() {
ConfigKey key = new ConfigKey("hond", Boolean.class, "naam", "truus", "thrown name", false);
Assert.assertFalse("zero and 0L should be considdered the same address", key.isSameKeyAs(0L));
Assert.assertFalse("zero and 0L should be considered the same address", key.isSameKeyAs(0L));
}
}

View File

@ -16,6 +16,7 @@
// under the License.
package org.apache.cloudstack.framework.config.impl;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@ -25,9 +26,13 @@ import java.util.ArrayList;
import junit.framework.TestCase;
import org.apache.cloudstack.framework.config.dao.ConfigurationGroupDao;
import org.apache.cloudstack.framework.config.dao.ConfigurationSubGroupDao;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.apache.cloudstack.framework.config.ConfigDepot;
@ -36,11 +41,15 @@ import org.apache.cloudstack.framework.config.Configurable;
import org.apache.cloudstack.framework.config.ScopedConfigStorage;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import com.cloud.utils.Pair;
import com.cloud.utils.Ternary;
import com.cloud.utils.db.EntityManager;
public class ConfigDepotAdminTest extends TestCase {
private final static ConfigKey<Integer> DynamicIntCK = new ConfigKey<Integer>(Integer.class, "dynIntKey", "Advance", "10", "Test Key", true);
private final static ConfigKey<Integer> StaticIntCK = new ConfigKey<Integer>(Integer.class, "statIntKey", "Advance", "10", "Test Key", false);
private final static ConfigKey<Integer> TestCK = new ConfigKey<>(Integer.class, "testKey", "Advance", "30", "Test Key", false,
ConfigKey.Scope.Global, null, "Test Display Text", null, new Ternary<>("TestGroup", "Test Group", 3L), new Pair<>("Test SubGroup", 1L));
@Mock
Configurable _configurable;
@ -56,6 +65,12 @@ public class ConfigDepotAdminTest extends TestCase {
@Mock
ConfigurationDao _configDao;
@Mock
ConfigurationGroupDao _configGroupDao;
@Mock
ConfigurationSubGroupDao _configSubGroupDao;
@Mock
ScopedConfigStorage _scopedStorage;
@ -68,6 +83,8 @@ public class ConfigDepotAdminTest extends TestCase {
MockitoAnnotations.initMocks(this);
_depotAdmin = new ConfigDepotImpl();
_depotAdmin._configDao = _configDao;
_depotAdmin._configGroupDao = _configGroupDao;
_depotAdmin._configSubGroupDao = _configSubGroupDao;
_depotAdmin._configurables = new ArrayList<Configurable>();
_depotAdmin._configurables.add(_configurable);
_depotAdmin._scopedStorages = new ArrayList<ScopedConfigStorage>();
@ -80,22 +97,58 @@ public class ConfigDepotAdminTest extends TestCase {
dynamicIntCV.setValue("100");
ConfigurationVO staticIntCV = new ConfigurationVO("UnitTestComponent", StaticIntCK);
dynamicIntCV.setValue("200");
ConfigurationVO testCV = new ConfigurationVO("UnitTestComponent", TestCK);
when(_configurable.getConfigComponentName()).thenReturn("UnitTestComponent");
when(_configurable.getConfigKeys()).thenReturn(new ConfigKey<?>[] {DynamicIntCK, StaticIntCK});
when(_configurable.getConfigKeys()).thenReturn(new ConfigKey<?>[] {DynamicIntCK, StaticIntCK, TestCK});
when(_configDao.findById(StaticIntCK.key())).thenReturn(null);
when(_configDao.findById(DynamicIntCK.key())).thenReturn(dynamicIntCV);
when(_configDao.findById(TestCK.key())).thenReturn(testCV);
when(_configDao.persist(any(ConfigurationVO.class))).thenReturn(dynamicIntCV);
_depotAdmin.populateConfigurations();
// This is once because DynamicIntCK is returned.
verify(_configDao, times(1)).persist(any(ConfigurationVO.class));
when(_configDao.findById(DynamicIntCK.key())).thenReturn(dynamicIntCV);
when(_configDao.findById(TestCK.key())).thenReturn(null);
_depotAdmin._configured.clear();
_depotAdmin.populateConfigurations();
// This is two because DynamicIntCK also returns null.
verify(_configDao, times(2)).persist(any(ConfigurationVO.class));
// This is three because DynamicIntCK, TestCK also returns null.
verify(_configDao, times(3)).persist(any(ConfigurationVO.class));
}
@Test
public void testDefaultConfigurationGroupAndSubGroup() {
Mockito.when(_configSubGroupDao.findByName(anyString())).thenReturn(null);
Mockito.when(_configSubGroupDao.findByKeyword(anyString())).thenReturn(null);
Pair<Long, Long> configGroupAndSubGroup = _depotAdmin.getConfigurationGroupAndSubGroupByName("test.storage.config.setting");
Assert.assertEquals(1L, configGroupAndSubGroup.first().longValue());
Assert.assertEquals(1L, configGroupAndSubGroup.second().longValue());
}
@Test
public void testConfigurationGroupAndSubGroup() {
ConfigurationGroupVO testGroup = new ConfigurationGroupVO("TestGroup", "Test Group", 3L);
ConfigurationSubGroupVO testSubGroup = new ConfigurationSubGroupVO("TestSubGroup", null, 1L);
testSubGroup.setGroupId(9L);
Mockito.when(_configSubGroupDao.findByName("storage")).thenReturn(testSubGroup);
Mockito.when(_configSubGroupDao.findByKeyword(anyString())).thenReturn(null);
Pair<Long, Long> configGroupAndSubGroup = _depotAdmin.getConfigurationGroupAndSubGroupByName("test.storage.config.setting");
Assert.assertEquals(9L, configGroupAndSubGroup.first().longValue());
Assert.assertEquals(1L, configGroupAndSubGroup.second().longValue());
testSubGroup.setGroupId(5L);
Mockito.when(_configSubGroupDao.findByName(anyString())).thenReturn(null);
Mockito.when(_configSubGroupDao.findByKeyword("storage")).thenReturn(testSubGroup);
configGroupAndSubGroup = _depotAdmin.getConfigurationGroupAndSubGroupByName("test.storage.config.setting");
Assert.assertEquals(5L, configGroupAndSubGroup.first().longValue());
Assert.assertEquals(1L, configGroupAndSubGroup.second().longValue());
}
}

View File

@ -18,14 +18,17 @@
*/
package org.apache.cloudstack.framework.jobs;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.apache.cloudstack.framework.config.ConfigDepot;
import org.apache.cloudstack.framework.config.ScopedConfigStorage;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.framework.config.dao.ConfigurationDaoImpl;
import org.apache.cloudstack.framework.config.dao.ConfigurationGroupDao;
import org.apache.cloudstack.framework.config.dao.ConfigurationGroupDaoImpl;
import org.apache.cloudstack.framework.config.dao.ConfigurationSubGroupDao;
import org.apache.cloudstack.framework.config.dao.ConfigurationSubGroupDaoImpl;
import org.apache.cloudstack.framework.config.impl.ConfigDepotImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.cloud.storage.dao.StoragePoolDetailsDaoImpl;
@ -42,6 +45,16 @@ public class AsyncJobManagerTestConfiguration {
return new ConfigurationDaoImpl();
}
@Bean
public ConfigurationGroupDao configGroupDao() {
return new ConfigurationGroupDaoImpl();
}
@Bean
public ConfigurationSubGroupDao configSubGroupDao() {
return new ConfigurationSubGroupDaoImpl();
}
@Bean
public ScopedConfigStorage scopedConfigStorage() {
return new StoragePoolDetailsDaoImpl();

View File

@ -136,14 +136,14 @@ public class ExtensionRegistry implements Registry<Object>, Configurable, BeanNa
List<ConfigKey<String>> result = new ArrayList<ConfigKey<String>>();
if (orderConfigKey != null && orderConfigKeyObj == null) {
orderConfigKeyObj = new ConfigKey<String>("Advanced", String.class, orderConfigKey, orderConfigDefault, "The order of precedence for the extensions", false);
orderConfigKeyObj = new ConfigKey<>(String.class, orderConfigKey, "Advanced", orderConfigDefault, "The order of precedence for the extensions", false, ConfigKey.Scope.Global, null, null, null, null, null, ConfigKey.Kind.Order, orderConfigDefault);
}
if (orderConfigKeyObj != null)
result.add(orderConfigKeyObj);
if (excludeKey != null && excludeKeyObj == null) {
excludeKeyObj = new ConfigKey<String>("Advanced", String.class, excludeKey, excludeDefault, "Extensions to exclude from being registered", false);
excludeKeyObj = new ConfigKey<>(String.class, excludeKey, "Advanced", excludeDefault, "Extensions to exclude from being registered", false, ConfigKey.Scope.Global, null, null, null, null, null, ConfigKey.Kind.CSV, null);
}
if (excludeKeyObj != null) {

View File

@ -45,33 +45,39 @@ public interface KubernetesClusterService extends PluggableService, Configurable
"cloud.kubernetes.cluster.network.offering",
"DefaultNetworkOfferingforKubernetesService",
"Name of the network offering that will be used to create isolated network in which Kubernetes cluster VMs will be launched",
false);
false,
KubernetesServiceEnabled.key());
static final ConfigKey<Long> KubernetesClusterStartTimeout = new ConfigKey<Long>("Advanced", Long.class,
"cloud.kubernetes.cluster.start.timeout",
"3600",
"Timeout interval (in seconds) in which start operation for a Kubernetes cluster should be completed",
true);
true,
KubernetesServiceEnabled.key());
static final ConfigKey<Long> KubernetesClusterScaleTimeout = new ConfigKey<Long>("Advanced", Long.class,
"cloud.kubernetes.cluster.scale.timeout",
"3600",
"Timeout interval (in seconds) in which scale operation for a Kubernetes cluster should be completed",
true);
true,
KubernetesServiceEnabled.key());
static final ConfigKey<Long> KubernetesClusterUpgradeTimeout = new ConfigKey<Long>("Advanced", Long.class,
"cloud.kubernetes.cluster.upgrade.timeout",
"3600",
"Timeout interval (in seconds) in which upgrade operation for a Kubernetes cluster should be completed. Not strictly obeyed while upgrade is in progress on a node",
true);
true,
KubernetesServiceEnabled.key());
static final ConfigKey<Boolean> KubernetesClusterExperimentalFeaturesEnabled = new ConfigKey<Boolean>("Advanced", Boolean.class,
"cloud.kubernetes.cluster.experimental.features.enabled",
"false",
"Indicates whether experimental feature for Kubernetes cluster such as Docker private registry are enabled or not",
true);
true,
KubernetesServiceEnabled.key());
static final ConfigKey<Integer> KubernetesMaxClusterSize = new ConfigKey<Integer>("Advanced", Integer.class,
"cloud.kubernetes.cluster.max.size",
"10",
"Maximum size of the kubernetes cluster.",
true, ConfigKey.Scope.Account);
true,
ConfigKey.Scope.Account,
KubernetesServiceEnabled.key());
KubernetesCluster findById(final Long id);

View File

@ -26,10 +26,10 @@ public interface PrometheusExporterServer extends Manager {
"Enable the prometheus exporter plugin, management server restart needed.", false);
ConfigKey<Integer> PrometheusExporterServerPort = new ConfigKey<>("Advanced", Integer.class, "prometheus.exporter.port", "9595",
"The prometheus exporter server port", true);
"The prometheus exporter server port", true, EnablePrometheusExporter.key());
ConfigKey<String> PrometheusExporterAllowedAddresses = new ConfigKey<>("Advanced", String.class, "prometheus.exporter.allowed.ips", "127.0.0.1",
"List of comma separated prometheus server ips (with no spaces) that should be allowed to access the URLs", true);
"List of comma separated prometheus server ips (with no spaces) that should be allowed to access the URLs", true, EnablePrometheusExporter.key());
ConfigKey<Integer> PrometheusExporterOfferingCountLimit = new ConfigKey<>("Advanced", Integer.class, "prometheus.exporter.offering.output.limit", "-1",
"Limit the number of output for cloudstack_vms_total_by_size to the provided value. -1 for unlimited output.", true);

View File

@ -21,6 +21,8 @@ import java.io.IOException;
import javax.inject.Inject;
import org.apache.cloudstack.framework.config.dao.ConfigurationGroupDaoImpl;
import org.apache.cloudstack.framework.config.dao.ConfigurationSubGroupDaoImpl;
import org.eclipse.jetty.security.IdentityService;
import org.mockito.Matchers;
import org.mockito.Mockito;
@ -304,14 +306,14 @@ import com.cloud.vm.snapshot.dao.VMSnapshotDaoImpl;
ApiDBUtils.class, ApplicationLoadBalancerRuleDaoImpl.class, AsyncJobDaoImpl.class, AsyncJobJoinDaoImpl.class, AsyncJobJoinMapDaoImpl.class,
AsyncJobJournalDaoImpl.class, AsyncJobManagerImpl.class, AutoScalePolicyConditionMapDaoImpl.class, AutoScalePolicyDaoImpl.class, AutoScaleVmGroupDaoImpl.class,
AutoScaleVmGroupPolicyMapDaoImpl.class, AutoScaleVmProfileDaoImpl.class, CapacityDaoImpl.class, ClusterDaoImpl.class, ClusterDetailsDaoImpl.class,
ConditionDaoImpl.class, ConfigurationDaoImpl.class, ConfigurationManagerImpl.class, ConfigurationServerImpl.class, ConsoleProxyDaoImpl.class,
ContrailElementImpl.class, ContrailGuru.class, ContrailManagerImpl.class, CounterDaoImpl.class, DataCenterDaoImpl.class, DataCenterDetailsDaoImpl.class, DataCenterIpAddressDaoImpl.class,
DataCenterJoinDaoImpl.class, DataCenterLinkLocalIpAddressDaoImpl.class, DataCenterVnetDaoImpl.class, DcDetailsDaoImpl.class, DedicatedResourceDaoImpl.class,
ConditionDaoImpl.class, ConfigurationDaoImpl.class, ConfigurationGroupDaoImpl.class, ConfigurationSubGroupDaoImpl.class, ConfigurationManagerImpl.class, ConfigurationServerImpl.class,
ConsoleProxyDaoImpl.class, ContrailElementImpl.class, ContrailGuru.class, ContrailManagerImpl.class, CounterDaoImpl.class, DataCenterDaoImpl.class, DataCenterDetailsDaoImpl.class,
DataCenterIpAddressDaoImpl.class, DataCenterJoinDaoImpl.class, DataCenterLinkLocalIpAddressDaoImpl.class, DataCenterVnetDaoImpl.class, DcDetailsDaoImpl.class, DedicatedResourceDaoImpl.class,
DiskOfferingDaoImpl.class, DiskOfferingJoinDaoImpl.class, DomainDaoImpl.class, DomainDetailsDaoImpl.class, DomainManagerImpl.class, DomainRouterDaoImpl.class, DomainRouterJoinDaoImpl.class,
EventDaoImpl.class, EventJoinDaoImpl.class, EventUtils.class, ExtensionRegistry.class, FirewallManagerImpl.class, FirewallRulesCidrsDaoImpl.class,
FirewallRulesDaoImpl.class, GuestOSCategoryDaoImpl.class, GuestOSDaoImpl.class, HostDaoImpl.class, HostDetailsDaoImpl.class, HostJoinDaoImpl.class,
HostPodDaoImpl.class, HostTagsDaoImpl.class, HostTransferMapDaoImpl.class, HypervisorCapabilitiesDaoImpl.class, HypervisorGuruManagerImpl.class,
ImageStoreDaoImpl.class, ImageStoreJoinDaoImpl.class, InstanceGroupDaoImpl.class, InstanceGroupJoinDaoImpl.class,
ImageStoreDaoImpl.class, ImageStoreJoinDaoImpl.class, InstanceGroupDaoImpl.class, InstanceGroupJoinDaoImpl.class,
InstanceGroupVMMapDaoImpl.class, InternalLoadBalancerElement.class, IPAddressDaoImpl.class, IpAddressManagerImpl.class, Ipv6AddressManagerImpl.class, ItWorkDaoImpl.class, LBHealthCheckPolicyDaoImpl.class,
LBStickinessPolicyDaoImpl.class, LaunchPermissionDaoImpl.class, LoadBalancerDaoImpl.class, LoadBalancerVMMapDaoImpl.class, LoadBalancingRulesManagerImpl.class,
ManagementServerHostDaoImpl.class, MockAccountManager.class, NetworkACLDaoImpl.class, NetworkACLItemDaoImpl.class, NetworkACLManagerImpl.class,

View File

@ -61,8 +61,8 @@ public interface SAML2AuthManager extends PluggableAPIAuthenticator, PluggableSe
public static final ConfigKey<String> SAMLDefaultIdentityProviderId = new ConfigKey<String>("Advanced", String.class, "saml2.default.idpid", "https://openidp.feide.no",
"The default IdP entity ID to use only in case of multiple IdPs", true);
public static final ConfigKey<String> SAMLSignatureAlgorithm = new ConfigKey<String>("Advanced", String.class, "saml2.sigalg", "SHA1",
"The algorithm to use to when signing a SAML request. Default is SHA1, allowed algorithms: SHA1, SHA256, SHA384, SHA512", true);
public static final ConfigKey<String> SAMLSignatureAlgorithm = new ConfigKey<>(String.class, "saml2.sigalg", "Advanced", "SHA1",
"The algorithm to use to when signing a SAML request. Default is SHA1, allowed algorithms: SHA1, SHA256, SHA384, SHA512", true, ConfigKey.Scope.Global, null, null, null, null, null, ConfigKey.Kind.Select, "SHA1,SHA256,SHA384,SHA512");
public static final ConfigKey<Boolean> SAMLAppendDomainSuffix = new ConfigKey<Boolean>("Advanced", Boolean.class, "saml2.append.idpdomain", "false",
"If enabled, create account/user dialog with SAML SSO enabled will append the IdP domain to the user or account name in the UI dialog", true);

View File

@ -22,6 +22,8 @@ under the License.
<dao name="host pod dao" class="com.cloud.dc.dao.HostPodDaoImpl" />
<dao name="Datacenter dao" class="com.cloud.dc.dao.DataCenterDaoImpl" />
<dao name="Configuration dao" class="com.cloud.configuration.dao.ConfigurationDaoImpl" />
<dao name="Configuration group dao" class="com.cloud.configuration.dao.ConfigurationGroupDaoImpl" />
<dao name="Configuration subgroup dao" class="com.cloud.configuration.dao.ConfigurationSubGroupDaoImpl" />
<dao name="Cluster dao" class="com.cloud.dc.dao.ClusterDaoImpl" />
<dao name="Host dao" class="com.cloud.host.dao.HostDaoImpl" />
<dao name="StoragePool dao" class="com.cloud.storage.dao.StoragePoolDaoImpl" />

View File

@ -65,7 +65,9 @@ import org.apache.cloudstack.api.response.CapabilityResponse;
import org.apache.cloudstack.api.response.CapacityResponse;
import org.apache.cloudstack.api.response.ClusterResponse;
import org.apache.cloudstack.api.response.ConditionResponse;
import org.apache.cloudstack.api.response.ConfigurationGroupResponse;
import org.apache.cloudstack.api.response.ConfigurationResponse;
import org.apache.cloudstack.api.response.ConfigurationSubGroupResponse;
import org.apache.cloudstack.api.response.ControlledEntityResponse;
import org.apache.cloudstack.api.response.ControlledViewEntityResponse;
import org.apache.cloudstack.api.response.CounterResponse;
@ -170,6 +172,8 @@ import org.apache.cloudstack.backup.BackupOffering;
import org.apache.cloudstack.backup.BackupSchedule;
import org.apache.cloudstack.backup.dao.BackupOfferingDao;
import org.apache.cloudstack.config.Configuration;
import org.apache.cloudstack.config.ConfigurationGroup;
import org.apache.cloudstack.config.ConfigurationSubGroup;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.direct.download.DirectDownloadCertificate;
import org.apache.cloudstack.direct.download.DirectDownloadCertificateHostMap;
@ -560,6 +564,9 @@ public class ApiResponseHelper implements ResponseGenerator {
public ConfigurationResponse createConfigurationResponse(Configuration cfg) {
ConfigurationResponse cfgResponse = new ConfigurationResponse();
cfgResponse.setCategory(cfg.getCategory());
Pair<String, String> configGroupAndSubGroup = _configMgr.getConfigurationGroupAndSubGroup(cfg.getName());
cfgResponse.setGroup(configGroupAndSubGroup.first());
cfgResponse.setSubGroup(configGroupAndSubGroup.second());
cfgResponse.setDescription(cfg.getDescription());
cfgResponse.setName(cfg.getName());
if (cfg.isEncrypted()) {
@ -567,12 +574,48 @@ public class ApiResponseHelper implements ResponseGenerator {
} else {
cfgResponse.setValue(cfg.getValue());
}
cfgResponse.setDefaultValue(cfg.getDefaultValue());
cfgResponse.setIsDynamic(cfg.isDynamic());
cfgResponse.setComponent(cfg.getComponent());
if (cfg.getParent() != null) {
cfgResponse.setParent(cfg.getParent());
}
cfgResponse.setDisplayText(cfg.getDisplayText());
cfgResponse.setType(_configMgr.getConfigurationType(cfg.getName()));
if (cfg.getOptions() != null) {
cfgResponse.setOptions(cfg.getOptions());
}
cfgResponse.setObjectName("configuration");
return cfgResponse;
}
@Override
public ConfigurationGroupResponse createConfigurationGroupResponse(ConfigurationGroup cfgGroup) {
ConfigurationGroupResponse cfgGroupResponse = new ConfigurationGroupResponse();
cfgGroupResponse.setGroupName(cfgGroup.getName());
cfgGroupResponse.setDescription(cfgGroup.getDescription());
cfgGroupResponse.setPrecedence(cfgGroup.getPrecedence());
List<? extends ConfigurationSubGroup> subgroups = _configMgr.getConfigurationSubGroups(cfgGroup.getId());
List<ConfigurationSubGroupResponse> cfgSubGroupResponses = new ArrayList<>();
for (ConfigurationSubGroup subgroup : subgroups) {
ConfigurationSubGroupResponse cfgSubGroupResponse = createConfigurationSubGroupResponse(subgroup);
cfgSubGroupResponses.add(cfgSubGroupResponse);
}
cfgGroupResponse.setSubGroups(cfgSubGroupResponses);
cfgGroupResponse.setObjectName("configurationgroup");
return cfgGroupResponse;
}
private ConfigurationSubGroupResponse createConfigurationSubGroupResponse(ConfigurationSubGroup cfgSubGroup) {
ConfigurationSubGroupResponse cfgSubGroupResponse = new ConfigurationSubGroupResponse();
cfgSubGroupResponse.setSubGroupName(cfgSubGroup.getName());
cfgSubGroupResponse.setPrecedence(cfgSubGroup.getPrecedence());
cfgSubGroupResponse.setObjectName("subgroup");
return cfgSubGroupResponse;
}
@Override
public SnapshotResponse createSnapshotResponse(Snapshot snapshot) {
SnapshotResponse snapshotResponse = new SnapshotResponse();

View File

@ -54,6 +54,8 @@ public enum Config {
"alert.email.addresses",
null,
"Comma separated list of email addresses which are going to receive alert emails.",
null,
ConfigKey.Kind.CSV,
null),
AlertEmailSender("Alert", ManagementServer.class, String.class, "alert.email.sender", null, "Sender of alert email (will be in the From header of the email).", null),
AlertSMTPHost("Alert", ManagementServer.class, String.class, "alert.smtp.host", null, "SMTP hostname used for sending out email alerts.", null),
@ -229,7 +231,9 @@ public enum Config {
"network.loadbalancer.haproxy.stats.visibility",
"global",
"Load Balancer(haproxy) stats visibility, the value can be one of the following six parameters : global,guest-network,link-local,disabled,all,default",
null),
null,
ConfigKey.Kind.Select,
"global,guest-network,link-local,disabled,all,default"),
NetworkLBHaproxyStatsUri(
"Network",
ManagementServer.class,
@ -354,6 +358,8 @@ public enum Config {
"network.dhcp.nondefaultnetwork.setgateway.guestos",
"Windows",
"The guest OS's name start with this fields would result in DHCP server response gateway information even when the network it's on is not default network. Names are separated by comma.",
null,
ConfigKey.Kind.CSV,
null),
//VPN
@ -425,8 +431,22 @@ public enum Config {
"8001",
"Console proxy command port that is used to communicate with management server",
null),
ConsoleProxyRestart("Console Proxy", AgentManager.class, Boolean.class, "consoleproxy.restart", "true", "Console proxy restart flag, defaulted to true", null),
ConsoleProxyUrlDomain("Console Proxy", AgentManager.class, String.class, "consoleproxy.url.domain", "", "Console proxy url domain", "domainName", "privateip"),
ConsoleProxyRestart(
"Console Proxy",
AgentManager.class,
Boolean.class,
"consoleproxy.restart",
"true",
"Console proxy restart flag, defaulted to true",
null),
ConsoleProxyUrlDomain(
"Console Proxy",
AgentManager.class,
String.class,
"consoleproxy.url.domain",
"",
"Console proxy url domain",
"domainName,privateip"),
ConsoleProxySessionMax(
"Console Proxy",
AgentManager.class,
@ -649,7 +669,9 @@ public enum Config {
HypervisorType.Hyperv + "," + HypervisorType.KVM + "," + HypervisorType.XenServer + "," + HypervisorType.VMware + "," + HypervisorType.BareMetal + "," +
HypervisorType.Ovm + "," + HypervisorType.LXC + "," + HypervisorType.Ovm3,
"The list of hypervisors that this deployment will use.",
"hypervisorList"),
"hypervisorList",
ConfigKey.Kind.CSV,
null),
ManagementNetwork("Advanced", ManagementServer.class, String.class, "management.network.cidr", null, "The cidr of management server network", null),
EventPurgeDelay(
"Advanced",
@ -903,7 +925,9 @@ public enum Config {
"vm.allocation.algorithm",
"random",
"'random', 'firstfit', 'userdispersing', 'userconcentratedpod_random', 'userconcentratedpod_firstfit', 'firstfitleastconsumed' : Order in which hosts within a cluster will be considered for VM/volume allocation.",
null),
null,
ConfigKey.Kind.Select,
"random,firstfit,userdispersing,userconcentratedpod_random,userconcentratedpod_firstfit,firstfitleastconsumed"),
VmDeploymentPlanner(
"Advanced",
ManagementServer.class,
@ -911,7 +935,9 @@ public enum Config {
"vm.deployment.planner",
"FirstFitPlanner",
"'FirstFitPlanner', 'UserDispersingPlanner', 'UserConcentratedPodPlanner': DeploymentPlanner heuristic that will be used for VM deployment.",
null),
null,
ConfigKey.Kind.Select,
"FirstFitPlanner,UserDispersingPlanner,UserConcentratedPodPlanner"),
ElasticLoadBalancerEnabled(
"Advanced",
ManagementServer.class,
@ -1061,6 +1087,8 @@ public enum Config {
"xenserver.pvdriver.version",
"xenserver61",
"default Xen PV driver version for registered template, valid value:xenserver56,xenserver61 ",
"xenserver56,xenserver61",
ConfigKey.Kind.Select,
"xenserver56,xenserver61"),
XenServerHotFix("Advanced",
ManagementServer.class,
@ -1127,7 +1155,9 @@ public enum Config {
"vmware.root.disk.controller",
"ide",
"Specify the default disk controller for root volumes, valid values are scsi, ide, osdefault. Please check documentation for more details on each of these values.",
null),
null,
ConfigKey.Kind.Select,
"scsi,ide,osdefault"),
VmwareSystemVmNicDeviceType(
"Advanced",
ManagementServer.class,
@ -1135,7 +1165,9 @@ public enum Config {
"vmware.systemvm.nic.device.type",
"E1000",
"Specify the default network device type for system VMs, valid values are E1000, PCNet32, Vmxnet2, Vmxnet3",
null),
null,
ConfigKey.Kind.Select,
"E1000,PCNet32,Vmxnet2,Vmxnet3"),
VmwareRecycleHungWorker(
"Advanced",
ManagementServer.class,
@ -1217,7 +1249,7 @@ public enum Config {
TrafficSentinelIncludeZones(
"Usage",
ManagementServer.class,
Integer.class,
String.class,
"traffic.sentinel.include.zones",
"EXTERNAL",
"Traffic going into specified list of zones is metered. For metering all traffic leave this parameter empty",
@ -1225,7 +1257,7 @@ public enum Config {
TrafficSentinelExcludeZones(
"Usage",
ManagementServer.class,
Integer.class,
String.class,
"traffic.sentinel.exclude.zones",
"",
"Traffic going into specified list of zones is not metered.",
@ -1361,6 +1393,8 @@ public enum Config {
"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",
ConfigKey.Kind.Select,
"all,pod"),
ClusterMessageTimeOutSeconds(
@ -1783,8 +1817,10 @@ public enum Config {
private final String _name;
private final String _defaultValue;
private final String _description;
private final String[] _range;
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
private final ConfigKey.Kind _kind;
private final String _options;
private static final HashMap<String, List<Config>> s_scopeLevelConfigsMap = new HashMap<String, List<Config>>();
static {
@ -1833,7 +1869,11 @@ public enum Config {
}
}
private Config(String category, Class<?> componentClass, Class<?> type, String name, String defaultValue, String description, String... range) {
private Config(String category, Class<?> componentClass, Class<?> type, String name, String defaultValue, String description, String range) {
this(category, componentClass, type, name, defaultValue, description, range, null, null);
}
private Config(String category, Class<?> componentClass, Class<?> type, String name, String defaultValue, String description, String range, ConfigKey.Kind kind, String options) {
_category = category;
_componentClass = componentClass;
_type = type;
@ -1842,6 +1882,8 @@ public enum Config {
_description = description;
_range = range;
_scope = ConfigKey.Scope.Global.toString();
_kind = kind;
_options = options;
}
public String getCategory() {
@ -1868,6 +1910,17 @@ public enum Config {
return _scope;
}
public String getKind() {
if (_kind == null) {
return null;
}
return _kind.toString();
}
public String getOptions() {
return _options;
}
public String getComponent() {
if (_componentClass == ManagementServer.class) {
return "management-server";
@ -1896,7 +1949,7 @@ public enum Config {
}
}
public String[] getRange() {
public String getRange() {
return _range;
}

View File

@ -97,6 +97,10 @@ import org.apache.cloudstack.framework.config.ConfigDepot;
import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.framework.config.Configurable;
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.cloudstack.framework.config.impl.ConfigurationGroupVO;
import org.apache.cloudstack.framework.config.impl.ConfigurationSubGroupVO;
import org.apache.cloudstack.framework.config.impl.ConfigurationVO;
import org.apache.cloudstack.framework.messagebus.MessageBus;
import org.apache.cloudstack.framework.messagebus.MessageSubscriber;
@ -296,6 +300,10 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
@Inject
ConfigurationDao _configDao;
@Inject
ConfigurationGroupDao _configGroupDao;
@Inject
ConfigurationSubGroupDao _configSubGroupDao;
@Inject
ConfigDepot _configDepot;
@Inject
HostPodDao _podDao;
@ -1232,10 +1240,10 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
return null;
}
final String[] range = configuration.getRange();
if (range == null) {
if (configuration.getRange() == null) {
return null;
}
String[] range = configuration.getRange().split(",");
if (type.equals(String.class)) {
return validateIfStringValueIsInRange(name, value, range);
@ -7563,6 +7571,91 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
};
}
@Override
public String getConfigurationType(final String configName) {
final ConfigurationVO cfg = _configDao.findByName(configName);
if (cfg == null) {
s_logger.warn("Configuration " + configName + " not found");
return Configuration.ValueType.String.name();
}
if (weightBasedParametersForValidation.contains(configName)) {
return Configuration.ValueType.Range.name();
}
Class<?> type = null;
final Config c = Config.getConfig(configName);
if (c == null) {
s_logger.warn("Configuration " + configName + " no found. Perhaps moved to ConfigDepot");
final ConfigKey<?> configKey = _configDepot.get(configName);
if (configKey == null) {
s_logger.warn("Couldn't find configuration " + configName + " in ConfigDepot too.");
return Configuration.ValueType.String.name();
}
type = configKey.type();
} else {
type = c.getType();
}
return getInputType(type, cfg);
}
private String getInputType(Class<?> type, ConfigurationVO cfg) {
if (type == null) {
return Configuration.ValueType.String.name();
}
if (type == String.class || type == Character.class) {
if (cfg.getKind() == null) {
return Configuration.ValueType.String.name();
}
return cfg.getKind();
}
if (type == Integer.class || type == Long.class || type == Short.class) {
return Configuration.ValueType.Number.name();
}
if (type == Float.class || type == Double.class) {
return Configuration.ValueType.Decimal.name();
}
if (type == Boolean.class) {
return Configuration.ValueType.Boolean.name();
}
return Configuration.ValueType.String.name();
}
@Override
public Pair<String, String> getConfigurationGroupAndSubGroup(final String configName) {
if (StringUtils.isBlank(configName)) {
throw new CloudRuntimeException("Empty configuration name provided");
}
final ConfigurationVO cfg = _configDao.findByName(configName);
if (cfg == null) {
s_logger.warn("Configuration " + configName + " not found");
throw new InvalidParameterValueException("configuration with name " + configName + " doesn't exist");
}
String groupName = "Miscellaneous";
String subGroupName = "Others";
ConfigurationSubGroupVO configSubGroup = _configSubGroupDao.findById(cfg.getSubGroupId());
if (configSubGroup != null) {
subGroupName = configSubGroup.getName();
}
ConfigurationGroupVO configGroup = _configGroupDao.findById(cfg.getGroupId());
if (configGroup != null) {
groupName = configGroup.getName();
}
return new Pair<String, String>(groupName, subGroupName);
}
@Override
public List<ConfigurationSubGroupVO> getConfigurationSubGroups(final Long groupId) {
List<ConfigurationSubGroupVO> configSubGroups = _configSubGroupDao.findByGroup(groupId);
return configSubGroups;
}
static class ParamCountPair {
private Long id;
private int paramCount;

View File

@ -63,7 +63,7 @@ public interface VirtualNetworkApplianceManager extends Manager, VirtualNetworkA
static final ConfigKey<String> RouterTemplateOvm3 = new ConfigKey<String>(String.class, RouterTemplateOvm3CK, "Advanced", "SystemVM Template (Ovm3)",
"Name of the default router template on Ovm3.", true, ConfigKey.Scope.Zone, null);
static final ConfigKey<String> SetServiceMonitor = new ConfigKey<String>(String.class, SetServiceMonitorCK, "Advanced", "true",
static final ConfigKey<Boolean> SetServiceMonitor = new ConfigKey<Boolean>(Boolean.class, SetServiceMonitorCK, "Advanced", "true",
"service monitoring in router enable/disable option, default true", true, ConfigKey.Scope.Zone, null);
static final ConfigKey<Integer> RouterAlertsCheckInterval = new ConfigKey<Integer>(Integer.class, RouterAlertsCheckIntervalCK, "Advanced", "1800",
@ -84,36 +84,36 @@ public interface VirtualNetworkApplianceManager extends Manager, VirtualNetworkA
true, ConfigKey.Scope.Global, null);
static final ConfigKey<Integer> RouterHealthChecksBasicInterval = new ConfigKey<Integer>(Integer.class, "router.health.checks.basic.interval", "Advanced", "3",
"Interval in minutes at which basic router health checks are performed. If set to 0, no tests are scheduled.",
true, ConfigKey.Scope.Global, null);
true, ConfigKey.Scope.Global, null, RouterHealthChecksEnabled.key());
static final ConfigKey<Integer> RouterHealthChecksAdvancedInterval = new ConfigKey<Integer>(Integer.class, "router.health.checks.advanced.interval", "Advanced", "10",
"Interval in minutes at which advanced router health checks are performed. If set to 0, no tests are scheduled.",
true, ConfigKey.Scope.Global, null);
true, ConfigKey.Scope.Global, null, RouterHealthChecksEnabled.key());
static final ConfigKey<Integer> RouterHealthChecksConfigRefreshInterval = new ConfigKey<Integer>(Integer.class, RouterHealthChecksConfigRefreshIntervalCK, "Advanced", "10",
"Interval in minutes at which router health checks config - such as scheduling intervals, excluded checks, etc is updated on virtual routers by the management server. This value should" +
" be sufficiently high (like 2x) from the router.health.checks.basic.interval and router.health.checks.advanced.interval so that there is time between new results generation and results generation for passed data.",
false, ConfigKey.Scope.Global, null);
false, ConfigKey.Scope.Global, null, RouterHealthChecksEnabled.key());
static final ConfigKey<Integer> RouterHealthChecksResultFetchInterval = new ConfigKey<Integer>(Integer.class, RouterHealthChecksResultFetchIntervalCK, "Advanced", "10",
"Interval in minutes at which router health checks results are fetched by management server. On each result fetch, management server evaluates need to recreate VR as per configuration of " + RouterHealthChecksFailuresToRecreateVrCK +
"This value should be sufficiently high (like 2x) from the router.health.checks.basic.interval and router.health.checks.advanced.interval so that there is time between new results generation and fetch.",
false, ConfigKey.Scope.Global, null);
false, ConfigKey.Scope.Global, null, RouterHealthChecksEnabled.key());
static final ConfigKey<String> RouterHealthChecksFailuresToRecreateVr = new ConfigKey<String>(String.class, RouterHealthChecksFailuresToRecreateVrCK, "Advanced", "",
"Health checks failures defined by this config are the checks that should cause router recreation. If empty the recreate is not attempted for any health check failure. Possible values are comma separated script names " +
"from systemvms /root/health_scripts/ (namely - cpu_usage_check.py, dhcp_check.py, disk_space_check.py, dns_check.py, gateways_check.py, haproxy_check.py, iptables_check.py, memory_usage_check.py, router_version_check.py), connectivity.test, filesystem.writable.test " +
" or services (namely - loadbalancing.service, webserver.service, dhcp.service) ",
true, ConfigKey.Scope.Zone, null);
true, ConfigKey.Scope.Zone, null, null, RouterHealthChecksEnabled.key(), null, null, ConfigKey.Kind.CSV, null);
static final ConfigKey<String> RouterHealthChecksToExclude = new ConfigKey<String>(String.class, "router.health.checks.to.exclude", "Advanced", "",
"Health checks that should be excluded when executing scheduled checks on the router. This can be a comma separated list of script names placed in the '/root/health_checks/' folder. Currently the following scripts are " +
"placed in default systemvm template - cpu_usage_check.py, disk_space_check.py, gateways_check.py, iptables_check.py, router_version_check.py, dhcp_check.py, dns_check.py, haproxy_check.py, memory_usage_check.py.",
true, ConfigKey.Scope.Zone, null);
true, ConfigKey.Scope.Zone, null, null, RouterHealthChecksEnabled.key(), null, null, ConfigKey.Kind.CSV, null);
static final ConfigKey<Double> RouterHealthChecksFreeDiskSpaceThreshold = new ConfigKey<Double>(Double.class, "router.health.checks.free.disk.space.threshold",
"Advanced", "100", "Free disk space threshold (in MB) on VR below which the check is considered a failure.",
true, ConfigKey.Scope.Zone, null);
true, ConfigKey.Scope.Zone, null, RouterHealthChecksEnabled.key());
static final ConfigKey<Double> RouterHealthChecksMaxCpuUsageThreshold = new ConfigKey<Double>(Double.class, "router.health.checks.max.cpu.usage.threshold",
"Advanced", "100", " Max CPU Usage threshold as % above which check is considered a failure.",
true, ConfigKey.Scope.Zone, null);
true, ConfigKey.Scope.Zone, null, RouterHealthChecksEnabled.key());
static final ConfigKey<Double> RouterHealthChecksMaxMemoryUsageThreshold = new ConfigKey<Double>(Double.class, "router.health.checks.max.memory.usage.threshold",
"Advanced", "100", "Max Memory Usage threshold as % above which check is considered a failure.",
true, ConfigKey.Scope.Zone, null);
true, ConfigKey.Scope.Zone, null, RouterHealthChecksEnabled.key());
ConfigKey<String> RouterLogrotateFrequency = new ConfigKey<>(String.class, "router.logrotate.frequency", "Advanced", "*:00:00",
"Sets the frequency of the logrotate service on the virtual router. The default value is *:00:00 (hourly) and follows the last block of " +
"OnCalendar standard [Hour:Minute:Second]. e.g, *:*:00 is for every minute and */12:00:00 is for every 12 hours. See Systemd Timers for more options. " +

View File

@ -1862,10 +1862,10 @@ Configurable, StateListener<VirtualMachine.State, VirtualMachine.Event, VirtualM
s_logger.debug("Found " + routers.size() + " running routers. ");
for (final DomainRouterVO router : routers) {
final String serviceMonitoringFlag = SetServiceMonitor.valueIn(router.getDataCenterId());
final Boolean serviceMonitoringFlag = SetServiceMonitor.valueIn(router.getDataCenterId());
// Skip the routers in VPC network or skip the routers where
// Monitor service is not enabled in the corresponding Zone
if (!Boolean.parseBoolean(serviceMonitoringFlag)) {
if (serviceMonitoringFlag == null || !serviceMonitoringFlag) {
continue;
}
String controlIP = _routerControlHelper.getRouterControlIp(router.getId());

View File

@ -98,6 +98,7 @@ 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.Pair;
import com.cloud.utils.PasswordGenerator;
import com.cloud.utils.PropertiesUtil;
import com.cloud.utils.component.ComponentLifecycle;
@ -202,6 +203,16 @@ public class ConfigurationServerImpl extends ManagerBase implements Configuratio
String description = c.getDescription();
ConfigurationVO configVO = new ConfigurationVO(category, instance, component, name, value, description);
configVO.setDefaultValue(value);
Pair<Long, Long> configGroupAndSubGroup = _configDepotAdmin.getConfigurationGroupAndSubGroupByName(name);
configVO.setGroupId(configGroupAndSubGroup.first());
configVO.setSubGroupId(configGroupAndSubGroup.second());
if (c.getKind() != null) {
configVO.setKind(c.getKind());
}
if (c.getOptions() != null) {
configVO.setOptions(c.getOptions());
}
_configDao.persist(configVO);
}
}

View File

@ -76,6 +76,7 @@ import org.apache.cloudstack.api.command.admin.cluster.AddClusterCmd;
import org.apache.cloudstack.api.command.admin.cluster.DeleteClusterCmd;
import org.apache.cloudstack.api.command.admin.cluster.ListClustersCmd;
import org.apache.cloudstack.api.command.admin.cluster.UpdateClusterCmd;
import org.apache.cloudstack.api.command.admin.config.ListCfgGroupsByCmd;
import org.apache.cloudstack.api.command.admin.config.ListCfgsByCmd;
import org.apache.cloudstack.api.command.admin.config.ListDeploymentPlannersCmd;
import org.apache.cloudstack.api.command.admin.config.ListHypervisorCapabilitiesCmd;
@ -582,6 +583,7 @@ import org.apache.cloudstack.api.command.user.vpn.UpdateVpnGatewayCmd;
import org.apache.cloudstack.api.command.user.zone.ListZonesCmd;
import org.apache.cloudstack.config.ApiServiceConfiguration;
import org.apache.cloudstack.config.Configuration;
import org.apache.cloudstack.config.ConfigurationGroup;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService;
import org.apache.cloudstack.engine.subsystem.api.storage.StoragePoolAllocator;
@ -589,6 +591,10 @@ import org.apache.cloudstack.framework.config.ConfigDepot;
import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.framework.config.Configurable;
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.cloudstack.framework.config.impl.ConfigurationGroupVO;
import org.apache.cloudstack.framework.config.impl.ConfigurationSubGroupVO;
import org.apache.cloudstack.framework.config.impl.ConfigurationVO;
import org.apache.cloudstack.framework.security.keystore.KeystoreManager;
import org.apache.cloudstack.managed.context.ManagedContextRunnable;
@ -858,6 +864,10 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
@Inject
private ConfigurationDao _configDao;
@Inject
private ConfigurationGroupDao _configGroupDao;
@Inject
private ConfigurationSubGroupDao _configSubGroupDao;
@Inject
private ConsoleProxyManager _consoleProxyMgr;
@Inject
private SecondaryStorageVmManager _secStorageVmMgr;
@ -2117,6 +2127,9 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
final Long imageStoreId = cmd.getImageStoreId();
Long accountId = cmd.getAccountId();
Long domainId = cmd.getDomainId();
final String groupName = cmd.getGroupName();
final String subGroupName = cmd.getSubGroupName();
final String parentName = cmd.getParentName();
String scope = null;
Long id = null;
int paramCountCheck = 0;
@ -2186,6 +2199,29 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
sc.addAnd("name", SearchCriteria.Op.LIKE, "%" + name + "%");
}
if (groupName != null) {
ConfigurationGroupVO configGroupVO = _configGroupDao.findByName(groupName);
if (configGroupVO == null) {
throw new InvalidParameterValueException("Invalid configuration group: " + groupName);
}
Long groupId = configGroupVO.getId();
sc.addAnd("groupId", SearchCriteria.Op.EQ, groupId);
}
if (subGroupName != null) {
ConfigurationSubGroupVO configSubGroupVO = _configSubGroupDao.findByName(subGroupName);
if (configSubGroupVO == null) {
throw new InvalidParameterValueException("Invalid configuration subgroup: " + subGroupName);
}
Long subGroupId = configSubGroupVO.getId();
sc.addAnd("subGroupId", SearchCriteria.Op.EQ, subGroupId);
}
if (parentName != null) {
sc.addAnd("parent", SearchCriteria.Op.EQ, parentName);
}
if (category != null) {
sc.addAnd("category", SearchCriteria.Op.EQ, category);
}
@ -2235,6 +2271,20 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
return new Pair<List<? extends Configuration>, Integer>(result.first(), result.second());
}
@Override
public Pair<List<? extends ConfigurationGroup>, Integer> listConfigurationGroups(ListCfgGroupsByCmd cmd) {
final Filter searchFilter = new Filter(ConfigurationGroupVO.class, "precedence", true, null, null);
final SearchCriteria<ConfigurationGroupVO> sc = _configGroupDao.createSearchCriteria();
final String groupName = cmd.getGroupName();
if (StringUtils.isNotBlank(groupName)) {
sc.addAnd("name", SearchCriteria.Op.EQ, groupName);
}
final Pair<List<ConfigurationGroupVO>, Integer> result = _configGroupDao.searchAndCount(sc, searchFilter);
return new Pair<List<? extends ConfigurationGroup>, Integer>(result.first(), result.second());
}
@Override
public Pair<List<? extends IpAddress>, Integer> searchForIPAddresses(final ListPublicIpAddressesCmd cmd) {
final Long associatedNetworkId = cmd.getAssociatedNetworkId();
@ -3221,6 +3271,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
cmdList.add(ListClustersCmd.class);
cmdList.add(UpdateClusterCmd.class);
cmdList.add(ListCfgsByCmd.class);
cmdList.add(ListCfgGroupsByCmd.class);
cmdList.add(ListHypervisorCapabilitiesCmd.class);
cmdList.add(UpdateCfgCmd.class);
cmdList.add(UpdateHypervisorCapabilitiesCmd.class);

View File

@ -624,14 +624,14 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
private static final ConfigKey<Boolean> EnableAdditionalVmConfig = new ConfigKey<>("Advanced", Boolean.class,
"enable.additional.vm.configuration", "false", "allow additional arbitrary configuration to vm", true, ConfigKey.Scope.Account);
private static final ConfigKey<String> KvmAdditionalConfigAllowList = new ConfigKey<>("Advanced", String.class,
"allow.additional.vm.configuration.list.kvm", "", "Comma separated list of allowed additional configuration options.", true);
private static final ConfigKey<String> KvmAdditionalConfigAllowList = new ConfigKey<>(String.class,
"allow.additional.vm.configuration.list.kvm", "Advanced", "", "Comma separated list of allowed additional configuration options.", true, ConfigKey.Scope.Global, null, null, EnableAdditionalVmConfig.key(), null, null, ConfigKey.Kind.CSV, null);
private static final ConfigKey<String> XenServerAdditionalConfigAllowList = new ConfigKey<>("Advanced", String.class,
"allow.additional.vm.configuration.list.xenserver", "", "Comma separated list of allowed additional configuration options", true);
private static final ConfigKey<String> XenServerAdditionalConfigAllowList = new ConfigKey<>(String.class,
"allow.additional.vm.configuration.list.xenserver", "Advanced", "", "Comma separated list of allowed additional configuration options", true, ConfigKey.Scope.Global, null, null, EnableAdditionalVmConfig.key(), null, null, ConfigKey.Kind.CSV, null);
private static final ConfigKey<String> VmwareAdditionalConfigAllowList = new ConfigKey<>("Advanced", String.class,
"allow.additional.vm.configuration.list.vmware", "", "Comma separated list of allowed additional configuration options.", true);
private static final ConfigKey<String> VmwareAdditionalConfigAllowList = new ConfigKey<>(String.class,
"allow.additional.vm.configuration.list.vmware", "Advanced", "", "Comma separated list of allowed additional configuration options.", true, ConfigKey.Scope.Global, null, null, EnableAdditionalVmConfig.key(), null, null, ConfigKey.Kind.CSV, null);
private static final ConfigKey<Boolean> VmDestroyForcestop = new ConfigKey<Boolean>("Advanced", Boolean.class, "vm.destroy.forcestop", "false",
"On destroy, force-stop takes this value ", true);

View File

@ -50,10 +50,10 @@ import org.apache.commons.lang3.StringUtils;
public class IndirectAgentLBServiceImpl extends ComponentLifecycleBase implements IndirectAgentLB, Configurable {
public static final Logger LOG = Logger.getLogger(IndirectAgentLBServiceImpl.class);
public static final ConfigKey<String> IndirectAgentLBAlgorithm = new ConfigKey<>("Advanced", String.class,
"indirect.agent.lb.algorithm", "static",
public static final ConfigKey<String> IndirectAgentLBAlgorithm = new ConfigKey<>(String.class,
"indirect.agent.lb.algorithm", "Advanced", "static",
"The algorithm to be applied on the provided 'host' management server list that is sent to indirect agents. Allowed values are: static, roundrobin and shuffle.",
true, ConfigKey.Scope.Global);
true, ConfigKey.Scope.Global, null, null, null, null, null, ConfigKey.Kind.Select, "static,roundrobin,shuffle");
public static final ConfigKey<Long> IndirectAgentLBCheckInterval = new ConfigKey<>("Advanced", Long.class,
"indirect.agent.lb.check.interval", "0",

View File

@ -106,7 +106,7 @@ public class DiagnosticsServiceImpl extends ManagerBase implements PluggableServ
"Enable the garbage collector background task to delete old files from secondary storage.", false);
private static final ConfigKey<Integer> GarbageCollectionInterval = new ConfigKey<>("Advanced", Integer.class,
"diagnostics.data.gc.interval", "86400",
"The interval at which the garbage collector background tasks in seconds", false);
"The interval at which the garbage collector background tasks in seconds", false, EnableGarbageCollector.key());
// These are easily computed properties and need not need a restart of the management server
private static final ConfigKey<Long> DataRetrievalTimeout = new ConfigKey<>("Advanced", Long.class,
@ -114,7 +114,7 @@ public class DiagnosticsServiceImpl extends ManagerBase implements PluggableServ
"Overall system VM script execution time out in seconds.", true);
private static final ConfigKey<Long> MaximumFileAgeforGarbageCollection = new ConfigKey<>("Advanced", Long.class,
"diagnostics.data.max.file.age", "86400",
"Sets the maximum time in seconds a file can stay in secondary storage before it is deleted.", true);
"Sets the maximum time in seconds a file can stay in secondary storage before it is deleted.", true, EnableGarbageCollector.key());
private static final ConfigKey<Double> DiskQuotaPercentageThreshold = new ConfigKey<>("Advanced", Double.class,
"diagnostics.data.disable.threshold", "0.9",
"Sets the secondary storage disk utilisation percentage for file retrieval. " +

View File

@ -30,18 +30,18 @@ public interface DiagnosticsFilesList {
* in the system vm and grab output for retrieval, e.g. the output from iptables-save is written to a file
* which will then be retrieved.
*/
ConfigKey<String> SystemVMDefaultSupportedFiles = new ConfigKey<>("Advanced", String.class,
"diagnostics.data.systemvm.defaults", "iptables,ipaddr,iprule,iproute,/etc/cloudstack-release," +
ConfigKey<String> SystemVMDefaultSupportedFiles = new ConfigKey<>(String.class,
"diagnostics.data.systemvm.defaults", "Advanced", "iptables,ipaddr,iprule,iproute,/etc/cloudstack-release," +
"/usr/local/cloud/systemvm/conf/agent.properties,/usr/local/cloud/systemvm/conf/consoleproxy.properties," +
"/var/log/cloud.log,/var/log/patchsystemvm.log,/var/log/daemon.log",
"List of supported diagnostics data file options for the CPVM and SSVM.", true);
"List of supported diagnostics data file options for the CPVM and SSVM.", true, ConfigKey.Scope.Global, null, null, null, null, null, ConfigKey.Kind.CSV, null);
ConfigKey<String> RouterDefaultSupportedFiles = new ConfigKey<>("Advanced", String.class,
"diagnostics.data.router.defaults", "iptables,ipaddr,iprule,iproute,/etc/cloudstack-release," +
ConfigKey<String> RouterDefaultSupportedFiles = new ConfigKey<>(String.class,
"diagnostics.data.router.defaults", "Advanced", "iptables,ipaddr,iprule,iproute,/etc/cloudstack-release," +
"/etc/dnsmasq.conf,/etc/dhcphosts.txt,/etc/dhcpopts.txt,/etc/dnsmasq.d/cloud.conf,/etc/dnsmasq-resolv.conf,/var/lib/misc/dnsmasq.leases,/var/log/dnsmasq.log," +
"/etc/hosts,/etc/resolv.conf,/etc/haproxy/haproxy.cfg,/var/log/haproxy.log,/etc/ipsec.d/l2tp.conf,/var/log/cloud.log," +
"/var/log/routerServiceMonitor.log,/var/log/daemon.log",
"List of supported diagnostics data file options for the domain router.", true);
"List of supported diagnostics data file options for the domain router.", true, ConfigKey.Scope.Global, null, null, null, null, null, ConfigKey.Kind.CSV, null);
List<String> generateFileList();
}

View File

@ -81,6 +81,8 @@
<dao name="VM Template Pool" class="com.cloud.storage.dao.VMTemplatePoolDaoImpl" />
<dao name="Launch Permission" class="com.cloud.storage.dao.LaunchPermissionDaoImpl" />
<dao name="Configuration" class="com.cloud.configuration.dao.ConfigurationDaoImpl" />
<dao name="Configuration Group" class="com.cloud.configuration.dao.ConfigurationGroupDaoImpl" />
<dao name="Configuration SubGroup" class="com.cloud.configuration.dao.ConfigurationSubGroupDaoImpl" />
<dao name="HA" class="com.cloud.ha.dao.HighAvailabilityDaoImpl" />
<dao name="Console Proxy" class="com.cloud.vm.dao.ConsoleProxyDaoImpl" />
<dao name="Upgrade" class="com.cloud.maint.dao.AgentUpgradeDaoImpl" />

View File

@ -24,7 +24,7 @@ public class ConfigTest {
for (Config configuration : Config.values()) {
if (configuration.getType().equals(Integer.class) && configuration.getRange() != null) {
try {
final String[] options = configuration.getRange()[0].split("-");
final String[] options = configuration.getRange().split("-");
final int min = Integer.parseInt(options[0]);
final int max = Integer.parseInt(options[1]);
if (options.length != 2) {

View File

@ -31,6 +31,8 @@ import org.springframework.core.type.filter.TypeFilter;
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
import org.apache.cloudstack.framework.config.dao.ConfigurationDaoImpl;
import org.apache.cloudstack.framework.config.dao.ConfigurationGroupDaoImpl;
import org.apache.cloudstack.framework.config.dao.ConfigurationSubGroupDaoImpl;
import org.apache.cloudstack.test.utils.SpringUtils;
import com.cloud.agent.AgentManager;
@ -70,8 +72,8 @@ import com.cloud.vm.dao.UserVmDetailsDaoImpl;
import com.cloud.vm.dao.VMInstanceDaoImpl;
@Configuration
@ComponentScan(basePackageClasses = {SecurityGroupRulesDaoImpl.class, UserVmDaoImpl.class, AccountDaoImpl.class, ConfigurationDaoImpl.class,
SecurityGroupWorkDaoImpl.class, VmRulesetLogDaoImpl.class, VMInstanceDaoImpl.class, DomainDaoImpl.class, UsageEventDaoImpl.class,
@ComponentScan(basePackageClasses = {SecurityGroupRulesDaoImpl.class, UserVmDaoImpl.class, AccountDaoImpl.class, ConfigurationDaoImpl.class, ConfigurationGroupDaoImpl.class,
ConfigurationSubGroupDaoImpl.class, SecurityGroupWorkDaoImpl.class, VmRulesetLogDaoImpl.class, VMInstanceDaoImpl.class, DomainDaoImpl.class, UsageEventDaoImpl.class,
ResourceTagsDaoImpl.class, HostDaoImpl.class, HostDetailsDaoImpl.class, HostTagsDaoImpl.class, ClusterDaoImpl.class, HostPodDaoImpl.class,
DataCenterDaoImpl.class, DataCenterIpAddressDaoImpl.class, HostTransferMapDaoImpl.class, SecurityGroupManagerImpl2.class, SecurityGroupDaoImpl.class,
SecurityGroupVMMapDaoImpl.class, UserVmDetailsDaoImpl.class, DataCenterIpAddressDaoImpl.class, DataCenterLinkLocalIpAddressDaoImpl.class,

View File

@ -37,6 +37,8 @@ import org.apache.cloudstack.framework.config.ConfigDepot;
import org.apache.cloudstack.framework.config.ConfigKey;
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.cloudstack.framework.config.impl.ConfigDepotImpl;
import org.apache.cloudstack.framework.config.impl.ConfigurationVO;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
@ -116,6 +118,10 @@ public class FirstFitPlannerTest {
@Inject
ConfigurationDao configDao;
@Inject
ConfigurationGroupDao configGroupDao;
@Inject
ConfigurationSubGroupDao configSubGroupDao;
@Inject
CapacityDao capacityDao;
@Inject
AccountManager accountMgr;
@ -431,6 +437,16 @@ public class FirstFitPlannerTest {
return Mockito.mock(ConfigurationDao.class);
}
@Bean
public ConfigurationGroupDao configurationGroupDao() {
return Mockito.mock(ConfigurationGroupDao.class);
}
@Bean
public ConfigurationSubGroupDao configurationSubGroupDao() {
return Mockito.mock(ConfigurationSubGroupDao.class);
}
@Bean
public PrimaryDataStoreDao primaryDataStoreDao() {
return Mockito.mock(PrimaryDataStoreDao.class);

View File

@ -56,6 +56,7 @@ import org.apache.cloudstack.api.command.admin.zone.DeleteZoneCmd;
import org.apache.cloudstack.api.command.admin.zone.UpdateZoneCmd;
import org.apache.cloudstack.api.command.user.network.ListNetworkOfferingsCmd;
import org.apache.cloudstack.config.Configuration;
import org.apache.cloudstack.framework.config.impl.ConfigurationSubGroupVO;
import org.apache.cloudstack.region.PortableIp;
import org.apache.cloudstack.region.PortableIpRange;
import org.springframework.stereotype.Component;
@ -661,4 +662,21 @@ public class MockConfigurationManagerImpl extends ManagerBase implements Configu
return null;
}
@Override
public String getConfigurationType(String configName) {
// TODO Auto-generated method stub
return null;
}
@Override
public Pair<String, String> getConfigurationGroupAndSubGroup(String configName) {
// TODO Auto-generated method stub
return null;
}
@Override
public List<ConfigurationSubGroupVO> getConfigurationSubGroups(Long groupId) {
// TODO Auto-generated method stub
return null;
}
}

View File

@ -30,6 +30,8 @@ import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
import org.apache.cloudstack.framework.config.dao.ConfigurationDaoImpl;
import org.apache.cloudstack.framework.config.dao.ConfigurationGroupDaoImpl;
import org.apache.cloudstack.framework.config.dao.ConfigurationSubGroupDaoImpl;
import org.apache.cloudstack.test.utils.SpringUtils;
import com.cloud.alert.AlertManager;
@ -107,9 +109,9 @@ import com.cloud.vpc.dao.MockVpcOfferingDaoImpl;
import com.cloud.vpc.dao.MockVpcOfferingServiceMapDaoImpl;
@Configuration
@ComponentScan(basePackageClasses = {VpcManagerImpl.class, NetworkElement.class, VpcOfferingDao.class, ConfigurationDaoImpl.class, IPAddressDaoImpl.class,
DomainRouterDaoImpl.class, VpcGatewayDaoImpl.class, PrivateIpDaoImpl.class, StaticRouteDaoImpl.class, PhysicalNetworkDaoImpl.class,
ResourceTagsDaoImpl.class, FirewallRulesDaoImpl.class, VlanDaoImpl.class, AccountDaoImpl.class, ResourceCountDaoImpl.class,
@ComponentScan(basePackageClasses = {VpcManagerImpl.class, NetworkElement.class, VpcOfferingDao.class, ConfigurationDaoImpl.class, ConfigurationGroupDaoImpl.class,
ConfigurationSubGroupDaoImpl.class, IPAddressDaoImpl.class, DomainRouterDaoImpl.class, VpcGatewayDaoImpl.class, PrivateIpDaoImpl.class, StaticRouteDaoImpl.class,
PhysicalNetworkDaoImpl.class, ResourceTagsDaoImpl.class, FirewallRulesDaoImpl.class, VlanDaoImpl.class, AccountDaoImpl.class, ResourceCountDaoImpl.class,
Site2SiteVpnGatewayDaoImpl.class, PodVlanMapDaoImpl.class, AccountVlanMapDaoImpl.class, DomainVlanMapDaoImpl.class, HostDaoImpl.class, HostDetailsDaoImpl.class,
HostTagsDaoImpl.class, HostTransferMapDaoImpl.class, ClusterDaoImpl.class, HostPodDaoImpl.class, RouterNetworkDaoImpl.class,
UserStatisticsDaoImpl.class, PhysicalNetworkTrafficTypeDaoImpl.class, FirewallRulesCidrsDaoImpl.class, ResourceLimitManagerImpl.class,

View File

@ -19,7 +19,6 @@ package com.cloud.vpc.dao;
import java.util.HashMap;
import java.util.Map;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.framework.config.impl.ConfigurationVO;

View File

@ -70,4 +70,6 @@
<bean id="VsphereStoragePolicyDaoImpl" class="com.cloud.dc.dao.VsphereStoragePolicyDaoImpl" />
<bean id="annotationDaoImpl" class="org.apache.cloudstack.annotation.dao.AnnotationDaoImpl" />
<bean id="PassphraseDaoImpl" class="org.apache.cloudstack.secret.dao.PassphraseDaoImpl" />
<bean id="configurationGroupDaoImpl" class="org.apache.cloudstack.framework.config.dao.ConfigurationGroupDaoImpl" />
<bean id="configurationSubGroupDaoImpl" class="org.apache.cloudstack.framework.config.dao.ConfigurationSubGroupDaoImpl" />
</beans>

View File

@ -267,7 +267,7 @@ public class SecondaryStorageManagerImpl extends ManagerBase implements Secondar
private final GlobalLock _allocLock = GlobalLock.getInternLock(getAllocLockName());
static final ConfigKey<String> NTPServerConfig = new ConfigKey<String>(String.class, "ntp.server.list", "Advanced", null,
"Comma separated list of NTP servers to configure in Secondary storage VM", true, ConfigKey.Scope.Global, null);
"Comma separated list of NTP servers to configure in Secondary storage VM", true, ConfigKey.Scope.Global, null, null, null, null, null, ConfigKey.Kind.CSV, null);
static final ConfigKey<Integer> MaxNumberOfSsvmsForMigration = new ConfigKey<Integer>("Advanced", Integer.class, "max.ssvm.count", "5",
"Number of additional SSVMs to handle migration of data objects concurrently", true, ConfigKey.Scope.Global);

View File

@ -75,3 +75,109 @@ class TestUpdateConfigWithScope(cloudstackTestCase):
updateConfigurationCmd.scopename = "zone"
updateConfigurationCmd.scopeid = 1
self.apiClient.updateConfiguration(updateConfigurationCmd)
class TestListConfigurations(cloudstackTestCase):
"""
Test to list configurations (global settings)
"""
@classmethod
def setUpClass(cls):
cls.apiclient = cls.testClient.getApiClient()
cls._cleanup = []
@classmethod
def tearDownClass(cls):
super(TestListConfigurations, cls).tearDownClass()
def setUp(self):
self.apiClient = self.testClient.getApiClient()
self.cleanup = []
def tearDown(self):
super(TestListConfigurations, self).tearDown()
@attr(tags=["devcloud", "basic", "advanced"], required_hardware="false")
def test_01_list_configs(self):
"""
test list configuration setting at global level
@return:
"""
listConfigurationsCmd = listConfigurations.listConfigurationsCmd()
listConfigurationsCmd.name = "agent.lb.enabled"
listConfigurationsResponse = self.apiClient.listConfigurations(listConfigurationsCmd)
self.assertNotEqual(len(listConfigurationsResponse), 0, "Check if the list configurations API returns a non-empty response")
self.debug("The parameter %s listed with value %s" %(listConfigurationsCmd.name, listConfigurationsResponse[0].value))
self.assertEqual(listConfigurationsResponse[0].type, 'Boolean', "Wrong type for the config")
self.assertEqual(listConfigurationsResponse[0].defaultvalue, 'false', "Wrong default value for the config")
self.assertEqual(listConfigurationsResponse[0].group, 'Management Server', "Check the group for the config")
self.assertEqual(listConfigurationsResponse[0].subgroup, 'Agent', "Check the subgroup for the config")
listConfigurationsCmd.name = "storage.cleanup.interval"
listConfigurationsResponse = self.apiClient.listConfigurations(listConfigurationsCmd)
self.assertNotEqual(len(listConfigurationsResponse), 0, "Check if the list configurations API returns a non-empty response")
self.debug("The parameter %s listed with value %s" % (listConfigurationsCmd.name, listConfigurationsResponse[0].value))
self.assertEqual(listConfigurationsResponse[0].type, 'Number', "Wrong type for the config")
self.assertEqual(listConfigurationsResponse[0].defaultvalue, '86400', "Wrong default value for the config")
self.assertEqual(listConfigurationsResponse[0].group, 'Infrastructure', "Check the group for the config")
self.assertEqual(listConfigurationsResponse[0].subgroup, 'Primary Storage', "Check the subgroup for the config")
listConfigurationsCmd.name = "agent.load.threshold"
listConfigurationsResponse = self.apiClient.listConfigurations(listConfigurationsCmd)
self.assertNotEqual(len(listConfigurationsResponse), 0, "Check if the list configurations API returns a non-empty response")
self.debug("The parameter %s listed with value %s" % (listConfigurationsCmd.name, listConfigurationsResponse[0].value))
self.assertEqual(listConfigurationsResponse[0].type, 'Range', "Wrong type for the config")
self.assertEqual(listConfigurationsResponse[0].defaultvalue, '0.7', "Wrong default value for the config")
self.assertEqual(listConfigurationsResponse[0].group, 'Management Server', "Check the group for the config")
self.assertEqual(listConfigurationsResponse[0].subgroup, 'Agent', "Check the subgroup for the config")
listConfigurationsCmd.name = "endpoint.url"
listConfigurationsResponse = self.apiClient.listConfigurations(listConfigurationsCmd)
self.assertNotEqual(len(listConfigurationsResponse), 0, "Check if the list configurations API returns a non-empty response")
self.debug("The parameter %s listed with value %s" % (listConfigurationsCmd.name, listConfigurationsResponse[0].value))
self.assertEqual(listConfigurationsResponse[0].type, 'String', "Wrong type for the config")
self.assertEqual(listConfigurationsResponse[0].defaultvalue, 'http://localhost:8080/client/api', "Wrong default value for the config")
@attr(tags=["devcloud", "basic", "advanced"], required_hardware="false")
def test_02_list_config_parent(self):
"""
test list configuration setting parent
@return:
"""
listConfigurationsCmd = listConfigurations.listConfigurationsCmd()
listConfigurationsCmd.name = "api.throttling.cachesize"
listConfigurationsResponse = self.apiClient.listConfigurations(listConfigurationsCmd)
self.assertNotEqual(len(listConfigurationsResponse), 0, "Check if the list configurations API returns a non-empty response")
self.assertEqual(listConfigurationsResponse[0].parent, 'api.throttling.enabled', "Wrong parent for the config")
listConfigurationsCmd.name = "storage.cache.replacement.interval"
listConfigurationsResponse = self.apiClient.listConfigurations(listConfigurationsCmd)
self.assertNotEqual(len(listConfigurationsResponse), 0, "Check if the list configurations API returns a non-empty response")
self.assertEqual(listConfigurationsResponse[0].parent, 'storage.cache.replacement.enabled', "Wrong parent for the config")
listConfigurationsCmd.name = "cloud.kubernetes.cluster.max.size"
listConfigurationsResponse = self.apiClient.listConfigurations(listConfigurationsCmd)
self.assertNotEqual(len(listConfigurationsResponse), 0, "Check if the list configurations API returns a non-empty response")
self.assertEqual(listConfigurationsResponse[0].parent, 'cloud.kubernetes.service.enabled', "Wrong parent for the config")
@attr(tags=["devcloud", "basic", "advanced"], required_hardware="false")
def test_03_config_groups(self):
"""
test list configuration groups
@return:
"""
listConfigurationGroupsResponse = Configurations.listGroups(self.apiclient)
self.assertNotEqual(len(listConfigurationGroupsResponse), 0, "Check if the list configurationgroups API returns a non-empty response")
self.debug("Total %d configuration groups listed" %(len(listConfigurationGroupsResponse)))
self.debug("Configuration groups: %s" % (str(listConfigurationGroupsResponse)))
group = listConfigurationGroupsResponse[0].name
subgroup = listConfigurationGroupsResponse[0].subgroup[0].name
listConfigurationsResponse = Configurations.list(self.apiclient,
group=group,
subgroup=subgroup)
self.assertNotEqual(len(listConfigurationsResponse), 0, "Check if the list configurations API returns a non-empty response")
self.debug("Total %d configurations for group %s, subgroup %s" % (len(listConfigurationsResponse), group, subgroup))

View File

@ -4653,6 +4653,14 @@ class Configurations:
return (apiclient.listCapabilities(cmd))
@classmethod
def listGroups(cls, apiclient, **kwargs):
"""Lists configuration groups"""
cmd = listConfigurationGroups.listConfigurationGroupsCmd()
[setattr(cmd, k, v) for k, v in list(kwargs.items())]
return (apiclient.listConfigurationGroups(cmd))
@classmethod
def reset(cls, apiclient, name, zoneid=None, clusterid=None, storageid=None, domainid=None, accountid=None):
"""Resets the specified configuration to original value"""

View File

@ -778,7 +778,7 @@
"label.fwdeviceid": "ID",
"label.fwdevicestate": "Status",
"label.gateway": "Gateway",
"label.global.settings": "Global settings",
"label.global.settings": "Global Settings",
"label.globo.dns": "GloboDNS",
"label.globo.dns.configuration": "GloboDNS configuration",
"label.glustervolume": "Volume",
@ -2598,6 +2598,7 @@
"message.scaleup.policy.name.continue": "Please input a name to ScaleUp policy to continue",
"message.select.a.zone": "A zone typically corresponds to a single datacenter. Multiple zones help make the cloud more reliable by providing physical isolation and redundancy.",
"message.select.affinity.groups": "Please select any affinity groups you want this VM to belong to:",
"message.select.deselect.to.sort": "Please select / deselect to sort the values",
"message.select.destination.image.stores": "Please select Image Store(s) to which data is to be migrated to",
"message.select.disk.offering": "Please select a disk offering for disk",
"message.select.end.date.and.time": "Select an end date & time.",
@ -2720,6 +2721,7 @@
"message.template.type.change.warning": "WARNING: Changing the template type to SYSTEM will disable further changes to the template.",
"message.tooltip.reserved.system.netmask": "The network prefix that defines the pod subnet. Uses CIDR notation.",
"message.traffic.type.to.basic.zone": "traffic type to basic zone",
"message.type.values.to.add": "Please add additonal values by typing them in",
"message.update.autoscale.policy.failed": "Failed to update autoscale policy",
"message.update.autoscale.vmgroup.failed": "Failed to update autoscale vm group",
"message.update.autoscale.vm.profile.failed": "Failed to update autoscale vm profile",

View File

@ -105,6 +105,7 @@
<router-link v-else :to="{ path: $route.path + '/' + record.name }" >{{ text }}</router-link>
</span>
<span v-else-if="$route.path.startsWith('/globalsetting')">{{ text }}</span>
<span v-else-if="$route.path.startsWith('/preferences')">{{ text }}</span>
<span v-else-if="$route.path.startsWith('/alert')">
<router-link :to="{ path: $route.path + '/' + record.id }" v-if="record.id">{{ $t(text.toLowerCase()) }}</router-link>
<router-link :to="{ path: $route.path + '/' + record.name }" v-else>{{ $t(text.toLowerCase()) }}</router-link>

View File

@ -26,7 +26,9 @@ export default {
title: 'label.global.settings',
icon: 'setting-outlined',
permission: ['listConfigurations'],
columns: ['name', 'description', 'category', 'value', 'actions']
listView: true,
popup: true,
component: () => import('@/views/setting/ConfigurationTab.vue')
},
{
name: 'ldapsetting',

View File

@ -400,6 +400,10 @@ a {
background-color: #f9f9f9;
}
.child-row {
background-color: #f5f5f5;
}
.tag-disabled-input, .btn-add-tag {
background-color: #fff;
}

View File

@ -0,0 +1,90 @@
// 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.
<template>
<a-table
size="small"
:showHeader="false"
:columns="columns"
:dataSource="config"
:rowKey="record => record.name"
:pagination="false"
:rowClassName="getRowClassName"
style="overflow-y: auto; margin-left: 10px" >
<template #name="{ record }">
<span :style="hierarchyExists ? 'padding-left: 0px;' : 'padding-left: 25px;'">
<b><span v-if="record.parent"> &nbsp;</span>{{record.displaytext }} </b> {{ ' (' + record.name + ')' }}
</span>
<br/>
<span :style="record.parent ? 'padding-left: 50px; display:block' : 'padding-left: 25px; display:block'">{{ record.description }}</span>
</template>
<template #value="{ record }">
<ConfigurationValue :configrecord="record" />
</template>
</a-table>
</template>
<script>
import ConfigurationValue from './ConfigurationValue'
export default {
name: 'ConfigurationHierarchy',
components: {
ConfigurationValue
},
props: {
config: {
type: Array,
default: () => { return [] }
},
columns: {
type: Array,
default: () => { return [] }
}
},
computed: {
hierarchyExists () {
for (var c of this.config) {
if (c.children) {
return true
}
}
return false
}
},
data () {
return {
apiName: 'listConfigurations',
configdata: []
}
},
methods: {
getRowClassName (record, index) {
if (record.parent) {
return 'child-row'
}
if (index % 2 === 0) {
return 'light-row'
}
return 'dark-row'
}
}
}
</script>

View File

@ -0,0 +1,365 @@
// 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.
<template>
<a-row :gutter="12">
<a-col :md="24">
<a-card class="breadcrumb-card">
<a-col :span="12" style="display: inline-flex">
<breadcrumb style="padding-top: 6px; padding-left: 8px" />
<a-button
style="margin-left: 12px; margin-top: 4px"
:loading="loading"
size="small"
shape="round"
@click="fetchConfigurationData()" >
<template #icon><ReloadOutlined /></template>
{{ $t('label.refresh') }}
</a-button>
</a-col>
<a-col :span="12" style="float: right">
<a-input-search
style="width: 25vw; float: right; margin-bottom: 10px; z-index: 8; display: flex"
:placeholder="$t('label.search')"
v-model:value="filter"
@search="changePage()"
v-focus="true" />
</a-col>
</a-card>
</a-col>
<a-col :span="24">
<a-card style="margin-left: 10px;">
<a-spin :spinning="configLoading">
<a-tabs
tabPosition="left"
:animated="false"
:activeKey="this.group || ''"
@change="changeGroupTab" >
<a-tab-pane
key=''
tab='All Settings' >
<ConfigurationTable
:columns="columns"
:config="config"
:count="count"
:page="page"
:pagesize="pagesize"
@change-page="changePage" />
</a-tab-pane>
<a-tab-pane
v-for="(group) in groups"
:key="group.name"
:tab="group.name" >
<a-tabs
:activeKey="this.subgroup || ''"
:animated="false"
@change="changeSubgroupTab" >
<a-tab-pane
v-for="(subgroup) in group.subgroup"
:key="subgroup.name"
:tab="subgroup.name" >
<ConfigurationHierarchy
:columns="columns"
:config="config" />
</a-tab-pane>
</a-tabs>
</a-tab-pane>
</a-tabs>
</a-spin>
</a-card>
</a-col>
</a-row>
</template>
<script>
import { api } from '@/api'
import { mixin, mixinDevice } from '@/utils/mixin.js'
import Breadcrumb from '@/components/widgets/Breadcrumb'
import OsLogo from '@/components/widgets/OsLogo'
import Status from '@/components/widgets/Status'
import ActionButton from '@/components/view/ActionButton'
import InfoCard from '@/components/view/InfoCard'
import QuickView from '@/components/view/QuickView'
import TooltipButton from '@/components/widgets/TooltipButton'
import ConfigurationHierarchy from './ConfigurationHierarchy'
import ConfigurationTable from './ConfigurationTable'
export default {
name: 'ConfigurationTab',
components: {
Breadcrumb,
OsLogo,
Status,
ActionButton,
InfoCard,
QuickView,
TooltipButton,
ConfigurationHierarchy,
ConfigurationTable
},
mixins: [mixin, mixinDevice],
props: {
loading: {
type: Boolean,
default: false
},
actions: {
type: Array,
default: () => []
}
},
data () {
return {
groups: [],
config: [],
configLoading: true,
page: 1,
pagesize: this.$store.getters.defaultListViewPageSize,
group: '',
subgroup: '',
filter: '',
count: 0,
apiName: 'listConfigurations',
columns: [
{
title: 'name',
dataIndex: 'name',
slots: { customRender: 'name' }
},
{
title: 'value',
dataIndex: 'value',
slots: { customRender: 'value' },
width: '29%'
}
]
}
},
watch: {
'$route.fullPath': function () {
this.group = this.$route.query.group || ''
this.subgroup = this.$route.query.subgroup || ''
this.page = parseInt(this.$route.query.page) || 1
this.pagesize = parseInt(this.$route.query.pagesize) || this.pagesize
this.filter = this.$route.query.filter || ''
this.fetchConfigurationData()
}
},
created () {
this.fetchConfigurationGroups()
},
methods: {
fetchConfigurationGroups () {
this.configLoading = true
const params = {
pagesize: -1
}
api('listConfigurationGroups', params).then(response => {
this.groups = response.listconfigurationgroupsresponse.configurationgroup
}).catch(error => {
console.error(error)
this.$message.error(this.$t('message.error.loading.setting'))
}).finally(() => {
this.group = this.$route.query.group || ''
this.subgroup = this.$route.query.subgroup || ''
this.fetchConfigurationData()
})
},
fetchConfigurationData () {
this.configLoading = true
const params = {
listAll: true
}
if (this.group.length > 0) {
params.group = this.group
params.pagesize = -1
} else {
params.pagesize = this.pagesize || 20
params.page = this.page || 1
}
if (this.subgroup.length > 0) {
params.subgroup = this.subgroup
}
if (this.filter) {
params.keyword = this.filter
}
api('listConfigurations', params).then(response => {
this.config = []
let config = response.listconfigurationsresponse.configuration || []
this.count = response.listconfigurationsresponse.count || 0
if (this.group.length > 0) {
config = this.convertConfigToHierarchy(config)
}
this.config = config
window.scrollTo(0, 0)
}).catch(error => {
console.error(error)
this.$message.error(this.$t('message.error.loading.setting'))
}).finally(() => {
this.configLoading = false
})
},
convertConfigToHierarchy (config) {
var hierarchy = {}
for (var c of config) {
if (c.parent && c.parent.length !== 0) {
if (hierarchy[c.parent]) {
hierarchy[c.parent].push(c)
} else {
hierarchy[c.parent] = [c]
}
}
}
for (c of config) {
if (hierarchy[c.name]) {
c.children = hierarchy[c.name]
}
}
config = config.filter(c => !c.parent)
return config
},
changePage (page, pagesize) {
const query = {}
if (page) {
query.page = page
this.page = page
} else {
this.page = 1
}
if (pagesize) {
query.pagesize = pagesize
this.pagesize = pagesize
}
if (this.filter) {
query.filter = this.filter
}
if (this.group !== '') {
query.group = this.group
}
if (this.subgroup !== '') {
query.subgroup = this.subgroup
}
this.pushToHistory(query)
this.fetchConfigurationData()
},
pushToHistory (query) {
history.pushState(
{},
null,
'#' + this.$route.path + '?' + Object.keys(query).map(key => {
return (
encodeURIComponent(key) + '=' + encodeURIComponent(query[key])
)
}).join('&')
)
},
changeGroupTab (e) {
this.group = e
if (this.group.length > 0) {
for (const groupIndex in this.groups) {
if (this.groups[groupIndex].name === this.group) {
const group = this.groups[groupIndex]
this.subgroup = group.subgroup[0].name
}
}
} else {
this.group = ''
this.subgroup = ''
this.changePage(1, this.pagesize)
}
if (this.group.length > 0 && this.subgroup.length > 0) {
const query = Object.assign({}, this.$route.query)
delete query.page
delete query.pagesize
query.group = this.group
query.subgroup = this.subgroup
query.filter = this.filter
// this.pagesize = -1
this.page = 0
this.pushToHistory(query)
this.fetchConfigurationData()
}
},
changeSubgroupTab (e) {
this.subgroup = e || this.subgroup
if (this.group.length > 0 && this.subgroup.length > 0) {
const query = Object.assign({}, this.$route.query)
delete query.page
delete query.pagesize
query.group = this.group
query.subgroup = this.subgroup
// this.pagesize = -1
this.page = 0
this.pushToHistory(query)
this.fetchConfigurationData()
} else {
history.pushState(
{},
null,
'#' + this.$route.path
)
}
}
}
}
</script>
<style scoped lang="scss">
.breadcrumb-card {
margin-left: -24px;
margin-right: -24px;
margin-top: -16px;
margin-bottom: 12px;
}
.shift-btns {
display: flex;
}
.shift-btn {
display: flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
font-size: 12px;
&:not(:last-child) {
margin-right: 5px;
}
&--rotated {
font-size: 10px;
transform: rotate(90deg);
}
}
.alert-notification-threshold {
background-color: rgba(255, 231, 175, 0.75);
color: #e87900;
padding: 10%;
}
.alert-disable-threshold {
background-color: rgba(255, 190, 190, 0.75);
color: #f50000;
padding: 10%;
}
</style>

View File

@ -0,0 +1,191 @@
// 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.
<template>
<div class="config-row-element">
<a-table
class="config-list-view"
size="small"
:showHeader="false"
:pagination="false"
:columns="columns"
:dataSource="config"
:rowKey="record => record.name"
:rowClassName="getRowClassName" >
<template #name="{ record }">
<b> {{record.displaytext }} </b> {{ ' (' + record.name + ')' }} <br/> {{ record.description }}
</template>
<template #value="{ record }">
<ConfigurationValue :configrecord="record" />
</template>
</a-table>
<a-pagination
class="config-row-element"
style="margin-top: 10px"
size="small"
:current="page"
:pageSize="pagesize"
:total="count"
:showTotal="count => `${$t('label.showing')} ${Math.min(count, 1+((page-1)*pagesize))}-${Math.min(page*pagesize, count)} ${$t('label.of')} ${count} ${$t('label.items')}`"
:pageSizeOptions="pageSizeOptions"
@change="changePage"
@showSizeChange="changePage"
showSizeChanger
showQuickJumper>
<template #buildOptionText="props">
<span>{{ props.value }} / {{ $t('label.page') }}</span>
</template>
</a-pagination>
</div>
</template>
<script>
import ConfigurationValue from './ConfigurationValue'
export default {
components: {
ConfigurationValue
},
name: 'ConfigurationTable',
props: {
config: {
type: Array,
default: () => { return [] }
},
columns: {
type: Array,
default: () => { return [] }
},
count: {
type: Number,
default: 0
},
page: {
type: Number,
default: 0
},
pagesize: {
type: Number,
default: 20
}
},
data () {
return {
apiName: 'listConfigurations'
}
},
computed: {
pageSizeOptions () {
var sizes = [20, 50, 100, 200, this.$store.getters.defaultListViewPageSize]
return [...new Set(sizes)].sort(function (a, b) {
return a - b
}).map(String)
}
},
methods: {
changePage (page, pagesize) {
this.$emit('change-page', page, pagesize)
},
getRowClassName (record, index) {
if (index % 2 === 0) {
return 'config-light-row'
}
return 'config-dark-row'
}
}
}
</script>
<style scoped lang="scss">
.list {
clear:both;
}
.editable-value {
@media (min-width: 760px) {
text-align: right;
margin-left: 40px;
margin-right: -40px;
}
}
.item {
display: flex;
flex-direction: column;
align-items: stretch;
@media (min-width: 760px) {
flex-direction: row;
}
&__content {
width: 100%;
display: block;
word-break: break-all;
@media (min-width: 760px) {
width: auto;
}
}
}
.action {
margin-top: 20px;
margin-left: -12px;
@media (min-width: 480px) {
margin-left: -24px;
}
@media (min-width: 760px) {
margin-top: 0;
margin-left: 0;
}
}
.value {
margin-top: 20px;
@media (min-width: 760px) {
margin-top: 0;
}
}
.config-light-row {
background-color: #fff;
}
.config-dark-row {
background-color: #f9f9f9;
}
.config-row-element {
margin-bottom: 10px;
}
.config-list-view {
overflow-y: auto;
display: block;
width: 100%;
margin-top: 20px;
}
</style>

View File

@ -0,0 +1,363 @@
// 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.
<template>
<a-list>
<a-list-item>
<span v-if="configrecord.type ==='Boolean'">
<a-tooltip :title="editableValue?'true':'false'">
<a-switch
:disabled="(!('updateConfiguration' in $store.getters.apis) || configDisabled)"
v-model:checked="editableValue"
@keydown.esc="editableValueKey = null"
@pressEnter="updateConfigurationValue(configrecord)"
@change="value => setConfigurationEditable(configrecord, value)"
/>
</a-tooltip>
</span>
<span v-else-if="configrecord.type ==='Number'">
<a-tooltip :title="editableValue">
<a-input-number
style="width: 20vw;"
:defaultValue="actualValue"
:disabled="(!('updateConfiguration' in $store.getters.apis) || configDisabled)"
v-model:value="editableValue"
@keydown.esc="editableValueKey = null"
@pressEnter="updateConfigurationValue(configrecord)"
@change="value => setConfigurationEditable(configrecord, value)"
/>
</a-tooltip>
</span>
<span v-else-if="configrecord.type ==='Decimal'">
<a-tooltip :title="editableValue">
<a-input-number
style="width: 20vw"
:defaultValue="actualValue"
:disabled="(!('updateConfiguration' in $store.getters.apis) || configDisabled)"
v-model:value="editableValue"
@keydown.esc="editableValueKey = null"
@pressEnter="updateConfigurationValue(configrecord)"
@change="value => setConfigurationEditable(configrecord, value)"
/>
</a-tooltip>
</span>
<span v-else-if="configrecord.type ==='Range'">
<a-row>
<a-col>
<a-tooltip :title="editableValue">
<a-slider
style="width: 13vw"
class="config-slider-value"
:defaultValue="configrecord.value * 100"
:min="0"
:max="100"
:disabled="(!('updateConfiguration' in $store.getters.apis) || configDisabled)"
v-model:value="editableValue"
@keydown.esc="editableValueKey = null"
@pressEnter="updateConfigurationValue(configrecord)"
@change="value => setConfigurationEditable(configrecord, value)"
/>
</a-tooltip>
</a-col>
<a-col>
<a-tooltip :title="editableValue">
<a-input-number
style="width: 5vw; margin-left: 10px; float: right"
class="config-slider-text"
:defaultValue="configrecord.value * 100"
:min="0"
:max="100"
:disabled="(!('updateConfiguration' in $store.getters.apis) || configDisabled)"
:formatter="(value) => `${value}%`"
v-model:value="editableValue"
@keydown.esc="editableValueKey = null"
@pressEnter="updateConfigurationValue(configrecord)"
@change="value => setConfigurationEditable(configrecord, value)"
/>
</a-tooltip>
</a-col>
</a-row>
</span>
<span v-else-if="configrecord.type === 'Select'">
<a-tooltip :title="editableValue">
<a-select
style="width: 20vw"
:defaultValue="actualValue"
:disabled="(!('updateConfiguration' in $store.getters.apis) || configDisabled)"
v-model:value="editableValue"
@keydown.esc="editableValueKey = null"
@pressEnter="updateConfigurationValue(configrecord)"
@change="value => setConfigurationEditable(configrecord, value)">
<a-select-option
v-for="value of configrecord.options.split(',')"
:key="value">
{{ value }}
</a-select-option>
</a-select>
</a-tooltip>
</span>
<span v-else-if="configrecord.type === 'Order'">
<a-tooltip :title="editableValue.join(', ')">
<b>{{ $t('message.select.deselect.to.sort') }}</b>
<br />
<a-select
style="width: 20vw"
mode="multiple"
:defaultValue="actualValue"
:disabled="(!('updateConfiguration' in $store.getters.apis) || configDisabled)"
v-model:value="editableValue"
@keydown.esc="editableValueKey = null"
@pressEnter="updateConfigurationValue(configrecord)"
@change="value => setConfigurationEditable(configrecord, value)">
<a-select-option
v-for="value of configrecord.options.split(',')"
:key="value">
{{ value }}
</a-select-option>
</a-select>
</a-tooltip>
</span>
<span v-else-if="configrecord.type === 'CSV'">
<a-tooltip :title="editableValue.join(', ')">
<b>{{ $t('message.type.values.to.add') }}</b>
<br />
<a-select
style="width: 20vw"
mode="tags"
:disabled="(!('updateConfiguration' in $store.getters.apis) || configDisabled)"
v-model:value="editableValue"
@keydown.esc="editableValueKey = null"
@pressEnter="updateConfigurationValue(configrecord)"
@change="value => setConfigurationEditable(configrecord, value)">
</a-select>
</a-tooltip>
</span>
<span v-else>
<a-tooltip :title="editableValue">
<a-textarea
style="width: 20vw; word-break: break-all"
:defaultValue="actualValue"
:disabled="(!('updateConfiguration' in $store.getters.apis) || configDisabled)"
v-model:value="editableValue"
@keydown.esc="editableValueKey = null"
@pressEnter="updateConfigurationValue(configrecord)"
@change="value => setConfigurationEditable(configrecord, value)"
/>
</a-tooltip>
</span>
<span class="actions">
<tooltip-button
:tooltip="$t('label.cancel')"
@onClick="cancelEditConfigurationValue(configrecord)"
v-if="editableValueKey !== null"
iconType="CloseCircleTwoTone"
iconTwoToneColor="#f5222d"
:disabled="valueLoading" />
<tooltip-button
:tooltip="$t('label.ok')"
@onClick="updateConfigurationValue(configrecord)"
v-if="editableValueKey !== null"
iconType="CheckCircleTwoTone"
iconTwoToneColor="#52c41a"
:disabled="valueLoading" />
<tooltip-button
:tooltip="$t('label.reset.config.value')"
@onClick="resetConfigurationValue(configrecord)"
v-if="editableValueKey === null"
icon="reload-outlined"
:disabled="(!('resetConfiguration' in $store.getters.apis) || configDisabled || valueLoading)" />
</span>
</a-list-item>
</a-list>
</template>
<script>
import { api } from '@/api'
import TooltipButton from '@/components/widgets/TooltipButton'
export default {
name: 'ConfigurationValue',
components: {
TooltipButton
},
props: {
configrecord: {
type: Object,
required: true
},
loading: {
type: Boolean,
default: false
},
configDisabled: {
type: Boolean,
default: false
},
actions: {
type: Array,
default: () => []
}
},
data () {
return {
valueLoading: this.loading,
actualValue: null,
editableValue: null,
editableValueKey: null
}
},
created () {
this.setConfigData()
},
watch: {
},
methods: {
setConfigData () {
this.valueLoading = false
this.editableValue = this.getEditableValue(this.configrecord)
this.actualValue = this.editableValue
this.editableValueKey = null
},
updateConfigurationValue (configrecord) {
this.valueLoading = true
this.editableValueKey = null
var newValue = this.editableValue
if (configrecord.type === 'Range') {
newValue = newValue / 100
}
if (['Order', 'CSV'].includes(configrecord.type)) {
newValue = newValue.join(',')
}
const params = {
name: configrecord.name,
value: newValue
}
api('updateConfiguration', params).then(json => {
this.editableValue = this.getEditableValue(json.updateconfigurationresponse.configuration)
this.actualValue = this.editableValue
this.$emit('change-config', { value: newValue })
this.$store.dispatch('RefreshFeatures')
this.$message.success(`${this.$t('message.setting.updated')} ${configrecord.name}`)
if (json.updateconfigurationresponse &&
json.updateconfigurationresponse.configuration &&
!json.updateconfigurationresponse.configuration.isdynamic &&
['Admin'].includes(this.$store.getters.userInfo.roletype)) {
this.$notification.warning({
message: this.$t('label.status'),
description: this.$t('message.restart.mgmt.server')
})
}
}).catch(error => {
this.editableValue = this.actualValue
console.error(error)
this.$message.error(this.$t('message.error.save.setting'))
this.$notification.error({
message: this.$t('label.error'),
description: this.$t('message.error.save.setting')
})
}).finally(() => {
this.valueLoading = false
this.$emit('refresh')
})
},
resetConfigurationValue (configrecord) {
this.valueLoading = true
this.editableValueKey = null
api('resetConfiguration', {
name: configrecord.name
}).then(json => {
this.editableValue = this.getEditableValue(json.resetconfigurationresponse.configuration)
this.actualValue = this.editableValue
var newValue = this.editableValue
if (configrecord.type === 'Range') {
newValue = newValue / 100
}
this.$emit('change-config', { value: newValue })
this.$store.dispatch('RefreshFeatures')
this.$message.success(`${this.$t('label.setting')} ${configrecord.name} ${this.$t('label.reset.config.value')}`)
if (json.resetconfigurationresponse &&
json.resetconfigurationresponse.configuration &&
!json.resetconfigurationresponse.configuration.isdynamic &&
['Admin'].includes(this.$store.getters.userInfo.roletype)) {
this.$notification.warning({
message: this.$t('label.status'),
description: this.$t('message.restart.mgmt.server')
})
}
}).catch(error => {
this.editableValue = this.actualValue
console.error(error)
this.$message.error(this.$t('message.error.reset.config'))
this.$notification.error({
message: this.$t('label.error'),
description: this.$t('message.error.reset.config')
})
}).finally(() => {
this.valueLoading = false
this.$emit('refresh')
})
},
getEditableValue (configrecord) {
if (configrecord.type === 'Range') {
return Number(configrecord.value) * 100 || 0
}
if (configrecord.type === 'Boolean') {
return configrecord.value === 'true'
}
if (configrecord.type === 'Number' || configrecord.type === 'Decimal') {
if (configrecord.value) {
return Number(configrecord.value)
} else if (configrecord.defaultvalue) {
return Number(configrecord.defaultvalue)
}
return 0
}
if (['Order', 'CSV'].includes(configrecord.type)) {
if (configrecord.value && configrecord.value.length > 0) {
return String(configrecord.value).split(',')
} else {
return []
}
}
if (configrecord.value) {
return String(configrecord.value)
}
if (configrecord.defaultvalue) {
return String(configrecord.defaultvalue)
}
return ''
},
cancelEditConfigurationValue (configrecord) {
this.editableValueKey = null
this.editableValue = this.getEditableValue(configrecord)
},
setConfigurationEditable (configrecord, value) {
if (this.actualValue !== this.editableValue) {
this.editableValueKey = 'edit'
} else {
this.editableValueKey = null
}
}
}
}
</script>
<style lang="scss" scoped>
.actions {
margin-left: 10px;
width: 100px;
}
</style>

View File

@ -30,6 +30,8 @@ import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
import org.apache.cloudstack.framework.config.dao.ConfigurationDaoImpl;
import org.apache.cloudstack.framework.config.dao.ConfigurationGroupDaoImpl;
import org.apache.cloudstack.framework.config.dao.ConfigurationSubGroupDaoImpl;
import org.apache.cloudstack.test.utils.SpringUtils;
import com.cloud.alert.AlertManager;
@ -66,7 +68,7 @@ import com.cloud.user.dao.UserStatisticsDaoImpl;
@ComponentScan(basePackageClasses = {AccountDaoImpl.class, UsageDaoImpl.class, UsageJobDaoImpl.class, UsageVMInstanceDaoImpl.class, UsageIPAddressDaoImpl.class,
UsageNetworkDaoImpl.class, UsageVolumeDaoImpl.class, UsageStorageDaoImpl.class, UsageLoadBalancerPolicyDaoImpl.class,
UsagePortForwardingRuleDaoImpl.class, UsageNetworkOfferingDaoImpl.class, UsageVPNUserDaoImpl.class, UsageVmDiskDaoImpl.class,
UsageSecurityGroupDaoImpl.class, ConfigurationDaoImpl.class, UsageManagerImpl.class, VMInstanceUsageParser.class, IPAddressUsageParser.class,
UsageSecurityGroupDaoImpl.class, ConfigurationDaoImpl.class, ConfigurationGroupDaoImpl.class, ConfigurationSubGroupDaoImpl.class, UsageManagerImpl.class, VMInstanceUsageParser.class, IPAddressUsageParser.class,
LoadBalancerUsageParser.class, NetworkOfferingUsageParser.class, NetworkUsageParser.class, PortForwardingUsageParser.class,
SecurityGroupUsageParser.class, StorageUsageParser.class, VmDiskUsageParser.class, VolumeUsageParser.class, VPNUserUsageParser.class,
UserStatisticsDaoImpl.class},