Add release optionality to subcloud prestage

Add an optional --release parameter to subcloud prestage and
prestage_strategy create commands to enable release optionality
in subcloud prestage.

Test Plan:
  - Verify successful subcloud prestage with specified 21.12 or 22.12
    release.
  - Verify successful subcloud prestage with the active release (22.12)
    when the release parameter is absent.
  - Verify the subcloud prestage request was rejected by specifying
    a release other than the subcloud and system controller's current
    release(s) and any inactive load release.
  - Verify successful creation of prestage strategy with specified
    21.12 or 22.12 release.
  - Verify successful creation of prestage strategy with the active
    release (22.12) when the release parameter is absent.

Depends-On: https://review.opendev.org/c/starlingx/ansible-playbooks/+/880788

Story: 2010611
Task: 47848

Signed-off-by: lzhu1 <li.zhu@windriver.com>
Change-Id: I125b164c223074b42f16c9cf039771a4802d44dc
This commit is contained in:
Li Zhu 2023-04-18 23:40:21 -04:00
parent 4af71c2e29
commit f174505b66
17 changed files with 362 additions and 381 deletions

View File

@ -541,6 +541,78 @@ Response Example
.. literalinclude:: samples/subclouds/subcloud-patch-reinstall-response.json .. literalinclude:: samples/subclouds/subcloud-patch-reinstall-response.json
:language: json :language: json
********************************
Prestage a specific subcloud
********************************
.. rest_method:: PATCH /v1.0/subclouds/{subcloud}/prestage
Prestages a subcloud with software packages and container image archives.
The prestaged data is stored in the subcloud persistent file system
that can be used when the subcloud is reinstalled next.
**Normal response codes**
200
**Error response codes**
badRequest (400), unauthorized (401), forbidden (403), badMethod (405),
HTTPUnprocessableEntity (422), internalServerError (500),
serviceUnavailable (503)
**Request parameters**
.. rest_parameters:: parameters.yaml
- subcloud: subcloud_uri
- release: release
- sysadmin_password: sysadmin_password
- force: force
Request Example
----------------
.. literalinclude:: samples/subclouds/subcloud-patch-prestage-request.json
:language: json
**Response parameters**
.. rest_parameters:: parameters.yaml
- id: subcloud_id
- group_id: group_id
- name: subcloud_name
- description: subcloud_description
- location: subcloud_location
- software-version: software_version
- availability-status: availability_status
- error-description: error_description
- deploy-status: deploy_status
- backup-status: backup_status
- backup-datetime: backup_datetime
- openstack-installed: openstack_installed
- management-state: management_state
- systemcontroller-gateway-ip: systemcontroller_gateway_ip
- management-start-ip: management_start_ip
- management-end-ip: management_end_ip
- management-subnet: management_subnet
- management-gateway-ip: management_gateway_ip
- prestage_software_version: prestage_software_version
- created-at: created_at
- updated-at: updated_at
- data_install: data_install
- data_upgrade: data_upgrade
- endpoint_sync_status: endpoint_sync_status
- sync_status: sync_status
- endpoint_type: sync_status_type
Response Example
----------------
.. literalinclude:: samples/subclouds/subcloud-patch-prestage-response.json
:language: json
***************************************** *****************************************
Update the status of a specific subcloud Update the status of a specific subcloud
***************************************** *****************************************

View File

@ -225,6 +225,12 @@ extra_args:
in: body in: body
required: false required: false
type: dictionary type: dictionary
force:
description: |
Indicates whether to disregard subcloud management alarm condition.
in: body
required: false
type: boolean
group_id: group_id:
description: | description: |
The ID of a subcloud group. Default is 1. The ID of a subcloud group. Default is 1.
@ -317,6 +323,12 @@ patch_strategy_upload_only:
in: body in: body
required: false required: false
type: boolean type: boolean
prestage_software_version:
description: |
The prestage software version for the subcloud.
in: body
required: true
type: string
region_name: region_name:
description: | description: |
The name provisioned for the subcloud (synonym for subcloud name). The name provisioned for the subcloud (synonym for subcloud name).

View File

@ -0,0 +1,5 @@
{
"sysadmin_password": "XXXXXXX",
"release": "21.12",
"force": "true"
}

View File

@ -0,0 +1,26 @@
{
"id": 1,
"name": "subcloud1",
"created-at": "2021-11-08T18:41:19.530228",
"updated-at": "2021-11-15T14:15:59.944851",
"availability-status": "online",
"data_install": {
"bootstrap_interface": "eno1"
},
"data_upgrade": null,
"deploy-status": "complete",
"backup-status": "complete",
"backup-datetime": "2022-07-08 11:23:58.132134",
"description": "Ottawa Site",
"group_id": 1,
"location": "YOW",
"management-end-ip": "192.168.101.50",
"management-gateway-ip": "192.168.101.1",
"management-start-ip": "192.168.101.2",
"management-state": "unmanaged",
"management-subnet": "192.168.101.0/24",
"openstack-installed": false,
"software-version": "21.12",
"systemcontroller-gateway-ip": "192.168.204.101",
"prestage-software-version": "21.12",
}

View File

