You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by GitBox <gi...@apache.org> on 2022/04/05 14:15:42 UTC

[GitHub] [cloudstack] nvazquez commented on a diff in pull request #5797: [WIP] Improve global settings UI to be more intuitive/logical

nvazquez commented on code in PR #5797:
URL: https://github.com/apache/cloudstack/pull/5797#discussion_r842784512


##########
api/src/main/java/org/apache/cloudstack/api/command/admin/config/ListCfgGroupsByCmd.java:
##########
@@ -0,0 +1,80 @@
+// 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.17.0")
+public class ListCfgGroupsByCmd extends BaseListCmd {
+    public static final Logger s_logger = Logger.getLogger(ListCfgGroupsByCmd.class.getName());
+
+    public static final String APINAME = "listConfigurationGroups";
+
+    // ///////////////////////////////////////////////////
+    // ////////////// API parameters /////////////////////
+    // ///////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.GROUP, type = CommandType.STRING, description = "lists configuration group by group name")
+    private String groupName;
+
+    // ///////////////////////////////////////////////////
+    // ///////////////// Accessors ///////////////////////
+    // ///////////////////////////////////////////////////
+
+    public String getGroupName() {
+        return groupName;
+    }
+
+    // ///////////////////////////////////////////////////
+    // ///////////// API Implementation///////////////////
+    // ///////////////////////////////////////////////////
+
+    @Override
+    public String getCommandName() {
+        return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
+    }
+
+    @Override
+    public void execute() {
+        Pair<List<? extends ConfigurationGroup>, Integer> result = _mgr.listConfigurationGroups(this);
+        ListResponse<ConfigurationGroupResponse> response = new ListResponse<ConfigurationGroupResponse>();
+        List<ConfigurationGroupResponse> configGroupResponses = new ArrayList<ConfigurationGroupResponse>();
+        for (ConfigurationGroup cfgGroup : result.first()) {
+            ConfigurationGroupResponse cfgGroupResponse = _responseGenerator.createConfigurationGroupResponse(cfgGroup);
+            cfgGroupResponse.setObjectName("configurationgroup");

Review Comment:
   Can include this into the `createConfigurationGroupResponse` method



##########
api/src/main/java/org/apache/cloudstack/api/response/ConfigurationResponse.java:
##########
@@ -52,6 +60,22 @@
     @Param(description = "true if the configuration is dynamic")
     private boolean isDynamic;
 
+    @SerializedName(ApiConstants.COMPONENT)
+    @Param(description = "the component of the configuration", since = "4.17.0")
+    private String component;
+
+    @SerializedName("parent")

Review Comment:
   ```suggestion
       @SerializedName(ApiConstants.PARENT)
   ```



##########
plugins/hypervisors/simulator/src/main/java/com/cloud/simulator/dao/MockConfigurationSubGroupDaoImpl.java:
##########
@@ -0,0 +1,55 @@
+// 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.simulator.dao;
+
+import java.util.List;
+
+import org.springframework.stereotype.Component;
+
+import com.cloud.simulator.MockConfigurationSubGroupVO;
+import com.cloud.utils.db.GenericDaoBase;
+
+@Component
+public class MockConfigurationSubGroupDaoImpl extends GenericDaoBase<MockConfigurationSubGroupVO, Long> implements MockConfigurationSubGroupDao {

Review Comment:
   And here



##########
plugins/hypervisors/simulator/src/main/java/com/cloud/simulator/MockConfigurationGroupVO.java:
##########
@@ -0,0 +1,51 @@
+// 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.simulator;
+
+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.api.InternalIdentity;
+
+@Entity
+@Table(name = "mockconfigurationgroup")

Review Comment:
   Also here



##########
server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java:
##########
@@ -7311,6 +7319,82 @@ public String getConfigComponentName() {
         };
     }
 
+    @Override
+    public String getConfigurationType(final String configName) {
+        final ConfigurationVO cfg = _configDao.findByName(configName);
+        if (cfg == null) {
+            s_logger.error("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 null;
+            }
+            type = configKey.type();
+        } else {
+            type = c.getType();
+        }
+
+        return getInputType(type);
+    }
+
+    private String getInputType(Class<?> type) {
+        if (type == null) {
+            return Configuration.ValueType.String.name();
+        }
+
+        if (type == String.class || type == Character.class) {
+            return Configuration.ValueType.String.name();
+        } else if (type == Integer.class || type == Long.class || type == Short.class) {
+            return Configuration.ValueType.Number.name();
+        } else if (type == Float.class || type == Double.class) {
+            return Configuration.ValueType.Decimal.name();
+        } else if (type == Boolean.class) {
+            return Configuration.ValueType.Boolean.name();
+        //} else if (type == Date.class) {

Review Comment:
   Can be removed



##########
engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41610to41700.java:
##########
@@ -83,4 +92,99 @@ public void updateSystemVmTemplates(Connection conn) {
             throw new CloudRuntimeException("Failed to find / register SystemVM template(s)");
         }
     }
+
+    private void updateConfigurationGroups(Connection conn) {
+        LOG.debug("Updating configuration groups");
+        try {
+            String stmt = "SELECT name FROM `cloud`.`configuration`";
+            PreparedStatement pstmt = conn.prepareStatement(stmt);
+            ResultSet rs = pstmt.executeQuery();
+
+            while (rs.next()) {
+                String configName = rs.getString(1);
+                if (StringUtils.isBlank(configName)) {
+                    continue;
+                }
+
+                // Get words from the dot notation in the configuration
+                String[] nameWords = configName.split("\\.");
+                if (nameWords.length <= 0) {
+                    continue;
+                }
+
+                for (int index = 0; index < nameWords.length; index++) {
+                    Pair<Long, Long> configGroupAndSubGroup = getConfigurationGroupAndSubGroup(conn, nameWords[index]);
+                    if (configGroupAndSubGroup.first() != 1 && configGroupAndSubGroup.second() != 1) {
+                        stmt = "UPDATE `cloud`.`configuration` SET group_id = ?, subgroup_id = ? WHERE name = ?";
+                        pstmt = conn.prepareStatement(stmt);
+                        pstmt.setLong(1, configGroupAndSubGroup.first());

Review Comment:
   Can maybe add an NPE check before setting these parameters?



##########
engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41610to41700.java:
##########
@@ -83,4 +92,99 @@ public void updateSystemVmTemplates(Connection conn) {
             throw new CloudRuntimeException("Failed to find / register SystemVM template(s)");
         }
     }
+
+    private void updateConfigurationGroups(Connection conn) {
+        LOG.debug("Updating configuration groups");
+        try {
+            String stmt = "SELECT name FROM `cloud`.`configuration`";
+            PreparedStatement pstmt = conn.prepareStatement(stmt);
+            ResultSet rs = pstmt.executeQuery();
+
+            while (rs.next()) {
+                String configName = rs.getString(1);
+                if (StringUtils.isBlank(configName)) {
+                    continue;
+                }
+
+                // Get words from the dot notation in the configuration
+                String[] nameWords = configName.split("\\.");
+                if (nameWords.length <= 0) {
+                    continue;
+                }
+
+                for (int index = 0; index < nameWords.length; index++) {
+                    Pair<Long, Long> configGroupAndSubGroup = getConfigurationGroupAndSubGroup(conn, nameWords[index]);
+                    if (configGroupAndSubGroup.first() != 1 && configGroupAndSubGroup.second() != 1) {
+                        stmt = "UPDATE `cloud`.`configuration` SET group_id = ?, subgroup_id = ? WHERE name = ?";
+                        pstmt = conn.prepareStatement(stmt);
+                        pstmt.setLong(1, configGroupAndSubGroup.first());
+                        pstmt.setLong(2, configGroupAndSubGroup.second());
+                        pstmt.setString(3, configName);
+                        pstmt.executeUpdate();
+                        break;
+                    }
+                }
+            }
+
+            rs.close();
+            pstmt.close();
+            LOG.debug("Successfully updated configuration groups.");
+        } catch (SQLException e) {
+            String errorMsg = "Failed to update configuration groups due to " + e.getMessage();
+            LOG.error(errorMsg, e);
+            throw new CloudRuntimeException(errorMsg, e);
+        }
+    }
+
+    private Pair<Long, Long> getConfigurationGroupAndSubGroup(Connection conn, String name) {
+        Long subGroupId = 1L;
+        Long groupId = 1L;
+        try {
+            String stmt = "SELECT id, group_id FROM `cloud`.`configuration_subgroup` WHERE name = ?";
+            PreparedStatement pstmt = conn.prepareStatement(stmt);
+            pstmt.setString(1, name);
+            ResultSet rs = pstmt.executeQuery();
+            if (rs.next()) {
+                subGroupId = rs.getLong(1);
+                groupId = rs.getLong(2);
+            } else {
+                // Try with keywords in the configuration subgroup
+                stmt = "SELECT id, group_id, keywords FROM `cloud`.`configuration_subgroup` WHERE keywords IS NOT NULL";
+                pstmt = conn.prepareStatement(stmt);
+                ResultSet rsConfigurationSubGroups = pstmt.executeQuery();
+                while (rsConfigurationSubGroups.next()) {
+                    Long keywordsSubGroupId = rsConfigurationSubGroups.getLong(1);
+                    Long keywordsGroupId = rsConfigurationSubGroups.getLong(2);
+                    String keywords = rsConfigurationSubGroups.getString(3);
+                    if(StringUtils.isBlank(keywords)) {
+                        continue;
+                    }
+
+                    String[] configKeywords = keywords.split(",");
+                    if (configKeywords.length <= 0) {
+                        continue;
+                    }
+
+                    List<String> keywordsList = Arrays.asList(configKeywords);
+                    for (String configKeyword : keywordsList) {
+                        if (StringUtils.isNotBlank(configKeyword)) {
+                            configKeyword = configKeyword.strip();
+                            if (configKeyword.equalsIgnoreCase(name)) {
+                                subGroupId = keywordsSubGroupId;
+                                groupId = keywordsGroupId;
+                                return new Pair<Long, Long>(groupId, subGroupId);
+                            }
+                        }
+                    }
+                }
+                rsConfigurationSubGroups.close();
+            }
+            rs.close();
+            pstmt.close();
+        } catch (SQLException e) {
+            LOG.error("Failed to get configuration subgroup due to " + e.getMessage(), e);

Review Comment:
   Should we do something else in case or errors apart from logging? What would be the consequences of having incorrect configurations (do not have group or/and subgroup correctly set)?



##########
plugins/hypervisors/simulator/src/main/java/com/cloud/simulator/dao/MockConfigurationSubGroupDao.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 com.cloud.simulator.dao;
+
+import java.util.List;
+
+import com.cloud.simulator.MockConfigurationSubGroupVO;
+import com.cloud.utils.db.GenericDao;
+
+public interface MockConfigurationSubGroupDao extends GenericDao<MockConfigurationSubGroupVO, Long> {

Review Comment:
   Same here, then your VO mock classes won't be needed



##########
plugins/hypervisors/simulator/src/main/java/com/cloud/simulator/dao/MockConfigurationGroupDaoImpl.java:
##########
@@ -0,0 +1,33 @@
+// 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.simulator.dao;
+
+import org.springframework.stereotype.Component;
+
+import com.cloud.simulator.MockConfigurationGroupVO;
+import com.cloud.utils.db.GenericDaoBase;
+
+@Component
+public class MockConfigurationGroupDaoImpl extends GenericDaoBase<MockConfigurationGroupVO, Long> implements MockConfigurationGroupDao {

Review Comment:
   ```suggestion
   public class MockConfigurationGroupDaoImpl extends GenericDaoBase<ConfigurationGroupVO, Long> implements MockConfigurationGroupDao {
   ```



##########
plugins/hypervisors/simulator/src/main/java/com/cloud/simulator/MockConfigurationSubGroupVO.java:
##########
@@ -0,0 +1,51 @@
+// 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.simulator;
+
+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.api.InternalIdentity;
+
+@Entity
+@Table(name = "mockconfigurationsubgroup")
+public class MockConfigurationSubGroupVO implements InternalIdentity {

Review Comment:
   This will create a new table for a mock? Won't needed IMO



##########
server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java:
##########
@@ -7311,6 +7319,82 @@ public String getConfigComponentName() {
         };
     }
 
+    @Override
+    public String getConfigurationType(final String configName) {
+        final ConfigurationVO cfg = _configDao.findByName(configName);
+        if (cfg == null) {
+            s_logger.error("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 null;
+            }
+            type = configKey.type();
+        } else {
+            type = c.getType();
+        }
+
+        return getInputType(type);
+    }
+
+    private String getInputType(Class<?> type) {
+        if (type == null) {
+            return Configuration.ValueType.String.name();
+        }
+
+        if (type == String.class || type == Character.class) {
+            return Configuration.ValueType.String.name();
+        } else if (type == Integer.class || type == Long.class || type == Short.class) {
+            return Configuration.ValueType.Number.name();
+        } else if (type == Float.class || type == Double.class) {
+            return Configuration.ValueType.Decimal.name();
+        } else if (type == Boolean.class) {
+            return Configuration.ValueType.Boolean.name();
+        //} else if (type == Date.class) {
+        //    return Configuration.InputType.Date.name();
+        } else {
+            return Configuration.ValueType.String.name();
+        }
+    }
+
+    @Override
+    public Pair<String, String> getConfigurationGroupAndSubGroup(final String configName) {
+        if (StringUtils.isNotBlank(configName)) {
+            final ConfigurationVO cfg = _configDao.findByName(configName);
+            if (cfg != null) {
+                ConfigurationSubGroupVO configSubGroup = _configSubGroupDao.findById(cfg.getSubGroupId());
+                if (configSubGroup != null) {
+                    String subGroupName = configSubGroup.getName();
+                    ConfigurationGroupVO configGroup = _configGroupDao.findById(configSubGroup.getGroupId());
+                    String groupName = configGroup != null ? configGroup.getName() : "Miscellaneous";
+                    return new Pair<String, String>(groupName, subGroupName);
+                }
+            } else {
+                s_logger.warn("Configuration " + configName + " not found");
+            }
+        }
+
+        s_logger.debug("Returning default configuration group for config: " + configName);

Review Comment:
   What about the case configName is null or empty? Should this still return a pair or fail?



##########
server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java:
##########
@@ -7311,6 +7319,82 @@ public String getConfigComponentName() {
         };
     }
 
+    @Override
+    public String getConfigurationType(final String configName) {
+        final ConfigurationVO cfg = _configDao.findByName(configName);
+        if (cfg == null) {
+            s_logger.error("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 null;

Review Comment:
   Not sure why in this case null is returned, but when cfg == null then ValueType.String is returned, can maybe add a javadoc to this method?



##########
engine/schema/src/main/resources/META-INF/db/schema-41610to41700.sql:
##########
@@ -655,3 +653,107 @@ INSERT INTO `cloud`.`user_vm_details`(`vm_id`, `name`, `value`)
 ALTER TABLE `cloud`.`kubernetes_cluster` ADD COLUMN `security_group_id` bigint unsigned DEFAULT NULL,
 ADD CONSTRAINT `fk_kubernetes_cluster__security_group_id` FOREIGN KEY `fk_kubernetes_cluster__security_group_id`(`security_group_id`) REFERENCES `security_group`(`id`) ON DELETE CASCADE;
 
+ALTER TABLE `cloud`.`configuration` ADD COLUMN `group_id` bigint(20) unsigned DEFAULT '1' COMMENT 'group id this configuration belongs to';
+ALTER TABLE `cloud`.`configuration` ADD COLUMN `subgroup_id` bigint(20) unsigned DEFAULT '1' COMMENT 'subgroup id this configuration belongs to';
+ALTER TABLE `cloud`.`configuration` ADD COLUMN `parent` VARCHAR(255) DEFAULT NULL COMMENT 'name of the parent configuration if this depends on it';
+ALTER TABLE `cloud`.`configuration` ADD COLUMN `display_text` VARCHAR(255) DEFAULT NULL COMMENT 'Short text about configuration to display to the users';
+
+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);

Review Comment:
   Should we consider idempotent changes on these?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@cloudstack.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org