From 338ceaca818e7409a799a7d84625e4882a3059b5 Mon Sep 17 00:00:00 2001 From: Gustavo Herzmann Date: Mon, 7 Aug 2023 18:04:19 -0300 Subject: [PATCH] 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 --- api-ref/source/api-ref-dcmanager-v1.rst | 150 +-------- .../subcloud-patch-reconfigure-request.json | 5 - .../subcloud-patch-reconfigure-response.json | 23 -- .../subcloud-patch-reinstall-request.json | 5 - .../subcloud-patch-reinstall-response.json | 25 -- .../controllers/v1/phased_subcloud_deploy.py | 9 +- .../dcmanager/api/controllers/v1/subclouds.py | 212 ++----------- .../audit/subcloud_audit_worker_manager.py | 7 +- distributedcloud/dcmanager/common/consts.py | 17 +- .../common/phased_subcloud_deploy.py | 9 +- distributedcloud/dcmanager/common/utils.py | 3 +- distributedcloud/dcmanager/manager/service.py | 16 - .../dcmanager/manager/subcloud_manager.py | 126 +------- distributedcloud/dcmanager/rpc/client.py | 10 - .../v1/controllers/test_subcloud_backup.py | 8 +- .../unit/api/v1/controllers/test_subclouds.py | 293 +----------------- .../unit/manager/test_subcloud_manager.py | 155 +++++---- 17 files changed, 139 insertions(+), 934 deletions(-) delete mode 100644 api-ref/source/samples/subclouds/subcloud-patch-reconfigure-request.json delete mode 100644 api-ref/source/samples/subclouds/subcloud-patch-reconfigure-response.json delete mode 100644 api-ref/source/samples/subclouds/subcloud-patch-reinstall-request.json delete mode 100644 api-ref/source/samples/subclouds/subcloud-patch-reinstall-response.json diff --git a/api-ref/source/api-ref-dcmanager-v1.rst b/api-ref/source/api-ref-dcmanager-v1.rst index 3f6f74b02..2be454753 100644 --- a/api-ref/source/api-ref-dcmanager-v1.rst +++ b/api-ref/source/api-ref-dcmanager-v1.rst @@ -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 diff --git a/api-ref/source/samples/subclouds/subcloud-patch-reconfigure-request.json b/api-ref/source/samples/subclouds/subcloud-patch-reconfigure-request.json deleted file mode 100644 index 6abc828f9..000000000 --- a/api-ref/source/samples/subclouds/subcloud-patch-reconfigure-request.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "sysadmin_password": "XXXXXXX", - "deploy_config": "path to some file" - -} diff --git a/api-ref/source/samples/subclouds/subcloud-patch-reconfigure-response.json b/api-ref/source/samples/subclouds/subcloud-patch-reconfigure-response.json deleted file mode 100644 index abf248c26..000000000 --- a/api-ref/source/samples/subclouds/subcloud-patch-reconfigure-response.json +++ /dev/null @@ -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", -} diff --git a/api-ref/source/samples/subclouds/subcloud-patch-reinstall-request.json b/api-ref/source/samples/subclouds/subcloud-patch-reinstall-request.json deleted file mode 100644 index 6d9252079..000000000 --- a/api-ref/source/samples/subclouds/subcloud-patch-reinstall-request.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "sysadmin_password": "XXXXXXX", - "bootstrap_values": "path to boostrap file", - "deploy_config": "path to deploy file" -} diff --git a/api-ref/source/samples/subclouds/subcloud-patch-reinstall-response.json b/api-ref/source/samples/subclouds/subcloud-patch-reinstall-response.json deleted file mode 100644 index ce36f462e..000000000 --- a/api-ref/source/samples/subclouds/subcloud-patch-reinstall-response.json +++ /dev/null @@ -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", -} diff --git a/distributedcloud/dcmanager/api/controllers/v1/phased_subcloud_deploy.py b/distributedcloud/dcmanager/api/controllers/v1/phased_subcloud_deploy.py index f1685a232..f6d94c17d 100644 --- a/distributedcloud/dcmanager/api/controllers/v1/phased_subcloud_deploy.py +++ b/distributedcloud/dcmanager/api/controllers/v1/phased_subcloud_deploy.py @@ -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 = ( diff --git a/distributedcloud/dcmanager/api/controllers/v1/subclouds.py b/distributedcloud/dcmanager/api/controllers/v1/subclouds.py index 4d28b40e8..daa0f6a1d 100644 --- a/distributedcloud/dcmanager/api/controllers/v1/subclouds.py +++ b/distributedcloud/dcmanager/api/controllers/v1/subclouds.py @@ -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 diff --git a/distributedcloud/dcmanager/audit/subcloud_audit_worker_manager.py b/distributedcloud/dcmanager/audit/subcloud_audit_worker_manager.py index 68111de43..9e925ab7d 100644 --- a/distributedcloud/dcmanager/audit/subcloud_audit_worker_manager.py +++ b/distributedcloud/dcmanager/audit/subcloud_audit_worker_manager.py @@ -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, diff --git a/distributedcloud/dcmanager/common/consts.py b/distributedcloud/dcmanager/common/consts.py index 116e94cab..64bfdea64 100644 --- a/distributedcloud/dcmanager/common/consts.py +++ b/distributedcloud/dcmanager/common/consts.py @@ -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 ' # 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 <> " @@ -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] diff --git a/distributedcloud/dcmanager/common/phased_subcloud_deploy.py b/distributedcloud/dcmanager/common/phased_subcloud_deploy.py index 7ab06a950..891aca398 100644 --- a/distributedcloud/dcmanager/common/phased_subcloud_deploy.py +++ b/distributedcloud/dcmanager/common/phased_subcloud_deploy.py @@ -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: diff --git a/distributedcloud/dcmanager/common/utils.py b/distributedcloud/dcmanager/common/utils.py index 9b15237b0..9cf04537c 100644 --- a/distributedcloud/dcmanager/common/utils.py +++ b/distributedcloud/dcmanager/common/utils.py @@ -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") diff --git a/distributedcloud/dcmanager/manager/service.py b/distributedcloud/dcmanager/manager/service.py index 7ab90b6a5..45b13da54 100644 --- a/distributedcloud/dcmanager/manager/service.py +++ b/distributedcloud/dcmanager/manager/service.py @@ -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 diff --git a/distributedcloud/dcmanager/manager/subcloud_manager.py b/distributedcloud/dcmanager/manager/subcloud_manager.py index 06830daba..151a5927b 100644 --- a/distributedcloud/dcmanager/manager/subcloud_manager.py +++ b/distributedcloud/dcmanager/manager/subcloud_manager.py @@ -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 diff --git a/distributedcloud/dcmanager/rpc/client.py b/distributedcloud/dcmanager/rpc/client.py index be118b39b..f93bcdab2 100644 --- a/distributedcloud/dcmanager/rpc/client.py +++ b/distributedcloud/dcmanager/rpc/client.py @@ -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, diff --git a/distributedcloud/dcmanager/tests/unit/api/v1/controllers/test_subcloud_backup.py b/distributedcloud/dcmanager/tests/unit/api/v1/controllers/test_subcloud_backup.py index 58ac2c025..224fc2c1f 100644 --- a/distributedcloud/dcmanager/tests/unit/api/v1/controllers/test_subcloud_backup.py +++ b/distributedcloud/dcmanager/tests/unit/api/v1/controllers/test_subcloud_backup.py @@ -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, diff --git a/distributedcloud/dcmanager/tests/unit/api/v1/controllers/test_subclouds.py b/distributedcloud/dcmanager/tests/unit/api/v1/controllers/test_subclouds.py index 999860a3a..608ab4790 100644 --- a/distributedcloud/dcmanager/tests/unit/api/v1/controllers/test_subclouds.py +++ b/distributedcloud/dcmanager/tests/unit/api/v1/controllers/test_subclouds.py @@ -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') diff --git a/distributedcloud/dcmanager/tests/unit/manager/test_subcloud_manager.py b/distributedcloud/dcmanager/tests/unit/manager/test_subcloud_manager.py index eed1df3fd..8fa9d5a6f 100644 --- a/distributedcloud/dcmanager/tests/unit/manager/test_subcloud_manager.py +++ b/distributedcloud/dcmanager/tests/unit/manager/test_subcloud_manager.py @@ -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')