@ -416,7 +416,7 @@ class SubcloudInstall(object):
result.stdout.decode('utf-8').replace('\n', ', ')) result.stdout.decode('utf-8').replace('\n', ', '))
raise Exception(msg) raise Exception(msg)
def cleanup(self): def cleanup(self, software_version=None):
# Do not remove the input_iso if it is in the Load Vault # Do not remove the input_iso if it is in the Load Vault
if (self.input_iso is not None and if (self.input_iso is not None and
not self.input_iso.startswith(consts.LOAD_VAULT_DIR) and not self.input_iso.startswith(consts.LOAD_VAULT_DIR) and
@ -424,7 +424,7 @@ class SubcloudInstall(object):
os.remove(self.input_iso) os.remove(self.input_iso)
if (self.www_root is not None and os.path.isdir(self.www_root)): if (self.www_root is not None and os.path.isdir(self.www_root)):
if dccommon_utils.is_debian(): if dccommon_utils.is_debian(software_version):
cleanup_cmd = [ cleanup_cmd = [
GEN_ISO_COMMAND, GEN_ISO_COMMAND,
"--id", self.name, "--id", self.name,
@ -439,6 +439,7 @@ class SubcloudInstall(object):
"--delete" "--delete"
] ]
try: try:
LOG.info("Running install cleanup: %s", self.name)
with open(os.devnull, "w") as fnull: with open(os.devnull, "w") as fnull:
subprocess.check_call( # pylint: disable=E1102 subprocess.check_call( # pylint: disable=E1102
cleanup_cmd, stdout=fnull, stderr=fnull) cleanup_cmd, stdout=fnull, stderr=fnull)
@ -569,7 +570,7 @@ class SubcloudInstall(object):
if os.path.isdir(iso_dir_path): if os.path.isdir(iso_dir_path):
LOG.info("Found preexisting iso dir for subcloud %s, cleaning up", LOG.info("Found preexisting iso dir for subcloud %s, cleaning up",
self.name) self.name)
self.cleanup() self.cleanup(software_version)
# Update the default iso image based on the install values # Update the default iso image based on the install values
# Runs gen-bootloader-iso.sh # Runs gen-bootloader-iso.sh

View File

@ -213,7 +213,7 @@ class SubcloudsController(object):
@staticmethod @staticmethod
def _get_prestage_payload(request): def _get_prestage_payload(request):
fields = ['sysadmin_password', 'force'] fields = ['sysadmin_password', 'force', consts.PRESTAGE_REQUEST_RELEASE]
payload = { payload = {
'force': False 'force': False
} }
@ -243,6 +243,8 @@ class SubcloudsController(object):
else: else:
pecan.abort( pecan.abort(
400, _('Invalid value for force option: %s' % val)) 400, _('Invalid value for force option: %s' % val))
elif field == consts.PRESTAGE_REQUEST_RELEASE:
payload[consts.PRESTAGE_REQUEST_RELEASE] = val
return payload return payload
def _upload_config_file(self, file_item, config_file, config_type): def _upload_config_file(self, file_item, config_file, config_type):
@ -1516,12 +1518,18 @@ class SubcloudsController(object):
LOG.exception("validate_prestage failed") LOG.exception("validate_prestage failed")
pecan.abort(400, _(str(exc))) pecan.abort(400, _(str(exc)))
prestage_software_version = payload.get(
consts.PRESTAGE_REQUEST_RELEASE, tsc.SW_VERSION)
try: try:
self.dcmanager_rpc_client.prestage_subcloud(context, payload) self.dcmanager_rpc_client.prestage_subcloud(context, payload)
# local update to deploy_status - this is just for CLI response: # local update to deploy_status - this is just for CLI response:
subcloud.deploy_status = consts.PRESTAGE_STATE_PREPARE subcloud.deploy_status = consts.PRESTAGE_STATE_PACKAGES
return db_api.subcloud_db_model_to_dict(subcloud)
subcloud_dict = db_api.subcloud_db_model_to_dict(subcloud)
subcloud_dict.update(
{consts.PRESTAGE_SOFTWARE_VERSION: prestage_software_version})
return subcloud_dict
except RemoteError as e: except RemoteError as e:
pecan.abort(422, e.value) pecan.abort(422, e.value)
except Exception: except Exception:

View File

@ -154,7 +154,6 @@ STRATEGY_STATE_APPLYING_VIM_KUBE_ROOTCA_UPDATE_STRATEGY = \
# Prestage orchestration states (ordered) # Prestage orchestration states (ordered)
STRATEGY_STATE_PRESTAGE_PRE_CHECK = "prestage-precheck" STRATEGY_STATE_PRESTAGE_PRE_CHECK = "prestage-precheck"
STRATEGY_STATE_PRESTAGE_PREPARE = "prestage-prepare"
STRATEGY_STATE_PRESTAGE_PACKAGES = "prestaging-packages" STRATEGY_STATE_PRESTAGE_PACKAGES = "prestaging-packages"
STRATEGY_STATE_PRESTAGE_IMAGES = "prestaging-images" STRATEGY_STATE_PRESTAGE_IMAGES = "prestaging-images"
@ -255,15 +254,11 @@ UPGRADE_STATE_ACTIVATION_FAILED = 'activation-failed'
UPGRADE_STATE_ACTIVATION_COMPLETE = 'activation-complete' UPGRADE_STATE_ACTIVATION_COMPLETE = 'activation-complete'
# Prestage States # Prestage States
PRESTAGE_STATE_PREPARE = STRATEGY_STATE_PRESTAGE_PREPARE
PRESTAGE_STATE_PACKAGES = STRATEGY_STATE_PRESTAGE_PACKAGES PRESTAGE_STATE_PACKAGES = STRATEGY_STATE_PRESTAGE_PACKAGES
PRESTAGE_STATE_IMAGES = STRATEGY_STATE_PRESTAGE_IMAGES PRESTAGE_STATE_IMAGES = STRATEGY_STATE_PRESTAGE_IMAGES
PRESTAGE_STATE_FAILED = 'prestage-failed' PRESTAGE_STATE_FAILED = 'prestage-failed'
PRESTAGE_STATE_COMPLETE = 'prestage-complete' PRESTAGE_STATE_COMPLETE = 'prestage-complete'
# Prestage preparation timeout
PRESTAGE_PREPARE_TIMEOUT = 900 # 15 minutes
# Alarm aggregation # Alarm aggregation
ALARMS_DISABLED = "disabled" ALARMS_DISABLED = "disabled"
ALARM_OK_STATUS = "OK" ALARM_OK_STATUS = "OK"
@ -302,6 +297,7 @@ SYSTEM_MODE_DUPLEX_DIRECT = "duplex-direct"
# Load states # Load states
ACTIVE_LOAD_STATE = 'active' ACTIVE_LOAD_STATE = 'active'
INACTIVE_LOAD_STATE = 'inactive'
IMPORTING_LOAD_STATE = 'importing' IMPORTING_LOAD_STATE = 'importing'
IMPORTED_LOAD_STATE = 'imported' IMPORTED_LOAD_STATE = 'imported'
IMPORTED_METADATA_LOAD_STATE = 'imported-metadata' IMPORTED_METADATA_LOAD_STATE = 'imported-metadata'
@ -324,6 +320,10 @@ EXTRA_ARGS_FORCE = 'force'
# extra_args for patching # extra_args for patching
EXTRA_ARGS_UPLOAD_ONLY = 'upload-only' EXTRA_ARGS_UPLOAD_ONLY = 'upload-only'
# http request/response arguments for prestage
PRESTAGE_SOFTWARE_VERSION = 'prestage-software-version'
PRESTAGE_REQUEST_RELEASE = 'release'
# Device Image Bitstream Types # Device Image Bitstream Types
BITSTREAM_TYPE_ROOT_KEY = 'root-key' BITSTREAM_TYPE_ROOT_KEY = 'root-key'
BITSTREAM_TYPE_FUNCTIONAL = 'functional' BITSTREAM_TYPE_FUNCTIONAL = 'functional'

View File

@ -1,4 +1,4 @@
# Copyright (c) 2023 Wind River Systems, Inc. # Copyright (c) 2022-2023 Wind River Systems, Inc.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@ -34,7 +34,6 @@ from dccommon.drivers.openstack.sdk_platform import OpenStackDriver
from dccommon.drivers.openstack.sysinv_v1 import SysinvClient from dccommon.drivers.openstack.sysinv_v1 import SysinvClient
from dccommon.exceptions import PlaybookExecutionFailed from dccommon.exceptions import PlaybookExecutionFailed
from dccommon.exceptions import PlaybookExecutionTimeout from dccommon.exceptions import PlaybookExecutionTimeout
from dccommon.utils import LAST_SW_VERSION_IN_CENTOS
from dccommon.utils import run_playbook from dccommon.utils import run_playbook
from dcmanager.common import consts from dcmanager.common import consts
@ -45,14 +44,7 @@ from dcmanager.db import api as db_api
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
CONF = cfg.CONF CONF = cfg.CONF
PRESTAGING_REPO_DIR = '/var/run/prestaging_repo' DEPLOY_BASE_DIR = dccommon_consts.DEPLOY_DIR
DEPLOY_BASE_DIR = dccommon_consts.DEPLOY_DIR + '/' + SW_VERSION
PRESTAGE_PREPARATION_COMPLETED_FILE = os.path.join(
PRESTAGING_REPO_DIR, '.prestage_preparation_completed')
PRESTAGE_PREPARATION_FAILED_FILE = os.path.join(
DEPLOY_BASE_DIR, '.prestage_preparation_failed')
ANSIBLE_PREPARE_PRESTAGE_PACKAGES_PLAYBOOK = \
"/usr/share/ansible/stx-ansible/playbooks/prepare_prestage_packages.yml"
ANSIBLE_PRESTAGE_SUBCLOUD_PACKAGES_PLAYBOOK = \ ANSIBLE_PRESTAGE_SUBCLOUD_PACKAGES_PLAYBOOK = \
"/usr/share/ansible/stx-ansible/playbooks/prestage_sw_packages.yml" "/usr/share/ansible/stx-ansible/playbooks/prestage_sw_packages.yml"
ANSIBLE_PRESTAGE_SUBCLOUD_IMAGES_PLAYBOOK = \ ANSIBLE_PRESTAGE_SUBCLOUD_IMAGES_PLAYBOOK = \
@ -61,8 +53,7 @@ ANSIBLE_PRESTAGE_INVENTORY_SUFFIX = '_prestage_inventory.yml'
def is_deploy_status_prestage(deploy_status): def is_deploy_status_prestage(deploy_status):
return deploy_status in (consts.PRESTAGE_STATE_PREPARE, return deploy_status in (consts.PRESTAGE_STATE_PACKAGES,
consts.PRESTAGE_STATE_PACKAGES,
consts.PRESTAGE_STATE_IMAGES, consts.PRESTAGE_STATE_IMAGES,
consts.PRESTAGE_STATE_FAILED, consts.PRESTAGE_STATE_FAILED,
consts.PRESTAGE_STATE_COMPLETE) consts.PRESTAGE_STATE_COMPLETE)
@ -121,7 +112,7 @@ def global_prestage_validate(payload):
" Details: %s" % ex) " Details: %s" % ex)
def initial_subcloud_validate(subcloud): def initial_subcloud_validate(subcloud, installed_loads, software_version):
"""Basic validation a subcloud prestage operation. """Basic validation a subcloud prestage operation.
Raises a PrestageCheckFailedException on failure. Raises a PrestageCheckFailedException on failure.
@ -152,6 +143,18 @@ def initial_subcloud_validate(subcloud):
" The current deploy status is %s." " The current deploy status is %s."
% (', '.join(allowed_deploy_states), subcloud.deploy_status)) % (', '.join(allowed_deploy_states), subcloud.deploy_status))
# The request software version must be either the same as the software version
# of the subcloud or any active/inactive/imported load on the system controller
# (can be checked with "system load-list" command).
if software_version and \
software_version != subcloud.software_version and \
software_version not in installed_loads:
raise exceptions.PrestagePreCheckFailedException(
subcloud=subcloud.name,
orch_skip=True,
details="Specified release is not supported. "
"%s version must first be imported" % software_version)
def validate_prestage(subcloud, payload): def validate_prestage(subcloud, payload):
"""Validate a subcloud prestage operation. """Validate a subcloud prestage operation.
@ -167,8 +170,14 @@ def validate_prestage(subcloud, payload):
""" """
LOG.debug("Validating subcloud prestage '%s'", subcloud.name) LOG.debug("Validating subcloud prestage '%s'", subcloud.name)
installed_loads = []
software_version = None
if payload.get(consts.PRESTAGE_REQUEST_RELEASE):
software_version = payload.get(consts.PRESTAGE_REQUEST_RELEASE)
installed_loads = utils.get_systemcontroller_installed_loads()
# re-run the initial validation # re-run the initial validation
initial_subcloud_validate(subcloud) initial_subcloud_validate(subcloud, installed_loads, software_version)
subcloud_type, system_health, oam_floating_ip = \ subcloud_type, system_health, oam_floating_ip = \
_get_prestage_subcloud_info(subcloud.name) _get_prestage_subcloud_info(subcloud.name)
@ -192,18 +201,10 @@ def validate_prestage(subcloud, payload):
return oam_floating_ip return oam_floating_ip
@utils.synchronized('prestage-prepare-cleanup', external=True)
def cleanup_failed_preparation():
"""Remove the preparation failed file if it exists from a previous run"""
if os.path.exists(PRESTAGE_PREPARATION_FAILED_FILE):
LOG.debug("Cleanup: removing %s", PRESTAGE_PREPARATION_FAILED_FILE)
os.remove(PRESTAGE_PREPARATION_FAILED_FILE)
def prestage_start(context, subcloud_id): def prestage_start(context, subcloud_id):
subcloud = db_api.subcloud_update( subcloud = db_api.subcloud_update(
context, subcloud_id, context, subcloud_id,
deploy_status=consts.PRESTAGE_STATE_PREPARE) deploy_status=consts.PRESTAGE_STATE_PACKAGES)
return subcloud return subcloud
@ -219,8 +220,8 @@ def prestage_fail(context, subcloud_id):
deploy_status=consts.PRESTAGE_STATE_FAILED) deploy_status=consts.PRESTAGE_STATE_FAILED)
def is_upgrade(subcloud_version): def is_local(subcloud_version, specified_version):
return SW_VERSION != subcloud_version return subcloud_version == specified_version
def prestage_subcloud(context, payload): def prestage_subcloud(context, payload):
@ -228,15 +229,13 @@ def prestage_subcloud(context, payload):
This is the standalone (not orchestrated) prestage implementation. This is the standalone (not orchestrated) prestage implementation.
4 phases: 3 phases:
1. Prestage validation (already done by this point) 1. Prestage validation (already done by this point)
- Subcloud exists, is online, is managed, is AIO-SX - Subcloud exists, is online, is managed, is AIO-SX
- Subcloud has no management-affecting alarms (unless force is given) - Subcloud has no management-affecting alarms (unless force is given)
2. Packages preparation 2. Packages prestaging
- prestage-prepare-packages.sh
3. Packages prestaging
- run prestage_packages.yml ansible playbook - run prestage_packages.yml ansible playbook
4. Images prestaging 3. Images prestaging
- run prestage_images.yml ansible playbook - run prestage_images.yml ansible playbook
""" """
subcloud_name = payload['subcloud_name'] subcloud_name = payload['subcloud_name']
@ -251,7 +250,6 @@ def prestage_subcloud(context, payload):
subcloud=subcloud_name, subcloud=subcloud_name,
details="Subcloud does not exist") details="Subcloud does not exist")
cleanup_failed_preparation()
subcloud = prestage_start(context, subcloud.id) subcloud = prestage_start(context, subcloud.id)
try: try:
apply_thread = threading.Thread( apply_thread = threading.Thread(
@ -267,81 +265,9 @@ def prestage_subcloud(context, payload):
prestage_fail(context, subcloud.id) prestage_fail(context, subcloud.id)
def _sync_run_prestage_prepare_packages(context, subcloud, payload):
"""Run prepare prestage packages ansible script."""
if os.path.exists(PRESTAGE_PREPARATION_FAILED_FILE):
LOG.warn("Subcloud %s prestage preparation aborted due to "
"previous %s failure", subcloud.name,
consts.PRESTAGE_STATE_PREPARE)
raise Exception("Aborted due to previous %s failure"
% consts.PRESTAGE_STATE_PREPARE)
LOG.info("Running prepare prestage ansible script, version=%s "
"(subcloud_id=%s)", SW_VERSION, subcloud.id)
db_api.subcloud_update(context,
subcloud.id,
deploy_status=consts.PRESTAGE_STATE_PREPARE)
# Ansible inventory filename for the specified subcloud
ansible_subcloud_inventory_file = \
utils.get_ansible_filename(subcloud.name,
ANSIBLE_PRESTAGE_INVENTORY_SUFFIX)
extra_vars_str = "current_software_version=%s previous_software_version=%s" \
% (SW_VERSION, subcloud.software_version)
try:
_run_ansible(context,
["ansible-playbook",
ANSIBLE_PREPARE_PRESTAGE_PACKAGES_PLAYBOOK,
"--inventory", ansible_subcloud_inventory_file,
"--extra-vars", extra_vars_str],
"prepare",
subcloud,
consts.PRESTAGE_STATE_PREPARE,
payload['sysadmin_password'],
payload['oam_floating_ip'],
ansible_subcloud_inventory_file,
consts.PRESTAGE_PREPARE_TIMEOUT)
except Exception:
# Flag the failure on file system so that other orchestrated
# strategy steps in this run fail immediately. This file is
# removed at the start of each orchestrated/standalone run.
# This creates the file if it doesn't exist:
with open(PRESTAGE_PREPARATION_FAILED_FILE, 'a'):
pass
raise
LOG.info("Prepare prestage ansible successful")
# TODO(Shrikumar): Cleanup this function, especially the comparison for
# software versions.
# Rationale: In CentOS, prestage_prepare is required; in Debian, it is not.
@utils.synchronized('prestage-prepare-packages', external=True)
def prestage_prepare(context, subcloud, payload):
"""Run the prepare prestage packages playbook if required."""
if SW_VERSION > LAST_SW_VERSION_IN_CENTOS:
LOG.info("Skipping prestage package preparation in Debian")
return
if is_upgrade(subcloud.software_version):
if not os.path.exists(PRESTAGE_PREPARATION_COMPLETED_FILE):
_sync_run_prestage_prepare_packages(context, subcloud, payload)
else:
LOG.info(
"Skipping prestage package preparation (not required)")
else:
LOG.info("Skipping prestage package preparation (reinstall)")
def _prestage_standalone_thread(context, subcloud, payload): def _prestage_standalone_thread(context, subcloud, payload):
"""Run the prestage operations inside a separate thread""" """Run the prestage operations inside a separate thread"""
try: try:
prestage_prepare(context, subcloud, payload)
prestage_packages(context, subcloud, payload) prestage_packages(context, subcloud, payload)
prestage_images(context, subcloud, payload) prestage_images(context, subcloud, payload)
@ -382,19 +308,15 @@ def _get_prestage_subcloud_info(subcloud_name):
def _run_ansible(context, prestage_command, phase, def _run_ansible(context, prestage_command, phase,
subcloud, deploy_status, subcloud, deploy_status,
sysadmin_password, oam_floating_ip, sysadmin_password, oam_floating_ip,
software_version,
ansible_subcloud_inventory_file, ansible_subcloud_inventory_file,
timeout_seconds=None): timeout_seconds=None):
if not timeout_seconds: if not timeout_seconds:
# We always want to set a timeout in prestaging operations: # We always want to set a timeout in prestaging operations:
timeout_seconds = CONF.playbook_timeout timeout_seconds = CONF.playbook_timeout
if deploy_status == consts.PRESTAGE_STATE_PREPARE: LOG.info("Prestaging %s for subcloud: %s, version: %s, timeout: %ss",
LOG.info(("Preparing prestage shared packages for subcloud: %s, " phase, subcloud.name, software_version, timeout_seconds)
"version: %s, timeout: %ss"),
subcloud.name, SW_VERSION, timeout_seconds)
else:
LOG.info("Prestaging %s for subcloud: %s, version: %s, timeout: %ss",
phase, subcloud.name, SW_VERSION, timeout_seconds)
db_api.subcloud_update(context, db_api.subcloud_update(context,
subcloud.id, subcloud.id,
@ -436,7 +358,9 @@ def prestage_packages(context, subcloud, payload):
utils.get_ansible_filename(subcloud.name, utils.get_ansible_filename(subcloud.name,
ANSIBLE_PRESTAGE_INVENTORY_SUFFIX) ANSIBLE_PRESTAGE_INVENTORY_SUFFIX)
extra_vars_str = "software_version=%s" % SW_VERSION prestage_software_version = payload.get(
consts.PRESTAGE_REQUEST_RELEASE, SW_VERSION)
extra_vars_str = "software_version=%s" % prestage_software_version
_run_ansible(context, _run_ansible(context,
["ansible-playbook", ["ansible-playbook",
ANSIBLE_PRESTAGE_SUBCLOUD_PACKAGES_PLAYBOOK, ANSIBLE_PRESTAGE_SUBCLOUD_PACKAGES_PLAYBOOK,
@ -447,6 +371,7 @@ def prestage_packages(context, subcloud, payload):
consts.PRESTAGE_STATE_PACKAGES, consts.PRESTAGE_STATE_PACKAGES,
payload['sysadmin_password'], payload['sysadmin_password'],
payload['oam_floating_ip'], payload['oam_floating_ip'],
prestage_software_version,
ansible_subcloud_inventory_file) ansible_subcloud_inventory_file)
@ -457,21 +382,26 @@ def prestage_images(context, subcloud, payload):
If the prestage images file has been uploaded for the target software If the prestage images file has been uploaded for the target software
version then pass the image_list_file to the prestage_images.yml playbook version then pass the image_list_file to the prestage_images.yml playbook
If the images file does not exist and the prestage is for upgrade, If the images file does not exist and the prestage source is remote,
skip calling prestage_images.yml playbook. skip calling prestage_images.yml playbook.
Ensure the final state is either prestage-failed or prestage-complete Ensure the final state is either prestage-failed or prestage-complete
regardless of whether prestage_images.yml playbook is executed or skipped. regardless of whether prestage_images.yml playbook is executed or skipped.
""" """
upgrade = is_upgrade(subcloud.software_version) prestage_software_version = payload.get(
extra_vars_str = "software_version=%s" % SW_VERSION consts.PRESTAGE_REQUEST_RELEASE, SW_VERSION)
local = is_local(subcloud.software_version, prestage_software_version)
extra_vars_str = "software_version=%s" % prestage_software_version
image_list_file = None image_list_file = None
if upgrade: deploy_dir = os.path.join(DEPLOY_BASE_DIR, prestage_software_version)
image_list_filename = utils.get_filename_by_prefix(DEPLOY_BASE_DIR, if not local:
'prestage_images') image_list_filename = None
if os.path.isdir(deploy_dir):
image_list_filename = utils.get_filename_by_prefix(deploy_dir,
'prestage_images')
if image_list_filename: if image_list_filename:
image_list_file = os.path.join(DEPLOY_BASE_DIR, image_list_filename) image_list_file = os.path.join(deploy_dir, image_list_filename)
# include this file in the ansible args: # include this file in the ansible args:
extra_vars_str += (" image_list_file=%s" % image_list_file) extra_vars_str += (" image_list_file=%s" % image_list_file)
LOG.debug("prestage images list file: %s", image_list_file) LOG.debug("prestage images list file: %s", image_list_file)
@ -480,9 +410,9 @@ def prestage_images(context, subcloud, payload):
# There are only two scenarios where we want to run ansible # There are only two scenarios where we want to run ansible
# for prestaging images: # for prestaging images:
# 1. reinstall # 1. local
# 2. upgrade, with supplied image list # 2. remote, with supplied image list
if not upgrade or (upgrade and image_list_file): if local or ((not local) and image_list_file):
# Ansible inventory filename for the specified subcloud # Ansible inventory filename for the specified subcloud
ansible_subcloud_inventory_file = \ ansible_subcloud_inventory_file = \
utils.get_ansible_filename(subcloud.name, utils.get_ansible_filename(subcloud.name,
@ -497,8 +427,9 @@ def prestage_images(context, subcloud, payload):
consts.PRESTAGE_STATE_IMAGES, consts.PRESTAGE_STATE_IMAGES,
payload['sysadmin_password'], payload['sysadmin_password'],
payload['oam_floating_ip'], payload['oam_floating_ip'],
prestage_software_version,
ansible_subcloud_inventory_file, ansible_subcloud_inventory_file,
timeout_seconds=CONF.playbook_timeout * 2) timeout_seconds=CONF.playbook_timeout * 2)
else: else:
LOG.info("Skipping ansible prestage images step, upgrade: %s," LOG.info("Skipping ansible prestage images step, is_local: %s,"
" image_list_file: %s", upgrade, image_list_file) " image_list_file: %s", local, image_list_file)

View File

@ -456,6 +456,16 @@ def get_loads_for_patching(loads):
return [load.software_version for load in loads if load.state in valid_states] return [load.software_version for load in loads if load.state in valid_states]
def get_loads_for_prestage(loads):
"""Filter the loads that can be prestaged. Return their software versions"""
valid_states = [
consts.ACTIVE_LOAD_STATE,
consts.IMPORTED_LOAD_STATE,
consts.INACTIVE_LOAD_STATE
]
return [load.software_version for load in loads if load.state in valid_states]
def subcloud_get_by_ref(context, subcloud_ref): def subcloud_get_by_ref(context, subcloud_ref):
"""Handle getting a subcloud by either name, or ID """Handle getting a subcloud by either name, or ID
@ -787,6 +797,26 @@ def is_subcloud_healthy(subcloud_name):
return False return False
def get_systemcontroller_installed_loads():
try:
os_client = OpenStackDriver(
region_name=dccommon_consts.SYSTEM_CONTROLLER_NAME,
region_clients=None)
except Exception:
LOG.exception("Failed to get keystone client for %s",
dccommon_consts.SYSTEM_CONTROLLER_NAME)
raise
ks_client = os_client.keystone_client
sysinv_client = SysinvClient(
dccommon_consts.SYSTEM_CONTROLLER_NAME, ks_client.session,
endpoint=ks_client.endpoint_cache.get_endpoint('sysinv'))
loads = sysinv_client.get_loads()
return get_loads_for_prestage(loads)
def get_certificate_from_secret(secret_name, secret_ns): def get_certificate_from_secret(secret_name, secret_ns):
"""Get certificate from k8s secret """Get certificate from k8s secret

View File

@ -111,7 +111,6 @@ TRANSITORY_STATES = {
consts.DEPLOY_STATE_MIGRATING_DATA: consts.DEPLOY_STATE_DATA_MIGRATION_FAILED, consts.DEPLOY_STATE_MIGRATING_DATA: consts.DEPLOY_STATE_DATA_MIGRATION_FAILED,
consts.DEPLOY_STATE_PRE_RESTORE: consts.DEPLOY_STATE_RESTORE_PREP_FAILED, consts.DEPLOY_STATE_PRE_RESTORE: consts.DEPLOY_STATE_RESTORE_PREP_FAILED,
consts.DEPLOY_STATE_RESTORING: consts.DEPLOY_STATE_RESTORE_FAILED, consts.DEPLOY_STATE_RESTORING: consts.DEPLOY_STATE_RESTORE_FAILED,
consts.PRESTAGE_STATE_PREPARE: consts.PRESTAGE_STATE_FAILED,
consts.PRESTAGE_STATE_PACKAGES: consts.PRESTAGE_STATE_FAILED, consts.PRESTAGE_STATE_PACKAGES: consts.PRESTAGE_STATE_FAILED,
consts.PRESTAGE_STATE_IMAGES: consts.PRESTAGE_STATE_FAILED, consts.PRESTAGE_STATE_IMAGES: consts.PRESTAGE_STATE_FAILED,
} }
@ -227,10 +226,9 @@ class SubcloudManager(manager.Manager):
"-i", ansible_subcloud_inventory_file, "-i", ansible_subcloud_inventory_file,
"--limit", subcloud_name, "--limit", subcloud_name,
"-e", "@%s" % consts.ANSIBLE_OVERRIDES_PATH + "/" + "-e", "@%s" % consts.ANSIBLE_OVERRIDES_PATH + "/" +
subcloud_name + '/' + "install_values.yml"] subcloud_name + '/' + "install_values.yml",
if software_version and software_version != SW_VERSION: "-e", "install_release_version=%s" %
install_command += [ software_version if software_version else SW_VERSION]
"-e", "install_release_version=%s" % software_version]
return install_command return install_command
def compose_apply_command(self, subcloud_name, def compose_apply_command(self, subcloud_name,
@ -247,7 +245,9 @@ class SubcloudManager(manager.Manager):
# which overrides to load # which overrides to load
apply_command += [ apply_command += [
"-e", str("override_files_dir='%s' region_name=%s") % ( "-e", str("override_files_dir='%s' region_name=%s") % (
consts.ANSIBLE_OVERRIDES_PATH, subcloud_name)] consts.ANSIBLE_OVERRIDES_PATH, subcloud_name),
"-e", "install_release_version=%s" %
software_version if software_version else SW_VERSION]
return apply_command return apply_command
def compose_deploy_command(self, subcloud_name, ansible_subcloud_inventory_file, payload): def compose_deploy_command(self, subcloud_name, ansible_subcloud_inventory_file, payload):
@ -1385,11 +1385,13 @@ class SubcloudManager(manager.Manager):
@staticmethod @staticmethod
def _run_subcloud_install( def _run_subcloud_install(
context, subcloud, install_command, log_file, payload): context, subcloud, install_command, log_file, payload):
LOG.info("Preparing remote install of %s" % subcloud.name) software_version = str(payload['software_version'])
LOG.info("Preparing remote install of %s, version: %s",
subcloud.name, software_version)
db_api.subcloud_update( db_api.subcloud_update(
context, subcloud.id, context, subcloud.id,
deploy_status=consts.DEPLOY_STATE_PRE_INSTALL, deploy_status=consts.DEPLOY_STATE_PRE_INSTALL,
software_version=str(payload['software_version'])) software_version=software_version)
try: try:
install = SubcloudInstall(context, subcloud.name) install = SubcloudInstall(context, subcloud.name)
install.prep(consts.ANSIBLE_OVERRIDES_PATH, payload) install.prep(consts.ANSIBLE_OVERRIDES_PATH, payload)
@ -1399,7 +1401,7 @@ class SubcloudManager(manager.Manager):
context, subcloud.id, context, subcloud.id,
deploy_status=consts.DEPLOY_STATE_PRE_INSTALL_FAILED) deploy_status=consts.DEPLOY_STATE_PRE_INSTALL_FAILED)
LOG.error(str(e)) LOG.error(str(e))
install.cleanup() install.cleanup(software_version)
return False return False
# Run the remote install playbook # Run the remote install playbook
@ -1419,9 +1421,9 @@ class SubcloudManager(manager.Manager):
context, subcloud.id, context, subcloud.id,
deploy_status=consts.DEPLOY_STATE_INSTALL_FAILED, deploy_status=consts.DEPLOY_STATE_INSTALL_FAILED,
error_description=msg[0:consts.ERROR_DESCRIPTION_LENGTH]) error_description=msg[0:consts.ERROR_DESCRIPTION_LENGTH])
install.cleanup() install.cleanup(software_version)
return False return False
install.cleanup() install.cleanup(software_version)
LOG.info("Successfully installed %s" % subcloud.name) LOG.info("Successfully installed %s" % subcloud.name)
return True return True

View File

@ -1,4 +1,4 @@
# Copyright (c) 2022 Wind River Systems, Inc. # Copyright (c) 2022-2023 Wind River Systems, Inc.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@ -30,8 +30,6 @@ class PrestageOrchThread(OrchThread):
STATE_OPERATORS = { STATE_OPERATORS = {
consts.STRATEGY_STATE_PRESTAGE_PRE_CHECK: consts.STRATEGY_STATE_PRESTAGE_PRE_CHECK:
states.PrestagePreCheckState, states.PrestagePreCheckState,
consts.STRATEGY_STATE_PRESTAGE_PREPARE:
states.PrestagePrepareState,
consts.STRATEGY_STATE_PRESTAGE_PACKAGES: consts.STRATEGY_STATE_PRESTAGE_PACKAGES:
states.PrestagePackagesState, states.PrestagePackagesState,
consts.STRATEGY_STATE_PRESTAGE_IMAGES: consts.STRATEGY_STATE_PRESTAGE_IMAGES:

View File

@ -1,5 +1,5 @@
# #
# Copyright (c) 2022 Wind River Systems, Inc. # Copyright (c) 2022-2023 Wind River Systems, Inc.
# #
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
# #
@ -51,7 +51,7 @@ class PrestagePreCheckState(PrestageState):
def __init__(self, region_name): def __init__(self, region_name):
super(PrestagePreCheckState, self).__init__( super(PrestagePreCheckState, self).__init__(
next_state=consts.STRATEGY_STATE_PRESTAGE_PREPARE, next_state=consts.STRATEGY_STATE_PRESTAGE_PACKAGES,
region_name=region_name) region_name=region_name)
@utils.synchronized('prestage-update-extra-args', external=True) @utils.synchronized('prestage-update-extra-args', external=True)
@ -85,18 +85,14 @@ class PrestagePreCheckState(PrestageState):
'sysadmin_password': extra_args['sysadmin_password'], 'sysadmin_password': extra_args['sysadmin_password'],
'force': extra_args['force'] 'force': extra_args['force']
} }
if extra_args.get(consts.PRESTAGE_SOFTWARE_VERSION):
payload.update({consts.PRESTAGE_REQUEST_RELEASE:
extra_args.get(consts.PRESTAGE_SOFTWARE_VERSION)})
try: try:
oam_floating_ip = prestage.validate_prestage( oam_floating_ip = prestage.validate_prestage(
strategy_step.subcloud, payload) strategy_step.subcloud, payload)
self._update_oam_floating_ip(strategy_step, oam_floating_ip) self._update_oam_floating_ip(strategy_step, oam_floating_ip)
if strategy_step.stage == 1:
# Note: this cleanup happens for every subcloud, but they are all
# processed before moving on to the next strategy step
# TODO(kmacleod) although this is a quick check, it is
# synchronized, so we may want to figure out a better
# way to only run this once
prestage.cleanup_failed_preparation()
prestage.prestage_start(self.context, strategy_step.subcloud.id) prestage.prestage_start(self.context, strategy_step.subcloud.id)
except exceptions.PrestagePreCheckFailedException as ex: except exceptions.PrestagePreCheckFailedException as ex:
@ -112,26 +108,6 @@ class PrestagePreCheckState(PrestageState):
self.info_log(strategy_step, "Pre-check pass") self.info_log(strategy_step, "Pre-check pass")
class PrestagePrepareState(PrestageState):
"""Perform prepare operation"""
def __init__(self, region_name):
super(PrestagePrepareState, self).__init__(
next_state=consts.STRATEGY_STATE_PRESTAGE_PACKAGES,
region_name=region_name)
def _do_state_action(self, strategy_step):
extra_args = utils.get_sw_update_strategy_extra_args(self.context)
payload = {
'sysadmin_password': extra_args['sysadmin_password'],
'oam_floating_ip':
extra_args['oam_floating_ip_dict'][strategy_step.subcloud.name],
'force': extra_args['force']
}
prestage.prestage_prepare(self.context, strategy_step.subcloud, payload)
self.info_log(strategy_step, "Prepare finished")
class PrestagePackagesState(PrestageState): class PrestagePackagesState(PrestageState):
"""Perform prestage packages operation""" """Perform prestage packages operation"""
@ -148,6 +124,9 @@ class PrestagePackagesState(PrestageState):
extra_args['oam_floating_ip_dict'][strategy_step.subcloud.name], extra_args['oam_floating_ip_dict'][strategy_step.subcloud.name],
'force': extra_args['force'] 'force': extra_args['force']
} }
if extra_args.get(consts.PRESTAGE_SOFTWARE_VERSION):
payload.update({consts.PRESTAGE_REQUEST_RELEASE:
extra_args.get(consts.PRESTAGE_SOFTWARE_VERSION)})
prestage.prestage_packages(self.context, prestage.prestage_packages(self.context,
strategy_step.subcloud, payload) strategy_step.subcloud, payload)
self.info_log(strategy_step, "Packages finished") self.info_log(strategy_step, "Packages finished")
@ -169,6 +148,9 @@ class PrestageImagesState(PrestageState):
extra_args['oam_floating_ip_dict'][strategy_step.subcloud.name], extra_args['oam_floating_ip_dict'][strategy_step.subcloud.name],
'force': extra_args['force'] 'force': extra_args['force']
} }
if extra_args.get(consts.PRESTAGE_SOFTWARE_VERSION):
payload.update({consts.PRESTAGE_REQUEST_RELEASE:
extra_args.get(consts.PRESTAGE_SOFTWARE_VERSION)})
prestage.prestage_images(self.context, strategy_step.subcloud, payload) prestage.prestage_images(self.context, strategy_step.subcloud, payload)
self.info_log(strategy_step, "Images finished") self.info_log(strategy_step, "Images finished")
prestage.prestage_complete(self.context, strategy_step.subcloud.id) prestage.prestage_complete(self.context, strategy_step.subcloud.id)

