
To better adjust with scalability improvements, this commit changes the number of dcmanager-audit workers from 4 to 8, dcmanager-state workers from 4 to 8 and increase the limit of maximum number of subclouds in a group and parallel orchestration. Test plan: - PASS: Verify changes in number of workers were applied correctly when the config was not present on dcmanager.conf - PASS: Verify it's possible to create a group with new subcloud limit - PASS: Verify it's possible to create a sw-deploy-strategy with new subcloud limit in parallel Story: 2011106 Task: 51020 Change-Id: I075f06f82639c2d5c2e48257a9cf326b6d105dca Signed-off-by: Victor Romano <victor.gluzromano@windriver.com>
296 lines
11 KiB
Python
296 lines
11 KiB
Python
# Copyright (c) 2017 Ericsson AB.
|
|
# Copyright (c) 2020-2022, 2024 Wind River Systems, Inc.
|
|
# All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
#
|
|
|
|
import http.client as httpclient
|
|
from oslo_config import cfg
|
|
from oslo_db import exception as db_exc
|
|
from oslo_log import log as logging
|
|
from oslo_messaging import RemoteError
|
|
import pecan
|
|
from pecan import expose
|
|
from pecan import request
|
|
|
|
from dcmanager.api.controllers import restcomm
|
|
from dcmanager.api.policies import subcloud_group as subcloud_group_policy
|
|
from dcmanager.api import policy
|
|
from dcmanager.common import consts
|
|
from dcmanager.common.i18n import _
|
|
from dcmanager.common import utils
|
|
from dcmanager.db import api as db_api
|
|
from dcmanager.rpc import client as rpc_client
|
|
|
|
CONF = cfg.CONF
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
SUPPORTED_GROUP_APPLY_TYPES = [
|
|
consts.SUBCLOUD_APPLY_TYPE_PARALLEL,
|
|
consts.SUBCLOUD_APPLY_TYPE_SERIAL,
|
|
]
|
|
|
|
# validation constants for Subcloud Group
|
|
MAX_SUBCLOUD_GROUP_NAME_LEN = 255
|
|
MAX_SUBCLOUD_GROUP_DESCRIPTION_LEN = 255
|
|
MIN_SUBCLOUD_GROUP_MAX_PARALLEL_SUBCLOUDS = 1
|
|
MAX_SUBCLOUD_GROUP_MAX_PARALLEL_SUBCLOUDS = 5000
|
|
|
|
|
|
class SubcloudGroupsController(restcomm.GenericPathController):
|
|
|
|
def __init__(self):
|
|
super(SubcloudGroupsController, self).__init__()
|
|
self.rpc_client = rpc_client.ManagerClient()
|
|
|
|
@expose(generic=True, template="json")
|
|
def index(self):
|
|
# Route the request to specific methods with parameters
|
|
pass
|
|
|
|
def _get_subcloud_list_for_group(self, context, group_id):
|
|
subclouds = db_api.subcloud_get_for_group(context, group_id)
|
|
return utils.subcloud_db_list_to_dict(subclouds)
|
|
|
|
def _get_subcloud_group_list(self, context):
|
|
groups = db_api.subcloud_group_get_all(context)
|
|
subcloud_group_list = []
|
|
|
|
for group in groups:
|
|
group_dict = db_api.subcloud_group_db_model_to_dict(group)
|
|
subcloud_group_list.append(group_dict)
|
|
|
|
result = dict()
|
|
result["subcloud_groups"] = subcloud_group_list
|
|
return result
|
|
|
|
@index.when(method="GET", template="json")
|
|
def get(self, group_ref=None, subclouds=False):
|
|
"""Get details about subcloud group.
|
|
|
|
:param group_ref: ID or name of subcloud group
|
|
"""
|
|
policy.authorize(
|
|
subcloud_group_policy.POLICY_ROOT % "get",
|
|
{},
|
|
restcomm.extract_credentials_for_policy(),
|
|
)
|
|
context = restcomm.extract_context_from_environ()
|
|
|
|
if group_ref is None:
|
|
# List of subcloud groups requested
|
|
return self._get_subcloud_group_list(context)
|
|
|
|
group = utils.subcloud_group_get_by_ref(context, group_ref)
|
|
if group is None:
|
|
pecan.abort(httpclient.NOT_FOUND, _("Subcloud Group not found"))
|
|
if subclouds:
|
|
# Return only the subclouds for this subcloud group
|
|
return self._get_subcloud_list_for_group(context, group.id)
|
|
subcloud_group_dict = db_api.subcloud_group_db_model_to_dict(group)
|
|
return subcloud_group_dict
|
|
|
|
def _validate_description(self, description):
|
|
if not description:
|
|
return False
|
|
if len(description) >= MAX_SUBCLOUD_GROUP_DESCRIPTION_LEN:
|
|
return False
|
|
return True
|
|
|
|
def _validate_update_apply_type(self, update_apply_type):
|
|
if not update_apply_type:
|
|
return False
|
|
if update_apply_type not in SUPPORTED_GROUP_APPLY_TYPES:
|
|
return False
|
|
return True
|
|
|
|
def _validate_max_parallel_subclouds(self, max_parallel_str):
|
|
if not max_parallel_str:
|
|
return False
|
|
try:
|
|
# Check the value is an integer
|
|
val = int(max_parallel_str)
|
|
except ValueError:
|
|
return False
|
|
|
|
# We do not support less than min or greater than max
|
|
if val < MIN_SUBCLOUD_GROUP_MAX_PARALLEL_SUBCLOUDS:
|
|
return False
|
|
if val > MAX_SUBCLOUD_GROUP_MAX_PARALLEL_SUBCLOUDS:
|
|
return False
|
|
return True
|
|
|
|
@index.when(method="POST", template="json")
|
|
def post(self):
|
|
"""Create a new subcloud group."""
|
|
policy.authorize(
|
|
subcloud_group_policy.POLICY_ROOT % "create",
|
|
{},
|
|
restcomm.extract_credentials_for_policy(),
|
|
)
|
|
context = restcomm.extract_context_from_environ()
|
|
|
|
payload = eval(request.body)
|
|
if not payload:
|
|
pecan.abort(httpclient.BAD_REQUEST, _("Body required"))
|
|
|
|
name = payload.get("name")
|
|
description = payload.get("description")
|
|
update_apply_type = payload.get("update_apply_type")
|
|
max_parallel_subclouds = payload.get("max_parallel_subclouds")
|
|
|
|
# Validate payload
|
|
if not utils.validate_name(
|
|
name, prohibited_name_list=[consts.DEFAULT_SUBCLOUD_GROUP_NAME]
|
|
):
|
|
pecan.abort(httpclient.BAD_REQUEST, _("Invalid group name"))
|
|
if not self._validate_description(description):
|
|
pecan.abort(httpclient.BAD_REQUEST, _("Invalid group description"))
|
|
if not self._validate_update_apply_type(update_apply_type):
|
|
pecan.abort(httpclient.BAD_REQUEST, _("Invalid group update_apply_type"))
|
|
if not self._validate_max_parallel_subclouds(max_parallel_subclouds):
|
|
pecan.abort(
|
|
httpclient.BAD_REQUEST, _("Invalid group max_parallel_subclouds")
|
|
)
|
|
try:
|
|
group_ref = db_api.subcloud_group_create(
|
|
context, name, description, update_apply_type, max_parallel_subclouds
|
|
)
|
|
return db_api.subcloud_group_db_model_to_dict(group_ref)
|
|
except db_exc.DBDuplicateEntry:
|
|
LOG.info("Group create failed. Group %s already exists" % name)
|
|
pecan.abort(
|
|
httpclient.BAD_REQUEST,
|
|
_("A subcloud group with this name already exists"),
|
|
)
|
|
except RemoteError as e:
|
|
pecan.abort(httpclient.UNPROCESSABLE_ENTITY, e.value)
|
|
except Exception as e:
|
|
LOG.exception(e)
|
|
pecan.abort(
|
|
httpclient.INTERNAL_SERVER_ERROR, _("Unable to create subcloud group")
|
|
)
|
|
|
|
@index.when(method="PATCH", template="json")
|
|
def patch(self, group_ref):
|
|
"""Update a subcloud group.
|
|
|
|
:param group_ref: ID or name of subcloud group to update
|
|
"""
|
|
|
|
policy.authorize(
|
|
subcloud_group_policy.POLICY_ROOT % "modify",
|
|
{},
|
|
restcomm.extract_credentials_for_policy(),
|
|
)
|
|
context = restcomm.extract_context_from_environ()
|
|
if group_ref is None:
|
|
pecan.abort(httpclient.BAD_REQUEST, _("Subcloud Group Name or ID required"))
|
|
|
|
payload = eval(request.body)
|
|
if not payload:
|
|
pecan.abort(httpclient.BAD_REQUEST, _("Body required"))
|
|
|
|
group = utils.subcloud_group_get_by_ref(context, group_ref)
|
|
if group is None:
|
|
pecan.abort(httpclient.NOT_FOUND, _("Subcloud Group not found"))
|
|
|
|
name = payload.get("name")
|
|
description = payload.get("description")
|
|
update_apply_type = payload.get("update_apply_type")
|
|
max_parallel_str = payload.get("max_parallel_subclouds")
|
|
|
|
if not (name or description or update_apply_type or max_parallel_str):
|
|
pecan.abort(httpclient.BAD_REQUEST, _("nothing to update"))
|
|
|
|
# Check value is not None or empty before calling validate
|
|
if name:
|
|
if not utils.validate_name(
|
|
name, prohibited_name_list=[consts.DEFAULT_SUBCLOUD_GROUP_NAME]
|
|
):
|
|
pecan.abort(httpclient.BAD_REQUEST, _("Invalid group name"))
|
|
# Special case. Default group name cannot be changed
|
|
if group.id == consts.DEFAULT_SUBCLOUD_GROUP_ID:
|
|
pecan.abort(
|
|
httpclient.BAD_REQUEST, _("Default group name cannot be changed")
|
|
)
|
|
|
|
if description:
|
|
if not self._validate_description(description):
|
|
pecan.abort(httpclient.BAD_REQUEST, _("Invalid group description"))
|
|
if update_apply_type:
|
|
if not self._validate_update_apply_type(update_apply_type):
|
|
pecan.abort(
|
|
httpclient.BAD_REQUEST, _("Invalid group update_apply_type")
|
|
)
|
|
if max_parallel_str:
|
|
if not self._validate_max_parallel_subclouds(max_parallel_str):
|
|
pecan.abort(
|
|
httpclient.BAD_REQUEST, _("Invalid group max_parallel_subclouds")
|
|
)
|
|
|
|
try:
|
|
updated_group = db_api.subcloud_group_update(
|
|
context,
|
|
group.id,
|
|
name=name,
|
|
description=description,
|
|
update_apply_type=update_apply_type,
|
|
max_parallel_subclouds=max_parallel_str,
|
|
)
|
|
return db_api.subcloud_group_db_model_to_dict(updated_group)
|
|
except RemoteError as e:
|
|
pecan.abort(httpclient.UNPROCESSABLE_ENTITY, e.value)
|
|
except Exception as e:
|
|
# additional exceptions.
|
|
LOG.exception(e)
|
|
pecan.abort(
|
|
httpclient.INTERNAL_SERVER_ERROR, _("Unable to update subcloud group")
|
|
)
|
|
|
|
@index.when(method="delete", template="json")
|
|
def delete(self, group_ref):
|
|
"""Delete the subcloud group."""
|
|
policy.authorize(
|
|
subcloud_group_policy.POLICY_ROOT % "delete",
|
|
{},
|
|
restcomm.extract_credentials_for_policy(),
|
|
)
|
|
context = restcomm.extract_context_from_environ()
|
|
|
|
if group_ref is None:
|
|
pecan.abort(httpclient.BAD_REQUEST, _("Subcloud Group Name or ID required"))
|
|
group = utils.subcloud_group_get_by_ref(context, group_ref)
|
|
if group is None:
|
|
pecan.abort(httpclient.NOT_FOUND, _("Subcloud Group not found"))
|
|
if group.name == consts.DEFAULT_SUBCLOUD_GROUP_NAME:
|
|
pecan.abort(
|
|
httpclient.BAD_REQUEST, _("Default Subcloud Group may not be deleted")
|
|
)
|
|
try:
|
|
# a subcloud group may not be deleted if it is use by any subclouds
|
|
subclouds = db_api.subcloud_get_for_group(context, group.id)
|
|
if len(subclouds) > 0:
|
|
pecan.abort(httpclient.BAD_REQUEST, _("Subcloud Group not empty"))
|
|
db_api.subcloud_group_destroy(context, group.id)
|
|
except RemoteError as e:
|
|
pecan.abort(httpclient.UNPROCESSABLE_ENTITY, e.value)
|
|
except Exception as e:
|
|
LOG.exception(e)
|
|
pecan.abort(
|
|
httpclient.INTERNAL_SERVER_ERROR, _("Unable to delete subcloud group")
|
|
)
|
|
# This should return nothing
|
|
return None
|