Deprecate dcmanager subcloud reinstall and reconfig

This commit deprecates the dcmanager subcloud reinstall and reconfig
in favor of the new 'dcmanager subcloud redeploy' and 'dcmanager
subcloud deploy config' commands, respectively.

This commit also removes unused constants, add missing deploy states
to the subcloud audit function and to the subcloud restore invalid
state list, and fixes the api-ref method for the deploy install and
resume commands.

Test Plan:
1. PASS - Call the reinstall API endpoint and verify that it returns
   the 410 status code with the deprecation notice instructing the use
   of the /v1.0/subclouds/{subcloud}/redeploy URL;
2. PASS - Call the reconfigure API endpoint and verify that it returns
   the 410 status code with the deprecation notice instructing the use
   of the /v1.0/phased-subcloud-deploy/{subcloud}/configure URL.
3. PASS - Change the subcloud deploy state to the new states added to
   the INVALID_DEPLOY_STATES_FOR_RESTORE, call the subcloud backup
   restore and verify that the operation is not allowed;
4. PASS - Change the subcloud deploy state to the new states added to
   the audit function and verify that the audit is not skipped.

Story: 2010756
Task: 48572

Change-Id: I9a502a89ec1c6eb23f286a80c7bf39fcbed0b2c4
Signed-off-by: Gustavo Herzmann <gustavo.herzmann@windriver.com>
This commit is contained in:
Gustavo Herzmann 2023-08-07 18:04:19 -03:00
parent 6891432a6e
commit 338ceaca81
17 changed files with 139 additions and 934 deletions

View File

@ -404,151 +404,6 @@ Response Example
:language: json
**********************************
Reconfigures a specific subcloud
**********************************
.. rest_method:: PATCH /v1.0/subclouds/{subcloud}/reconfigure
The attributes of a subcloud which are modifiable:
- subcloud configuration (which is provided through deploy_config file)
**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
- deploy_config: deploy_config
- sysadmin_password: sysadmin_password
Accepts Content-Type multipart/form-data
Request Example
----------------
.. literalinclude:: samples/subclouds/subcloud-patch-reconfigure-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
- 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-reconfigure-response.json
:language: json
********************************
Reinstalls a specific subcloud
********************************
.. rest_method:: PATCH /v1.0/subclouds/{subcloud}/reinstall
Reinstall and bootstrap a subcloud based on its previous install configurations.
After reinstall, a reconfigure operation with deploy_config file is expected
to deploy the subcloud.
**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
- bootstrap_values: bootstrap_values
- deploy_config: deploy_config
- release: release
- sysadmin_password: sysadmin_password
Request Example
----------------
.. literalinclude:: samples/subclouds/subcloud-patch-reinstall-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
- 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-reinstall-response.json
:language: json
********************************
Redeploy a specific subcloud
********************************
@ -2064,7 +1919,7 @@ Response Example
Installs a subcloud
**********************************
.. rest_method:: POST /v1.0/phased-subcloud-deploy/{subcloud}/install
.. rest_method:: PATCH /v1.0/phased-subcloud-deploy/{subcloud}/install
**Normal response codes**
@ -2341,7 +2196,7 @@ Response Example
Resume subcloud deployment
****************************
.. rest_method:: POST /v1.0/phased-subcloud-deploy
.. rest_method:: PATCH /v1.0/phased-subcloud-deploy/{subcloud}/resume
Accepts Content-Type multipart/form-data.
@ -2360,6 +2215,7 @@ serviceUnavailable (503)
.. rest_parameters:: parameters.yaml
- subcloud: subcloud_uri
- bmc_password: bmc_password
- bootstrap-address: bootstrap_address
- bootstrap_values: bootstrap_values

View File

@ -1,5 +0,0 @@
{
"sysadmin_password": "XXXXXXX",
"deploy_config": "path to some file"
}

View File

@ -1,23 +0,0 @@
{
"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": null,
"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",
}

View File

@ -1,5 +0,0 @@
{
"sysadmin_password": "XXXXXXX",
"bootstrap_values": "path to boostrap file",
"deploy_config": "path to deploy file"
}

View File

@ -1,25 +0,0 @@
{
"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",
}

View File