View File

@ -20,6 +20,8 @@ import threading
from oslo_log import log as logging from oslo_log import log as logging
from tsconfig.tsconfig import SW_VERSION
from dccommon import consts as dccommon_consts from dccommon import consts as dccommon_consts
from dcmanager.audit import rpcapi as dcmanager_audit_rpc_client from dcmanager.audit import rpcapi as dcmanager_audit_rpc_client
from dcmanager.common import consts from dcmanager.common import consts
@ -307,6 +309,12 @@ class SwUpdateManager(manager.Manager):
else: else:
force = False force = False
installed_loads = []
software_version = None
if payload.get(consts.PRESTAGE_REQUEST_RELEASE):
software_version = payload.get(consts.PRESTAGE_REQUEST_RELEASE)
installed_loads = utils.get_systemcontroller_installed_loads()
# Has the user specified a specific subcloud? # Has the user specified a specific subcloud?
# todo(abailey): refactor this code to use classes # todo(abailey): refactor this code to use classes
cloud_name = payload.get('cloud_name') cloud_name = payload.get('cloud_name')
@ -375,7 +383,8 @@ class SwUpdateManager(manager.Manager):
try: try:
prestage.global_prestage_validate(payload) prestage.global_prestage_validate(payload)
prestage_global_validated = True prestage_global_validated = True
prestage.initial_subcloud_validate(subcloud) prestage.initial_subcloud_validate(
subcloud, installed_loads, software_version)
except exceptions.PrestagePreCheckFailedException as ex: except exceptions.PrestagePreCheckFailedException as ex:
raise exceptions.BadRequest(resource='strategy', raise exceptions.BadRequest(resource='strategy',
msg=str(ex)) msg=str(ex))
@ -410,7 +419,9 @@ class SwUpdateManager(manager.Manager):
extra_args = { extra_args = {
consts.EXTRA_ARGS_SYSADMIN_PASSWORD: consts.EXTRA_ARGS_SYSADMIN_PASSWORD:
payload.get(consts.EXTRA_ARGS_SYSADMIN_PASSWORD), payload.get(consts.EXTRA_ARGS_SYSADMIN_PASSWORD),
consts.EXTRA_ARGS_FORCE: force consts.EXTRA_ARGS_FORCE: force,
consts.PRESTAGE_SOFTWARE_VERSION:
software_version if software_version else SW_VERSION
} }
elif strategy_type == consts.SW_UPDATE_TYPE_PATCH: elif strategy_type == consts.SW_UPDATE_TYPE_PATCH:
upload_only_str = payload.get(consts.EXTRA_ARGS_UPLOAD_ONLY) upload_only_str = payload.get(consts.EXTRA_ARGS_UPLOAD_ONLY)
@ -496,7 +507,8 @@ class SwUpdateManager(manager.Manager):
if subcloud.name not in subclouds_processed: if subcloud.name not in subclouds_processed:
# Do initial validation for subcloud # Do initial validation for subcloud
try: try:
prestage.initial_subcloud_validate(subcloud) prestage.initial_subcloud_validate(
subcloud, installed_loads, software_version)
except exceptions.PrestagePreCheckFailedException: except exceptions.PrestagePreCheckFailedException:
LOG.warn("Excluding subcloud from prestage strategy: %s", LOG.warn("Excluding subcloud from prestage strategy: %s",
subcloud.name) subcloud.name)

