Add release optionality to subcloud add/reinstall
Add an optional --release parameter to subcloud add and reinstall commands to enable release optionality in subcloud add and subcloud reinstall. Test Plan: 1. Verify successful subcloud add which includes remote install with specified (previous/current) release 2. Verify successful subcloud reinstall with the specified (previous/current) release 3. Verify the subcloud is successfully installed with the active release when the release parameter is absent 4. Verify the subcloud is successfully reinstalled with the active release when the release parameter is absent 5. Verify the subcloud install request was rejected when the software_version in the install_values doesn't match the specified release 6. Verify the subcloud install/reinstall request was rejected when the kubernetes_version value specified in the subcloud bootstrap yaml file doesn't match the value of the fresh_install_k8s_version of the specified previous release Depends-On: https://review.opendev.org/c/starlingx/utilities/+/878545 https://review.opendev.org/c/starlingx/ansible-playbooks/+/878922 Story: 2010611 Task: 47684 Signed-off-by: lzhu1 <li.zhu@windriver.com> Change-Id: Ic4193c2901d8bfa485eeb683c08422d946802bcb
This commit is contained in:
parent
d2b8556aa6
commit
ad1f05ac5f
|
@ -140,6 +140,7 @@ serviceUnavailable (503)
|
|||
- management_subnet: management_subnet
|
||||
- migrate: migrate
|
||||
- name: subcloud_name
|
||||
- release: release
|
||||
- sysadmin_password: sysadmin_password
|
||||
- systemcontroller_gateway_address: systemcontroller_gateway_ip
|
||||
- system_mode: system_mode
|
||||
|
@ -493,6 +494,7 @@ serviceUnavailable (503)
|
|||
- subcloud: subcloud_uri
|
||||
- bootstrap_values: bootstrap_values
|
||||
- deploy_config: deploy_config
|
||||
- release: release
|
||||
- sysadmin_password: sysadmin_password
|
||||
|
||||
Request Example
|
||||
|
@ -1601,9 +1603,7 @@ files which include deploy playbook, deploy overrides, deploy helm charts, and p
|
|||
Show Subcloud Deploy Files
|
||||
**************************
|
||||
|
||||
.. rest_method:: GET /v1.0/subcloud-deploy
|
||||
|
||||
This operation does not accept a request body.
|
||||
.. rest_method:: GET /v1.0/subcloud-deploy/{release}
|
||||
|
||||
**Normal response codes**
|
||||
|
||||
|
@ -1615,6 +1615,13 @@ badRequest (400), unauthorized (401), forbidden
|
|||
(403), badMethod (405), HTTPUnprocessableEntity (422),
|
||||
internalServerError (500), serviceUnavailable (503)
|
||||
|
||||
**Request parameters**
|
||||
|
||||
.. rest_parameters:: parameters.yaml
|
||||
|
||||
- release: release_uri
|
||||
|
||||
This operation does not accept a request body.
|
||||
|
||||
**Response parameters**
|
||||
|
||||
|
@ -1625,6 +1632,7 @@ internalServerError (500), serviceUnavailable (503)
|
|||
- deploy_playbook: subcloud_deploy_playbook
|
||||
- deploy_overrides: subcloud_deploy_overrides
|
||||
- prestage_images: subcloud_deploy_prestage_images
|
||||
- software_version: software_version
|
||||
|
||||
Response Example
|
||||
----------------
|
||||
|
@ -1659,6 +1667,7 @@ serviceUnavailable (503)
|
|||
- deploy_playbook: subcloud_deploy_playbook_content
|
||||
- deploy_overrides: subcloud_deploy_overrides_content
|
||||
- prestage_images: subcloud_deploy_prestage_images_content
|
||||
- release: release
|
||||
|
||||
Request Example
|
||||
----------------
|
||||
|
@ -1674,6 +1683,7 @@ Request Example
|
|||
- deploy_playbook: subcloud_deploy_playbook
|
||||
- deploy_overrides: subcloud_deploy_overrides
|
||||
- prestage_images: subcloud_deploy_prestage_images
|
||||
- software_version: software_version
|
||||
|
||||
Response Example
|
||||
----------------
|
||||
|
|
|
@ -6,6 +6,12 @@ backup_delete_release:
|
|||
in: path
|
||||
required: true
|
||||
type: string
|
||||
release_uri:
|
||||
description: |
|
||||
The subcloud software version.
|
||||
in: path
|
||||
required: false
|
||||
type: string
|
||||
subcloud_group_uri:
|
||||
description: |
|
||||
The subcloud group reference, name or id.
|
||||
|
@ -317,6 +323,12 @@ region_name:
|
|||
in: body
|
||||
required: true
|
||||
type: string
|
||||
release:
|
||||
description: |
|
||||
The subcloud software version.
|
||||
in: body
|
||||
required: false
|
||||
type: string
|
||||
restore_values:
|
||||
description: |
|
||||
The content of a file containing restore parameters (e.g.
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
{
|
||||
"deploy_chart": "deployment-manager.tgz",
|
||||
"deploy_playbook": "deployment-manager-playbook.yaml",
|
||||
"deploy_overrides": "deployment-manager-overrides-subcloud.yaml"
|
||||
"deploy_overrides": "deployment-manager-overrides-subcloud.yaml",
|
||||
"software_version": "22.12"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"deploy_chart": "deployment manager contents",
|
||||
"deploy_playbook": "deployment manager playbook contents",
|
||||
"deploy_overrides": "deployment manager overrides contents"
|
||||
"deploy_overrides": "deployment manager overrides contents",
|
||||
"release": "22.12"
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"deploy_chart": "deployment-manager.tgz",
|
||||
"deploy_playbook": "deployment-manager-playbook.yaml",
|
||||
"deploy_overrides": "deployment-manager-overrides-subcloud.yaml"
|
||||
"deploy_overrides": "deployment-manager-overrides-subcloud.yaml",
|
||||
"software_version": "22.12"
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright (c) 2020-2021 Wind River Systems, Inc.
|
||||
# Copyright (c) 2020-2023 Wind River Systems, Inc.
|
||||
# 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
|
||||
|
@ -16,8 +16,6 @@
|
|||
SUPPORTED_INSTALL_TYPES = 6
|
||||
|
||||
MANDATORY_INSTALL_VALUES = [
|
||||
'image',
|
||||
'software_version',
|
||||
'bootstrap_interface',
|
||||
'bootstrap_address',
|
||||
'bootstrap_address_prefix',
|
||||
|
|
|
@ -105,12 +105,12 @@ class SubcloudDeployController(object):
|
|||
error_msg = "error: argument %s is required" % missing_str.rstrip()
|
||||
pecan.abort(httpclient.BAD_REQUEST, error_msg)
|
||||
|
||||
release = tsc.SW_VERSION
|
||||
if request.POST.get('release_version'):
|
||||
release = request.POST.get('release_version')
|
||||
deploy_dicts['release_version'] = release
|
||||
software_version = tsc.SW_VERSION
|
||||
if request.POST.get('release'):
|
||||
software_version = request.POST.get('release')
|
||||
deploy_dicts['software_version'] = software_version
|
||||
|
||||
dir_path = os.path.join(dccommon_consts.DEPLOY_DIR, release)
|
||||
dir_path = os.path.join(dccommon_consts.DEPLOY_DIR, software_version)
|
||||
for f in consts.DEPLOY_COMMON_FILE_OPTIONS:
|
||||
if f not in request.POST:
|
||||
continue
|
||||
|
@ -145,7 +145,7 @@ class SubcloudDeployController(object):
|
|||
deploy_dicts = dict()
|
||||
if not release:
|
||||
release = tsc.SW_VERSION
|
||||
deploy_dicts['release_version'] = release
|
||||
deploy_dicts['software_version'] = release
|
||||
dir_path = os.path.join(dccommon_consts.DEPLOY_DIR, release)
|
||||
for f in consts.DEPLOY_COMMON_FILE_OPTIONS:
|
||||
filename = None
|
||||
|
|
|
@ -96,6 +96,13 @@ INSTALL_VALUES_ADDRESSES = [
|
|||
'network_address'
|
||||
]
|
||||
|
||||
ANSIBLE_BOOTSTRAP_VALIDATE_CONFIG_VARS = \
|
||||
consts.ANSIBLE_CURRENT_VERSION_BASE_PATH + \
|
||||
'/roles/bootstrap/validate-config/vars/main.yml'
|
||||
|
||||
FRESH_INSTALL_K8S_VERSION = 'fresh_install_k8s_version'
|
||||
KUBERNETES_VERSION = 'kubernetes_version'
|
||||
|
||||
|
||||
def _get_multipart_field_name(part):
|
||||
content = part.headers[b"Content-Disposition"].decode("utf8")
|
||||
|
@ -132,14 +139,14 @@ class SubcloudsController(object):
|
|||
pecan.abort(400, _("Invalid group_id"))
|
||||
|
||||
@staticmethod
|
||||
def _get_common_deploy_files(payload):
|
||||
def _get_common_deploy_files(payload, software_version):
|
||||
for f in consts.DEPLOY_COMMON_FILE_OPTIONS:
|
||||
# Skip the prestage_images option as it is not relevant in this
|
||||
# context
|
||||
if f == consts.DEPLOY_PRESTAGE:
|
||||
continue
|
||||
filename = None
|
||||
dir_path = tsc.DEPLOY_PATH
|
||||
dir_path = os.path.join(dccommon_consts.DEPLOY_DIR, software_version)
|
||||
if os.path.isdir(dir_path):
|
||||
filename = utils.get_filename_by_prefix(dir_path, f + '_')
|
||||
if filename is None:
|
||||
|
@ -159,7 +166,7 @@ class SubcloudsController(object):
|
|||
fn = self._get_config_file_path(payload['name'], consts.DEPLOY_CONFIG)
|
||||
self._upload_config_file(contents, fn, consts.DEPLOY_CONFIG)
|
||||
payload.update({consts.DEPLOY_CONFIG: fn})
|
||||
self._get_common_deploy_files(payload)
|
||||
self._get_common_deploy_files(payload, payload['software_version'])
|
||||
|
||||
@staticmethod
|
||||
def _get_request_data(request):
|
||||
|
@ -242,7 +249,7 @@ class SubcloudsController(object):
|
|||
LOG.exception(msg)
|
||||
pecan.abort(400, msg)
|
||||
|
||||
def _get_reconfig_payload(self, request, subcloud_name):
|
||||
def _get_reconfig_payload(self, request, subcloud_name, software_version):
|
||||
payload = dict()
|
||||
multipart_data = decoder.MultipartDecoder(
|
||||
request.body, pecan.request.headers.get('Content-Type'))
|
||||
|
@ -260,7 +267,7 @@ class SubcloudsController(object):
|
|||
payload.update({consts.DEPLOY_CONFIG: fn})
|
||||
elif "sysadmin_password" in hv:
|
||||
payload.update({'sysadmin_password': part.content})
|
||||
self._get_common_deploy_files(payload)
|
||||
self._get_common_deploy_files(payload, software_version)
|
||||
return payload
|
||||
|
||||
def _get_config_file_path(self, subcloud_name, config_file_type=None):
|
||||
|
@ -615,7 +622,7 @@ class SubcloudsController(object):
|
|||
"""Validate install values if 'install_values' is present in payload.
|
||||
|
||||
The image in payload install values is optional, and if not provided,
|
||||
the image is set to the available active load image.
|
||||
the image is set to the available active/inactive load image.
|
||||
|
||||
:return boolean: True if bmc install requested, otherwise False
|
||||
"""
|
||||
|
@ -640,15 +647,18 @@ class SubcloudsController(object):
|
|||
pecan.abort(400, msg)
|
||||
payload['install_values'].update({'bmc_password': bmc_password})
|
||||
|
||||
software_version = payload.get('software_version')
|
||||
if not software_version and subcloud:
|
||||
software_version = subcloud.software_version
|
||||
if 'software_version' in install_values:
|
||||
software_version = str(install_values.get('software_version'))
|
||||
else:
|
||||
if original_install_values:
|
||||
pecan.abort(400, _("Mandatory install value software_version not present, "
|
||||
"existing software_version in DB: %s") %
|
||||
original_install_values.get("software_version"))
|
||||
else:
|
||||
pecan.abort(400, _("Mandatory install value software_version not present"))
|
||||
install_software_version = str(install_values.get('software_version'))
|
||||
if software_version and software_version != install_software_version:
|
||||
pecan.abort(400,
|
||||
_("The software_version value %s in the install values "
|
||||
"yaml file does not match with the specified/current "
|
||||
"software version of %s. Please correct or remove "
|
||||
"this parameter from the yaml file and try again.") %
|
||||
(install_software_version, software_version))
|
||||
if 'persistent_size' in install_values:
|
||||
persistent_size = install_values.get('persistent_size')
|
||||
if not isinstance(persistent_size, int):
|
||||
|
@ -671,27 +681,21 @@ class SubcloudsController(object):
|
|||
|
||||
for k in install_consts.MANDATORY_INSTALL_VALUES:
|
||||
if k not in install_values:
|
||||
if k == 'image':
|
||||
if software_version == tsc.SW_VERSION:
|
||||
# check for the image at load vault load location
|
||||
matching_iso, err_msg = utils.get_matching_iso()
|
||||
if err_msg:
|
||||
LOG.exception(err_msg)
|
||||
pecan.abort(400, _(err_msg))
|
||||
LOG.info("image was not in install_values: will reference %s" %
|
||||
matching_iso)
|
||||
else:
|
||||
pecan.abort(400, _("Image was not in install_values, and "
|
||||
"software version %s in install values "
|
||||
"did not match the active load %s") %
|
||||
(software_version, tsc.SW_VERSION))
|
||||
if original_install_values:
|
||||
pecan.abort(400, _("Mandatory install value %s not present, "
|
||||
"existing %s in DB: %s") %
|
||||
(k, k, original_install_values.get(k)))
|
||||
else:
|
||||
if original_install_values:
|
||||
pecan.abort(400, _("Mandatory install value %s not present, "
|
||||
"existing %s in DB: %s") %
|
||||
(k, k, original_install_values.get(k)))
|
||||
else:
|
||||
pecan.abort(400, _("Mandatory install value %s not present") % k)
|
||||
pecan.abort(400,
|
||||
_("Mandatory install value %s not present") % k)
|
||||
|
||||
# check for the image at load vault load location
|
||||
matching_iso, err_msg = utils.get_matching_iso(software_version)
|
||||
if err_msg:
|
||||
LOG.exception(err_msg)
|
||||
pecan.abort(400, _(err_msg))
|
||||
LOG.info("Image in install_values is set to %s" % matching_iso)
|
||||
payload['install_values'].update({'image': matching_iso})
|
||||
|
||||
if (install_values['install_type'] not in
|
||||
list(range(install_consts.SUPPORTED_INSTALL_TYPES))):
|
||||
|
@ -759,6 +763,45 @@ class SubcloudsController(object):
|
|||
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def _validate_k8s_version(payload):
|
||||
"""Validate k8s version.
|
||||
|
||||
If the specified release in the payload is not the active release,
|
||||
the kubernetes_version value if specified in the subcloud bootstrap
|
||||
yaml file must be of the same value as fresh_install_k8s_version of
|
||||
the specified release.
|
||||
"""
|
||||
if payload['software_version'] == tsc.SW_VERSION:
|
||||
return
|
||||
|
||||
kubernetes_version = payload.get(KUBERNETES_VERSION)
|
||||
if kubernetes_version:
|
||||
try:
|
||||
bootstrap_var_file = utils.get_playbook_for_software_version(
|
||||
ANSIBLE_BOOTSTRAP_VALIDATE_CONFIG_VARS,
|
||||
payload['software_version'])
|
||||
fresh_install_k8s_version = utils.get_value_from_yaml_file(
|
||||
bootstrap_var_file,
|
||||
FRESH_INSTALL_K8S_VERSION)
|
||||
if not fresh_install_k8s_version:
|
||||
pecan.abort(400, _("%s not found in %s")
|
||||
% (FRESH_INSTALL_K8S_VERSION,
|
||||
bootstrap_var_file))
|
||||
if kubernetes_version != fresh_install_k8s_version:
|
||||
pecan.abort(400, _("The kubernetes_version value (%s) "
|
||||
"specified in the subcloud bootstrap "
|
||||
"yaml file doesn't match "
|
||||
"fresh_install_k8s_version value (%s) "
|
||||
"of the specified release %s")
|
||||
% (kubernetes_version,
|
||||
fresh_install_k8s_version,
|
||||
payload['software_version']))
|
||||
except exceptions.PlaybookNotFound:
|
||||
pecan.abort(400, _("The bootstrap playbook validate-config vars "
|
||||
"not found for %s software version")
|
||||
% payload['software_version'])
|
||||
|
||||
def _get_subcloud_users(self):
|
||||
"""Get the subcloud users and passwords from keyring"""
|
||||
DEFAULT_SERVICE_PROJECT_NAME = 'services'
|
||||
|
@ -855,15 +898,11 @@ class SubcloudsController(object):
|
|||
resource='subcloud',
|
||||
msg='Subcloud with that name already exists')
|
||||
|
||||
# Subcloud is added with software version that matches system
|
||||
# controller.
|
||||
software_version = tsc.SW_VERSION
|
||||
# if group_id has been omitted from payload, use 'Default'.
|
||||
group_id = payload.get('group_id',
|
||||
consts.DEFAULT_SUBCLOUD_GROUP_ID)
|
||||
data_install = None
|
||||
if 'install_values' in payload:
|
||||
software_version = payload['install_values']['software_version']
|
||||
data_install = json.dumps(payload['install_values'])
|
||||
|
||||
subcloud = db_api.subcloud_create(
|
||||
|
@ -871,7 +910,7 @@ class SubcloudsController(object):
|
|||
payload['name'],
|
||||
payload.get('description'),
|
||||
payload.get('location'),
|
||||
software_version,
|
||||
payload.get('software_version'),
|
||||
utils.get_management_subnet(payload),
|
||||
utils.get_management_gateway_address(payload),
|
||||
utils.get_management_start_address(payload),
|
||||
|
@ -1140,6 +1179,10 @@ class SubcloudsController(object):
|
|||
group_id = payload.get('group_id',
|
||||
consts.DEFAULT_SUBCLOUD_GROUP_ID)
|
||||
|
||||
# If a subcloud release is not passed, use the current
|
||||
# system controller software_version
|
||||
payload['software_version'] = payload.get('release', tsc.SW_VERSION)
|
||||
|
||||
self._validate_system_controller_patch_status()
|
||||
|
||||
self._validate_subcloud_config(context,
|
||||
|
@ -1160,6 +1203,8 @@ class SubcloudsController(object):
|
|||
|
||||
self._validate_install_values(payload)
|
||||
|
||||
self._validate_k8s_version(payload)
|
||||
|
||||
self._format_ip_address(payload)
|
||||
|
||||
# Upload the deploy config files if it is included in the request
|
||||
|
@ -1219,6 +1264,7 @@ class SubcloudsController(object):
|
|||
subcloud_id = subcloud.id
|
||||
|
||||
if verb is None:
|
||||
# subcloud update
|
||||
payload = self._get_patch_data(request)
|
||||
if not payload:
|
||||
pecan.abort(400, _('Body required'))
|
||||
|
@ -1317,7 +1363,8 @@ class SubcloudsController(object):
|
|||
LOG.exception(e)
|
||||
pecan.abort(500, _('Unable to update subcloud'))
|
||||
elif verb == 'reconfigure':
|
||||
payload = self._get_reconfig_payload(request, subcloud.name)
|
||||
payload = self._get_reconfig_payload(
|
||||
request, subcloud.name, subcloud.software_version)
|
||||
if not payload:
|
||||
pecan.abort(400, _('Body required'))
|
||||
|
||||
|
@ -1454,25 +1501,31 @@ class SubcloudsController(object):
|
|||
external_oam_floating_ip,
|
||||
subcloud_subnets)
|
||||
|
||||
# If a subcloud release is not passed, use the current
|
||||
# system controller software_version
|
||||
payload['software_version'] = payload.get('release', tsc.SW_VERSION)
|
||||
|
||||
self._validate_k8s_version(payload)
|
||||
|
||||
# If the software version of the subcloud is different from the
|
||||
# central cloud, update the software version in install valuse and
|
||||
# delete the image path in install values, then the subcloud will
|
||||
# be reinstalled using the image in dc_vault.
|
||||
if install_values.get('software_version') != tsc.SW_VERSION:
|
||||
install_values['software_version'] = tsc.SW_VERSION
|
||||
# specified or active load, update the software version in install
|
||||
# value and delete the image path in install values, then the subcloud
|
||||
# will be reinstalled using the image in dc_vault.
|
||||
if install_values.get('software_version') != \
|
||||
payload['software_version']:
|
||||
install_values['software_version'] = payload['software_version']
|
||||
install_values.pop('image', None)
|
||||
|
||||
# Confirm the active system controller load is still in dc-vault if
|
||||
# Confirm the specified or active load is still in dc-vault if
|
||||
# image not in install values, add the matching image into the
|
||||
# install values.
|
||||
if 'image' not in install_values:
|
||||
matching_iso, err_msg = utils.get_matching_iso()
|
||||
if err_msg:
|
||||
LOG.exception(err_msg)
|
||||
pecan.abort(400, _(err_msg))
|
||||
LOG.info("image was not in install_values: will reference %s" %
|
||||
matching_iso)
|
||||
install_values['image'] = matching_iso
|
||||
matching_iso, err_msg = utils.get_matching_iso(
|
||||
payload['software_version'])
|
||||
if err_msg:
|
||||
LOG.exception(err_msg)
|
||||
pecan.abort(400, _(err_msg))
|
||||
LOG.info("Image in install_values is set to %s" % matching_iso)
|
||||
install_values['image'] = matching_iso
|
||||
|
||||
# Update the install values in payload
|
||||
payload.update({
|
||||
|
@ -1489,15 +1542,15 @@ class SubcloudsController(object):
|
|||
self._upload_deploy_config_file(request, payload)
|
||||
|
||||
try:
|
||||
# Align the software version of the subcloud with the central
|
||||
# cloud. Update description, location and group id if offered,
|
||||
# Align the software version of the subcloud with reinstall
|
||||
# version. Update description, location and group id if offered,
|
||||
# update the deploy status as pre-install.
|
||||
db_api.subcloud_update(
|
||||
subcloud = db_api.subcloud_update(
|
||||
context,
|
||||
subcloud_id,
|
||||
description=payload.get('description', subcloud.description),
|
||||
location=payload.get('location', subcloud.location),
|
||||
software_version=tsc.SW_VERSION,
|
||||
software_version=payload['software_version'],
|
||||
management_state=dccommon_consts.MANAGEMENT_UNMANAGED,
|
||||
deploy_status=consts.DEPLOY_STATE_PRE_INSTALL,
|
||||
data_install=data_install)
|
||||
|
|
|
@ -360,3 +360,7 @@ STATES_FOR_ONGOING_BACKUP = [BACKUP_STATE_INITIAL,
|
|||
OPENLDAP_CA_CERT_SECRET_NAME = "system-local-ca"
|
||||
|
||||
CERT_NAMESPACE_PLATFORM_CA_CERTS = 'cert-manager'
|
||||
|
||||
# The ansible playbook base directories
|
||||
ANSIBLE_CURRENT_VERSION_BASE_PATH = '/usr/share/ansible/stx-ansible/playbooks'
|
||||
ANSIBLE_PREVIOUS_VERSION_BASE_PATH = '/opt/dc-vault/playbooks'
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Copyright 2015 Huawei Technologies Co., Ltd.
|
||||
# Copyright 2015 Ericsson AB.
|
||||
# Copyright (c) 2017-2022 Wind River Systems, Inc.
|
||||
# Copyright (c) 2017-2023 Wind River Systems, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
|
@ -243,3 +243,7 @@ class StrategySkippedException(DCManagerException):
|
|||
|
||||
class StrategyStoppedException(DCManagerException):
|
||||
message = _("Strategy has been stopped")
|
||||
|
||||
|
||||
class PlaybookNotFound(NotFound):
|
||||
message = _("Playbook %(playbook_name)s not found")
|
||||
|
|
|
@ -26,6 +26,7 @@ import resource as sys_resource
|
|||
import six.moves
|
||||
import subprocess
|
||||
import tsconfig.tsconfig as tsc
|
||||
import yaml
|
||||
|
||||
from keystoneauth1 import exceptions as keystone_exceptions
|
||||
from oslo_concurrency import lockutils
|
||||
|
@ -749,14 +750,16 @@ def _is_valid_for_backup_restore(subcloud):
|
|||
return True
|
||||
|
||||
|
||||
def get_matching_iso():
|
||||
def get_matching_iso(software_version=None):
|
||||
try:
|
||||
matching_iso, _ = get_vault_load_files(tsc.SW_VERSION)
|
||||
if not software_version:
|
||||
software_version = tsc.SW_VERSION
|
||||
matching_iso, _ = get_vault_load_files(software_version)
|
||||
if not matching_iso:
|
||||
error_msg = ('Failed to get active load image. Provide '
|
||||
'active load image via '
|
||||
error_msg = ('Failed to get %s load image. Provide '
|
||||
'active/inactive load image via '
|
||||
'"system --os-region-name SystemController '
|
||||
'load-import --active"')
|
||||
'load-import --active/--inactive"' % software_version)
|
||||
LOG.exception(error_msg)
|
||||
return None, error_msg
|
||||
return matching_iso, None
|
||||
|
@ -900,3 +903,42 @@ def set_open_file_limit(new_soft_limit: int):
|
|||
(new_soft_limit, current_hard))
|
||||
except Exception as ex:
|
||||
LOG.exception(f'Failed to set NOFILE resource limit: {ex}')
|
||||
|
||||
|
||||
def get_playbook_for_software_version(playbook_filename, software_version=None):
|
||||
"""Get the ansible playbook filename in corresponding software version.
|
||||
|
||||
:param playbook_filename: ansible playbook filename
|
||||
:param software_version: software version
|
||||
:raises PlaybookNotFound: If the playbook is not found
|
||||
|
||||
Returns the unchanged ansible playbook filename if the software version
|
||||
parameter is not provided or the same as active release, otherwise, returns
|
||||
the filename in corresponding software version.
|
||||
"""
|
||||
if software_version and software_version != tsc.SW_VERSION:
|
||||
software_version_path = os.path.join(
|
||||
consts.ANSIBLE_PREVIOUS_VERSION_BASE_PATH, software_version)
|
||||
playbook_filename = playbook_filename.replace(
|
||||
consts.ANSIBLE_CURRENT_VERSION_BASE_PATH,
|
||||
software_version_path)
|
||||
if not os.path.isfile(playbook_filename):
|
||||
raise exceptions.PlaybookNotFound(playbook_name=playbook_filename)
|
||||
return playbook_filename
|
||||
|
||||
|
||||
def get_value_from_yaml_file(filename, key):
|
||||
"""Get corresponding value for a key in the given yaml file.
|
||||
|
||||
:param filename: the yaml filename
|
||||
:param key: the path for the value
|
||||
|
||||
Returns the value or None if not found.
|
||||
"""
|
||||
value = None
|
||||
if os.path.isfile(filename):
|
||||
with open(os.path.abspath(filename), 'r') as f:
|
||||
data = f.read()
|
||||
data = yaml.load(data, Loader=yaml.SafeLoader)
|
||||
value = data.get(key)
|
||||
return value
|
||||
|
|
|
@ -218,19 +218,28 @@ class SubcloudManager(manager.Manager):
|
|||
subcloud_name + postfix)
|
||||
return ansible_filename
|
||||
|
||||
def compose_install_command(self, subcloud_name, ansible_subcloud_inventory_file):
|
||||
def compose_install_command(self, subcloud_name,
|
||||
ansible_subcloud_inventory_file,
|
||||
software_version=None):
|
||||
install_command = [
|
||||
"ansible-playbook", ANSIBLE_SUBCLOUD_INSTALL_PLAYBOOK,
|
||||
"-i", ansible_subcloud_inventory_file,
|
||||
"--limit", subcloud_name,
|
||||
"-e", "@%s" % consts.ANSIBLE_OVERRIDES_PATH + "/" +
|
||||
subcloud_name + '/' + "install_values.yml"]
|
||||
if software_version and software_version != SW_VERSION:
|
||||
install_command += [
|
||||
"-e", "install_release_version=%s" % software_version]
|
||||
return install_command
|
||||
|
||||
def compose_apply_command(self, subcloud_name, ansible_subcloud_inventory_file):
|
||||
def compose_apply_command(self, subcloud_name,
|
||||
ansible_subcloud_inventory_file,
|
||||
software_version=None):
|
||||
apply_command = [
|
||||
"ansible-playbook", ANSIBLE_SUBCLOUD_PLAYBOOK, "-i",
|
||||
ansible_subcloud_inventory_file,
|
||||
"ansible-playbook",
|
||||
utils.get_playbook_for_software_version(
|
||||
ANSIBLE_SUBCLOUD_PLAYBOOK, software_version),
|
||||
"-i", ansible_subcloud_inventory_file,
|
||||
"--limit", subcloud_name
|
||||
]
|
||||
# Add the overrides dir and region_name so the playbook knows
|
||||
|
@ -291,9 +300,13 @@ class SubcloudManager(manager.Manager):
|
|||
subcloud_name + "_update_values.yml"]
|
||||
return subcloud_update_command
|
||||
|
||||
def compose_rehome_command(self, subcloud_name, ansible_subcloud_inventory_file):
|
||||
def compose_rehome_command(self, subcloud_name,
|
||||
ansible_subcloud_inventory_file,
|
||||
software_version):
|
||||
rehome_command = [
|
||||
"ansible-playbook", ANSIBLE_SUBCLOUD_REHOME_PLAYBOOK,
|
||||
"ansible-playbook",
|
||||
utils.get_playbook_for_software_version(
|
||||
ANSIBLE_SUBCLOUD_REHOME_PLAYBOOK, software_version),
|
||||
"-i", ansible_subcloud_inventory_file,
|
||||
"--limit", subcloud_name,
|
||||
"--timeout", REHOME_PLAYBOOK_TIMEOUT,
|
||||
|
@ -426,10 +439,6 @@ class SubcloudManager(manager.Manager):
|
|||
if "install_values" in payload:
|
||||
payload['install_values']['ansible_ssh_pass'] = \
|
||||
payload['sysadmin_password']
|
||||
if 'image' not in payload['install_values']:
|
||||
matching_iso, matching_sig = utils.get_vault_load_files(
|
||||
SW_VERSION)
|
||||
payload['install_values'].update({'image': matching_iso})
|
||||
|
||||
deploy_command = None
|
||||
if "deploy_playbook" in payload:
|
||||
|
@ -461,7 +470,8 @@ class SubcloudManager(manager.Manager):
|
|||
if migrate_flag:
|
||||
rehome_command = self.compose_rehome_command(
|
||||
subcloud.name,
|
||||
ansible_subcloud_inventory_file)
|
||||
ansible_subcloud_inventory_file,
|
||||
subcloud.software_version)
|
||||
apply_thread = threading.Thread(
|
||||
target=self.run_deploy,
|
||||
args=(subcloud, payload, context,
|
||||
|
@ -471,10 +481,12 @@ class SubcloudManager(manager.Manager):
|
|||
if "install_values" in payload:
|
||||
install_command = self.compose_install_command(
|
||||
subcloud.name,
|
||||
ansible_subcloud_inventory_file)
|
||||
ansible_subcloud_inventory_file,
|
||||
subcloud.software_version)
|
||||
apply_command = self.compose_apply_command(
|
||||
subcloud.name,
|
||||
ansible_subcloud_inventory_file)
|
||||
ansible_subcloud_inventory_file,
|
||||
subcloud.software_version)
|
||||
apply_thread = threading.Thread(
|
||||
target=self.run_deploy,
|
||||
args=(subcloud, payload, context,
|
||||
|
@ -590,10 +602,12 @@ class SubcloudManager(manager.Manager):
|
|||
|
||||
install_command = self.compose_install_command(
|
||||
subcloud.name,
|
||||
ansible_subcloud_inventory_file)
|
||||
ansible_subcloud_inventory_file,
|
||||
payload['software_version'])
|
||||
apply_command = self.compose_apply_command(
|
||||
subcloud.name,
|
||||
ansible_subcloud_inventory_file)
|
||||
ansible_subcloud_inventory_file,
|
||||
payload['software_version'])
|
||||
apply_thread = threading.Thread(
|
||||
target=self.run_deploy,
|
||||
args=(subcloud, payload, context,
|
||||
|
@ -958,11 +972,11 @@ class SubcloudManager(manager.Manager):
|
|||
|
||||
if payload.get('with_install'):
|
||||
install_command = self.compose_install_command(
|
||||
subcloud.name, subcloud_inventory_file)
|
||||
subcloud.name, subcloud_inventory_file, subcloud.software_version)
|
||||
# Update data_install with missing data
|
||||
matching_iso, _ = utils.get_vault_load_files(SW_VERSION)
|
||||
matching_iso, _ = utils.get_vault_load_files(subcloud.software_version)
|
||||
data_install['image'] = matching_iso
|
||||
data_install['software_version'] = SW_VERSION
|
||||
data_install['software_version'] = subcloud.software_version
|
||||
data_install['ansible_ssh_pass'] = payload['sysadmin_password']
|
||||
data_install['ansible_become_pass'] = payload['sysadmin_password']
|
||||
install_success = self._run_subcloud_install(
|
||||
|
|
|
@ -27,7 +27,7 @@ from dcmanager.tests import utils
|
|||
|
||||
from tsconfig.tsconfig import SW_VERSION
|
||||
|
||||
FAKE_RELEASE = '21.12'
|
||||
FAKE_SOFTWARE_VERSION = '21.12'
|
||||
FAKE_TENANT = utils.UUID1
|
||||
FAKE_ID = '1'
|
||||
FAKE_URL = '/v1.0/subcloud-deploy'
|
||||
|
@ -55,7 +55,7 @@ class TestSubcloudDeploy(testroot.DCManagerApiTest):
|
|||
@mock.patch.object(subcloud_deploy.SubcloudDeployController,
|
||||
'_upload_files')
|
||||
def test_post_subcloud_deploy(self, mock_upload_files):
|
||||
params = [('release_version', FAKE_RELEASE)]
|
||||
params = [('release', FAKE_SOFTWARE_VERSION)]
|
||||
fields = list()
|
||||
for opt in consts.DEPLOY_COMMON_FILE_OPTIONS:
|
||||
fake_name = opt + "_fake"
|
||||
|
@ -67,7 +67,7 @@ class TestSubcloudDeploy(testroot.DCManagerApiTest):
|
|||
headers=FAKE_HEADERS,
|
||||
params=params)
|
||||
self.assertEqual(response.status_code, http_client.OK)
|
||||
self.assertEqual(FAKE_RELEASE, response.json['release_version'])
|
||||
self.assertEqual(FAKE_SOFTWARE_VERSION, response.json['software_version'])
|
||||
|
||||
@mock.patch.object(subcloud_deploy.SubcloudDeployController,
|
||||
'_upload_files')
|
||||
|
@ -83,7 +83,7 @@ class TestSubcloudDeploy(testroot.DCManagerApiTest):
|
|||
upload_files=fields)
|
||||
self.assertEqual(response.status_code, http_client.OK)
|
||||
# Verify the active release will be returned if release doesn't present
|
||||
self.assertEqual(SW_VERSION, response.json['release_version'])
|
||||
self.assertEqual(SW_VERSION, response.json['software_version'])
|
||||
|
||||
@mock.patch.object(subcloud_deploy.SubcloudDeployController,
|
||||
'_upload_files')
|
||||
|
@ -206,11 +206,11 @@ class TestSubcloudDeploy(testroot.DCManagerApiTest):
|
|||
os.path.isdir = mock.Mock(return_value=True)
|
||||
mock_get_filename_by_prefix.side_effect = \
|
||||
get_filename_by_prefix_side_effect
|
||||
url = FAKE_URL + '/' + FAKE_RELEASE
|
||||
url = FAKE_URL + '/' + FAKE_SOFTWARE_VERSION
|
||||
response = self.app.get(url, headers=FAKE_HEADERS)
|
||||
self.assertEqual(response.status_code, http_client.OK)
|
||||
self.assertEqual(FAKE_RELEASE,
|
||||
response.json['subcloud_deploy']['release_version'])
|
||||
self.assertEqual(FAKE_SOFTWARE_VERSION,
|
||||
response.json['subcloud_deploy']['software_version'])
|
||||
self.assertEqual(FAKE_DEPLOY_PLAYBOOK_FILE,
|
||||
response.json['subcloud_deploy'][consts.DEPLOY_PLAYBOOK])
|
||||
self.assertEqual(FAKE_DEPLOY_OVERRIDES_FILE,
|
||||
|
@ -236,7 +236,7 @@ class TestSubcloudDeploy(testroot.DCManagerApiTest):
|
|||
response = self.app.get(FAKE_URL, headers=FAKE_HEADERS)
|
||||
self.assertEqual(response.status_code, http_client.OK)
|
||||
self.assertEqual(SW_VERSION,
|
||||
response.json['subcloud_deploy']['release_version'])
|
||||
response.json['subcloud_deploy']['software_version'])
|
||||
self.assertEqual(FAKE_DEPLOY_PLAYBOOK_FILE,
|
||||
response.json['subcloud_deploy'][consts.DEPLOY_PLAYBOOK])
|
||||
self.assertEqual(FAKE_DEPLOY_OVERRIDES_FILE,
|
||||
|
|
|
@ -40,6 +40,8 @@ from dcmanager.tests.unit.api.v1.controllers.mixins import PostMixin
|
|||
from dcmanager.tests.unit.common import fake_subcloud
|
||||
from dcmanager.tests import utils
|
||||
|
||||
from tsconfig.tsconfig import SW_VERSION
|
||||
|
||||
SAMPLE_SUBCLOUD_NAME = 'SubcloudX'
|
||||
SAMPLE_SUBCLOUD_DESCRIPTION = 'A Subcloud of mystery'
|
||||
|
||||
|
@ -186,8 +188,6 @@ class SubcloudAPIMixin(APIMixin):
|
|||
# based off MANDATORY_INSTALL_VALUES
|
||||
# bmc_password must be passed as a param
|
||||
FAKE_INSTALL_DATA = {
|
||||
"image": "fake image",
|
||||
"software_version": "123.456",
|
||||
"bootstrap_interface": "fake interface",
|
||||
"bootstrap_address": "10.10.10.12",
|
||||
"bootstrap_address_prefix": "10.10.10.12",
|
||||
|
@ -494,9 +494,12 @@ class TestSubcloudPost(testroot.DCManagerApiTest,
|
|||
bad_values,
|
||||
good_value)
|
||||
|
||||
def test_post_subcloud_install_values(self):
|
||||
@mock.patch('dcmanager.common.utils.get_vault_load_files')
|
||||
def test_post_subcloud_install_values(self, mock_vault_files):
|
||||
"""Test POST operation with install values is supported by the API."""
|
||||
|
||||
mock_vault_files.return_value = ('fake_iso', 'fake_sig')
|
||||
|
||||
# pass a different "install" list of files for this POST
|
||||
self.set_list_of_post_files(subclouds.SUBCLOUD_ADD_GET_FILE_CONTENTS)
|
||||
upload_files = self.get_post_upload_files()
|
||||
|
@ -513,6 +516,86 @@ class TestSubcloudPost(testroot.DCManagerApiTest,
|
|||
headers=self.get_api_headers())
|
||||
self._verify_post_success(response)
|
||||
|
||||
@mock.patch('dcmanager.common.utils.get_vault_load_files')
|
||||
def test_post_subcloud_without_release_parameter(self, mock_vault_files):
|
||||
"""Test POST operation without release parameter."""
|
||||
|
||||
mock_vault_files.return_value = ('fake_iso', 'fake_sig')
|
||||
|
||||
self.set_list_of_post_files(subclouds.SUBCLOUD_ADD_GET_FILE_CONTENTS)
|
||||
upload_files = self.get_post_upload_files()
|
||||
|
||||
params = self.get_post_params()
|
||||
# add bmc_password to params
|
||||
params.update(
|
||||
{'bmc_password':
|
||||
base64.b64encode('fake pass'.encode("utf-8")).decode("utf-8")})
|
||||
|
||||
response = self.app.post(self.get_api_prefix(),
|
||||
params=params,
|
||||
upload_files=upload_files,
|
||||
headers=self.get_api_headers())
|
||||
self._verify_post_success(response)
|
||||
# Verify that the subcloud installed with the active release
|
||||
# when no release parameter provided.
|
||||
self.assertEqual(SW_VERSION, response.json['software-version'])
|
||||
|
||||
def test_post_subcloud_release_not_match_install_values_sw(self):
|
||||
"""Release parameter not match software_version in the install_values."""
|
||||
|
||||
self.set_list_of_post_files(subclouds.SUBCLOUD_ADD_GET_FILE_CONTENTS)
|
||||
upload_files = self.get_post_upload_files()
|
||||
|
||||
params = self.get_post_params()
|
||||
# add bmc_password and release to params
|
||||
params.update(
|
||||
{'bmc_password':
|
||||
base64.b64encode('fake pass'.encode("utf-8")).decode("utf-8"),
|
||||
'release': '21.12'})
|
||||
|
||||
response = self.app.post(self.get_api_prefix(),
|
||||
params=params,
|
||||
upload_files=upload_files,
|
||||
headers=self.get_api_headers(),
|
||||
expect_errors=True)
|
||||
|
||||
# Verify the request was rejected
|
||||
self.assertEqual(response.status_code, http_client.BAD_REQUEST)
|
||||
|
||||
@mock.patch.object(subclouds.SubcloudsController, '_validate_k8s_version')
|
||||
@mock.patch('dcmanager.common.utils.get_vault_load_files')
|
||||
def test_post_subcloud_with_release_parameter(self, mock_vault_files,
|
||||
mock_validate_k8s_version):
|
||||
"""Test POST operation with release parameter."""
|
||||
|
||||
mock_vault_files.return_value = ('fake_iso', 'fake_sig')
|
||||
software_version = '21.12'
|
||||
# Update the software_version value to match the release parameter value,
|
||||
# otherwise, the request will be rejected
|
||||
self.install_data['software_version'] = software_version
|
||||
|
||||
self.set_list_of_post_files(subclouds.SUBCLOUD_ADD_GET_FILE_CONTENTS)
|
||||
upload_files = self.get_post_upload_files()
|
||||
|
||||
params = self.get_post_params()
|
||||
# add bmc_password and release to params
|
||||
params.update(
|
||||
{'bmc_password':
|
||||
base64.b64encode('fake pass'.encode("utf-8")).decode("utf-8"),
|
||||
'release': software_version})
|
||||
|
||||
response = self.app.post(self.get_api_prefix(),
|
||||
params=params,
|
||||
upload_files=upload_files,
|
||||
headers=self.get_api_headers(),
|
||||
expect_errors=True)
|
||||
|
||||
self.assertEqual(response.status_code, http_client.OK)
|
||||
self.assertEqual(software_version, response.json['software-version'])
|
||||
|
||||
# Revert the software_version value
|
||||
self.install_data['software_version'] = SW_VERSION
|
||||
|
||||
@mock.patch.object(subclouds.PatchingClient, 'query')
|
||||
def test_post_subcloud_when_partial_applied_patch(self, mock_query):
|
||||
"""Test POST operation when there is a partial-applied patch."""
|
||||
|
@ -528,9 +611,12 @@ class TestSubcloudPost(testroot.DCManagerApiTest,
|
|||
self.assertEqual(http_client.UNPROCESSABLE_ENTITY, response.status_code)
|
||||
self.assertEqual('text/plain', response.content_type)
|
||||
|
||||
def test_post_subcloud_install_values_no_bmc_password(self):
|
||||
@mock.patch('dcmanager.common.utils.get_vault_load_files')
|
||||
def test_post_subcloud_install_values_no_bmc_password(self, mock_vault_files):
|
||||
"""Test POST operation with install values is supported by the API."""
|
||||
|
||||
mock_vault_files.return_value = ('fake_iso', 'fake_sig')
|
||||
|
||||
# pass a different "install" list of files for this POST
|
||||
self.set_list_of_post_files(subclouds.SUBCLOUD_ADD_GET_FILE_CONTENTS)
|
||||
upload_files = self.get_post_upload_files()
|
||||
|
@ -555,12 +641,33 @@ class TestSubcloudPost(testroot.DCManagerApiTest,
|
|||
headers=self.get_api_headers())
|
||||
self._verify_post_success(response)
|
||||
|
||||
@mock.patch('dcmanager.common.utils.get_vault_load_files')
|
||||
def test_post_subcloud_missing_image(self, mock_vault_files):
|
||||
"""Test POST operation without image in install values and vault files."""
|
||||
|
||||
mock_vault_files.return_value = (None, None)
|
||||
|
||||
params = self.get_post_params()
|
||||
# add bmc_password to params
|
||||
params.update(
|
||||
{'bmc_password':
|
||||
base64.b64encode('fake pass'.encode("utf-8")).decode("utf-8")})
|
||||
|
||||
self.set_list_of_post_files(subclouds.SUBCLOUD_ADD_GET_FILE_CONTENTS)
|
||||
self.install_data = copy.copy(self.FAKE_INSTALL_DATA)
|
||||
upload_files = self.get_post_upload_files()
|
||||
response = self.app.post(self.get_api_prefix(),
|
||||
params=params,
|
||||
upload_files=upload_files,
|
||||
headers=self.get_api_headers(),
|
||||
expect_errors=True)
|
||||
self.assertEqual(response.status_code, http_client.BAD_REQUEST)
|
||||
|
||||
@mock.patch('dcmanager.common.utils.get_vault_load_files')
|
||||
def test_post_subcloud_install_values_missing(self, mock_vault_files):
|
||||
"""Test POST operation with install values fails if data missing."""
|
||||
|
||||
# todo(abailey): add a new unit test with no image and no vault files
|
||||
mock_vault_files.return_value = (None, None)
|
||||
mock_vault_files.return_value = ('fake_iso', 'fake_sig')
|
||||
|
||||
params = self.get_post_params()
|
||||
# add bmc_password to params
|
||||
|
@ -581,15 +688,45 @@ class TestSubcloudPost(testroot.DCManagerApiTest,
|
|||
expect_errors=True)
|
||||
self._verify_post_failure(response, key, None)
|
||||
|
||||
@mock.patch('dcmanager.common.utils.get_vault_load_files')
|
||||
@mock.patch.object(cutils, 'get_playbook_for_software_version')
|
||||
@mock.patch.object(cutils, 'get_value_from_yaml_file')
|
||||
def test_post_subcloud_bad_kubernetes_version(self,
|
||||
mock_get_value_from_yaml_file,
|
||||
mock_get_playbook_for_software_version,
|
||||
mock_vault_files):
|
||||
"""Test POST operation with bad kubernetes_version."""
|
||||
|
||||
mock_vault_files.return_value = ('fake_iso', 'fake_sig')
|
||||
# try with nothing removed and verify it works
|
||||
|
||||
software_version = '21.12'
|
||||
# Update the software_version value to match the release parameter value,
|
||||
# otherwise, the request will be rejected
|
||||
self.install_data['software_version'] = software_version
|
||||
|
||||
params = self.get_post_params()
|
||||
# add bmc_password to params
|
||||
params.update(
|
||||
{'bmc_password':
|
||||
base64.b64encode('fake pass'.encode("utf-8")).decode("utf-8"),
|
||||
'release': software_version})
|
||||
|
||||
# Add kubernetes version to bootstrap_data
|
||||
self.bootstrap_data['kubernetes_version'] = '1.21.8'
|
||||
mock_get_value_from_yaml_file.return_value = '1.23.1'
|
||||
|
||||
self.set_list_of_post_files(subclouds.SUBCLOUD_ADD_GET_FILE_CONTENTS)
|
||||
self.install_data = copy.copy(self.FAKE_INSTALL_DATA)
|
||||
upload_files = self.get_post_upload_files()
|
||||
response = self.app.post(self.get_api_prefix(),
|
||||
params=params,
|
||||
upload_files=upload_files,
|
||||
headers=self.get_api_headers())
|
||||
self._verify_post_success(response)
|
||||
headers=self.get_api_headers(),
|
||||
expect_errors=True)
|
||||
self.assertEqual(response.status_code, http_client.BAD_REQUEST)
|
||||
|
||||
# Revert the change of bootstrap_data
|
||||
del self.bootstrap_data['kubernetes_version']
|
||||
|
||||
def _test_post_input_value_inputs(self,
|
||||
setup_overrides,
|
||||
|
@ -614,6 +751,7 @@ class TestSubcloudPost(testroot.DCManagerApiTest,
|
|||
starting_data = copy.copy(self.FAKE_INSTALL_DATA)
|
||||
for key, val in setup_overrides.items():
|
||||
starting_data[key] = val
|
||||
starting_data['image'] = 'fake image'
|
||||
|
||||
# Test all the bad param values
|
||||
for bad_value in bad_values:
|
||||
|
@ -661,9 +799,11 @@ class TestSubcloudPost(testroot.DCManagerApiTest,
|
|||
headers=self.get_api_headers())
|
||||
self._verify_post_success(response)
|
||||
|
||||
def test_post_subcloud_install_values_invalid_type(self):
|
||||
@mock.patch('dcmanager.common.utils.get_vault_load_files')
|
||||
def test_post_subcloud_install_values_invalid_type(self, mock_vault_files):
|
||||
"""Test POST with an invalid type specified in install values."""
|
||||
|
||||
mock_vault_files.return_value = ('fake_iso', 'fake_sig')
|
||||
setup_overrides = {}
|
||||
required_overrides = {}
|
||||
# the install_type must a number 0 <= X <=5
|
||||
|
@ -679,9 +819,11 @@ class TestSubcloudPost(testroot.DCManagerApiTest,
|
|||
self._test_post_input_value_inputs(setup_overrides, required_overrides,
|
||||
install_key, bad_values, good_value)
|
||||
|
||||
def test_post_subcloud_install_bad_bootstrap_ip(self):
|
||||
@mock.patch('dcmanager.common.utils.get_vault_load_files')
|
||||
def test_post_subcloud_install_bad_bootstrap_ip(self, mock_vault_files):
|
||||
"""Test POST with invalid boostrap ip specified in install values."""
|
||||
|
||||
mock_vault_files.return_value = ('fake_iso', 'fake_sig')
|
||||
setup_overrides = {}
|
||||
required_overrides = {}
|
||||
install_key = "bootstrap_address"
|
||||
|
@ -694,9 +836,11 @@ class TestSubcloudPost(testroot.DCManagerApiTest,
|
|||
self._test_post_input_value_inputs(setup_overrides, required_overrides,
|
||||
install_key, bad_values, good_value)
|
||||
|
||||
def test_post_subcloud_install_bad_bmc_ip(self):
|
||||
@mock.patch('dcmanager.common.utils.get_vault_load_files')
|
||||
def test_post_subcloud_install_bad_bmc_ip(self, mock_vault_files):
|
||||
"""Test POST with invalid bmc ip specified in install values."""
|
||||
|
||||
mock_vault_files.return_value = ('fake_iso', 'fake_sig')
|
||||
setup_overrides = {}
|
||||
required_overrides = {}
|
||||
install_key = "bmc_address"
|
||||
|
@ -708,9 +852,11 @@ class TestSubcloudPost(testroot.DCManagerApiTest,
|
|||
self._test_post_input_value_inputs(setup_overrides, required_overrides,
|
||||
install_key, bad_values, good_value)
|
||||
|
||||
def test_post_subcloud_install_bad_persistent_size(self):
|
||||
@mock.patch('dcmanager.common.utils.get_vault_load_files')
|
||||
def test_post_subcloud_install_bad_persistent_size(self, mock_vault_files):
|
||||
"""Test POST with invalid persistent_size specified in install values."""
|
||||
|
||||
mock_vault_files.return_value = ('fake_iso', 'fake_sig')
|
||||
setup_overrides = {}
|
||||
required_overrides = {}
|
||||
install_key = "persistent_size"
|
||||
|
@ -723,9 +869,11 @@ class TestSubcloudPost(testroot.DCManagerApiTest,
|
|||
self._test_post_input_value_inputs(setup_overrides, required_overrides,
|
||||
install_key, bad_values, good_value)
|
||||
|
||||
def test_post_subcloud_install_bad_nexthop_gateway(self):
|
||||
@mock.patch('dcmanager.common.utils.get_vault_load_files')
|
||||
def test_post_subcloud_install_bad_nexthop_gateway(self, mock_vault_files):
|
||||
"""Test POST with invalid nexthop_gateway in install values."""
|
||||
|
||||
mock_vault_files.return_value = ('fake_iso', 'fake_sig')
|
||||
setup_overrides = {}
|
||||
required_overrides = {}
|
||||
# nexthop_gateway is not required. but if provided, it must be valid
|
||||
|
@ -738,9 +886,11 @@ class TestSubcloudPost(testroot.DCManagerApiTest,
|
|||
self._test_post_input_value_inputs(setup_overrides, required_overrides,
|
||||
install_key, bad_values, good_value)
|
||||
|
||||
def test_post_subcloud_install_bad_network_address(self):
|
||||
@mock.patch('dcmanager.common.utils.get_vault_load_files')
|
||||
def test_post_subcloud_install_bad_network_address(self, mock_vault_files):
|
||||
"""Test POST with invalid network_address in install values."""
|
||||
|
||||
mock_vault_files.return_value = ('fake_iso', 'fake_sig')
|
||||
setup_overrides = {}
|
||||
# The nexthop_gateway is required when network_address is present
|
||||
# The network mask is required when network address is present
|
||||
|
@ -757,9 +907,11 @@ class TestSubcloudPost(testroot.DCManagerApiTest,
|
|||
self._test_post_input_value_inputs(setup_overrides, required_overrides,
|
||||
install_key, bad_values, good_value)
|
||||
|
||||
def test_post_subcloud_install_bad_network_mask(self):
|
||||
@mock.patch('dcmanager.common.utils.get_vault_load_files')
|
||||
def test_post_subcloud_install_bad_network_mask(self, mock_vault_files):
|
||||
"""Test POST with invalid network_mask in install values."""
|
||||
|
||||
mock_vault_files.return_value = ('fake_iso', 'fake_sig')
|
||||
# network_address is not required. but if provided a valid network_mask
|
||||
# is needed
|
||||
setup_overrides = {
|
||||
|
@ -778,9 +930,11 @@ class TestSubcloudPost(testroot.DCManagerApiTest,
|
|||
self._test_post_input_value_inputs(setup_overrides, required_overrides,
|
||||
install_key, bad_values, good_value)
|
||||
|
||||
def test_post_subcloud_install_diff_bmc_ip_version(self):
|
||||
@mock.patch('dcmanager.common.utils.get_vault_load_files')
|
||||
def test_post_subcloud_install_diff_bmc_ip_version(self, mock_vault_files):
|
||||
"""Test POST install values with mismatched(ipv4/ipv6) bmc ip."""
|
||||
|
||||
mock_vault_files.return_value = ('fake_iso', 'fake_sig')
|
||||
setup_overrides = {
|
||||
"bootstrap_address": "192.168.1.2"
|
||||
}
|
||||
|
@ -795,9 +949,11 @@ class TestSubcloudPost(testroot.DCManagerApiTest,
|
|||
self._test_post_input_value_inputs(setup_overrides, required_overrides,
|
||||
install_key, bad_values, good_value)
|
||||
|
||||
def test_post_subcloud_install_diff_bmc_ip_version_ipv6(self):
|
||||
@mock.patch('dcmanager.common.utils.get_vault_load_files')
|
||||
def test_post_subcloud_install_diff_bmc_ip_version_ipv6(self, mock_vault_files):
|
||||
"""Test POST install values with mismatched(ipv6/ipv4) bmc ip."""
|
||||
|
||||
mock_vault_files.return_value = ('fake_iso', 'fake_sig')
|
||||
# version of bootstrap address must be same as bmc_address
|
||||
setup_overrides = {
|
||||
"bootstrap_address": "fd01:6::7"
|
||||
|
@ -812,9 +968,11 @@ class TestSubcloudPost(testroot.DCManagerApiTest,
|
|||
self._test_post_input_value_inputs(setup_overrides, required_overrides,
|
||||
install_key, bad_values, good_value)
|
||||
|
||||
def test_post_subcloud_install_diff_nexthop_ip_version(self):
|
||||
@mock.patch('dcmanager.common.utils.get_vault_load_files')
|
||||
def test_post_subcloud_install_diff_nexthop_ip_version(self, mock_vault_files):
|
||||
"""Test POST install values mismatched(ipv4/ipv6) nexthop_gateway."""
|
||||
|
||||
mock_vault_files.return_value = ('fake_iso', 'fake_sig')
|
||||
# ip version of bootstrap address must be same as nexthop_gateway
|
||||
# All required addresses (like bmc address) much match bootstrap
|
||||
# default bmc address is ipv4
|
||||
|
@ -828,9 +986,12 @@ class TestSubcloudPost(testroot.DCManagerApiTest,
|
|||
self._test_post_input_value_inputs(setup_overrides, required_overrides,
|
||||
install_key, bad_values, good_value)
|
||||
|
||||
def test_post_subcloud_install_diff_nexthop_ip_version_ipv6(self):
|
||||
@mock.patch('dcmanager.common.utils.get_vault_load_files')
|
||||
def test_post_subcloud_install_diff_nexthop_ip_version_ipv6(self,
|
||||
mock_vault_files):
|
||||
"""Test POST install values with mismatched(ipv6/ipv4) bmc ip."""
|
||||
|
||||
mock_vault_files.return_value = ('fake_iso', 'fake_sig')
|
||||
# version of bootstrap address must be same as nexthop_gateway
|
||||
# All required addresses must also be setup ipv6 such as bmc_address
|
||||
# default bmc address is ipv4
|
||||
|
@ -1037,9 +1198,11 @@ class TestSubcloudAPIOther(testroot.DCManagerApiTest):
|
|||
|
||||
@mock.patch.object(rpc_client, 'ManagerClient')
|
||||
@mock.patch.object(subclouds.SubcloudsController, '_get_patch_data')
|
||||
def test_update_subcloud_install_values_persistent_size(self,
|
||||
@mock.patch('dcmanager.common.utils.get_vault_load_files')
|
||||
def test_update_subcloud_install_values_persistent_size(self, mock_vault_files,
|
||||
mock_get_patch_data,
|
||||
mock_rpc_client):
|
||||
mock_vault_files.return_value = ('fake_iso', 'fake_sig')
|
||||
subcloud = fake_subcloud.create_fake_subcloud(self.ctx, data_install=None)
|
||||
payload = {}
|
||||
install_data = copy.copy(FAKE_SUBCLOUD_INSTALL_VALUES_WITH_PERSISTENT_SIZE)
|
||||
|
@ -1102,8 +1265,11 @@ class TestSubcloudAPIOther(testroot.DCManagerApiTest):
|
|||
|
||||
@mock.patch.object(rpc_client, 'ManagerClient')
|
||||
@mock.patch.object(subclouds.SubcloudsController, '_get_patch_data')
|
||||
def test_patch_subcloud_install_values(self, mock_get_patch_data,
|
||||
@mock.patch('dcmanager.common.utils.get_vault_load_files')
|
||||
def test_patch_subcloud_install_values(self, mock_vault_files,
|
||||
mock_get_patch_data,
|
||||
mock_rpc_client):
|
||||
mock_vault_files.return_value = ('fake_iso', 'fake_sig')
|
||||
subcloud = fake_subcloud.create_fake_subcloud(self.ctx, data_install=None)
|
||||
payload = {}
|
||||
install_data = copy.copy(FAKE_SUBCLOUD_INSTALL_VALUES)
|
||||
|
@ -1136,12 +1302,14 @@ class TestSubcloudAPIOther(testroot.DCManagerApiTest):
|
|||
|
||||
@mock.patch.object(rpc_client, 'ManagerClient')
|
||||
@mock.patch.object(subclouds.SubcloudsController, '_get_patch_data')
|
||||
@mock.patch('dcmanager.common.utils.get_vault_load_files')
|
||||
def test_patch_subcloud_install_values_with_existing_data_install(
|
||||
self, mock_get_patch_data, mock_rpc_client):
|
||||
self, mock_vault_files, mock_get_patch_data, mock_rpc_client):
|
||||
mock_vault_files.return_value = ('fake_iso', 'fake_sig')
|
||||
install_data = copy.copy(FAKE_SUBCLOUD_INSTALL_VALUES)
|
||||
subcloud = fake_subcloud.create_fake_subcloud(
|
||||
self.ctx, data_install=json.dumps(install_data))
|
||||
install_data.update({"software_version": "18.04"})
|
||||
install_data.update({"install_type": 2})
|
||||
payload = {}
|
||||
encoded_password = base64.b64encode(
|
||||
'bmc_password'.encode("utf-8")).decode('utf-8')
|
||||
|
@ -1455,8 +1623,10 @@ class TestSubcloudAPIOther(testroot.DCManagerApiTest):
|
|||
@mock.patch.object(subclouds.SubcloudsController, '_get_subcloud_db_install_values')
|
||||
@mock.patch.object(subclouds.SubcloudsController, '_validate_oam_network_config')
|
||||
@mock.patch.object(subclouds.SubcloudsController, '_get_request_data')
|
||||
@mock.patch.object(subclouds.SubcloudsController, '_upload_deploy_config_file')
|
||||
def test_reinstall_subcloud(
|
||||
self, mock_get_request_data, mock_validate_oam_network_config,
|
||||
self, mock_upload_deploy_config_file,
|
||||
mock_get_request_data, mock_validate_oam_network_config,
|
||||
mock_get_subcloud_db_install_values, mock_rpc_client,
|
||||
mock_get_vault_load_files):
|
||||
|
||||
|
@ -1485,6 +1655,56 @@ class TestSubcloudAPIOther(testroot.DCManagerApiTest):
|
|||
mock.ANY)
|
||||
self.assertEqual(response.status_int, 200)
|
||||
|
||||
mock_upload_deploy_config_file.assert_called_once()
|
||||
self.assertEqual(SW_VERSION, response.json['software-version'])
|
||||
|
||||
@mock.patch.object(cutils, 'get_vault_load_files')
|
||||
@mock.patch.object(rpc_client, 'ManagerClient')
|
||||
@mock.patch.object(subclouds.SubcloudsController,
|
||||
'_get_subcloud_db_install_values')
|
||||
@mock.patch.object(subclouds.SubcloudsController, '_validate_oam_network_config')
|
||||
@mock.patch.object(subclouds.SubcloudsController, '_get_request_data')
|
||||
@mock.patch.object(subclouds.SubcloudsController, '_upload_deploy_config_file')
|
||||
@mock.patch.object(subclouds.SubcloudsController, '_validate_k8s_version')
|
||||
def test_reinstall_subcloud_with_release_parameter(
|
||||
self, mock_validate_k8s_version, mock_upload_deploy_config_file,
|
||||
mock_get_request_data, mock_validate_oam_network_config,
|
||||
mock_get_subcloud_db_install_values, mock_rpc_client,
|
||||
mock_get_vault_load_files):
|
||||
|
||||
software_version = '21.12'
|
||||
subcloud = fake_subcloud.create_fake_subcloud(self.ctx)
|
||||
install_data = copy.copy(FAKE_SUBCLOUD_INSTALL_VALUES)
|
||||
reinstall_data = copy.copy(FAKE_SUBCLOUD_BOOTSTRAP_PAYLOAD)
|
||||
reinstall_data['release'] = software_version
|
||||
mock_get_request_data.return_value = reinstall_data
|
||||
|
||||
encoded_password = base64.b64encode(
|
||||
'bmc_password'.encode("utf-8")).decode('utf-8')
|
||||
bmc_password = {'bmc_password': encoded_password}
|
||||
install_data.update(bmc_password)
|
||||
mock_get_subcloud_db_install_values.return_value = install_data
|
||||
|
||||
mock_rpc_client().reinstall_subcloud.return_value = True
|
||||
mock_get_vault_load_files.return_value = ('iso_file_path', 'sig_file_path')
|
||||
|
||||
response = self.app.patch_json(
|
||||
FAKE_URL + '/' + str(subcloud.id) + '/reinstall',
|
||||
headers=FAKE_HEADERS, params=reinstall_data)
|
||||
|
||||
mock_validate_oam_network_config.assert_called_once()
|
||||
mock_rpc_client().reinstall_subcloud.assert_called_once_with(
|
||||
mock.ANY,
|
||||
subcloud.id,
|
||||
mock.ANY)
|
||||
self.assertEqual(response.status_int, 200)
|
||||
|
||||
mock_validate_k8s_version.assert_called_once()
|
||||
mock_upload_deploy_config_file.assert_called_once()
|
||||
self.assertEqual(software_version, response.json['software-version'])
|
||||
self.assertIn(software_version,
|
||||
json.loads(response.json['data_install'])['software_version'])
|
||||
|
||||
@mock.patch.object(cutils, 'get_vault_load_files')
|
||||
@mock.patch.object(rpc_client, 'ManagerClient')
|
||||
@mock.patch.object(subclouds.SubcloudsController, '_get_subcloud_db_install_values')
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#
|
||||
# Copyright (c) 2020-2022 Wind River Systems, Inc.
|
||||
# Copyright (c) 2020-2023 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
@ -16,6 +16,8 @@ FAKE_ID = '1'
|
|||
FAKE_URL = '/v1.0/subclouds'
|
||||
WRONG_URL = '/v1.0/wrong'
|
||||
|
||||
FAKE_SOFTWARE_VERSION = '18.03'
|
||||
|
||||
FAKE_HEADERS = {'X-Tenant-Id': FAKE_TENANT, 'X_ROLE': 'admin,member,reader',
|
||||
'X-Identity-Status': 'Confirmed', 'X-Project-Name': 'admin'}
|
||||
|
||||
|
@ -61,7 +63,7 @@ FAKE_SUBCLOUD_BOOTSTRAP_PAYLOAD = {
|
|||
|
||||
FAKE_SUBCLOUD_INSTALL_VALUES = {
|
||||
"image": "http://192.168.101.2:8080/iso/bootimage.iso",
|
||||
"software_version": "18.03",
|
||||
"software_version": FAKE_SOFTWARE_VERSION,
|
||||
"bootstrap_interface": "eno1",
|
||||
"bootstrap_address": "128.224.151.183",
|
||||
"bootstrap_address_prefix": 23,
|
||||
|
@ -81,7 +83,7 @@ FAKE_SUBCLOUD_INSTALL_VALUES = {
|
|||
|
||||
FAKE_SUBCLOUD_INSTALL_VALUES_WITH_PERSISTENT_SIZE = {
|
||||
"image": "http://192.168.101.2:8080/iso/bootimage.iso",
|
||||
"software_version": "18.03",
|
||||
"software_version": FAKE_SOFTWARE_VERSION,
|
||||
"bootstrap_interface": "eno1",
|
||||
"bootstrap_address": "128.224.151.183",
|
||||
"bootstrap_address_prefix": 23,
|
||||
|
@ -105,7 +107,7 @@ def create_fake_subcloud(ctxt, **kwargs):
|
|||
"name": "subcloud1",
|
||||
"description": "subcloud1 description",
|
||||
"location": "subcloud1 location",
|
||||
'software_version': "18.03",
|
||||
'software_version': FAKE_SOFTWARE_VERSION,
|
||||
"management_subnet": "192.168.101.0/24",
|
||||
"management_gateway_ip": "192.168.101.1",
|
||||
"management_start_ip": "192.168.101.2",
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
import copy
|
||||
import datetime
|
||||
|
||||
import mock
|
||||
|
||||
from os import path as os_path
|
||||
|
@ -40,6 +41,7 @@ from dcmanager.tests import utils
|
|||
from tsconfig.tsconfig import SW_VERSION
|
||||
|
||||
LAST_SW_VERSION_IN_CENTOS = "22.06"
|
||||
FAKE_PREVIOUS_SW_VERSION = '21.12'
|
||||
|
||||
|
||||
FAKE_ADMIN_USER_ID = 1
|
||||
|
@ -408,6 +410,8 @@ class TestSubcloudManager(base.DCManagerTestCase):
|
|||
self.assertEqual('localhost', sm.host)
|
||||
self.assertEqual(self.ctx, sm.context)
|
||||
|
||||
@mock.patch.object(subcloud_manager.SubcloudManager,
|
||||
'compose_apply_command')
|
||||
@mock.patch.object(subcloud_manager.SubcloudManager,
|
||||
'compose_rehome_command')
|
||||
@mock.patch.object(subcloud_manager.SubcloudManager,
|
||||
|
@ -435,12 +439,13 @@ class TestSubcloudManager(base.DCManagerTestCase):
|
|||
mock_keystone_client,
|
||||
mock_delete_subcloud_inventory,
|
||||
mock_create_intermediate_ca_cert,
|
||||
mock_compose_rehome_command):
|
||||
mock_compose_rehome_command,
|
||||
mock_compose_apply_command):
|
||||
values = utils.create_subcloud_dict(base.SUBCLOUD_SAMPLE_DATA_0)
|
||||
values['deploy_status'] = consts.DEPLOY_STATE_NONE
|
||||
|
||||
# dcmanager add_subcloud queries the data from the db
|
||||
self.create_subcloud_static(self.ctx, name=values['name'])
|
||||
subcloud = self.create_subcloud_static(self.ctx, name=values['name'])
|
||||
|
||||
mock_keystone_client().keystone_client = FakeKeystoneClient()
|
||||
mock_keyring.get_password.return_value = "testpassword"
|
||||
|
@ -458,6 +463,10 @@ class TestSubcloudManager(base.DCManagerTestCase):
|
|||
mock_thread_start.assert_called_once()
|
||||
mock_create_intermediate_ca_cert.assert_called_once()
|
||||
mock_compose_rehome_command.assert_not_called()
|
||||
mock_compose_apply_command.assert_called_once_with(
|
||||
values['name'],
|
||||
sm._get_ansible_filename(values['name'], consts.INVENTORY_FILE_POSTFIX),
|
||||
subcloud['software_version'])
|
||||
|
||||
# Verify subcloud was updated with correct values
|
||||
self.assertEqual(consts.DEPLOY_STATE_PRE_DEPLOY,
|
||||
|
@ -502,7 +511,7 @@ class TestSubcloudManager(base.DCManagerTestCase):
|
|||
values['migrate'] = 'true'
|
||||
|
||||
# dcmanager add_subcloud queries the data from the db
|
||||
self.create_subcloud_static(self.ctx, name=values['name'])
|
||||
subcloud = self.create_subcloud_static(self.ctx, name=values['name'])
|
||||
|
||||
mock_keystone_client().keystone_client = FakeKeystoneClient()
|
||||
mock_keyring.get_password.return_value = "testpassword"
|
||||
|
@ -518,7 +527,10 @@ class TestSubcloudManager(base.DCManagerTestCase):
|
|||
mock_write_subcloud_ansible_config.assert_called_once()
|
||||
mock_thread_start.assert_called_once()
|
||||
mock_create_intermediate_ca_cert.assert_called_once()
|
||||
mock_compose_rehome_command.assert_called_once()
|
||||
mock_compose_rehome_command.assert_called_once_with(
|
||||
values['name'],
|
||||
sm._get_ansible_filename(values['name'], consts.INVENTORY_FILE_POSTFIX),
|
||||
subcloud['software_version'])
|
||||
|
||||
# Verify subcloud was updated with correct values
|
||||
self.assertEqual(consts.DEPLOY_STATE_PRE_REHOME,
|
||||
|
@ -1379,7 +1391,9 @@ class TestSubcloudManager(base.DCManagerTestCase):
|
|||
def test_compose_install_command(self):
|
||||
sm = subcloud_manager.SubcloudManager()
|
||||
install_command = sm.compose_install_command(
|
||||
'subcloud1', '/var/opt/dc/ansible/subcloud1_inventory.yml')
|
||||
'subcloud1',
|
||||
'/var/opt/dc/ansible/subcloud1_inventory.yml',
|
||||
FAKE_PREVIOUS_SW_VERSION)
|
||||
self.assertEqual(
|
||||
install_command,
|
||||
[
|
||||
|
@ -1387,20 +1401,27 @@ class TestSubcloudManager(base.DCManagerTestCase):
|
|||
subcloud_manager.ANSIBLE_SUBCLOUD_INSTALL_PLAYBOOK,
|
||||
'-i', '/var/opt/dc/ansible/subcloud1_inventory.yml',
|
||||
'--limit', 'subcloud1',
|
||||
'-e', "@/var/opt/dc/ansible/subcloud1/install_values.yml"
|
||||
'-e', "@/var/opt/dc/ansible/subcloud1/install_values.yml",
|
||||
'-e', "install_release_version=%s" % FAKE_PREVIOUS_SW_VERSION
|
||||
]
|
||||
)
|
||||
|
||||
def test_compose_apply_command(self):
|
||||
@mock.patch('os.path.isfile')
|
||||
def test_compose_apply_command(self, mock_isfile):
|
||||
mock_isfile.return_value = True
|
||||
sm = subcloud_manager.SubcloudManager()
|
||||
apply_command = sm.compose_apply_command(
|
||||
'subcloud1', '/var/opt/dc/ansible/subcloud1_inventory.yml')
|
||||
'subcloud1',
|
||||
'/var/opt/dc/ansible/subcloud1_inventory.yml',
|
||||
FAKE_PREVIOUS_SW_VERSION)
|
||||
self.assertEqual(
|
||||
apply_command,
|
||||
[
|
||||
'ansible-playbook',
|
||||
subcloud_manager.ANSIBLE_SUBCLOUD_PLAYBOOK, '-i',
|
||||
'/var/opt/dc/ansible/subcloud1_inventory.yml',
|
||||
cutils.get_playbook_for_software_version(
|
||||
subcloud_manager.ANSIBLE_SUBCLOUD_PLAYBOOK,
|
||||
FAKE_PREVIOUS_SW_VERSION),
|
||||
'-i', '/var/opt/dc/ansible/subcloud1_inventory.yml',
|
||||
'--limit', 'subcloud1', '-e',
|
||||
"override_files_dir='/var/opt/dc/ansible' region_name=subcloud1"
|
||||
]
|
||||
|
@ -1427,16 +1448,22 @@ class TestSubcloudManager(base.DCManagerTestCase):
|
|||
]
|
||||
)
|
||||
|
||||
def test_compose_rehome_command(self):
|
||||
@mock.patch('os.path.isfile')
|
||||
def test_compose_rehome_command(self, mock_isfile):
|
||||
mock_isfile.return_value = True
|
||||
sm = subcloud_manager.SubcloudManager()
|
||||
rehome_command = sm.compose_rehome_command(
|
||||
'subcloud1', '/var/opt/dc/ansible/subcloud1_inventory.yml')
|
||||
'subcloud1',
|
||||
'/var/opt/dc/ansible/subcloud1_inventory.yml',
|
||||
FAKE_PREVIOUS_SW_VERSION)
|
||||
self.assertEqual(
|
||||
rehome_command,
|
||||
[
|
||||
'ansible-playbook',
|
||||
subcloud_manager.ANSIBLE_SUBCLOUD_REHOME_PLAYBOOK, '-i',
|
||||
'/var/opt/dc/ansible/subcloud1_inventory.yml',
|
||||
cutils.get_playbook_for_software_version(
|
||||
subcloud_manager.ANSIBLE_SUBCLOUD_REHOME_PLAYBOOK,
|
||||
FAKE_PREVIOUS_SW_VERSION),
|
||||
'-i', '/var/opt/dc/ansible/subcloud1_inventory.yml',
|
||||
'--limit', 'subcloud1',
|
||||
'--timeout', subcloud_manager.REHOME_PLAYBOOK_TIMEOUT,
|
||||
'-e',
|
||||
|
@ -1463,9 +1490,10 @@ class TestSubcloudManager(base.DCManagerTestCase):
|
|||
mock_compose_apply_command, mock_compose_install_command,
|
||||
mock_create_intermediate_ca_cert, mock_write_subcloud_ansible_config):
|
||||
|
||||
subcloud_name = 'subcloud1'
|
||||
subcloud = self.create_subcloud_static(
|
||||
self.ctx,
|
||||
name='subcloud1',
|
||||
name=subcloud_name,
|
||||
deploy_status=consts.DEPLOY_STATE_PRE_INSTALL)
|
||||
|
||||
fake_install_values = \
|
||||
|
@ -1474,7 +1502,7 @@ class TestSubcloudManager(base.DCManagerTestCase):
|
|||
fake_payload = copy.copy(fake_subcloud.FAKE_SUBCLOUD_BOOTSTRAP_PAYLOAD)
|
||||
fake_payload.update({
|
||||
'bmc_password': 'bmc_pass',
|
||||
'software_version': SW_VERSION,
|
||||
'software_version': FAKE_PREVIOUS_SW_VERSION,
|
||||
'install_values': fake_install_values})
|
||||
|
||||
sm = subcloud_manager.SubcloudManager()
|
||||
|
@ -1487,8 +1515,14 @@ class TestSubcloudManager(base.DCManagerTestCase):
|
|||
mock_create_subcloud_inventory.assert_called_once()
|
||||
mock_create_intermediate_ca_cert.assert_called_once()
|
||||
mock_write_subcloud_ansible_config.assert_called_once()
|
||||
mock_compose_install_command.assert_called_once()
|
||||
mock_compose_apply_command.assert_called_once()
|
||||
mock_compose_install_command.assert_called_once_with(
|
||||
subcloud_name,
|
||||
sm._get_ansible_filename(subcloud_name, consts.INVENTORY_FILE_POSTFIX),
|
||||
FAKE_PREVIOUS_SW_VERSION)
|
||||
mock_compose_apply_command.assert_called_once_with(
|
||||
subcloud_name,
|
||||
sm._get_ansible_filename(subcloud_name, consts.INVENTORY_FILE_POSTFIX),
|
||||
FAKE_PREVIOUS_SW_VERSION)
|
||||
mock_thread_start.assert_called_once()
|
||||
|
||||
def test_handle_subcloud_operations_in_progress(self):
|
||||
|
|
Loading…
Reference in New Issue