diff --git a/api/src/main/java/com/cloud/server/ManagementService.java b/api/src/main/java/com/cloud/server/ManagementService.java index e1b9705ecf54..df44ec670193 100644 --- a/api/src/main/java/com/cloud/server/ManagementService.java +++ b/api/src/main/java/com/cloud/server/ManagementService.java @@ -22,6 +22,7 @@ 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.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, Integer> searchForConfigurations(ListCfgsByCmd c); + /** + * returns the the configuration groups + * + * @return list of configuration groups + */ + Pair, Integer> listConfigurationGroups(ListCfgGroupsByCmd cmd); + /** * Searches for Clusters by the specified search criteria * diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java index 3cb937e84277..0753dbbfed3c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -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"; @@ -296,6 +298,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"; @@ -307,6 +310,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"; @@ -328,6 +332,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"; diff --git a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java index a90c222e3510..31f06dfc2625 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java +++ b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java @@ -40,6 +40,7 @@ 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.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); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/config/ListCfgGroupsByCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/config/ListCfgGroupsByCmd.java new file mode 100644 index 000000000000..46ab10cb2bcd --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/config/ListCfgGroupsByCmd.java @@ -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, Integer> result = _mgr.listConfigurationGroups(this); + ListResponse response = new ListResponse<>(); + List 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); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/config/ListCfgsByCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/config/ListCfgsByCmd.java index b47bb6b87d50..457aeabe2f8d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/config/ListCfgsByCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/config/ListCfgsByCmd.java @@ -19,7 +19,10 @@ 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.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 Long getImageStoreId() { 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 Long getPageSizeVal() { // ///////////// 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, Integer> result = _mgr.searchForConfigurations(this); - ListResponse response = new ListResponse(); - List configResponses = new ArrayList(); - for (Configuration cfg : result.first()) { - ConfigurationResponse cfgResponse = _responseGenerator.createConfigurationResponse(cfg); - cfgResponse.setObjectName("configuration"); - if (getZoneId() != null) { - cfgResponse.setScope("zone"); - } - if (getClusterId() != null) { - cfgResponse.setScope("cluster"); + validateParameters(); + try { + Pair, Integer> result = _mgr.searchForConfigurations(this); + ListResponse response = new ListResponse<>(); + List configResponses = new ArrayList<>(); + for (Configuration cfg : result.first()) { + ConfigurationResponse cfgResponse = _responseGenerator.createConfigurationResponse(cfg); + setScope(cfgResponse); + configResponses.add(cfgResponse); } - if (getStoragepoolId() != null) { - cfgResponse.setScope("storagepool"); - } - if (getAccountId() != null) { - cfgResponse.setScope("account"); + + if (StringUtils.isNotEmpty(getGroupName())) { + response.setResponses(configResponses, configResponses.size()); + } else { + response.setResponses(configResponses, result.second()); } - if (getDomainId() != null) { - cfgResponse.setScope("domain"); + 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 (getImageStoreId() != null){ - cfgResponse.setScope("imagestore"); + if (StringUtils.isNotEmpty(getSubGroupName()) && + !getSubGroupName().equalsIgnoreCase(cfgResponse.getSubGroup())) { + return false; } - configResponses.add(cfgResponse); } - - response.setResponses(configResponses, result.second()); - response.setResponseName(getCommandName()); - setResponseObject(response); + return true; } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ConfigurationGroupResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ConfigurationGroupResponse.java new file mode 100644 index 000000000000..053ee5fdc35e --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/ConfigurationGroupResponse.java @@ -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 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 getSubGroups() { + return subGroups; + } + + public void setSubGroups(List 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; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ConfigurationResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ConfigurationResponse.java index c42307c265a1..1818e914a97e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ConfigurationResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ConfigurationResponse.java @@ -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 void setCategory(String category) { 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 void setValue(String value) { 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 void setIsDynamic(boolean isDynamic) { 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; + } + } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ConfigurationSubGroupResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ConfigurationSubGroupResponse.java new file mode 100644 index 000000000000..fda8e18b361a --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/ConfigurationSubGroupResponse.java @@ -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; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java b/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java index 43b70fa76656..7b39804c738e 100644 --- a/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java +++ b/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java @@ -44,12 +44,12 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer ConfigKey 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 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 BackupEnableAttachDetachVolumes = new ConfigKey<>("Advanced", Boolean.class, "backup.enable.attach.detach.of.volumes", diff --git a/api/src/main/java/org/apache/cloudstack/ca/CAManager.java b/api/src/main/java/org/apache/cloudstack/ca/CAManager.java index dbabfa2a4841..12a9d3d7b41c 100644 --- a/api/src/main/java/org/apache/cloudstack/ca/CAManager.java +++ b/api/src/main/java/org/apache/cloudstack/ca/CAManager.java @@ -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 CertSignatureAlgorithm = new ConfigKey<>("Advanced", String.class, - "ca.framework.cert.signature.algorithm", + ConfigKey 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 CertValidityPeriod = new ConfigKey<>("Advanced", Integer.class, diff --git a/api/src/main/java/org/apache/cloudstack/config/ApiServiceConfiguration.java b/api/src/main/java/org/apache/cloudstack/config/ApiServiceConfiguration.java index 364588208703..a4aa860487f3 100644 --- a/api/src/main/java/org/apache/cloudstack/config/ApiServiceConfiguration.java +++ b/api/src/main/java/org/apache/cloudstack/config/ApiServiceConfiguration.java @@ -20,15 +20,15 @@ import org.apache.cloudstack.framework.config.Configurable; public class ApiServiceConfiguration implements Configurable { - public static final ConfigKey ManagementServerAddresses = new ConfigKey("Advanced", String.class, "host", "localhost", "The ip address of management server. This can also accept comma separated addresses.", true); + public static final ConfigKey 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 ApiServletPath = new ConfigKey("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 DefaultUIPageSize = new ConfigKey("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 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 ApiAllowedSourceCidrList = new ConfigKey("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 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(); diff --git a/api/src/main/java/org/apache/cloudstack/query/QueryService.java b/api/src/main/java/org/apache/cloudstack/query/QueryService.java index 231e0f2cbd18..0587294a826f 100644 --- a/api/src/main/java/org/apache/cloudstack/query/QueryService.java +++ b/api/src/main/java/org/apache/cloudstack/query/QueryService.java @@ -94,13 +94,13 @@ public interface QueryService { ConfigKey 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 UserVMDeniedDetails = new ConfigKey("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 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 UserVMReadOnlyDetails = new ConfigKey("Advanced", String.class, - "user.vm.readonly.details", "dataDiskController, rootDiskController", - "List of read-only VM settings/details as comma separated string", true); + static final ConfigKey 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 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, " + diff --git a/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java b/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java index 35c36d35cd96..dff3ee279e73 100644 --- a/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java +++ b/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java @@ -65,7 +65,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 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 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); diff --git a/engine/components-api/src/main/java/com/cloud/configuration/ConfigurationManager.java b/engine/components-api/src/main/java/com/cloud/configuration/ConfigurationManager.java index 0442cac86c8e..cf504fc25458 100644 --- a/engine/components-api/src/main/java/com/cloud/configuration/ConfigurationManager.java +++ b/engine/components-api/src/main/java/com/cloud/configuration/ConfigurationManager.java @@ -20,6 +20,8 @@ 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.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 @@ Vlan createVlanAndPublicIpRange(long zoneId, long networkId, long physicalNetwor AllocationState findPodAllocationState(HostPodVO pod); AllocationState findClusterAllocationState(ClusterVO cluster); + + String getConfigurationType(String configName); + + Pair getConfigurationGroupAndSubGroup(String configName); + + List getConfigurationSubGroups(Long groupId); } diff --git a/engine/components-api/src/main/java/com/cloud/network/IpAddressManager.java b/engine/components-api/src/main/java/com/cloud/network/IpAddressManager.java index 61489e5f7c89..32d5aa8dfa22 100644 --- a/engine/components-api/src/main/java/com/cloud/network/IpAddressManager.java +++ b/engine/components-api/src/main/java/com/cloud/network/IpAddressManager.java @@ -49,11 +49,12 @@ public interface IpAddressManager { "When true, ip address delete (ipassoc) failures are ignored", true); ConfigKey VrouterRedundantTiersPlacement = new ConfigKey( - "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. diff --git a/engine/components-api/src/main/java/com/cloud/resource/ResourceManager.java b/engine/components-api/src/main/java/com/cloud/resource/ResourceManager.java index 2857bbc9c448..9308be5fb320 100755 --- a/engine/components-api/src/main/java/com/cloud/resource/ResourceManager.java +++ b/engine/components-api/src/main/java/com/cloud/resource/ResourceManager.java @@ -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 HOST_MAINTENANCE_LOCAL_STRATEGY = new ConfigKey<>("Advanced", String.class, - "host.maintenance.local.storage.strategy", "Error", + ConfigKey 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. diff --git a/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java b/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java index b256d8fb86e4..c2bac59fd520 100644 --- a/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java +++ b/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java @@ -45,6 +45,14 @@ import com.cloud.vm.VMInstanceVO; public interface StorageManager extends StorageService { + ConfigKey StorageCleanupEnabled = new ConfigKey<>(Boolean.class, + "storage.cleanup.enabled", + "Advanced", + "true", + "Enables/disables the storage cleanup thread.", + false, + ConfigKey.Scope.Global, + null); ConfigKey StorageCleanupInterval = new ConfigKey<>(Integer.class, "storage.cleanup.interval", "Advanced", @@ -52,7 +60,8 @@ public interface StorageManager extends StorageService { "The interval (in seconds) to wait before running the storage cleanup thread.", false, ConfigKey.Scope.Global, - null); + null, + StorageCleanupEnabled.key()); ConfigKey StorageCleanupDelay = new ConfigKey<>(Integer.class, "storage.cleanup.delay", "Advanced", @@ -60,15 +69,8 @@ public interface StorageManager extends StorageService { "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 StorageCleanupEnabled = new ConfigKey<>(Boolean.class, - "storage.cleanup.enabled", - "Advanced", - "true", - "Enables/disables the storage cleanup thread.", - false, - ConfigKey.Scope.Global, - null); + null, + StorageCleanupEnabled.key()); ConfigKey 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 KvmStorageOfflineMigrationWait = new ConfigKey<>(Integer.class, "kvm.storage.offline.migration.wait", "Storage", diff --git a/engine/orchestration/src/main/java/com/cloud/agent/manager/ClusteredAgentManagerImpl.java b/engine/orchestration/src/main/java/com/cloud/agent/manager/ClusteredAgentManagerImpl.java index cc8305c57238..749a738e63ed 100644 --- a/engine/orchestration/src/main/java/com/cloud/agent/manager/ClusteredAgentManagerImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/agent/manager/ClusteredAgentManagerImpl.java @@ -136,7 +136,7 @@ protected ClusteredAgentManagerImpl() { protected final ConfigKey EnableLB = new ConfigKey(Boolean.class, "agent.lb.enabled", "Advanced", "false", "Enable agent load balancing between management server nodes", true); protected final ConfigKey ConnectedAgentThreshold = new ConfigKey(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 LoadSize = new ConfigKey(Integer.class, "direct.agent.load.size", "Advanced", "16", "How many agents to connect to in each round", true); protected final ConfigKey ScanInterval = new ConfigKey(Integer.class, "direct.agent.scan.interval", "Advanced", "90", "Interval between scans to load agents", false, ConfigKey.Scope.Global, 1000); diff --git a/engine/schema/src/main/java/com/cloud/upgrade/ConfigurationGroupsAggregator.java b/engine/schema/src/main/java/com/cloud/upgrade/ConfigurationGroupsAggregator.java new file mode 100644 index 000000000000..34de1bccb82f --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/upgrade/ConfigurationGroupsAggregator.java @@ -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 configs = configDao.listAllIncludingRemoved(); + if (CollectionUtils.isEmpty(configs)) { + return; + } + + for (final ConfigurationVO config : configs) { + String configName = config.getName(); + if (StringUtils.isBlank(configName)) { + continue; + } + + try { + Pair 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 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); + } +} diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41610to41700.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41610to41700.java index 98497512fcea..bb4e70567c62 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41610to41700.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41610to41700.java @@ -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.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); diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41720to41800.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41720to41800.java index 468e4ec8f431..c24da883506f 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41720to41800.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41720to41800.java @@ -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 void performDataMigration(Connection conn) { correctGuestOsNames(); updateGuestOsMappings(); correctGuestOsIdsInHypervisorMapping(conn); + updateConfigurationGroups(); } @Override @@ -702,4 +705,8 @@ private void correctGuestOsIdsInHypervisorMapping(final Connection conn) { 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(); + } } diff --git a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml index cec86d2d7be6..a8587c7814d2 100644 --- a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml +++ b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml @@ -61,6 +61,8 @@ + + diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41610to41700.sql b/engine/schema/src/main/resources/META-INF/db/schema-41610to41700.sql index 68c2e344fa7d..c259c1414d1f 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-41610to41700.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-41610to41700.sql @@ -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'); \ No newline at end of file diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41720to41800.sql b/engine/schema/src/main/resources/META-INF/db/schema-41720to41800.sql index 726d36caea80..889f7d423e5a 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-41720to41800.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-41720to41800.sql @@ -169,12 +169,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`; @@ -1040,6 +1040,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` ( diff --git a/engine/storage/image/src/main/java/org/apache/cloudstack/storage/image/manager/ImageStoreProviderManagerImpl.java b/engine/storage/image/src/main/java/org/apache/cloudstack/storage/image/manager/ImageStoreProviderManagerImpl.java index 1ca155cb7d9e..41e83f4f7bbc 100644 --- a/engine/storage/image/src/main/java/org/apache/cloudstack/storage/image/manager/ImageStoreProviderManagerImpl.java +++ b/engine/storage/image/src/main/java/org/apache/cloudstack/storage/image/manager/ImageStoreProviderManagerImpl.java @@ -65,8 +65,8 @@ public class ImageStoreProviderManagerImpl implements ImageStoreProviderManager, Map driverMaps; - static final ConfigKey ImageStoreAllocationAlgorithm = new ConfigKey("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 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() { diff --git a/engine/storage/integration-test/src/test/java/org/apache/cloudstack/storage/test/ChildTestConfiguration.java b/engine/storage/integration-test/src/test/java/org/apache/cloudstack/storage/test/ChildTestConfiguration.java index 51156246b37c..c5321ae6102f 100644 --- a/engine/storage/integration-test/src/test/java/org/apache/cloudstack/storage/test/ChildTestConfiguration.java +++ b/engine/storage/integration-test/src/test/java/org/apache/cloudstack/storage/test/ChildTestConfiguration.java @@ -33,6 +33,8 @@ 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 @@ @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) diff --git a/engine/storage/integration-test/src/test/resources/component.xml b/engine/storage/integration-test/src/test/resources/component.xml index 66a4aa80bab7..aee37145114f 100644 --- a/engine/storage/integration-test/src/test/resources/component.xml +++ b/engine/storage/integration-test/src/test/resources/component.xml @@ -188,5 +188,9 @@ --> - + + + + + diff --git a/framework/config/src/main/java/org/apache/cloudstack/config/Configuration.java b/framework/config/src/main/java/org/apache/cloudstack/config/Configuration.java index 19219305ca16..b93817a99191 100644 --- a/framework/config/src/main/java/org/apache/cloudstack/config/Configuration.java +++ b/framework/config/src/main/java/org/apache/cloudstack/config/Configuration.java @@ -22,9 +22,17 @@ * 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(); } diff --git a/framework/config/src/main/java/org/apache/cloudstack/config/ConfigurationGroup.java b/framework/config/src/main/java/org/apache/cloudstack/config/ConfigurationGroup.java new file mode 100644 index 000000000000..cd48e7f44de9 --- /dev/null +++ b/framework/config/src/main/java/org/apache/cloudstack/config/ConfigurationGroup.java @@ -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(); +} diff --git a/framework/config/src/main/java/org/apache/cloudstack/config/ConfigurationSubGroup.java b/framework/config/src/main/java/org/apache/cloudstack/config/ConfigurationSubGroup.java new file mode 100644 index 000000000000..833a04d7fb0a --- /dev/null +++ b/framework/config/src/main/java/org/apache/cloudstack/config/ConfigurationSubGroup.java @@ -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(); +} diff --git a/framework/config/src/main/java/org/apache/cloudstack/framework/config/ConfigDepotAdmin.java b/framework/config/src/main/java/org/apache/cloudstack/framework/config/ConfigDepotAdmin.java index 444c718caead..894bc201a4c8 100644 --- a/framework/config/src/main/java/org/apache/cloudstack/framework/config/ConfigDepotAdmin.java +++ b/framework/config/src/main/java/org/apache/cloudstack/framework/config/ConfigDepotAdmin.java @@ -18,6 +18,8 @@ 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 getComponentsInDepot(); + + Pair getConfigurationGroupAndSubGroupByName(String configName); } diff --git a/framework/config/src/main/java/org/apache/cloudstack/framework/config/ConfigKey.java b/framework/config/src/main/java/org/apache/cloudstack/framework/config/ConfigKey.java index 926e65cbd180..13c594f9367a 100644 --- a/framework/config/src/main/java/org/apache/cloudstack/framework/config/ConfigKey.java +++ b/framework/config/src/main/java/org/apache/cloudstack/framework/config/ConfigKey.java @@ -21,6 +21,8 @@ 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 { 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 String description() { return _description; } + public String displayText() { + return _displayText; + } + public Scope scope() { return _scope; } @@ -67,6 +77,26 @@ public boolean isDynamic() { return _isDynamic; } + public Ternary group() { + return _group; + } + + public Pair 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 String toString() { 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 _group; // Group name, description with precedence + private final Pair _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 ConfigKey(String category, Class type, String name, String defaultValu this(type, name, category, defaultValue, description, isDynamic, scope, null); } + public ConfigKey(String category, Class 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 type, String name, String defaultValue, String description, boolean isDynamic) { this(type, name, category, defaultValue, description, isDynamic, Scope.Global, null); } + public ConfigKey(String category, Class 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 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 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 type, String name, String category, String defaultValue, String description, boolean isDynamic, Scope scope, T multiplier, + String displayText, String parent, Ternary group, Pair subGroup) { + this(type, name, category, defaultValue, description, isDynamic, scope, multiplier, null, parent, null, null, null, null); + } + + public ConfigKey(Class type, String name, String category, String defaultValue, String description, boolean isDynamic, Scope scope, T multiplier, + String displayText, String parent, Ternary group, Pair 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 ConfigKey(Class type, String name, String category, String defaultValu this(type, name, category, defaultValue, description, isDynamic, Scope.Global, null); } + public ConfigKey(Class 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; } diff --git a/framework/config/src/main/java/org/apache/cloudstack/framework/config/dao/ConfigurationGroupDao.java b/framework/config/src/main/java/org/apache/cloudstack/framework/config/dao/ConfigurationGroupDao.java new file mode 100644 index 000000000000..1aa1d9c8169c --- /dev/null +++ b/framework/config/src/main/java/org/apache/cloudstack/framework/config/dao/ConfigurationGroupDao.java @@ -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 findByName(String name); +} diff --git a/framework/config/src/main/java/org/apache/cloudstack/framework/config/dao/ConfigurationGroupDaoImpl.java b/framework/config/src/main/java/org/apache/cloudstack/framework/config/dao/ConfigurationGroupDaoImpl.java new file mode 100644 index 000000000000..d0fafcea1d9e --- /dev/null +++ b/framework/config/src/main/java/org/apache/cloudstack/framework/config/dao/ConfigurationGroupDaoImpl.java @@ -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 implements ConfigurationGroupDao { + + final SearchBuilder nameSearch; + + public ConfigurationGroupDaoImpl() { + super(); + + nameSearch = createSearchBuilder(); + nameSearch.and("name", nameSearch.entity().getName(), SearchCriteria.Op.EQ); + } + + @Override + public ConfigurationGroupVO findByName(String name) { + SearchCriteria sc = nameSearch.create(); + sc.setParameters("name", name); + return findOneIncludingRemovedBy(sc); + } +} diff --git a/framework/config/src/main/java/org/apache/cloudstack/framework/config/dao/ConfigurationSubGroupDao.java b/framework/config/src/main/java/org/apache/cloudstack/framework/config/dao/ConfigurationSubGroupDao.java new file mode 100644 index 000000000000..10cd86b5b1ac --- /dev/null +++ b/framework/config/src/main/java/org/apache/cloudstack/framework/config/dao/ConfigurationSubGroupDao.java @@ -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 findByName(String name); + ConfigurationSubGroupVO startsWithName(String name); + ConfigurationSubGroupVO findByKeyword(String keyword); + ConfigurationSubGroupVO findByNameAndGroup(String name, Long groupId); + List findByGroup(Long groupId); +} diff --git a/framework/config/src/main/java/org/apache/cloudstack/framework/config/dao/ConfigurationSubGroupDaoImpl.java b/framework/config/src/main/java/org/apache/cloudstack/framework/config/dao/ConfigurationSubGroupDaoImpl.java new file mode 100644 index 000000000000..718adeec542e --- /dev/null +++ b/framework/config/src/main/java/org/apache/cloudstack/framework/config/dao/ConfigurationSubGroupDaoImpl.java @@ -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 implements ConfigurationSubGroupDao { + + final SearchBuilder nameSearch; + final SearchBuilder groupSearch; + final SearchBuilder nameAndGroupSearch; + final SearchBuilder 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 sc = nameSearch.create(); + sc.setParameters("name", name); + return findOneIncludingRemovedBy(sc); + } + + @Override + public ConfigurationSubGroupVO startsWithName(String name) { + SearchCriteria sc = nameSearch.create(); + sc.setParameters("name", name + "%"); + return findOneIncludingRemovedBy(sc); + } + + private ConfigurationSubGroupVO matchKeywordBy(BiPredicate matcher, List configurationSubGroups, String keyword) { + for (ConfigurationSubGroupVO configurationSubGroup : configurationSubGroups) { + if (StringUtils.isBlank(configurationSubGroup.getKeywords())) { + continue; + } + + String[] configKeywords = configurationSubGroup.getKeywords().split(","); + if (configKeywords.length <= 0) { + continue; + } + + List 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 sc = keywordSearch.create(); + List configurationSubGroups = listBy(sc); + BiPredicate equals = (a, b) -> { return a.equalsIgnoreCase(b); }; + ConfigurationSubGroupVO configSubGroup = matchKeywordBy(equals, configurationSubGroups, keyword); + if (configSubGroup == null) { + BiPredicate startsWith = (a, b) -> { return a.startsWith(b); }; + configSubGroup = matchKeywordBy(startsWith, configurationSubGroups, keyword.toLowerCase()); + } + return configSubGroup; + } + + @Override + public ConfigurationSubGroupVO findByNameAndGroup(String name, Long groupId) { + SearchCriteria sc = nameAndGroupSearch.create(); + sc.setParameters("name", name); + sc.setParameters("groupId", groupId); + return findOneIncludingRemovedBy(sc); + } + + @Override + public List findByGroup(Long groupId) { + SearchCriteria sc = groupSearch.create(); + sc.setParameters("groupId", groupId); + final Filter filter = new Filter(ConfigurationSubGroupVO.class, "precedence", true, null, null); + return listIncludingRemovedBy(sc, filter); + } +} diff --git a/framework/config/src/main/java/org/apache/cloudstack/framework/config/impl/ConfigDepotImpl.java b/framework/config/src/main/java/org/apache/cloudstack/framework/config/impl/ConfigDepotImpl.java index 1cb93dc542a2..75a3ea4d947b 100644 --- a/framework/config/src/main/java/org/apache/cloudstack/framework/config/impl/ConfigDepotImpl.java +++ b/framework/config/src/main/java/org/apache/cloudstack/framework/config/impl/ConfigDepotImpl.java @@ -33,10 +33,14 @@ 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 _configurables; List _scopedStorages; Set _configured = Collections.synchronizedSet(new HashSet()); @@ -139,6 +147,28 @@ protected void populateConfiguration(Date date, Configurable configurable) { } private void createOrupdateConfigObject(Date date, String componentName, ConfigKey key, String value) { + Long groupId = 1L; + Long subGroupId = 1L; + if (key.group() != null) { + Ternary 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 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 @@ private void createOrupdateConfigObject(Date date, String componentName, ConfigK if (value != null) { vo.setValue(value); } + + if (key.group() == null && key.subGroup() == null ) { + Pair 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 @@ private void createOrupdateConfigObject(Date date, String componentName, ConfigK 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 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 void set(ConfigKey key, T value) { @Override public void createOrUpdateConfigObject(String componentName, ConfigKey key, String value) { createOrupdateConfigObject(new Date(), componentName, key, value); + } + + @Override + public Pair 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); } } diff --git a/framework/config/src/main/java/org/apache/cloudstack/framework/config/impl/ConfigurationGroupVO.java b/framework/config/src/main/java/org/apache/cloudstack/framework/config/impl/ConfigurationGroupVO.java new file mode 100644 index 000000000000..5c232b1b015c --- /dev/null +++ b/framework/config/src/main/java/org/apache/cloudstack/framework/config/impl/ConfigurationGroupVO.java @@ -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; + } +} diff --git a/framework/config/src/main/java/org/apache/cloudstack/framework/config/impl/ConfigurationSubGroupVO.java b/framework/config/src/main/java/org/apache/cloudstack/framework/config/impl/ConfigurationSubGroupVO.java new file mode 100644 index 000000000000..cb529e1cca02 --- /dev/null +++ b/framework/config/src/main/java/org/apache/cloudstack/framework/config/impl/ConfigurationSubGroupVO.java @@ -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; + } +} diff --git a/framework/config/src/main/java/org/apache/cloudstack/framework/config/impl/ConfigurationVO.java b/framework/config/src/main/java/org/apache/cloudstack/framework/config/impl/ConfigurationVO.java index 7cd9afb83846..08ec9bfe83f8 100644 --- a/framework/config/src/main/java/org/apache/cloudstack/framework/config/impl/ConfigurationVO.java +++ b/framework/config/src/main/java/org/apache/cloudstack/framework/config/impl/ConfigurationVO.java @@ -27,6 +27,7 @@ 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 Date getUpdated() { 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; + } + } diff --git a/framework/config/src/main/resources/META-INF/cloudstack/system/spring-framework-config-system-context.xml b/framework/config/src/main/resources/META-INF/cloudstack/system/spring-framework-config-system-context.xml index be116fc5e775..1a3bcf441e47 100644 --- a/framework/config/src/main/resources/META-INF/cloudstack/system/spring-framework-config-system-context.xml +++ b/framework/config/src/main/resources/META-INF/cloudstack/system/spring-framework-config-system-context.xml @@ -45,6 +45,12 @@ + + + + diff --git a/framework/config/src/test/java/org/apache/cloudstack/framework/config/ConfigKeyTest.java b/framework/config/src/test/java/org/apache/cloudstack/framework/config/ConfigKeyTest.java index 01a3509d7f02..a3a8aadfa604 100644 --- a/framework/config/src/test/java/org/apache/cloudstack/framework/config/ConfigKeyTest.java +++ b/framework/config/src/test/java/org/apache/cloudstack/framework/config/ConfigKeyTest.java @@ -39,12 +39,12 @@ public void testEquals() { @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)); } } diff --git a/framework/config/src/test/java/org/apache/cloudstack/framework/config/impl/ConfigDepotAdminTest.java b/framework/config/src/test/java/org/apache/cloudstack/framework/config/impl/ConfigDepotAdminTest.java index da76804eb779..46490335bf5c 100644 --- a/framework/config/src/test/java/org/apache/cloudstack/framework/config/impl/ConfigDepotAdminTest.java +++ b/framework/config/src/test/java/org/apache/cloudstack/framework/config/impl/ConfigDepotAdminTest.java @@ -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 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.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 DynamicIntCK = new ConfigKey(Integer.class, "dynIntKey", "Advance", "10", "Test Key", true); private final static ConfigKey StaticIntCK = new ConfigKey(Integer.class, "statIntKey", "Advance", "10", "Test Key", false); + private final static ConfigKey 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 void setUp() throws Exception { MockitoAnnotations.initMocks(this); _depotAdmin = new ConfigDepotImpl(); _depotAdmin._configDao = _configDao; + _depotAdmin._configGroupDao = _configGroupDao; + _depotAdmin._configSubGroupDao = _configSubGroupDao; _depotAdmin._configurables = new ArrayList(); _depotAdmin._configurables.add(_configurable); _depotAdmin._scopedStorages = new ArrayList(); @@ -80,22 +97,58 @@ public void testAutoPopulation() { 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 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 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()); } } diff --git a/framework/jobs/src/test/java/org/apache/cloudstack/framework/jobs/AsyncJobManagerTestConfiguration.java b/framework/jobs/src/test/java/org/apache/cloudstack/framework/jobs/AsyncJobManagerTestConfiguration.java index a70913cd983b..91c2cc3a760b 100644 --- a/framework/jobs/src/test/java/org/apache/cloudstack/framework/jobs/AsyncJobManagerTestConfiguration.java +++ b/framework/jobs/src/test/java/org/apache/cloudstack/framework/jobs/AsyncJobManagerTestConfiguration.java @@ -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 ConfigurationDao configDao() { return new ConfigurationDaoImpl(); } + @Bean + public ConfigurationGroupDao configGroupDao() { + return new ConfigurationGroupDaoImpl(); + } + + @Bean + public ConfigurationSubGroupDao configSubGroupDao() { + return new ConfigurationSubGroupDaoImpl(); + } + @Bean public ScopedConfigStorage scopedConfigStorage() { return new StoragePoolDetailsDaoImpl(); diff --git a/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/ExtensionRegistry.java b/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/ExtensionRegistry.java index eda9a231fe52..a077bc8f4f01 100644 --- a/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/ExtensionRegistry.java +++ b/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/ExtensionRegistry.java @@ -136,14 +136,14 @@ public ConfigKey[] getConfigKeys() { List> result = new ArrayList>(); if (orderConfigKey != null && orderConfigKeyObj == null) { - orderConfigKeyObj = new ConfigKey("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("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) { diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterService.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterService.java index 138889a2fb37..420f35527207 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterService.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterService.java @@ -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 KubernetesClusterStartTimeout = new ConfigKey("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 KubernetesClusterScaleTimeout = new ConfigKey("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 KubernetesClusterUpgradeTimeout = new ConfigKey("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 KubernetesClusterExperimentalFeaturesEnabled = new ConfigKey("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 KubernetesMaxClusterSize = new ConfigKey("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); diff --git a/plugins/integrations/prometheus/src/main/java/org/apache/cloudstack/metrics/PrometheusExporterServer.java b/plugins/integrations/prometheus/src/main/java/org/apache/cloudstack/metrics/PrometheusExporterServer.java index 0ec83066f619..f0f5e3c6987b 100644 --- a/plugins/integrations/prometheus/src/main/java/org/apache/cloudstack/metrics/PrometheusExporterServer.java +++ b/plugins/integrations/prometheus/src/main/java/org/apache/cloudstack/metrics/PrometheusExporterServer.java @@ -26,10 +26,10 @@ public interface PrometheusExporterServer extends Manager { "Enable the prometheus exporter plugin, management server restart needed.", false); ConfigKey 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 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 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); diff --git a/plugins/network-elements/juniper-contrail/src/test/java/org/apache/cloudstack/network/contrail/management/IntegrationTestConfiguration.java b/plugins/network-elements/juniper-contrail/src/test/java/org/apache/cloudstack/network/contrail/management/IntegrationTestConfiguration.java index 989d74b6c950..d80d812916c5 100644 --- a/plugins/network-elements/juniper-contrail/src/test/java/org/apache/cloudstack/network/contrail/management/IntegrationTestConfiguration.java +++ b/plugins/network-elements/juniper-contrail/src/test/java/org/apache/cloudstack/network/contrail/management/IntegrationTestConfiguration.java @@ -21,6 +21,8 @@ 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 @@ 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, diff --git a/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/saml/SAML2AuthManager.java b/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/saml/SAML2AuthManager.java index c65f8b3ad1f9..e52a7e32695f 100644 --- a/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/saml/SAML2AuthManager.java +++ b/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/saml/SAML2AuthManager.java @@ -61,8 +61,8 @@ public interface SAML2AuthManager extends PluggableAPIAuthenticator, PluggableSe public static final ConfigKey SAMLDefaultIdentityProviderId = new ConfigKey("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 SAMLSignatureAlgorithm = new ConfigKey("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 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 SAMLAppendDomainSuffix = new ConfigKey("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); diff --git a/server/conf/migration-components.xml b/server/conf/migration-components.xml index 6b107dfc31b3..168b72abf54e 100644 --- a/server/conf/migration-components.xml +++ b/server/conf/migration-components.xml @@ -22,6 +22,8 @@ under the License. + + diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index 437ff05d28d0..76397c426c13 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -70,7 +70,9 @@ 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; @@ -175,6 +177,8 @@ 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 ServiceOfferingResponse createServiceOfferingResponse(ServiceOffering off public ConfigurationResponse createConfigurationResponse(Configuration cfg) { ConfigurationResponse cfgResponse = new ConfigurationResponse(); cfgResponse.setCategory(cfg.getCategory()); + Pair 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 ConfigurationResponse createConfigurationResponse(Configuration cfg) { } 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 subgroups = _configMgr.getConfigurationSubGroups(cfgGroup.getId()); + List 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(); diff --git a/server/src/main/java/com/cloud/configuration/Config.java b/server/src/main/java/com/cloud/configuration/Config.java index c07115d87c29..aeefdb58f8c3 100644 --- a/server/src/main/java/com/cloud/configuration/Config.java +++ b/server/src/main/java/com/cloud/configuration/Config.java @@ -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> s_scopeLevelConfigsMap = new HashMap>(); 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 @@ private Config(String category, Class componentClass, Class type, String n _description = description; _range = range; _scope = ConfigKey.Scope.Global.toString(); + _kind = kind; + _options = options; } public String getCategory() { @@ -1868,6 +1910,17 @@ public String getScope() { 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 String getComponent() { } } - public String[] getRange() { + public String getRange() { return _range; } diff --git a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java index 9bb226c59910..7373e1c477ee 100644 --- a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java @@ -97,6 +97,10 @@ 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 @@ private String validateConfigurationValue(final String name, String value, final 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); @@ -7562,6 +7570,91 @@ public ConfigKey[] getConfigKeys() { }; } + @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 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(groupName, subGroupName); + } + + @Override + public List getConfigurationSubGroups(final Long groupId) { + List configSubGroups = _configSubGroupDao.findByGroup(groupId); + return configSubGroups; + } + static class ParamCountPair { private Long id; private int paramCount; diff --git a/server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManager.java b/server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManager.java index efd15a4283bc..8b2fd81bb480 100644 --- a/server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManager.java +++ b/server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManager.java @@ -63,7 +63,7 @@ public interface VirtualNetworkApplianceManager extends Manager, VirtualNetworkA static final ConfigKey RouterTemplateOvm3 = new ConfigKey(String.class, RouterTemplateOvm3CK, "Advanced", "SystemVM Template (Ovm3)", "Name of the default router template on Ovm3.", true, ConfigKey.Scope.Zone, null); - static final ConfigKey SetServiceMonitor = new ConfigKey(String.class, SetServiceMonitorCK, "Advanced", "true", + static final ConfigKey SetServiceMonitor = new ConfigKey(Boolean.class, SetServiceMonitorCK, "Advanced", "true", "service monitoring in router enable/disable option, default true", true, ConfigKey.Scope.Zone, null); static final ConfigKey RouterAlertsCheckInterval = new ConfigKey(Integer.class, RouterAlertsCheckIntervalCK, "Advanced", "1800", @@ -84,36 +84,36 @@ public interface VirtualNetworkApplianceManager extends Manager, VirtualNetworkA true, ConfigKey.Scope.Global, null); static final ConfigKey RouterHealthChecksBasicInterval = new ConfigKey(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 RouterHealthChecksAdvancedInterval = new ConfigKey(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 RouterHealthChecksConfigRefreshInterval = new ConfigKey(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 RouterHealthChecksResultFetchInterval = new ConfigKey(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 RouterHealthChecksFailuresToRecreateVr = new ConfigKey(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 systemvm’s /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 RouterHealthChecksToExclude = new ConfigKey(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 RouterHealthChecksFreeDiskSpaceThreshold = new ConfigKey(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 RouterHealthChecksMaxCpuUsageThreshold = new ConfigKey(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 RouterHealthChecksMaxMemoryUsageThreshold = new ConfigKey(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 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. " + diff --git a/server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index 2cd377a04f9d..2da864bc955f 100644 --- a/server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -1862,10 +1862,10 @@ protected void getRouterAlerts() { 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()); diff --git a/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java b/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java index 96d0f2ac3c38..56f20b4fa7e4 100644 --- a/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java +++ b/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java @@ -98,6 +98,7 @@ 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 void persistDefaultValues() throws InternalErrorException { String description = c.getDescription(); ConfigurationVO configVO = new ConfigurationVO(category, instance, component, name, value, description); configVO.setDefaultValue(value); + Pair 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); } } diff --git a/server/src/main/java/com/cloud/server/ManagementServerImpl.java b/server/src/main/java/com/cloud/server/ManagementServerImpl.java index 5fc34659992e..25e969ac5630 100644 --- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java +++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java @@ -269,6 +269,7 @@ 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; @@ -774,6 +775,7 @@ 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; @@ -781,6 +783,10 @@ 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 Pair, Integer> searchForConfigurations(fina 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 Pair, Integer> searchForConfigurations(fina 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 Pair, Integer> searchForConfigurations(fina return new Pair, Integer>(result.first(), result.second()); } + @Override + public Pair, Integer> listConfigurationGroups(ListCfgGroupsByCmd cmd) { + final Filter searchFilter = new Filter(ConfigurationGroupVO.class, "precedence", true, null, null); + final SearchCriteria sc = _configGroupDao.createSearchCriteria(); + + final String groupName = cmd.getGroupName(); + if (StringUtils.isNotBlank(groupName)) { + sc.addAnd("name", SearchCriteria.Op.EQ, groupName); + } + + final Pair, Integer> result = _configGroupDao.searchAndCount(sc, searchFilter); + return new Pair, Integer>(result.first(), result.second()); + } + @Override public Pair, Integer> searchForIPAddresses(final ListPublicIpAddressesCmd cmd) { final Long associatedNetworkId = cmd.getAssociatedNetworkId(); @@ -3221,6 +3271,7 @@ public List> getCommands() { 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); diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 72fe27a48d53..de442ffce2df 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -627,14 +627,14 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir private static final ConfigKey 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 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 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 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 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 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 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 VmDestroyForcestop = new ConfigKey("Advanced", Boolean.class, "vm.destroy.forcestop", "false", "On destroy, force-stop takes this value ", true); diff --git a/server/src/main/java/org/apache/cloudstack/agent/lb/IndirectAgentLBServiceImpl.java b/server/src/main/java/org/apache/cloudstack/agent/lb/IndirectAgentLBServiceImpl.java index b93f2b340d9b..1414a94907e6 100644 --- a/server/src/main/java/org/apache/cloudstack/agent/lb/IndirectAgentLBServiceImpl.java +++ b/server/src/main/java/org/apache/cloudstack/agent/lb/IndirectAgentLBServiceImpl.java @@ -50,10 +50,10 @@ public class IndirectAgentLBServiceImpl extends ComponentLifecycleBase implements IndirectAgentLB, Configurable { public static final Logger LOG = Logger.getLogger(IndirectAgentLBServiceImpl.class); - public static final ConfigKey IndirectAgentLBAlgorithm = new ConfigKey<>("Advanced", String.class, - "indirect.agent.lb.algorithm", "static", + public static final ConfigKey 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 IndirectAgentLBCheckInterval = new ConfigKey<>("Advanced", Long.class, "indirect.agent.lb.check.interval", "0", diff --git a/server/src/main/java/org/apache/cloudstack/diagnostics/DiagnosticsServiceImpl.java b/server/src/main/java/org/apache/cloudstack/diagnostics/DiagnosticsServiceImpl.java index 3777996c7ebb..4ebbc91d5e53 100644 --- a/server/src/main/java/org/apache/cloudstack/diagnostics/DiagnosticsServiceImpl.java +++ b/server/src/main/java/org/apache/cloudstack/diagnostics/DiagnosticsServiceImpl.java @@ -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 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 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 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 DiskQuotaPercentageThreshold = new ConfigKey<>("Advanced", Double.class, "diagnostics.data.disable.threshold", "0.9", "Sets the secondary storage disk utilisation percentage for file retrieval. " + diff --git a/server/src/main/java/org/apache/cloudstack/diagnostics/fileprocessor/DiagnosticsFilesList.java b/server/src/main/java/org/apache/cloudstack/diagnostics/fileprocessor/DiagnosticsFilesList.java index cd9baa9f5d28..420edb640dcd 100644 --- a/server/src/main/java/org/apache/cloudstack/diagnostics/fileprocessor/DiagnosticsFilesList.java +++ b/server/src/main/java/org/apache/cloudstack/diagnostics/fileprocessor/DiagnosticsFilesList.java @@ -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 SystemVMDefaultSupportedFiles = new ConfigKey<>("Advanced", String.class, - "diagnostics.data.systemvm.defaults", "iptables,ipaddr,iprule,iproute,/etc/cloudstack-release," + + ConfigKey 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 RouterDefaultSupportedFiles = new ConfigKey<>("Advanced", String.class, - "diagnostics.data.router.defaults", "iptables,ipaddr,iprule,iproute,/etc/cloudstack-release," + + ConfigKey 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 generateFileList(); } diff --git a/server/src/test/async-job-component.xml b/server/src/test/async-job-component.xml index 413194c67180..d1b0ca600d77 100644 --- a/server/src/test/async-job-component.xml +++ b/server/src/test/async-job-component.xml @@ -81,6 +81,8 @@ + + diff --git a/server/src/test/java/com/cloud/configuration/ConfigTest.java b/server/src/test/java/com/cloud/configuration/ConfigTest.java index 1c9b2e71972c..ca0c26dd2228 100644 --- a/server/src/test/java/com/cloud/configuration/ConfigTest.java +++ b/server/src/test/java/com/cloud/configuration/ConfigTest.java @@ -24,7 +24,7 @@ public void configTestIntegerRange() { 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) { diff --git a/server/src/test/java/com/cloud/network/security/SecurityGroupManagerTestConfiguration.java b/server/src/test/java/com/cloud/network/security/SecurityGroupManagerTestConfiguration.java index a5940cd779ac..f6136e31049b 100644 --- a/server/src/test/java/com/cloud/network/security/SecurityGroupManagerTestConfiguration.java +++ b/server/src/test/java/com/cloud/network/security/SecurityGroupManagerTestConfiguration.java @@ -31,6 +31,8 @@ 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.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, diff --git a/server/src/test/java/com/cloud/vm/FirstFitPlannerTest.java b/server/src/test/java/com/cloud/vm/FirstFitPlannerTest.java index bb6487fb1181..841658697789 100644 --- a/server/src/test/java/com/cloud/vm/FirstFitPlannerTest.java +++ b/server/src/test/java/com/cloud/vm/FirstFitPlannerTest.java @@ -37,6 +37,8 @@ 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 ConfigurationDao configurationDao() { 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); diff --git a/server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java b/server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java index 088f4e67e8fc..a8436f95876b 100644 --- a/server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java +++ b/server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java @@ -56,6 +56,7 @@ 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 Pair resetConfiguration(ResetCfgCmd cmd) throws In return null; } + @Override + public String getConfigurationType(String configName) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Pair getConfigurationGroupAndSubGroup(String configName) { + // TODO Auto-generated method stub + return null; + } + + @Override + public List getConfigurationSubGroups(Long groupId) { + // TODO Auto-generated method stub + return null; + } } diff --git a/server/src/test/java/com/cloud/vpc/VpcTestConfiguration.java b/server/src/test/java/com/cloud/vpc/VpcTestConfiguration.java index 2dfbb3799b8d..285a7ae45237 100644 --- a/server/src/test/java/com/cloud/vpc/VpcTestConfiguration.java +++ b/server/src/test/java/com/cloud/vpc/VpcTestConfiguration.java @@ -30,6 +30,8 @@ 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.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, diff --git a/server/src/test/java/com/cloud/vpc/dao/MockConfigurationDaoImpl.java b/server/src/test/java/com/cloud/vpc/dao/MockConfigurationDaoImpl.java index 94f8d659d288..d82a5d8e75a7 100644 --- a/server/src/test/java/com/cloud/vpc/dao/MockConfigurationDaoImpl.java +++ b/server/src/test/java/com/cloud/vpc/dao/MockConfigurationDaoImpl.java @@ -19,7 +19,6 @@ import java.util.HashMap; import java.util.Map; - import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.framework.config.impl.ConfigurationVO; diff --git a/server/src/test/resources/createNetworkOffering.xml b/server/src/test/resources/createNetworkOffering.xml index 214fa29cd75f..0f558d11a7ae 100644 --- a/server/src/test/resources/createNetworkOffering.xml +++ b/server/src/test/resources/createNetworkOffering.xml @@ -70,4 +70,6 @@ + + diff --git a/services/secondary-storage/controller/src/main/java/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerImpl.java b/services/secondary-storage/controller/src/main/java/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerImpl.java index 9c9b4f56324b..3f7ca82588c5 100644 --- a/services/secondary-storage/controller/src/main/java/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerImpl.java +++ b/services/secondary-storage/controller/src/main/java/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerImpl.java @@ -267,7 +267,7 @@ public class SecondaryStorageManagerImpl extends ManagerBase implements Secondar private final GlobalLock _allocLock = GlobalLock.getInternLock(getAllocLockName()); static final ConfigKey NTPServerConfig = new ConfigKey(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 MaxNumberOfSsvmsForMigration = new ConfigKey("Advanced", Integer.class, "max.ssvm.count", "5", "Number of additional SSVMs to handle migration of data objects concurrently", true, ConfigKey.Scope.Global); diff --git a/test/integration/smoke/test_global_settings.py b/test/integration/smoke/test_global_settings.py index c0dec787cc4e..2018384ab3a2 100644 --- a/test/integration/smoke/test_global_settings.py +++ b/test/integration/smoke/test_global_settings.py @@ -75,3 +75,109 @@ def tearDown(self): 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)) diff --git a/tools/marvin/marvin/lib/base.py b/tools/marvin/marvin/lib/base.py index c9633210aa0c..22864f9cf7b0 100755 --- a/tools/marvin/marvin/lib/base.py +++ b/tools/marvin/marvin/lib/base.py @@ -4653,6 +4653,14 @@ def listCapabilities(cls, apiclient, **kwargs): 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""" diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json index 2c00a45733b6..978f8c94a064 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -761,7 +761,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", @@ -2573,6 +2573,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.", @@ -2695,6 +2696,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", diff --git a/ui/src/components/view/ListView.vue b/ui/src/components/view/ListView.vue index 6111300881c6..c1508e8211e8 100644 --- a/ui/src/components/view/ListView.vue +++ b/ui/src/components/view/ListView.vue @@ -105,6 +105,7 @@ {{ text }} {{ text }} + {{ text }} {{ $t(text.toLowerCase()) }} {{ $t(text.toLowerCase()) }} diff --git a/ui/src/config/section/config.js b/ui/src/config/section/config.js index 5dc3a76e88a9..c7e097277e26 100644 --- a/ui/src/config/section/config.js +++ b/ui/src/config/section/config.js @@ -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', diff --git a/ui/src/style/vars.less b/ui/src/style/vars.less index 0c3bff4cbda0..67a584453d1a 100644 --- a/ui/src/style/vars.less +++ b/ui/src/style/vars.less @@ -400,6 +400,10 @@ a { background-color: #f9f9f9; } +.child-row { + background-color: #f5f5f5; +} + .tag-disabled-input, .btn-add-tag { background-color: #fff; } diff --git a/ui/src/views/setting/ConfigurationHierarchy.vue b/ui/src/views/setting/ConfigurationHierarchy.vue new file mode 100644 index 000000000000..c3cfddb763d6 --- /dev/null +++ b/ui/src/views/setting/ConfigurationHierarchy.vue @@ -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. + + + + diff --git a/ui/src/views/setting/ConfigurationTab.vue b/ui/src/views/setting/ConfigurationTab.vue new file mode 100644 index 000000000000..e327bbe8e387 --- /dev/null +++ b/ui/src/views/setting/ConfigurationTab.vue @@ -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. + + + + + + diff --git a/ui/src/views/setting/ConfigurationTable.vue b/ui/src/views/setting/ConfigurationTable.vue new file mode 100644 index 000000000000..d81825143766 --- /dev/null +++ b/ui/src/views/setting/ConfigurationTable.vue @@ -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. + + + + + + diff --git a/ui/src/views/setting/ConfigurationValue.vue b/ui/src/views/setting/ConfigurationValue.vue new file mode 100644 index 000000000000..0069896f7a56 --- /dev/null +++ b/ui/src/views/setting/ConfigurationValue.vue @@ -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. + + + + + diff --git a/usage/src/test/java/com/cloud/usage/UsageManagerTestConfiguration.java b/usage/src/test/java/com/cloud/usage/UsageManagerTestConfiguration.java index 26b610656c78..d0e153b0b835 100644 --- a/usage/src/test/java/com/cloud/usage/UsageManagerTestConfiguration.java +++ b/usage/src/test/java/com/cloud/usage/UsageManagerTestConfiguration.java @@ -30,6 +30,8 @@ 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 @@ @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},