View File

@ -1879,6 +1879,38 @@ class TestSubcloudAPIOther(testroot.DCManagerApiTest):
mock.ANY) mock.ANY)
self.assertEqual(response.status_int, 200) self.assertEqual(response.status_int, 200)
@mock.patch.object(cutils, 'get_systemcontroller_installed_loads')
@mock.patch.object(rpc_client, 'ManagerClient')
@mock.patch.object(prestage, '_get_system_controller_upgrades')
@mock.patch.object(subclouds.SubcloudsController, '_get_prestage_payload')
def test_prestage_subcloud_invalid_release(self, mock_get_prestage_payload,
mock_controller_upgrade,
mock_rpc_client,
mock_installed_loads):
subcloud = fake_subcloud.create_fake_subcloud(self.ctx)
subcloud = db_api.subcloud_update(
self.ctx, subcloud.id,
availability_status=dccommon_consts.AVAILABILITY_ONLINE,
management_state=dccommon_consts.MANAGEMENT_MANAGED)
fake_release = '21.12'
mock_installed_loads.return_value = ['22.12']
fake_password = (base64.b64encode('testpass'.encode("utf-8"))). \
decode('ascii')
data = {'sysadmin_password': fake_password,
'force': False,
'release': fake_release}
mock_controller_upgrade.return_value = list()
mock_rpc_client().prestage_subcloud.return_value = True
mock_get_prestage_payload.return_value = data
six.assertRaisesRegex(self, webtest.app.AppError, "400 *",
self.app.patch_json, FAKE_URL + '/' +
str(subcloud.id) + '/prestage',
headers=FAKE_HEADERS, params=data)
@mock.patch.object(rpc_client, 'ManagerClient') @mock.patch.object(rpc_client, 'ManagerClient')
@mock.patch.object(subclouds.SubcloudsController, '_get_prestage_payload') @mock.patch.object(subclouds.SubcloudsController, '_get_prestage_payload')
@mock.patch.object(prestage, '_get_system_controller_upgrades') @mock.patch.object(prestage, '_get_system_controller_upgrades')
@ -1925,6 +1957,7 @@ class TestSubcloudAPIOther(testroot.DCManagerApiTest):
str(subcloud.id) + '/prestage', str(subcloud.id) + '/prestage',
headers=FAKE_HEADERS, params=data) headers=FAKE_HEADERS, params=data)
@mock.patch.object(cutils, 'get_systemcontroller_installed_loads')
@mock.patch.object(rpc_client, 'ManagerClient') @mock.patch.object(rpc_client, 'ManagerClient')
@mock.patch.object(prestage, '_get_system_controller_upgrades') @mock.patch.object(prestage, '_get_system_controller_upgrades')
@mock.patch.object(prestage, '_get_prestage_subcloud_info') @mock.patch.object(prestage, '_get_prestage_subcloud_info')
@ -1932,13 +1965,18 @@ class TestSubcloudAPIOther(testroot.DCManagerApiTest):
def test_prestage_subcloud_duplex(self, mock_get_prestage_payload, def test_prestage_subcloud_duplex(self, mock_get_prestage_payload,
mock_prestage_subcloud_info, mock_prestage_subcloud_info,
mock_controller_upgrade, mock_controller_upgrade,
mock_rpc_client): mock_rpc_client,
mock_installed_loads):
subcloud = fake_subcloud.create_fake_subcloud(self.ctx) subcloud = fake_subcloud.create_fake_subcloud(self.ctx)
subcloud = db_api.subcloud_update( subcloud = db_api.subcloud_update(
self.ctx, subcloud.id, availability_status=dccommon_consts.AVAILABILITY_ONLINE, self.ctx, subcloud.id, availability_status=dccommon_consts.AVAILABILITY_ONLINE,
management_state=dccommon_consts.MANAGEMENT_MANAGED) management_state=dccommon_consts.MANAGEMENT_MANAGED)
fake_password = (base64.b64encode('testpass'.encode("utf-8"))).decode('ascii') fake_release = '21.12'
mock_installed_loads.return_value = [fake_release]
fake_password = (base64.b64encode('testpass'.encode("utf-8"))).\
decode('ascii')
data = {'sysadmin_password': fake_password, data = {'sysadmin_password': fake_password,
'force': False} 'force': False}
mock_controller_upgrade.return_value = list() mock_controller_upgrade.return_value = list()