@ -80,15 +80,16 @@ VALID_STATES_FOR_DEPLOY_BOOTSTRAP = [
consts.DEPLOY_STATE_CREATED
]
# TODO(vgluzrom): remove deploy_failed once 'subcloud reconfig'
# has been deprecated
VALID_STATES_FOR_DEPLOY_CONFIG = (
consts.DEPLOY_STATE_DONE,
consts.DEPLOY_STATE_PRE_CONFIG_FAILED,
consts.DEPLOY_STATE_CONFIG_FAILED,
consts.DEPLOY_STATE_DEPLOY_FAILED,
consts.DEPLOY_STATE_BOOTSTRAPPED,
consts.DEPLOY_STATE_CONFIG_ABORTED
consts.DEPLOY_STATE_CONFIG_ABORTED,
# The next two states are needed due to upgrade scenario:
# TODO(gherzman): remove states when they are no longer needed
consts.DEPLOY_STATE_DEPLOY_FAILED,
consts.DEPLOY_STATE_DEPLOY_PREP_FAILED,
)
VALID_STATES_FOR_DEPLOY_ABORT = (

View File

@ -62,52 +62,22 @@ LOG = logging.getLogger(__name__)
LOCK_NAME = 'SubcloudsController'
BOOTSTRAP_VALUES = 'bootstrap_values'
INSTALL_VALUES = 'install_values'
SUBCLOUD_ADD_MANDATORY_FILE = [
BOOTSTRAP_VALUES,
]
SUBCLOUD_RECONFIG_MANDATORY_FILE = [
consts.DEPLOY_CONFIG,
]
SUBCLOUD_ADD_GET_FILE_CONTENTS = [
BOOTSTRAP_VALUES,
INSTALL_VALUES,
consts.BOOTSTRAP_VALUES,
consts.INSTALL_VALUES,
]
SUBCLOUD_REDEPLOY_GET_FILE_CONTENTS = [
INSTALL_VALUES,
BOOTSTRAP_VALUES,
consts.INSTALL_VALUES,
consts.BOOTSTRAP_VALUES,
consts.DEPLOY_CONFIG
]
BOOTSTRAP_VALUES_ADDRESSES = [
'bootstrap-address', 'bootstrap_address', 'management_start_address', 'management_end_address',
'management_gateway_address', 'systemcontroller_gateway_address',
'external_oam_gateway_address', 'external_oam_floating_address',
'admin_start_address', 'admin_end_address', 'admin_gateway_address'
]
INSTALL_VALUES_ADDRESSES = [
'bootstrap_address', 'bmc_address', 'nexthop_gateway',
'network_address'
]
SUBCLOUD_MANDATORY_NETWORK_PARAMS = [
'management_subnet', 'management_gateway_ip',
'management_start_ip', 'management_end_ip'
]
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")
@ -146,7 +116,7 @@ class SubcloudsController(object):
field_content = part.text
# only the install_values field is yaml, force should be bool
if field_name in [INSTALL_VALUES, 'force']:
if field_name in [consts.INSTALL_VALUES, 'force']:
field_content = yaml.safe_load(field_content)
payload[field_name] = field_content
@ -189,27 +159,6 @@ class SubcloudsController(object):
payload[consts.PRESTAGE_REQUEST_RELEASE] = val
return payload
def _get_reconfig_payload(self, request, subcloud_name, software_version):
payload = dict()
multipart_data = decoder.MultipartDecoder(
request.body, pecan.request.headers.get('Content-Type'))
for filename in SUBCLOUD_RECONFIG_MANDATORY_FILE:
for part in multipart_data.parts:
for hk, hv in part.headers.items():
hv = hv.decode('utf8')
if hk.decode('utf8') == 'Content-Disposition':
if filename in hv:
fn = psd_common.get_config_file_path(
subcloud_name, consts.DEPLOY_CONFIG)
psd_common.upload_config_file(
part.content, fn, consts.DEPLOY_CONFIG)
payload.update({consts.DEPLOY_CONFIG: fn})
elif "sysadmin_password" in hv:
payload.update({'sysadmin_password': part.content})
psd_common.get_common_deploy_files(payload, software_version)
return payload
@staticmethod
def _get_updatestatus_payload(request):
"""retrieve payload of a patch request for update_status
@ -382,8 +331,8 @@ class SubcloudsController(object):
re.search(r"err_code\s*=\s*(\S*)", err_msg[0], re.IGNORECASE)
if err_code and err_code.group(1) in err_dict:
err_msg.append(err_dict.get(err_code.group(1)))
if status == consts.DEPLOY_STATE_DEPLOY_FAILED:
err_msg.append(err_dict.get(consts.DEPLOY_ERROR_MSG))
if status == consts.DEPLOY_STATE_CONFIG_FAILED:
err_msg.append(err_dict.get(consts.CONFIG_ERROR_MSG))
elif status == consts.DEPLOY_STATE_BOOTSTRAP_FAILED:
err_msg.append(err_dict.get(consts.BOOTSTRAP_ERROR_MSG))
subcloud['error-description'] = '\n'.join(err_msg)
@ -661,9 +610,9 @@ class SubcloudsController(object):
exceptions.SubcloudGroupNotFound):
pecan.abort(400, _('Invalid group'))
if INSTALL_VALUES in payload:
if consts.INSTALL_VALUES in payload:
psd_common.validate_install_values(payload, subcloud)
payload['data_install'] = json.dumps(payload[INSTALL_VALUES])
payload['data_install'] = json.dumps(payload[consts.INSTALL_VALUES])
try:
if reconfigure_network:
@ -684,138 +633,7 @@ class SubcloudsController(object):
# additional exceptions.
LOG.exception(e)
pecan.abort(500, _('Unable to update subcloud'))
elif verb == 'reconfigure':
if utils.subcloud_is_secondary_state(subcloud.deploy_status):
pecan.abort(500, _("Cannot perform on %s "
"state subcloud" % subcloud.deploy_status))
payload = self._get_reconfig_payload(
request, subcloud.name, subcloud.software_version)
if not payload:
pecan.abort(400, _('Body required'))
if (subcloud.deploy_status
not in [consts.DEPLOY_STATE_DONE,
consts.DEPLOY_STATE_DEPLOY_PREP_FAILED,
consts.DEPLOY_STATE_DEPLOY_FAILED]
and not prestage.is_deploy_status_prestage(
subcloud.deploy_status)):
pecan.abort(400,
_('Subcloud deploy status must be either '
'complete, deploy-prep-failed, deploy-failed, '
'or prestage-...'))
sysadmin_password = \
payload.get('sysadmin_password')
if not sysadmin_password:
pecan.abort(400, _('subcloud sysadmin_password required'))
try:
payload['sysadmin_password'] = \
utils.decode_and_normalize_passwd(sysadmin_password)
except Exception:
msg = _('Failed to decode subcloud sysadmin_password, '
'verify the password is base64 encoded')
LOG.exception(msg)
pecan.abort(400, msg)
try:
subcloud = self.dcmanager_rpc_client.reconfigure_subcloud(
context, subcloud_id, payload)
return subcloud
except RemoteError as e:
pecan.abort(422, e.value)
except Exception:
LOG.exception("Unable to reconfigure subcloud %s" % subcloud.name)
pecan.abort(500, _('Unable to reconfigure subcloud'))
elif verb == "reinstall":
if utils.subcloud_is_secondary_state(subcloud.deploy_status):
pecan.abort(500, _("Cannot perform on %s "
"state subcloud" % subcloud.deploy_status))
psd_common.check_required_parameters(request,
SUBCLOUD_ADD_MANDATORY_FILE)
payload = psd_common.get_request_data(
request, subcloud, SUBCLOUD_ADD_GET_FILE_CONTENTS)
install_values = psd_common.get_subcloud_db_install_values(subcloud)
if subcloud.availability_status == dccommon_consts.AVAILABILITY_ONLINE:
msg = _('Cannot re-install an online subcloud')
LOG.exception(msg)
pecan.abort(400, msg)
psd_common.validate_bootstrap_values(payload)
psd_common.validate_sysadmin_password(payload)
if payload.get('name') != subcloud.name:
pecan.abort(400, _('name is incorrect for the subcloud'))
psd_common.validate_subcloud_config(context, payload, verb)
# If a subcloud release is not passed, use the current
# system controller software_version
payload['software_version'] = payload.get('release', tsc.SW_VERSION)
psd_common.validate_k8s_version(payload)
# If the software version of the subcloud is different from the
# 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 specified or active load is still in dc-vault if
# image not in install values, add the matching image into the
# install values.
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({
'bmc_password': install_values.get('bmc_password'),
'install_values': install_values,
})
# Update data install(software version, image path)
data_install = None
if 'install_values' in payload:
data_install = json.dumps(payload['install_values'])
# Upload the deploy config files if it is included in the request
psd_common.upload_deploy_config_file(request, payload)
try:
# 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.
subcloud = db_api.subcloud_update(
context,
subcloud_id,
description=payload.get('description', subcloud.description),
location=payload.get('location', subcloud.location),
software_version=payload['software_version'],
management_state=dccommon_consts.MANAGEMENT_UNMANAGED,
deploy_status=consts.DEPLOY_STATE_PRE_INSTALL,
first_identity_sync_complete=False,
data_install=data_install)
self.dcmanager_rpc_client.reinstall_subcloud(
context, subcloud_id, payload)
return db_api.subcloud_db_model_to_dict(subcloud)
except RemoteError as e:
pecan.abort(422, e.value)
except Exception:
LOG.exception("Unable to reinstall subcloud %s" % subcloud.name)
pecan.abort(500, _('Unable to reinstall subcloud'))
elif verb == "redeploy":
if utils.subcloud_is_secondary_state(subcloud.deploy_status):
pecan.abort(500, _("Cannot perform on %s "
@ -845,7 +663,7 @@ class SubcloudsController(object):
# values if this phase should be executed.
files_for_redeploy = SUBCLOUD_REDEPLOY_GET_FILE_CONTENTS.copy()
if has_bootstrap_values:
files_for_redeploy.remove(BOOTSTRAP_VALUES)
files_for_redeploy.remove(consts.BOOTSTRAP_VALUES)
if not has_config_values:
files_for_redeploy.remove(consts.DEPLOY_CONFIG)
@ -883,9 +701,19 @@ class SubcloudsController(object):
except Exception:
LOG.exception("Unable to redeploy subcloud %s" % subcloud.name)
pecan.abort(500, _('Unable to redeploy subcloud'))
elif verb == "restore":
pecan.abort(410, _('This API is deprecated. '
'Please use /v1.0/subcloud-backup/restore'))
elif verb == "reconfigure":
pecan.abort(410, _('This API is deprecated. '
'Please use /v1.0/phased-subcloud-deploy/{subcloud}/configure'))
elif verb == "reinstall":
pecan.abort(410, _('This API is deprecated. '
'Please use /v1.0/subclouds/{subcloud}/redeploy'))
elif verb == 'update_status':
res = self.updatestatus(subcloud.name)
return res

View File

@ -112,9 +112,12 @@ class SubcloudAuditWorkerManager(manager.Manager):
# so that the subcloud can be set as offline
if (subcloud.deploy_status not in
[consts.DEPLOY_STATE_DONE,
consts.DEPLOY_STATE_DEPLOYING,
consts.DEPLOY_STATE_DEPLOY_FAILED,
consts.DEPLOY_STATE_CONFIGURING,
consts.DEPLOY_STATE_CONFIG_FAILED,
consts.DEPLOY_STATE_CONFIG_ABORTED,
consts.DEPLOY_STATE_PRE_CONFIG_FAILED,
consts.DEPLOY_STATE_INSTALL_FAILED,
consts.DEPLOY_STATE_INSTALL_ABORTED,
consts.DEPLOY_STATE_PRE_INSTALL_FAILED,
consts.DEPLOY_STATE_DATA_MIGRATION_FAILED,
consts.DEPLOY_STATE_MIGRATED,

View File

@ -202,7 +202,6 @@ DEPLOY_STATE_DEPLOY_FAILED = 'deploy-failed'
DEPLOY_STATE_ABORTING_INSTALL = 'aborting-install'
DEPLOY_STATE_INSTALL_ABORTED = 'install-aborted'
DEPLOY_STATE_ABORTING_BOOTSTRAP = 'aborting-bootstrap'
DEPLOY_STATE_BOOTSTRAP_ABORTED = 'bootstrap-aborted'
DEPLOY_STATE_ABORTING_CONFIG = 'aborting-config'
DEPLOY_STATE_CONFIG_ABORTED = 'config-aborted'
DEPLOY_STATE_MIGRATING_DATA = 'migrating-data'
@ -228,7 +227,7 @@ ERROR_DESC_CMD = 'dcmanager subcloud errors <subcloud-name>'
# Static content for error messages
BOOTSTRAP_ERROR_MSG = DEPLOY_STATE_BOOTSTRAP_FAILED
DEPLOY_ERROR_MSG = DEPLOY_STATE_DEPLOY_FAILED
CONFIG_ERROR_MSG = DEPLOY_STATE_CONFIG_FAILED
ERR_MSG_DICT = {
@ -236,9 +235,9 @@ ERR_MSG_DICT = {
"the subcloud after the cause of failure has been "
"resolved.",
DEPLOY_ERROR_MSG: "For deployment failures, please use dcmanager subcloud "
"reconfig command to reconfigure the subcloud after the "
"cause of failure has been resolved.",
CONFIG_ERROR_MSG: "For configuration failures, please use dcmanager subcloud "
"deploy config command to reconfigure the subcloud after "
"the cause of failure has been resolved.",
"bmc_cred": "Check BMC credentials in install-values.yml. Check basic "
"authenticacion to the BMC: curl -u <<user:pass>> "
@ -379,10 +378,14 @@ VALID_DEPLOY_STATES_FOR_BACKUP = [DEPLOY_STATE_DONE,
PRESTAGE_STATE_COMPLETE]
# States to reject when processing a subcloud-backup restore request
INVALID_DEPLOY_STATES_FOR_RESTORE = [DEPLOY_STATE_PRE_INSTALL,
INVALID_DEPLOY_STATES_FOR_RESTORE = [DEPLOY_STATE_CREATING,
DEPLOY_STATE_PRE_INSTALL,
DEPLOY_STATE_INSTALLING,
DEPLOY_STATE_PRE_BOOTSTRAP,
DEPLOY_STATE_BOOTSTRAPPING,
DEPLOY_STATE_DEPLOYING,
DEPLOY_STATE_PRE_CONFIG,
DEPLOY_STATE_CONFIGURING,
DEPLOY_STATE_PRE_REHOME,
DEPLOY_STATE_REHOMING,
DEPLOY_STATE_PRE_RESTORE,
DEPLOY_STATE_RESTORING]

View File

@ -37,7 +37,6 @@ ANSIBLE_BOOTSTRAP_VALIDATE_CONFIG_VARS = \
FRESH_INSTALL_K8S_VERSION = 'fresh_install_k8s_version'
KUBERNETES_VERSION = 'kubernetes_version'
INSTALL_VALUES = 'install_values'
INSTALL_VALUES_ADDRESSES = [
'bootstrap_address', 'bmc_address', 'nexthop_gateway',
'network_address'
@ -700,16 +699,16 @@ def format_ip_address(payload):
The IPv6 addresses can be represented in multiple ways. Format and
update the IP addresses in payload before saving it to database.
"""
if INSTALL_VALUES in payload:
if consts.INSTALL_VALUES in payload:
for k in INSTALL_VALUES_ADDRESSES:
if k in payload[INSTALL_VALUES]:
if k in payload[consts.INSTALL_VALUES]:
try:
address = netaddr.IPAddress(payload[INSTALL_VALUES]
address = netaddr.IPAddress(payload[consts.INSTALL_VALUES]
.get(k)).format()
except netaddr.AddrFormatError as e:
LOG.exception(e)
pecan.abort(400, _("%s invalid: %s") % (k, e))
payload[INSTALL_VALUES].update({k: address})
payload[consts.INSTALL_VALUES].update({k: address})
for k in BOOTSTRAP_VALUES_ADDRESSES:
if k in payload:

View File

@ -133,8 +133,7 @@ def validate_network_str(network_str, minimum_size, existing_networks=None,
"least %d addresses" % minimum_size)
elif network.version == 6 and network.prefixlen < 64:
raise exceptions.ValidateFail("IPv6 minimum prefix length is 64")
elif existing_networks and (operation != 'reinstall'
and operation != 'redeploy'):
elif existing_networks and operation != 'redeploy':
if any(network.ip in subnet for subnet in existing_networks):
raise exceptions.ValidateFail("Subnet overlaps with another "
"configured subnet")

View File

@ -138,22 +138,6 @@ class DCManagerService(service.Service):
subcloud_id,
payload)
@request_context
def reconfigure_subcloud(self, context, subcloud_id, payload):
# Reconfigures a subcloud
LOG.info("Handling reconfigure_subcloud request for: %s" % subcloud_id)
return self.subcloud_manager.reconfigure_subcloud(context,
subcloud_id,
payload)
@request_context
def reinstall_subcloud(self, context, subcloud_id, payload):
# Reinstall a subcloud
LOG.info("Handling reinstall_subcloud request for: %s" % payload.get('name'))
return self.subcloud_manager.reinstall_subcloud(context,
subcloud_id,
payload)
@request_context
def redeploy_subcloud(self, context, subcloud_id, payload):
# Redeploy a subcloud

View File

@ -105,8 +105,7 @@ SC_INTERMEDIATE_CERT_RENEW_BEFORE = "720h" # 30 days
CERT_NAMESPACE = "dc-cert"
TRANSITORY_STATES = {
consts.DEPLOY_STATE_NONE: consts.DEPLOY_STATE_DEPLOY_PREP_FAILED,
consts.DEPLOY_STATE_PRE_DEPLOY: consts.DEPLOY_STATE_DEPLOY_PREP_FAILED,
consts.DEPLOY_STATE_NONE: consts.DEPLOY_STATE_CREATE_FAILED,
consts.DEPLOY_STATE_CREATING: consts.DEPLOY_STATE_CREATE_FAILED,
consts.DEPLOY_STATE_PRE_INSTALL: consts.DEPLOY_STATE_PRE_INSTALL_FAILED,
consts.DEPLOY_STATE_INSTALLING: consts.DEPLOY_STATE_INSTALL_FAILED,
@ -114,15 +113,20 @@ TRANSITORY_STATES = {
consts.DEPLOY_STATE_BOOTSTRAPPING: consts.DEPLOY_STATE_BOOTSTRAP_FAILED,
consts.DEPLOY_STATE_PRE_CONFIG: consts.DEPLOY_STATE_PRE_CONFIG_FAILED,
consts.DEPLOY_STATE_CONFIGURING: consts.DEPLOY_STATE_CONFIG_FAILED,
consts.DEPLOY_STATE_DEPLOYING: consts.DEPLOY_STATE_DEPLOY_FAILED,
consts.DEPLOY_STATE_ABORTING_INSTALL: consts.DEPLOY_STATE_INSTALL_FAILED,
consts.DEPLOY_STATE_ABORTING_BOOTSTRAP: consts.DEPLOY_STATE_BOOTSTRAP_FAILED,
consts.DEPLOY_STATE_ABORTING_CONFIG: consts.DEPLOY_STATE_CONFIG_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_RESTORING: consts.DEPLOY_STATE_RESTORE_FAILED,
consts.DEPLOY_STATE_PRE_REHOME: consts.DEPLOY_STATE_REHOME_PREP_FAILED,
consts.DEPLOY_STATE_REHOMING: consts.DEPLOY_STATE_REHOME_FAILED,
consts.PRESTAGE_STATE_PACKAGES: consts.PRESTAGE_STATE_FAILED,
consts.PRESTAGE_STATE_IMAGES: consts.PRESTAGE_STATE_FAILED,
# The next two states are needed due to upgrade scenario:
# TODO(gherzman): remove states when they are no longer needed
consts.DEPLOY_STATE_PRE_DEPLOY: consts.DEPLOY_STATE_PRE_CONFIG_FAILED,
consts.DEPLOY_STATE_DEPLOYING: consts.DEPLOY_STATE_CONFIG_FAILED,
}
@ -448,122 +452,6 @@ class SubcloudManager(manager.Manager):
LOG.info(f"Finished adding subcloud {subcloud['name']}.")
def reconfigure_subcloud(self, context, subcloud_id, payload):
"""Reconfigure subcloud
:param context: request context object
:param subcloud_id: id of the subcloud
:param payload: subcloud configuration
"""
LOG.info("Reconfiguring subcloud %s." % subcloud_id)
subcloud = db_api.subcloud_update(
context, subcloud_id,
deploy_status=consts.DEPLOY_STATE_PRE_DEPLOY)
try:
# Ansible inventory filename for the specified subcloud
ansible_subcloud_inventory_file = self._get_ansible_filename(
subcloud.name, INVENTORY_FILE_POSTFIX)
config_command = None
if "deploy_playbook" in payload:
self._prepare_for_deployment(payload, subcloud.name)
config_command = self.compose_config_command(
subcloud.name,
ansible_subcloud_inventory_file,
payload)
del payload['sysadmin_password']
apply_thread = threading.Thread(
target=self.run_deploy_thread,
args=(subcloud, payload, context, None, None, config_command))
apply_thread.start()
return db_api.subcloud_db_model_to_dict(subcloud)
except Exception:
LOG.exception("Failed to create subcloud %s" % subcloud.name)
# If we failed to create the subcloud, update the
# deployment status
db_api.subcloud_update(
context, subcloud_id,
deploy_status=consts.DEPLOY_STATE_DEPLOY_PREP_FAILED)
def reinstall_subcloud(self, context, subcloud_id, payload):
"""Reinstall subcloud
:param context: request context object
:param subcloud_id: subcloud id from db
:param payload: subcloud reinstall
"""
# Retrieve the subcloud details from the database
subcloud = db_api.subcloud_get(context, subcloud_id)
LOG.info("Reinstalling subcloud %s." % subcloud_id)
try:
ansible_subcloud_inventory_file = self._get_ansible_filename(
subcloud.name, INVENTORY_FILE_POSTFIX)
m_ks_client = OpenStackDriver(
region_name=dccommon_consts.DEFAULT_REGION_NAME,
region_clients=None).keystone_client
cached_regionone_data = self._get_cached_regionone_data(m_ks_client)
self._populate_payload_with_cached_keystone_data(
cached_regionone_data, payload)
payload['install_values']['ansible_ssh_pass'] = \
payload['sysadmin_password']
payload['install_values']['ansible_become_pass'] = \
payload['sysadmin_password']
payload['bootstrap-address'] = \
payload['install_values']['bootstrap_address']
config_command = None
if "deploy_playbook" in payload:
self._prepare_for_deployment(payload, subcloud.name)
config_command = self.compose_config_command(
subcloud.name,
ansible_subcloud_inventory_file,
payload)
del payload['sysadmin_password']
payload['users'] = dict()
for user in USERS_TO_REPLICATE:
payload['users'][user] = \
str(keyring.get_password(
user, dccommon_consts.SERVICES_USER_NAME))
utils.create_subcloud_inventory(payload,
ansible_subcloud_inventory_file)
self._create_intermediate_ca_cert(payload)
self._write_subcloud_ansible_config(cached_regionone_data, payload)
install_command = self.compose_install_command(
subcloud.name,
ansible_subcloud_inventory_file,
payload['software_version'])
bootstrap_command = self.compose_bootstrap_command(
subcloud.name,
ansible_subcloud_inventory_file,
payload['software_version'])
network_reconfig = utils.has_network_reconfig(payload, subcloud)
apply_thread = threading.Thread(
target=self.run_deploy_thread,
args=(subcloud, payload, context,
install_command, bootstrap_command, config_command,
None, network_reconfig))
apply_thread.start()
return db_api.subcloud_db_model_to_dict(subcloud)
except Exception:
LOG.exception("Failed to reinstall subcloud %s" % subcloud.name)
# If we failed to reinstall the subcloud, update the
# deployment status
db_api.subcloud_update(
context, subcloud_id,
deploy_status=consts.DEPLOY_STATE_PRE_INSTALL_FAILED)
def redeploy_subcloud(self, context, subcloud_id, payload):
"""Redeploy subcloud

View File

@ -154,16 +154,6 @@ class ManagerClient(RPCClient):
subcloud_id=subcloud_id,
payload=payload))
def reconfigure_subcloud(self, ctxt, subcloud_id, payload):
return self.call(ctxt, self.make_msg('reconfigure_subcloud',
subcloud_id=subcloud_id,
payload=payload))
def reinstall_subcloud(self, ctxt, subcloud_id, payload):
return self.cast(ctxt, self.make_msg('reinstall_subcloud',
subcloud_id=subcloud_id,
payload=payload))
def redeploy_subcloud(self, ctxt, subcloud_id, payload):
return self.cast(ctxt, self.make_msg('redeploy_subcloud',
subcloud_id=subcloud_id,

View File

@ -1156,18 +1156,12 @@ class TestSubcloudRestore(testroot.DCManagerApiTest):
@mock.patch.object(rpc_client, 'ManagerClient')
def test_backup_restore_subcloud_invalid_deploy_states(self, mock_rpc_client):
invalid_deploy_states = [consts.DEPLOY_STATE_INSTALLING,
consts.DEPLOY_STATE_BOOTSTRAPPING,
consts.DEPLOY_STATE_DEPLOYING,
consts.DEPLOY_STATE_REHOMING]
subcloud = fake_subcloud.create_fake_subcloud(self.ctx)
fake_password = (base64.b64encode('testpass'.encode("utf-8"))).decode('ascii')
data = {'sysadmin_password': fake_password, 'subcloud': '1'}
mock_rpc_client().restore_subcloud_backups.return_value = True
for status in invalid_deploy_states:
for status in consts.INVALID_DEPLOY_STATES_FOR_RESTORE:
db_api.subcloud_update(self.ctx,
subcloud.id,
availability_status=dccommon_consts.AVAILABILITY_ONLINE,

View File

@ -29,6 +29,7 @@ from tsconfig.tsconfig import SW_VERSION
import webtest
from dccommon import consts as dccommon_consts
from dcmanager.api.controllers.v1 import phased_subcloud_deploy as psd
from dcmanager.api.controllers.v1 import subclouds
from dcmanager.common import consts
from dcmanager.common import exceptions
@ -197,7 +198,7 @@ class SubcloudAPIMixin(APIMixin):
"install_type": 2,
}
list_of_post_files = subclouds.SUBCLOUD_ADD_MANDATORY_FILE
list_of_post_files = psd.SUBCLOUD_BOOTSTRAP_GET_FILE_CONTENTS
bootstrap_data = copy.copy(FAKE_BOOTSTRAP_DATA)
install_data = copy.copy(FAKE_INSTALL_DATA)
@ -245,9 +246,9 @@ class SubcloudAPIMixin(APIMixin):
for f in self.list_of_post_files:
fake_name = f + "_fake"
# The data in the bootstrap file needs to be dictionary syntax
if f == subclouds.BOOTSTRAP_VALUES:
if f == consts.BOOTSTRAP_VALUES:
fake_content = json.dumps(self.bootstrap_data).encode("utf-8")
elif f == subclouds.INSTALL_VALUES:
elif f == consts.INSTALL_VALUES:
fake_content = json.dumps(self.install_data).encode("utf-8")
else:
fake_content = "fake content".encode("utf-8")
@ -270,7 +271,7 @@ class TestSubcloudPost(testroot.DCManagerApiTest,
PostMixin):
def setUp(self):
super(TestSubcloudPost, self).setUp()
self.list_of_post_files = subclouds.SUBCLOUD_ADD_MANDATORY_FILE
self.list_of_post_files = psd.SUBCLOUD_BOOTSTRAP_GET_FILE_CONTENTS
self.bootstrap_data = copy.copy(self.FAKE_BOOTSTRAP_DATA)
self.install_data = copy.copy(self.FAKE_INSTALL_DATA)
@ -345,7 +346,7 @@ class TestSubcloudPost(testroot.DCManagerApiTest,
Example: name is a required field
"""
self.list_of_post_files = subclouds.SUBCLOUD_ADD_MANDATORY_FILE
self.list_of_post_files = psd.SUBCLOUD_BOOTSTRAP_GET_FILE_CONTENTS
params = self.get_post_params()
for key in self.FAKE_BOOTSTRAP_DATA:
@ -1416,67 +1417,6 @@ class TestSubcloudAPIOther(testroot.DCManagerApiTest):
bootstrap_address=None)
self.assertEqual(response.status_int, 200)
@mock.patch.object(subclouds.SubcloudsController, '_get_reconfig_payload')
def test_reconfigure_subcloud(self, mock_get_reconfig_payload):
subcloud = fake_subcloud.create_fake_subcloud(self.ctx)
fake_password = (base64.b64encode('testpass'.encode("utf-8"))).decode('ascii')
data = {'sysadmin_password': fake_password}
self.mock_rpc_client().reconfigure_subcloud.return_value = True
mock_get_reconfig_payload.return_value = data
response = self.app.patch_json(FAKE_URL + '/' + str(subcloud.id) +
'/reconfigure',
headers=FAKE_HEADERS,
params=data)
self.mock_rpc_client().reconfigure_subcloud.assert_called_once_with(
mock.ANY,
subcloud.id,
mock.ANY)
self.assertEqual(response.status_int, 200)
@mock.patch.object(subclouds.SubcloudsController, '_get_reconfig_payload')
def test_reconfigure_subcloud_no_body(self, mock_get_reconfig_payload):
subcloud = fake_subcloud.create_fake_subcloud(self.ctx)
# Pass an empty request body
data = {}
mock_get_reconfig_payload.return_value = data
self.mock_rpc_client().reconfigure_subcloud.return_value = True
six.assertRaisesRegex(self, webtest.app.AppError, "400 *",
self.app.patch_json, FAKE_URL + '/' +
str(subcloud.id) + '/reconfigure',
headers=FAKE_HEADERS, params=data)
@mock.patch.object(subclouds.SubcloudsController, '_get_reconfig_payload')
def test_reconfigure_subcloud_bad_password(self, mock_get_reconfig_payload):
subcloud = fake_subcloud.create_fake_subcloud(self.ctx)
# Pass a sysadmin_password which is not base64 encoded
data = {'sysadmin_password': 'not_base64'}
mock_get_reconfig_payload.return_value = data
self.mock_rpc_client().reconfigure_subcloud.return_value = True
six.assertRaisesRegex(self, webtest.app.AppError, "400 *",
self.app.patch_json, FAKE_URL + '/' +
str(subcloud.id) + '/reconfigure',
headers=FAKE_HEADERS, params=data)
@mock.patch.object(subclouds.SubcloudsController, '_get_reconfig_payload')
def test_reconfigure_invalid_deploy_status(self,
mock_get_reconfig_payload):
subcloud = fake_subcloud.create_fake_subcloud(
self.ctx,
deploy_status=consts.DEPLOY_STATE_BOOTSTRAP_FAILED)
fake_password = base64.b64encode('testpass'.encode("utf-8")).decode("utf-8")
data = {'sysadmin_password': fake_password}
mock_get_reconfig_payload.return_value = data
self.mock_rpc_client().reconfigure_subcloud.return_value = True
six.assertRaisesRegex(self, webtest.app.AppError, "400 *",
self.app.patch_json, FAKE_URL + '/' +
str(subcloud.id) + '/reconfigure',
headers=FAKE_HEADERS, params=data)
@mock.patch.object(subclouds.SubcloudsController, '_get_updatestatus_payload')
def test_subcloud_updatestatus(self, mock_get_updatestatus_payload):
subcloud = fake_subcloud.create_fake_subcloud(self.ctx)
@ -1551,17 +1491,17 @@ class TestSubcloudAPIOther(testroot.DCManagerApiTest):
psd_common.format_ip_address(fake_payload)
self.assertEqual(fake_payload['bootstrap-address'], v)
fake_payload[subclouds.INSTALL_VALUES] = {}
fake_payload[consts.INSTALL_VALUES] = {}
for k, v in good_values.items():
fake_payload[subclouds.INSTALL_VALUES]['bmc_address'] = k
fake_payload[consts.INSTALL_VALUES]['bmc_address'] = k
psd_common.format_ip_address(fake_payload)
self.assertEqual(fake_payload[subclouds.INSTALL_VALUES]['bmc_address'], v)
self.assertEqual(fake_payload[consts.INSTALL_VALUES]['bmc_address'], v)
fake_payload['othervalues1'] = 'othervalues1'
fake_payload[subclouds.INSTALL_VALUES]['othervalues2'] = 'othervalues2'
fake_payload[consts.INSTALL_VALUES]['othervalues2'] = 'othervalues2'
psd_common.format_ip_address(fake_payload)
self.assertEqual(fake_payload['othervalues1'], 'othervalues1')
self.assertEqual(fake_payload[subclouds.INSTALL_VALUES]['othervalues2'], 'othervalues2')
self.assertEqual(fake_payload[consts.INSTALL_VALUES]['othervalues2'], 'othervalues2')
def test_get_subcloud_db_install_values(self):
install_data = copy.copy(FAKE_SUBCLOUD_INSTALL_VALUES)
@ -1587,218 +1527,9 @@ class TestSubcloudAPIOther(testroot.DCManagerApiTest):
six.assertRaisesRegex(self, webtest.app.AppError, "400 *",
self.app.patch_json, FAKE_URL + '/' +
str(subcloud.id) + '/reinstall',
str(subcloud.id) + '/redeploy',
headers=FAKE_HEADERS)
@mock.patch.object(cutils, 'get_vault_load_files')
@mock.patch.object(psd_common, 'upload_deploy_config_file')
@mock.patch.object(psd_common, 'validate_k8s_version')
@mock.patch.object(psd_common, 'validate_subcloud_config')
@mock.patch.object(psd_common, 'validate_bootstrap_values')
def test_reinstall_subcloud(
self, mock_validate_bootstrap_values, mock_validate_subcloud_config,
mock_validate_k8s_version, mock_upload_deploy_config_file,
mock_get_vault_load_files):
encoded_password = base64.b64encode(
'bmc_password'.encode("utf-8")).decode('utf-8')
data_install = {**FAKE_SUBCLOUD_INSTALL_VALUES,
'bmc_password': encoded_password}
subcloud = fake_subcloud.create_fake_subcloud(
self.ctx, name=fake_subcloud.FAKE_BOOTSTRAP_FILE_DATA["name"],
data_install=json.dumps(data_install))
fake_bootstrap_content = json.dumps(
fake_subcloud.FAKE_BOOTSTRAP_FILE_DATA).encode("utf-8")
mock_get_vault_load_files.return_value = ('iso_file_path', 'sig_file_path')
params = {'sysadmin_password': encoded_password}
response = self.app.patch(
FAKE_URL + '/' + str(subcloud.id) + '/reinstall',
headers=FAKE_HEADERS, params=params,
upload_files=[("bootstrap_values",
"bootstrap_fake_filename",
fake_bootstrap_content)])
mock_validate_bootstrap_values.assert_called_once()
mock_validate_subcloud_config.assert_called_once()
mock_validate_k8s_version.assert_called_once()
self.mock_rpc_client().reinstall_subcloud.assert_called_once_with(
mock.ANY,
subcloud.id,
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(psd_common, 'check_required_parameters')
@mock.patch.object(cutils, 'get_vault_load_files')
@mock.patch.object(psd_common, 'upload_deploy_config_file')
@mock.patch.object(psd_common, 'validate_k8s_version')
@mock.patch.object(psd_common, 'validate_subcloud_config')
@mock.patch.object(psd_common, 'validate_bootstrap_values')
@mock.patch.object(psd_common, 'get_subcloud_db_install_values')
@mock.patch.object(psd_common, 'get_request_data')
def test_reinstall_subcloud_with_release_parameter(
self, mock_get_request_data, mock_get_subcloud_db_install_values,
mock_validate_install_parameters, mock_validate_subcloud_config,
mock_validate_k8s_version, mock_upload_deploy_config_file,
mock_get_vault_load_files, mock_check_required_parameters):
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
self.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_install_parameters.assert_called_once()
mock_validate_subcloud_config.assert_called_once()
self.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(psd_common, 'get_subcloud_db_install_values')
@mock.patch.object(psd_common, 'validate_bootstrap_values')
@mock.patch.object(psd_common, 'get_request_data')
def test_reinstall_subcloud_no_body(
self, mock_get_request_data, mock_validate_install_parameters,
mock_get_subcloud_db_install_values, mock_get_vault_load_files):
subcloud = fake_subcloud.create_fake_subcloud(self.ctx)
install_data = copy.copy(FAKE_SUBCLOUD_INSTALL_VALUES)
mock_get_request_data.return_value = {}
encoded_password = base64.b64encode(
'bmc_password'.encode("utf-8")).decode('utf-8')
bmc_password = {'bmc_password': encoded_password}
install_data.update(bmc_password)
mock_validate_install_parameters.assert_not_called()
mock_get_subcloud_db_install_values.return_value = install_data
self.mock_rpc_client().reinstall_subcloud.return_value = True
six.assertRaisesRegex(self, webtest.app.AppError, "400 *",
self.app.patch_json, FAKE_URL + '/' +
str(subcloud.id) + '/reinstall',
headers=FAKE_HEADERS, params={})
@mock.patch.object(cutils, 'get_vault_load_files')
@mock.patch.object(psd_common, 'get_subcloud_db_install_values')
@mock.patch.object(psd_common, 'validate_bootstrap_values')
@mock.patch.object(psd_common, 'get_request_data')
def test_reinstall_online_subcloud(
self, mock_get_request_data, mock_validate_install_parameters,
mock_get_subcloud_db_install_values, mock_get_vault_load_files):
subcloud = fake_subcloud.create_fake_subcloud(self.ctx)
db_api.subcloud_update(
self.ctx, subcloud.id,
availability_status=dccommon_consts.AVAILABILITY_ONLINE
)
install_data = copy.copy(FAKE_SUBCLOUD_INSTALL_VALUES)
reinstall_data = copy.copy(FAKE_SUBCLOUD_BOOTSTRAP_PAYLOAD)
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_validate_install_parameters.assert_not_called()
mock_get_subcloud_db_install_values.return_value = install_data
self.mock_rpc_client().reinstall_subcloud.return_value = True
six.assertRaisesRegex(self, webtest.app.AppError, "400 *",
self.app.patch_json, FAKE_URL + '/' +
str(subcloud.id) + '/reinstall',
headers=FAKE_HEADERS, params={})
@mock.patch.object(psd_common, 'get_subcloud_db_install_values')
@mock.patch.object(psd_common, 'get_request_data')
def test_reinstall_subcloud_missing_required_value(
self, mock_get_request_data, mock_get_subcloud_db_install_values):
subcloud = fake_subcloud.create_fake_subcloud(self.ctx)
install_data = copy.copy(FAKE_SUBCLOUD_INSTALL_VALUES)
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
self.mock_rpc_client().reinstall_subcloud.return_value = True
for k in ['name', 'system_mode', 'external_oam_subnet',
'external_oam_gateway_address', 'external_oam_floating_address',
'sysadmin_password']:
reinstall_data = copy.copy(FAKE_SUBCLOUD_BOOTSTRAP_PAYLOAD)
del reinstall_data[k]
mock_get_request_data.return_value = reinstall_data
six.assertRaisesRegex(self, webtest.app.AppError, "400 *",
self.app.patch_json, FAKE_URL + '/' +
str(subcloud.id) + '/reinstall',
headers=FAKE_HEADERS, params=reinstall_data)
@mock.patch.object(psd_common, 'check_required_parameters')
@mock.patch.object(cutils, 'get_vault_load_files')
@mock.patch.object(psd_common, 'get_subcloud_db_install_values')
@mock.patch.object(psd_common, 'validate_k8s_version')
@mock.patch.object(psd_common, 'validate_subcloud_config')
@mock.patch.object(psd_common, 'validate_bootstrap_values')
@mock.patch.object(psd_common, 'get_request_data')
def test_reinstall_subcloud_missing_stored_value(
self, mock_get_request_data, mock_validate_install_parameters,
mock_validate_subcloud_config, mock_validate_k8s_version,
mock_get_subcloud_db_install_values, mock_get_vault_load_files,
mock_check_required_parameters):
subcloud = fake_subcloud.create_fake_subcloud(self.ctx)
install_data = copy.copy(FAKE_SUBCLOUD_INSTALL_VALUES)
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
self.mock_rpc_client().reinstall_subcloud.return_value = True
mock_get_vault_load_files.return_value = ('iso_file_path', 'sig_file_path')
for k in ['management_subnet', 'management_start_address',
'management_end_address', 'management_gateway_address',
'systemcontroller_gateway_address']:
reinstall_data = copy.copy(FAKE_SUBCLOUD_BOOTSTRAP_PAYLOAD)
del reinstall_data[k]
mock_get_request_data.return_value = reinstall_data
response = self.app.patch_json(
FAKE_URL + '/' + str(subcloud.id) + '/reinstall',
headers=FAKE_HEADERS, params=reinstall_data)
self.assertEqual(response.status_int, 200)
@mock.patch.object(psd_common, 'upload_config_file')
@mock.patch.object(psd_common.PatchingClient, 'query')
@mock.patch.object(os.path, 'isdir')

View File

@ -1674,29 +1674,6 @@ class TestSubcloudManager(base.DCManagerTestCase):
updated_subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud.name)
self.assertEqual(updated_subcloud.openstack_installed, False)
@mock.patch.object(subcloud_manager.SubcloudManager,
'_prepare_for_deployment')
@mock.patch.object(threading.Thread,
'start')
def test_reconfig_subcloud(self, mock_thread_start,
mock_prepare_for_deployment):
subcloud = self.create_subcloud_static(
self.ctx,
name='subcloud1',
deploy_status=consts.DEPLOY_STATE_PRE_DEPLOY)
fake_payload = {"sysadmin_password": "testpass",
"deploy_playbook": "test_playbook.yaml",
"deploy_overrides": "test_overrides.yaml",
"deploy_chart": "test_chart.yaml",
"deploy_config": "subcloud1.yaml"}
sm = subcloud_manager.SubcloudManager()
sm.reconfigure_subcloud(self.ctx,
subcloud.id,
payload=fake_payload)
mock_thread_start.assert_called_once()
mock_prepare_for_deployment.assert_called_once()
def test_get_ansible_filename(self):
filename = cutils.get_ansible_filename('subcloud1',
consts.INVENTORY_FILE_POSTFIX)
@ -1787,60 +1764,6 @@ class TestSubcloudManager(base.DCManagerTestCase):
]
)
@mock.patch.object(
subcloud_manager.SubcloudManager, '_write_subcloud_ansible_config')
@mock.patch.object(
subcloud_manager.SubcloudManager, '_create_intermediate_ca_cert')
@mock.patch.object(
subcloud_manager.SubcloudManager, 'compose_install_command')
@mock.patch.object(
subcloud_manager.SubcloudManager, 'compose_bootstrap_command')
@mock.patch.object(cutils, 'create_subcloud_inventory')
@mock.patch.object(subcloud_manager.SubcloudManager, '_get_cached_regionone_data')
@mock.patch.object(subcloud_manager, 'OpenStackDriver')
@mock.patch.object(threading.Thread, 'start')
@mock.patch.object(subcloud_manager, 'keyring')
def test_reinstall_subcloud(
self, mock_keyring, mock_thread_start,
mock_keystone_client, mock_get_cached_regionone_data, mock_create_subcloud_inventory,
mock_compose_bootstrap_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=subcloud_name,
deploy_status=consts.DEPLOY_STATE_PRE_INSTALL)
fake_install_values = \
copy.copy(fake_subcloud.FAKE_SUBCLOUD_INSTALL_VALUES)
fake_install_values['software_version'] = SW_VERSION
fake_payload = copy.copy(fake_subcloud.FAKE_SUBCLOUD_BOOTSTRAP_PAYLOAD)
fake_payload.update({
'bmc_password': 'bmc_pass',
'software_version': FAKE_PREVIOUS_SW_VERSION,
'install_values': fake_install_values})
sm = subcloud_manager.SubcloudManager()
mock_keyring.get_password.return_value = "testpassword"
mock_get_cached_regionone_data.return_value = FAKE_CACHED_REGIONONE_DATA
sm.reinstall_subcloud(self.ctx, subcloud.id, payload=fake_payload)
mock_keystone_client.assert_called_once()
mock_get_cached_regionone_data.assert_called_once()
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_with(
subcloud_name,
cutils.get_ansible_filename(subcloud_name, consts.INVENTORY_FILE_POSTFIX),
FAKE_PREVIOUS_SW_VERSION)
mock_compose_bootstrap_command.assert_called_once_with(
subcloud_name,
cutils.get_ansible_filename(subcloud_name, consts.INVENTORY_FILE_POSTFIX),
FAKE_PREVIOUS_SW_VERSION)
mock_thread_start.assert_called_once()
@mock.patch.object(subcloud_manager.SubcloudManager,
'_run_subcloud_install')
@mock.patch.object(subcloud_manager.SubcloudManager,
@ -1933,12 +1856,32 @@ class TestSubcloudManager(base.DCManagerTestCase):
self.ctx,
name='subcloud9',
deploy_status=consts.DEPLOY_STATE_NONE)
subcloud10 = self.create_subcloud_static(
self.ctx,
name='subcloud10',
deploy_status=consts.DEPLOY_STATE_CREATING)
subcloud11 = self.create_subcloud_static(
self.ctx,
name='subcloud11',
deploy_status=consts.DEPLOY_STATE_PRE_BOOTSTRAP)
subcloud12 = self.create_subcloud_static(
self.ctx,
name='subcloud12',
deploy_status=consts.DEPLOY_STATE_ABORTING_INSTALL)
subcloud13 = self.create_subcloud_static(
self.ctx,
name='subcloud13',
deploy_status=consts.DEPLOY_STATE_ABORTING_BOOTSTRAP)
subcloud14 = self.create_subcloud_static(
self.ctx,
name='subcloud14',
deploy_status=consts.DEPLOY_STATE_ABORTING_CONFIG)
sm = subcloud_manager.SubcloudManager()
sm.handle_subcloud_operations_in_progress()
subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud1.name)
self.assertEqual(consts.DEPLOY_STATE_DEPLOY_PREP_FAILED,
self.assertEqual(consts.DEPLOY_STATE_PRE_CONFIG_FAILED,
subcloud.deploy_status)
subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud2.name)
@ -1954,7 +1897,7 @@ class TestSubcloudManager(base.DCManagerTestCase):
subcloud.deploy_status)
subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud5.name)
self.assertEqual(consts.DEPLOY_STATE_DEPLOY_FAILED,
self.assertEqual(consts.DEPLOY_STATE_CONFIG_FAILED,
subcloud.deploy_status)
subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud6.name)
@ -1970,14 +1913,34 @@ class TestSubcloudManager(base.DCManagerTestCase):
subcloud.deploy_status)
subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud9.name)
self.assertEqual(consts.DEPLOY_STATE_DEPLOY_PREP_FAILED,
self.assertEqual(consts.DEPLOY_STATE_CREATE_FAILED,
subcloud.deploy_status)
subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud10.name)
self.assertEqual(consts.DEPLOY_STATE_CREATE_FAILED,
subcloud.deploy_status)
subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud11.name)
self.assertEqual(consts.DEPLOY_STATE_PRE_BOOTSTRAP_FAILED,
subcloud.deploy_status)
subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud12.name)
self.assertEqual(consts.DEPLOY_STATE_INSTALL_FAILED,
subcloud.deploy_status)
subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud13.name)
self.assertEqual(consts.DEPLOY_STATE_BOOTSTRAP_FAILED,
subcloud.deploy_status)
subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud14.name)
self.assertEqual(consts.DEPLOY_STATE_CONFIG_FAILED,
subcloud.deploy_status)
def test_handle_completed_subcloud_operations(self):
subcloud1 = self.create_subcloud_static(
self.ctx,
name='subcloud1',
deploy_status=consts.DEPLOY_STATE_DEPLOY_PREP_FAILED)
deploy_status=consts.DEPLOY_STATE_CREATE_FAILED)
subcloud2 = self.create_subcloud_static(
self.ctx,
name='subcloud2',
@ -1997,7 +1960,7 @@ class TestSubcloudManager(base.DCManagerTestCase):
subcloud6 = self.create_subcloud_static(
self.ctx,
name='subcloud6',
deploy_status=consts.DEPLOY_STATE_DEPLOY_FAILED)
deploy_status=consts.DEPLOY_STATE_CONFIG_FAILED)
subcloud7 = self.create_subcloud_static(
self.ctx,
name='subcloud7',
@ -2018,12 +1981,24 @@ class TestSubcloudManager(base.DCManagerTestCase):
self.ctx,
name='subcloud11',
deploy_status=consts.DEPLOY_STATE_DONE)
subcloud12 = self.create_subcloud_static(
self.ctx,
name='subcloud12',
deploy_status=consts.DEPLOY_STATE_CREATE_FAILED)
subcloud13 = self.create_subcloud_static(
self.ctx,
name='subcloud13',
deploy_status=consts.DEPLOY_STATE_PRE_BOOTSTRAP_FAILED)
subcloud14 = self.create_subcloud_static(
self.ctx,
name='subcloud14',
deploy_status=consts.DEPLOY_STATE_PRE_CONFIG_FAILED)
sm = subcloud_manager.SubcloudManager()
sm.handle_subcloud_operations_in_progress()
subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud1.name)
self.assertEqual(consts.DEPLOY_STATE_DEPLOY_PREP_FAILED,
self.assertEqual(consts.DEPLOY_STATE_CREATE_FAILED,
subcloud.deploy_status)
subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud2.name)
@ -2043,7 +2018,7 @@ class TestSubcloudManager(base.DCManagerTestCase):
subcloud.deploy_status)
subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud6.name)
self.assertEqual(consts.DEPLOY_STATE_DEPLOY_FAILED,
self.assertEqual(consts.DEPLOY_STATE_CONFIG_FAILED,
subcloud.deploy_status)
subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud7.name)
@ -2066,6 +2041,18 @@ class TestSubcloudManager(base.DCManagerTestCase):
self.assertEqual(consts.DEPLOY_STATE_DONE,
subcloud.deploy_status)
subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud12.name)
self.assertEqual(consts.DEPLOY_STATE_CREATE_FAILED,
subcloud.deploy_status)
subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud13.name)
self.assertEqual(consts.DEPLOY_STATE_PRE_BOOTSTRAP_FAILED,
subcloud.deploy_status)
subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud14.name)
self.assertEqual(consts.DEPLOY_STATE_PRE_CONFIG_FAILED,
subcloud.deploy_status)
@mock.patch.object(cutils, 'is_subcloud_healthy', return_value=True)
@mock.patch.object(subcloud_manager.SubcloudManager,
'_run_subcloud_backup_create_playbook')