View File

@ -11,7 +11,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
# #
import base64
import copy import copy
import datetime import datetime
@ -40,7 +40,6 @@ from dcmanager.tests.unit.common import fake_subcloud
from dcmanager.tests import utils from dcmanager.tests import utils
from tsconfig.tsconfig import SW_VERSION from tsconfig.tsconfig import SW_VERSION
LAST_SW_VERSION_IN_CENTOS = "22.06"
FAKE_PREVIOUS_SW_VERSION = '21.12' FAKE_PREVIOUS_SW_VERSION = '21.12'
@ -264,11 +263,15 @@ FAKE_SUBCLOUD_PRESTAGE_PAYLOAD = {
"sysadmin_password": "testpasswd", "sysadmin_password": "testpasswd",
} }
FAKE_PRESTAGE_RELEASE = '22.12'
FAKE_SUBCLOUD_SW_VERSION = '21.12'
FAKE_PASSWORD = (base64.b64encode('testpass'.encode("utf-8"))).decode('ascii')
FAKE_PRESTAGE_PAYLOAD = { FAKE_PRESTAGE_PAYLOAD = {
"subcloud_name": "subcloud1", "subcloud_name": "subcloud1",
"oam_floating_ip": "10.10.10.12", "oam_floating_ip": "10.10.10.12",
"sysadmin_password": 'testpassword', "sysadmin_password": FAKE_PASSWORD,
"force": False "force": False,
"release": FAKE_PRESTAGE_RELEASE
} }
FAKE_MGMT_IF_UUIDS = [ FAKE_MGMT_IF_UUIDS = [
@ -1437,7 +1440,8 @@ class TestSubcloudManager(base.DCManagerTestCase):
FAKE_PREVIOUS_SW_VERSION), FAKE_PREVIOUS_SW_VERSION),
'-i', '/var/opt/dc/ansible/subcloud1_inventory.yml', '-i', '/var/opt/dc/ansible/subcloud1_inventory.yml',
'--limit', 'subcloud1', '-e', '--limit', 'subcloud1', '-e',
"override_files_dir='/var/opt/dc/ansible' region_name=subcloud1" "override_files_dir='/var/opt/dc/ansible' region_name=subcloud1",
'-e', "install_release_version=%s" % FAKE_PREVIOUS_SW_VERSION
] ]
) )
@ -1750,7 +1754,7 @@ class TestSubcloudManager(base.DCManagerTestCase):
mock_run_playbook.assert_called_once() mock_run_playbook.assert_called_once()
mock_is_healthy.assert_called_once() mock_is_healthy.assert_called_once()
# Verify that subcloud has the correct deploy status consts.PRESTAGE_STATE_PREPARE # Verify that subcloud has the correct deploy status consts.PRESTAGE_STATE_PACKAGES
updated_subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud.name) updated_subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud.name)
self.assertEqual(consts.BACKUP_STATE_PRE_BACKUP, self.assertEqual(consts.BACKUP_STATE_PRE_BACKUP,
updated_subcloud.backup_status) updated_subcloud.backup_status)
@ -1777,7 +1781,7 @@ class TestSubcloudManager(base.DCManagerTestCase):
mock_parallel_group_operation.assert_called_once() mock_parallel_group_operation.assert_called_once()
# Verify that subcloud has the correct deploy status consts.PRESTAGE_STATE_PREPARE # Verify that subcloud has the correct deploy status consts.PRESTAGE_STATE_PACKAGES
updated_subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud.name) updated_subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud.name)
self.assertEqual(consts.BACKUP_STATE_VALIDATE_FAILED, self.assertEqual(consts.BACKUP_STATE_VALIDATE_FAILED,
updated_subcloud.backup_status) updated_subcloud.backup_status)
@ -1804,7 +1808,7 @@ class TestSubcloudManager(base.DCManagerTestCase):
mock_parallel_group_operation.assert_called_once() mock_parallel_group_operation.assert_called_once()
# Verify that subcloud has the correct deploy status consts.PRESTAGE_STATE_PREPARE # Verify that subcloud has the correct deploy status consts.PRESTAGE_STATE_PACKAGES
updated_subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud.name) updated_subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud.name)
self.assertEqual(consts.BACKUP_STATE_VALIDATE_FAILED, self.assertEqual(consts.BACKUP_STATE_VALIDATE_FAILED,
updated_subcloud.backup_status) updated_subcloud.backup_status)
@ -1831,7 +1835,7 @@ class TestSubcloudManager(base.DCManagerTestCase):
mock_parallel_group_operation.assert_called_once() mock_parallel_group_operation.assert_called_once()
# Verify that subcloud has the correct deploy status consts.PRESTAGE_STATE_PREPARE # Verify that subcloud has the correct deploy status consts.PRESTAGE_STATE_PACKAGES
updated_subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud.name) updated_subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud.name)
self.assertEqual(consts.BACKUP_STATE_VALIDATE_FAILED, self.assertEqual(consts.BACKUP_STATE_VALIDATE_FAILED,
updated_subcloud.backup_status) updated_subcloud.backup_status)
@ -1859,7 +1863,7 @@ class TestSubcloudManager(base.DCManagerTestCase):
mock_parallel_group_operation.assert_called_once() mock_parallel_group_operation.assert_called_once()
# Verify that subcloud has the correct deploy status consts.PRESTAGE_STATE_PREPARE # Verify that subcloud has the correct deploy status consts.PRESTAGE_STATE_PACKAGES
updated_subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud.name) updated_subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud.name)
self.assertEqual(consts.BACKUP_STATE_UNKNOWN, self.assertEqual(consts.BACKUP_STATE_UNKNOWN,
updated_subcloud.backup_status) updated_subcloud.backup_status)
@ -1887,7 +1891,7 @@ class TestSubcloudManager(base.DCManagerTestCase):
mock_parallel_group_operation.assert_called_once() mock_parallel_group_operation.assert_called_once()
# Verify that subcloud has the correct deploy status consts.PRESTAGE_STATE_PREPARE # Verify that subcloud has the correct deploy status consts.PRESTAGE_STATE_PACKAGES
updated_subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud.name) updated_subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud.name)
self.assertEqual(consts.BACKUP_STATE_UNKNOWN, self.assertEqual(consts.BACKUP_STATE_UNKNOWN,
updated_subcloud.backup_status) updated_subcloud.backup_status)
@ -2013,25 +2017,7 @@ class TestSubcloudManager(base.DCManagerTestCase):
updated_subcloud.backup_status) updated_subcloud.backup_status)
@mock.patch.object(threading.Thread, 'start') @mock.patch.object(threading.Thread, 'start')
def test_prestage_subcloud_prepare(self, mock_thread_start): def test_prestage_no_subcloud(self, mock_thread_start):
values = copy.copy(FAKE_PRESTAGE_PAYLOAD)
subcloud = self.create_subcloud_static(
self.ctx,
name='subcloud1',
deploy_status=consts.DEPLOY_STATE_NONE)
sm = subcloud_manager.SubcloudManager()
sm.prestage_subcloud(self.ctx, payload=values)
mock_thread_start.assert_called_once()
# Verify that subcloud has the correct deploy status consts.PRESTAGE_STATE_PREPARE
updated_subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud.name)
self.assertEqual(consts.PRESTAGE_STATE_PREPARE,
updated_subcloud.deploy_status)
@mock.patch.object(threading.Thread, 'start')
def test_prestage_subcloud_prepare_no_subcloud(self, mock_thread_start):
values = copy.copy(FAKE_PRESTAGE_PAYLOAD) values = copy.copy(FAKE_PRESTAGE_PAYLOAD)
values['subcloud_name'] = 'randomname' values['subcloud_name'] = 'randomname'
@ -2048,113 +2034,72 @@ class TestSubcloudManager(base.DCManagerTestCase):
@mock.patch.object(os_path, 'exists') @mock.patch.object(os_path, 'exists')
@mock.patch.object(cutils, 'get_filename_by_prefix') @mock.patch.object(cutils, 'get_filename_by_prefix')
@mock.patch.object(prestage, '_run_ansible') @mock.patch.object(prestage, '_run_ansible')
def test_prestage_upgrade_pass(self, mock_run_ansible, def test_prestage_remote_pass(self, mock_run_ansible,
mock_get_filename_by_prefix, mock_get_filename_by_prefix,
mock_file_exists): mock_file_exists):
values = copy.copy(FAKE_PRESTAGE_PAYLOAD) values = copy.copy(FAKE_PRESTAGE_PAYLOAD)
subcloud = self.create_subcloud_static(self.ctx, subcloud = self.create_subcloud_static(self.ctx,
name='subcloud1', name='subcloud1',
deploy_status=consts.DEPLOY_STATE_NONE, deploy_status=consts.DEPLOY_STATE_NONE,
software_version='18.02') software_version=FAKE_SUBCLOUD_SW_VERSION)
prestage._prestage_standalone_thread(self.ctx, subcloud, payload=values) prestage._prestage_standalone_thread(self.ctx, subcloud, payload=values)
mock_run_ansible.return_value = None mock_run_ansible.return_value = None
mock_get_filename_by_prefix.return_value = None mock_get_filename_by_prefix.return_value = 'prestage_images_list.txt'
mock_file_exists.return_value = False mock_file_exists.return_value = True
# Verify that subcloud has the correct deploy status # Verify that subcloud has the correct deploy status
updated_subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud.name) updated_subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud.name)
self.assertEqual(consts.PRESTAGE_STATE_COMPLETE, self.assertEqual(consts.PRESTAGE_STATE_COMPLETE,
updated_subcloud.deploy_status) updated_subcloud.deploy_status)
@mock.patch.object(os_path, 'exists') # Verify both of prestage package and image ansible playbooks were called
self.assertEqual(mock_run_ansible.call_count, 2)
# Verify the "image_list_file" was passed to the prestage image playbook
# for the remote prestage
self.assertTrue(
'image_list_file' in mock_run_ansible.call_args_list[1].args[1][5])
# Verify the prestage request release was passed to the playbooks
self.assertTrue(
FAKE_PRESTAGE_RELEASE in mock_run_ansible.call_args_list[0].args[1][5])
self.assertTrue(
FAKE_PRESTAGE_RELEASE in mock_run_ansible.call_args_list[1].args[1][5])
@mock.patch.object(prestage, '_run_ansible') @mock.patch.object(prestage, '_run_ansible')
def test_prestage_ansible_failed(self, mock_run_ansible, def test_prestage_local_pass(self, mock_run_ansible):
mock_file_exists):
values = copy.copy(FAKE_PRESTAGE_PAYLOAD) values = copy.copy(FAKE_PRESTAGE_PAYLOAD)
subcloud = self.create_subcloud_static(self.ctx, subcloud = self.create_subcloud_static(self.ctx,
name='subcloud1', name='subcloud1',
deploy_status=consts.DEPLOY_STATE_NONE, deploy_status=consts.DEPLOY_STATE_NONE,
software_version='18.02') software_version=FAKE_PRESTAGE_RELEASE)
mock_run_ansible.side_effect = FakeException('Test')
mock_file_exists.return_value = False
mock_open = mock.mock_open(read_data='test')
with mock.patch('six.moves.builtins.open', mock_open):
e = self.assertRaises(FakeException,
prestage._sync_run_prestage_prepare_packages,
context=self.ctx, subcloud=subcloud, payload=values)
self.assertEqual('Test', str(e))
@mock.patch.object(os_path, 'exists')
@mock.patch.object(cutils, 'get_filename_by_prefix')
@mock.patch.object(prestage, '_run_ansible')
def test_prestage_reinstall_pass(self, mock_run_ansible,
mock_get_filename_by_prefix,
mock_file_exists):
values = copy.copy(FAKE_PRESTAGE_PAYLOAD)
subcloud = self.create_subcloud_static(self.ctx,
name='subcloud1',
deploy_status=consts.DEPLOY_STATE_NONE,
software_version=SW_VERSION)
prestage._prestage_standalone_thread(self.ctx, subcloud, payload=values) prestage._prestage_standalone_thread(self.ctx, subcloud, payload=values)
mock_run_ansible.return_value = None mock_run_ansible.return_value = None
mock_get_filename_by_prefix.return_value = None
mock_file_exists.return_value = False
# Verify that subcloud has the correct deploy status # Verify that subcloud has the correct deploy status
updated_subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud.name) updated_subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud.name)
self.assertEqual(consts.PRESTAGE_STATE_COMPLETE, self.assertEqual(consts.PRESTAGE_STATE_COMPLETE,
updated_subcloud.deploy_status) updated_subcloud.deploy_status)
@mock.patch.object(prestage, 'prestage_complete') # Verify both of prestage package and image ansible playbooks were called
self.assertEqual(mock_run_ansible.call_count, 2)
# Verify the prestage request release was passed to the playbooks
self.assertTrue(
FAKE_PRESTAGE_RELEASE in mock_run_ansible.call_args_list[0].args[1][5])
self.assertTrue(
FAKE_PRESTAGE_RELEASE in mock_run_ansible.call_args_list[1].args[1][5])
@mock.patch.object(prestage, 'prestage_images') @mock.patch.object(prestage, 'prestage_images')
@mock.patch.object(prestage, 'prestage_packages') @mock.patch.object(prestage, 'prestage_packages')
@mock.patch.object(cutils, 'delete_subcloud_inventory') @mock.patch.object(cutils, 'delete_subcloud_inventory')
@mock.patch.object(prestage, '_run_ansible') @mock.patch.object(prestage, '_run_ansible')
def test_prestage_subcloud_prestage_prepare_centos(self, def test_prestage_subcloud_complete(self,
mock_run_ansible, mock_run_ansible,
mock_delete_subcloud_inventory, mock_delete_subcloud_inventory,
mock_prestage_packages, mock_prestage_packages,
mock_prestage_images, mock_prestage_images):
mock_prestage_complete):
values = copy.copy(FAKE_PRESTAGE_PAYLOAD)
subcloud = self.create_subcloud_static(self.ctx,
name='subcloud1',
deploy_status=consts.DEPLOY_STATE_NONE)
current_sw_version = prestage.SW_VERSION
prestage.SW_VERSION = LAST_SW_VERSION_IN_CENTOS
prestage._prestage_standalone_thread(self.ctx, subcloud, payload=values)
mock_run_ansible.return_value = None
mock_prestage_packages.assert_called_once_with(self.ctx, subcloud, values)
mock_prestage_images.assert_called_once_with(self.ctx, subcloud, values)
mock_prestage_complete.assert_called_once_with(self.ctx, subcloud.id)
mock_delete_subcloud_inventory.return_value = None
# Verify that subcloud has the correct deploy status
updated_subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud.name)
prestage.SW_VERSION = current_sw_version
self.assertEqual(consts.PRESTAGE_STATE_PREPARE,
updated_subcloud.deploy_status)
@mock.patch.object(prestage, 'prestage_complete')
@mock.patch.object(prestage, 'prestage_images')
@mock.patch.object(prestage, 'prestage_packages')
@mock.patch.object(cutils, 'delete_subcloud_inventory')
@mock.patch.object(prestage, '_run_ansible')
def test_prestage_subcloud_prestage_prepare_debian(self,
mock_run_ansible,
mock_delete_subcloud_inventory,
mock_prestage_packages,
mock_prestage_images,
mock_prestage_complete):
values = copy.copy(FAKE_PRESTAGE_PAYLOAD) values = copy.copy(FAKE_PRESTAGE_PAYLOAD)
subcloud = self.create_subcloud_static(self.ctx, subcloud = self.create_subcloud_static(self.ctx,
@ -2164,12 +2109,11 @@ class TestSubcloudManager(base.DCManagerTestCase):
mock_run_ansible.return_value = None mock_run_ansible.return_value = None
mock_prestage_packages.assert_called_once_with(self.ctx, subcloud, values) mock_prestage_packages.assert_called_once_with(self.ctx, subcloud, values)
mock_prestage_images.assert_called_once_with(self.ctx, subcloud, values) mock_prestage_images.assert_called_once_with(self.ctx, subcloud, values)
mock_prestage_complete.assert_called_once_with(self.ctx, subcloud.id)
mock_delete_subcloud_inventory.return_value = None mock_delete_subcloud_inventory.return_value = None
# Verify that subcloud has the correct deploy status # Verify that subcloud has the "prestage-complete" deploy status
updated_subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud.name) updated_subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud.name)
self.assertEqual(consts.DEPLOY_STATE_NONE, self.assertEqual(consts.PRESTAGE_STATE_COMPLETE,
updated_subcloud.deploy_status) updated_subcloud.deploy_status)
def test_get_cached_regionone_data(self): def test_get_cached_regionone_data(self):

View File

@ -1,5 +1,5 @@
# #
# Copyright (c) 2022 Wind River Systems, Inc. # Copyright (c) 2022-2023 Wind River Systems, Inc.
# #
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
# #
@ -17,7 +17,6 @@ from dcmanager.common.consts import STRATEGY_STATE_FAILED
from dcmanager.common.consts import STRATEGY_STATE_PRESTAGE_IMAGES from dcmanager.common.consts import STRATEGY_STATE_PRESTAGE_IMAGES
from dcmanager.common.consts import STRATEGY_STATE_PRESTAGE_PACKAGES from dcmanager.common.consts import STRATEGY_STATE_PRESTAGE_PACKAGES
from dcmanager.common.consts import STRATEGY_STATE_PRESTAGE_PRE_CHECK from dcmanager.common.consts import STRATEGY_STATE_PRESTAGE_PRE_CHECK
from dcmanager.common.consts import STRATEGY_STATE_PRESTAGE_PREPARE
from dcmanager.db.sqlalchemy import api as db_api from dcmanager.db.sqlalchemy import api as db_api
@ -58,27 +57,6 @@ class TestPrestagePreCheckState(TestPrestage):
self.strategy_step = \ self.strategy_step = \
self.setup_strategy_step(self.subcloud.id, STRATEGY_STATE_PRESTAGE_PRE_CHECK) self.setup_strategy_step(self.subcloud.id, STRATEGY_STATE_PRESTAGE_PRE_CHECK)
def test_prestage_prepare(self):
next_state = STRATEGY_STATE_PRESTAGE_PREPARE
# Update the subcloud to have deploy state as "complete"
db_api.subcloud_update(self.ctx,
self.subcloud.id,
deploy_status=DEPLOY_STATE_DONE)
extra_args = {"sysadmin_password": FAKE_PASSWORD,
"force": False,
'oam_floating_ip': OAM_FLOATING_IP}
self.strategy = fake_strategy.create_fake_strategy(
self.ctx,
self.DEFAULT_STRATEGY_TYPE,
extra_args=extra_args)
# invoke the strategy state operation on the orch thread
self.worker.perform_state_action(self.strategy_step)
# Verify the transition to the expected next state
self.assert_step_updated(self.strategy_step.subcloud_id, next_state)
def test_prestage_prepare_no_extra_args(self): def test_prestage_prepare_no_extra_args(self):
next_state = STRATEGY_STATE_FAILED next_state = STRATEGY_STATE_FAILED
# Update the subcloud to have deploy state as "complete" # Update the subcloud to have deploy state as "complete"
@ -154,73 +132,6 @@ class TestPrestagePreCheckState(TestPrestage):
self.assertTrue('test' in str(new_strategy_step.details)) self.assertTrue('test' in str(new_strategy_step.details))
class TestPrestagePrepareState(TestPrestage):
def setUp(self):
super(TestPrestagePrepareState, self).setUp()
# Add the subcloud being processed by this unit test
# The subcloud is online, managed with deploy_state 'installed'
self.subcloud = self.setup_subcloud()
p = mock.patch('dcmanager.common.prestage.prestage_prepare')
self.mock_prestage_prepare = p.start()
self.addCleanup(p.stop)
# Add the strategy_step state being processed by this unit test
self.strategy_step = \
self.setup_strategy_step(self.subcloud.id, STRATEGY_STATE_PRESTAGE_PREPARE)
def test_prestage_prestage_prepare(self):
next_state = STRATEGY_STATE_PRESTAGE_PACKAGES
# Update the subcloud to have deploy state as "complete"
db_api.subcloud_update(self.ctx,
self.subcloud.id,
deploy_status=DEPLOY_STATE_DONE)
oam_floating_ip_dict = {
self.subcloud.name: OAM_FLOATING_IP
}
extra_args = {"sysadmin_password": FAKE_PASSWORD,
"force": False,
"oam_floating_ip_dict": oam_floating_ip_dict}
self.strategy = fake_strategy.create_fake_strategy(
self.ctx,
self.DEFAULT_STRATEGY_TYPE,
extra_args=extra_args)
# invoke the strategy state operation on the orch thread
self.worker.perform_state_action(self.strategy_step)
# Verify the transition to the expected next state
self.assert_step_updated(self.strategy_step.subcloud_id, next_state)
def test_prestage_prestage_prepare_no_password(self):
next_state = STRATEGY_STATE_FAILED
# Update the subcloud to have deploy state as "complete"
db_api.subcloud_update(self.ctx,
self.subcloud.id,
deploy_status=DEPLOY_STATE_DONE)
oam_floating_ip_dict = {
self.subcloud.name: OAM_FLOATING_IP
}
extra_args = {"force": False,
"oam_floating_ip_dict": oam_floating_ip_dict}
self.strategy = fake_strategy.create_fake_strategy(
self.ctx,
self.DEFAULT_STRATEGY_TYPE,
extra_args=extra_args)
# invoke the strategy state operation on the orch thread
self.worker.perform_state_action(self.strategy_step)
# Verify the transition to the expected next state
self.assert_step_updated(self.strategy_step.subcloud_id, next_state)
class TestPrestagePackageState(TestPrestage): class TestPrestagePackageState(TestPrestage):
def setUp(self): def setUp(self):

View File

@ -23,6 +23,7 @@ from dcmanager.common import consts
from dcmanager.common import context from dcmanager.common import context
from dcmanager.common import exceptions from dcmanager.common import exceptions
from dcmanager.common import prestage from dcmanager.common import prestage
from dcmanager.common import utils as cutils
from dcmanager.db.sqlalchemy import api as db_api from dcmanager.db.sqlalchemy import api as db_api
from dcmanager.orchestrator import sw_update_manager from dcmanager.orchestrator import sw_update_manager
@ -639,13 +640,15 @@ class TestSwUpdateManager(base.DCManagerTestCase):
self.assertEqual(subcloud_ids, subcloud_id_processed) self.assertEqual(subcloud_ids, subcloud_id_processed)
self.assertEqual(stage, stage_processed) self.assertEqual(stage, stage_processed)
@mock.patch.object(cutils, 'get_systemcontroller_installed_loads')
@mock.patch.object(prestage, 'initial_subcloud_validate') @mock.patch.object(prestage, 'initial_subcloud_validate')
@mock.patch.object(prestage, '_get_system_controller_upgrades') @mock.patch.object(prestage, '_get_system_controller_upgrades')
@mock.patch.object(sw_update_manager, 'PatchOrchThread') @mock.patch.object(sw_update_manager, 'PatchOrchThread')
def test_create_sw_prestage_strategy_parallel(self, def test_create_sw_prestage_strategy_parallel(self,
mock_patch_orch_thread, mock_patch_orch_thread,
mock_controller_upgrade, mock_controller_upgrade,
mock_initial_subcloud_validate): mock_initial_subcloud_validate,
mock_installed_loads):
# Create fake subclouds and respective status # Create fake subclouds and respective status
# Subcloud1 will be prestaged # Subcloud1 will be prestaged
@ -679,6 +682,9 @@ class TestSwUpdateManager(base.DCManagerTestCase):
data = copy.copy(FAKE_SW_PRESTAGE_DATA) data = copy.copy(FAKE_SW_PRESTAGE_DATA)
fake_password = (base64.b64encode('testpass'.encode("utf-8"))).decode('ascii') fake_password = (base64.b64encode('testpass'.encode("utf-8"))).decode('ascii')
data['sysadmin_password'] = fake_password data['sysadmin_password'] = fake_password
fake_release = '21.12'
data[consts.PRESTAGE_REQUEST_RELEASE] = fake_release
mock_installed_loads.return_value = [fake_release]
um = sw_update_manager.SwUpdateManager() um = sw_update_manager.SwUpdateManager()
strategy_dict = um.create_sw_update_strategy(self.ctxt, payload=data) strategy_dict = um.create_sw_update_strategy(self.ctxt, payload=data)
@ -690,6 +696,9 @@ class TestSwUpdateManager(base.DCManagerTestCase):
self.assertEqual(strategy_dict['max-parallel-subclouds'], 2) self.assertEqual(strategy_dict['max-parallel-subclouds'], 2)
self.assertEqual(strategy_dict['subcloud-apply-type'], self.assertEqual(strategy_dict['subcloud-apply-type'],
consts.SUBCLOUD_APPLY_TYPE_PARALLEL) consts.SUBCLOUD_APPLY_TYPE_PARALLEL)
self.assertEqual(fake_release,
strategy_dict['extra-args'].get(
consts.PRESTAGE_SOFTWARE_VERSION))
# Verify the strategy step list # Verify the strategy step list
subcloud_ids = [1, 3, 5, 6, 7] subcloud_ids = [1, 3, 5, 6, 7]