Add subcloud deploy install option to dcmanager
This commit adds the command "subcloud deploy install" to dcmanager. It runs the subcloud install step only. The install values file is optional if it has already been provided in the previous phase using subcloud deploy create. Test Plan: Success cases: - PASS: Install passing install_values and verify that the subcloud was successfully installed. - PASS: Install without passing install_values and verify that the subcloud was successfully installed using install data previously saved in db. - PASS: Install passing current release and verify that the subcloud was successfully installed. - PASS: Install passing previous release and verify that the subcloud was successfully installed. - PASS: Repeat previous tests but directly call the API (using CURL) instead of using the CLI. Failure cases: - PASS: Verify that it's not possible to run the install if deploy state is not 'create-complete', 'pre-install-failed', 'install-failed' or 'install-complete'. - PASS: Call the API directly, passing bmc-password as plain text as opposed to b64encoded and verify that the response contains the correct error code and message. Story: 2010756 Task: 48056 Signed-off-by: Victor Romano <victor.gluzromano@windriver.com> Change-Id: I3a9f4e8c2f39964b2b0b784181bc78494f3078a2
This commit is contained in:
parent
6b7b012992
commit
77faee83d2
|
@ -1913,6 +1913,76 @@ Response Example
|
|||
.. literalinclude:: samples/phased-subcloud-deploy/phased-subcloud-deploy-post-response.json
|
||||
:language: json
|
||||
|
||||
**********************************
|
||||
Installs a subcloud
|
||||
**********************************
|
||||
|
||||
.. rest_method:: POST /v1.0/phased-subcloud-deploy/{subcloud}/install
|
||||
|
||||
**Normal response codes**
|
||||
|
||||
200
|
||||
|
||||
**Error response codes**
|
||||
|
||||
badRequest (400), unauthorized (401), forbidden (403), badMethod (405),
|
||||
HTTPUnprocessableEntity (422), internalServerError (500),
|
||||
serviceUnavailable (503)
|
||||
|
||||
**Request parameters**
|
||||
|
||||
.. rest_parameters:: parameters.yaml
|
||||
|
||||
- subcloud: subcloud_uri
|
||||
- release: release
|
||||
- sysadmin_password: sysadmin_password
|
||||
- bmc_password: bmc_password
|
||||
|
||||
Accepts Content-Type multipart/form-data
|
||||
|
||||
Request Example
|
||||
----------------
|
||||
|
||||
.. literalinclude:: samples/phased-subcloud-deploy/phased-subcloud-deploy-post-install-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/phased-subcloud-deploy/phased-subcloud-deploy-post-install-response.json
|
||||
:language: json
|
||||
|
||||
|
||||
**********************************
|
||||
Configures a subcloud
|
||||
**********************************
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"bmc_password": "YYYYYYY",
|
||||
"install_values": "content of install_values file",
|
||||
"release": "22.12",
|
||||
"subcloud": "subcloud1",
|
||||
"sysadmin_password": "XXXXXXX"
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"id": 1,
|
||||
"name": "subcloud1",
|
||||
"created-at": "2023-01-02T03:04:05.678987",
|
||||
"updated-at": "2023-04-08T15:16:23.424851",
|
||||
"availability-status": "offline",
|
||||
"data_install": null,
|
||||
"data_upgrade": null,
|
||||
"deploy-status": "pre-install",
|
||||
"backup-status": null,
|
||||
"backup-datetime": null,
|
||||
"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": "22.12",
|
||||
"systemcontroller-gateway-ip": "192.168.204.101"
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
#
|
||||
|
||||
import http.client as httpclient
|
||||
import json
|
||||
import os
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
@ -13,6 +14,7 @@ import pecan
|
|||
import tsconfig.tsconfig as tsc
|
||||
import yaml
|
||||
|
||||
from dccommon import consts as dccommon_consts
|
||||
from dcmanager.api.controllers import restcomm
|
||||
from dcmanager.api.policies import phased_subcloud_deploy as \
|
||||
phased_subcloud_deploy_policy
|
||||
|
@ -43,6 +45,10 @@ SUBCLOUD_CREATE_GET_FILE_CONTENTS = (
|
|||
consts.INSTALL_VALUES,
|
||||
)
|
||||
|
||||
SUBCLOUD_INSTALL_GET_FILE_CONTENTS = (
|
||||
consts.INSTALL_VALUES,
|
||||
)
|
||||
|
||||
SUBCLOUD_BOOTSTRAP_GET_FILE_CONTENTS = (
|
||||
consts.BOOTSTRAP_VALUES,
|
||||
)
|
||||
|
@ -51,6 +57,13 @@ SUBCLOUD_CONFIG_GET_FILE_CONTENTS = (
|
|||
consts.DEPLOY_CONFIG,
|
||||
)
|
||||
|
||||
VALID_STATES_FOR_DEPLOY_INSTALL = (
|
||||
consts.DEPLOY_STATE_CREATED,
|
||||
consts.DEPLOY_STATE_PRE_INSTALL_FAILED,
|
||||
consts.DEPLOY_STATE_INSTALL_FAILED,
|
||||
consts.DEPLOY_STATE_INSTALLED
|
||||
)
|
||||
|
||||
VALID_STATES_FOR_DEPLOY_BOOTSTRAP = [
|
||||
consts.DEPLOY_STATE_INSTALLED,
|
||||
consts.DEPLOY_STATE_BOOTSTRAP_FAILED,
|
||||
|
@ -147,6 +160,48 @@ class PhasedSubcloudDeployController(object):
|
|||
pecan.abort(httpclient.INTERNAL_SERVER_ERROR,
|
||||
_('Unable to create subcloud'))
|
||||
|
||||
def _deploy_install(self, context: RequestContext,
|
||||
request: pecan.Request, subcloud):
|
||||
payload = psd_common.get_request_data(
|
||||
request, subcloud, SUBCLOUD_INSTALL_GET_FILE_CONTENTS)
|
||||
if not payload:
|
||||
pecan.abort(400, _('Body required'))
|
||||
|
||||
if subcloud.deploy_status not in VALID_STATES_FOR_DEPLOY_INSTALL:
|
||||
allowed_states_str = ', '.join(VALID_STATES_FOR_DEPLOY_INSTALL)
|
||||
pecan.abort(400, _('Subcloud deploy status must be either: %s')
|
||||
% allowed_states_str)
|
||||
|
||||
payload['software_version'] = payload.get('release', tsc.SW_VERSION)
|
||||
psd_common.populate_payload_with_pre_existing_data(
|
||||
payload, subcloud, SUBCLOUD_INSTALL_GET_FILE_CONTENTS)
|
||||
|
||||
psd_common.validate_sysadmin_password(payload)
|
||||
psd_common.pre_deploy_install(payload, subcloud)
|
||||
|
||||
try:
|
||||
# Align the software version of the subcloud with install
|
||||
# version. 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,
|
||||
data_install=json.dumps(payload['install_values']))
|
||||
|
||||
self.dcmanager_rpc_client.subcloud_deploy_install(
|
||||
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 install subcloud %s" % subcloud.name)
|
||||
pecan.abort(500, _('Unable to install subcloud'))
|
||||
|
||||
def _deploy_bootstrap(self, context: RequestContext,
|
||||
request: pecan.Request,
|
||||
subcloud: models.Subcloud):
|
||||
|
@ -279,7 +334,9 @@ class PhasedSubcloudDeployController(object):
|
|||
except (exceptions.SubcloudNotFound, exceptions.SubcloudNameNotFound):
|
||||
pecan.abort(404, _('Subcloud not found'))
|
||||
|
||||
if verb == 'bootstrap':
|
||||
if verb == 'install':
|
||||
subcloud = self._deploy_install(context, pecan.request, subcloud)
|
||||
elif verb == 'bootstrap':
|
||||
subcloud = self._deploy_bootstrap(context, pecan.request, subcloud)
|
||||
elif verb == 'configure':
|
||||
subcloud = self._deploy_config(context, pecan.request, subcloud)
|
||||
|
|
|
@ -27,6 +27,10 @@ phased_subcloud_deploy_rules = [
|
|||
check_str='rule:' + base.ADMIN_IN_SYSTEM_PROJECTS,
|
||||
description="Modify the subcloud deployment.",
|
||||
operations=[
|
||||
{
|
||||
'method': 'PATCH',
|
||||
'path': '/v1.0/phased-subcloud-deploy/{subcloud}/install'
|
||||
},
|
||||
{
|
||||
'method': 'PATCH',
|
||||
'path': '/v1.0/phased-subcloud-deploy/{subcloud}/bootstrap'
|
||||
|
|
|
@ -603,7 +603,8 @@ def validate_k8s_version(payload):
|
|||
yaml file must be of the same value as fresh_install_k8s_version of
|
||||
the specified release.
|
||||
"""
|
||||
if payload['software_version'] == tsc.SW_VERSION:
|
||||
software_version = payload['software_version']
|
||||
if software_version == tsc.SW_VERSION:
|
||||
return
|
||||
|
||||
kubernetes_version = payload.get(KUBERNETES_VERSION)
|
||||
|
@ -611,7 +612,7 @@ def validate_k8s_version(payload):
|
|||
try:
|
||||
bootstrap_var_file = utils.get_playbook_for_software_version(
|
||||
ANSIBLE_BOOTSTRAP_VALIDATE_CONFIG_VARS,
|
||||
payload['software_version'])
|
||||
software_version)
|
||||
fresh_install_k8s_version = utils.get_value_from_yaml_file(
|
||||
bootstrap_var_file,
|
||||
FRESH_INSTALL_K8S_VERSION)
|
||||
|
@ -627,11 +628,11 @@ def validate_k8s_version(payload):
|
|||
"of the specified release %s")
|
||||
% (kubernetes_version,
|
||||
fresh_install_k8s_version,
|
||||
payload['software_version']))
|
||||
software_version))
|
||||
except exceptions.PlaybookNotFound:
|
||||
pecan.abort(400, _("The bootstrap playbook validate-config vars "
|
||||
"not found for %s software version")
|
||||
% payload['software_version'])
|
||||
% software_version)
|
||||
|
||||
|
||||
def validate_sysadmin_password(payload: dict):
|
||||
|
@ -809,12 +810,42 @@ def get_request_data(request: pecan.Request,
|
|||
return payload
|
||||
|
||||
|
||||
def get_subcloud_db_install_values(subcloud):
|
||||
if not subcloud.data_install:
|
||||
msg = _("Failed to read data install from db")
|
||||
LOG.exception(msg)
|
||||
pecan.abort(400, msg)
|
||||
|
||||
install_values = json.loads(subcloud.data_install)
|
||||
|
||||
# mandatory install parameters
|
||||
mandatory_install_parameters = [
|
||||
'bootstrap_interface',
|
||||
'bootstrap_address',
|
||||
'bootstrap_address_prefix',
|
||||
'bmc_username',
|
||||
'bmc_address',
|
||||
'bmc_password',
|
||||
]
|
||||
for p in mandatory_install_parameters:
|
||||
if p not in install_values:
|
||||
msg = _("Failed to get %s from data_install" % p)
|
||||
LOG.exception(msg)
|
||||
pecan.abort(400, msg)
|
||||
|
||||
return install_values
|
||||
|
||||
|
||||
def populate_payload_with_pre_existing_data(payload: dict,
|
||||
subcloud: models.Subcloud,
|
||||
mandatory_values: typing.Sequence):
|
||||
for value in mandatory_values:
|
||||
if value == consts.INSTALL_VALUES:
|
||||
pass
|
||||
if not payload.get(consts.INSTALL_VALUES):
|
||||
install_values = get_subcloud_db_install_values(subcloud)
|
||||
payload.update({value: install_values})
|
||||
else:
|
||||
validate_install_values(payload)
|
||||
elif value == consts.BOOTSTRAP_VALUES:
|
||||
filename = get_config_file_path(subcloud.name)
|
||||
LOG.info("Loading existing bootstrap values from: %s" % filename)
|
||||
|
@ -834,3 +865,33 @@ def populate_payload_with_pre_existing_data(payload: dict,
|
|||
pecan.abort(400, msg)
|
||||
payload.update({value: fn})
|
||||
get_common_deploy_files(payload, subcloud.software_version)
|
||||
|
||||
|
||||
def pre_deploy_install(payload: dict,
|
||||
subcloud: models.Subcloud):
|
||||
|
||||
install_values = payload['install_values']
|
||||
|
||||
# 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
|
||||
if not payload.get('bmc_password'):
|
||||
payload.update({'bmc_password': install_values.get('bmc_password')})
|
||||
payload.update({'install_values': install_values})
|
||||
|
|
|
@ -214,6 +214,14 @@ class DCManagerService(service.Service):
|
|||
subcloud_id,
|
||||
payload)
|
||||
|
||||
@request_context
|
||||
def subcloud_deploy_install(self, context, subcloud_id, payload):
|
||||
# Install a subcloud
|
||||
LOG.info("Handling subcloud_deploy_install request for: %s" % subcloud_id)
|
||||
return self.subcloud_manager.subcloud_deploy_install(context,
|
||||
subcloud_id,
|
||||
payload)
|
||||
|
||||
def _stop_rpc_server(self):
|
||||
# Stop RPC connection to prevent new requests
|
||||
LOG.debug(_("Attempting to stop RPC service..."))
|
||||
|
|
|
@ -53,6 +53,7 @@ from dcmanager.common import exceptions
|
|||
from dcmanager.common.exceptions import DCManagerException
|
||||
from dcmanager.common.i18n import _
|
||||
from dcmanager.common import manager
|
||||
from dcmanager.common import phased_subcloud_deploy as psd_common
|
||||
from dcmanager.common import prestage
|
||||
from dcmanager.common import utils
|
||||
from dcmanager.db import api as db_api
|
||||
|
@ -758,6 +759,33 @@ class SubcloudManager(manager.Manager):
|
|||
return self._subcloud_operation_notice('restore', restore_subclouds,
|
||||
failed_subclouds, invalid_subclouds)
|
||||
|
||||
def _deploy_install_prep(self, subcloud, payload: dict,
|
||||
ansible_subcloud_inventory_file):
|
||||
payload['install_values']['ansible_ssh_pass'] = \
|
||||
payload['sysadmin_password']
|
||||
payload['install_values']['ansible_become_pass'] = \
|
||||
payload['sysadmin_password']
|
||||
|
||||
# If all update_values already exists on override file or are
|
||||
# the same as the existing ones, the update won't happen
|
||||
# and the file will remain untouched
|
||||
bootstrap_file = psd_common.get_config_file_path(subcloud.name,
|
||||
consts.BOOTSTRAP_VALUES)
|
||||
update_values = {'software_version': payload['software_version'],
|
||||
'bmc_password': payload['bmc_password'],
|
||||
'ansible_ssh_pass': payload['sysadmin_password'],
|
||||
'ansible_become_pass': payload['sysadmin_password']
|
||||
}
|
||||
utils.update_values_on_yaml_file(bootstrap_file,
|
||||
update_values)
|
||||
|
||||
install_command = self.compose_install_command(
|
||||
subcloud.name,
|
||||
ansible_subcloud_inventory_file,
|
||||
payload['software_version'])
|
||||
|
||||
return install_command
|
||||
|
||||
def subcloud_deploy_create(self, context, subcloud_id, payload):
|
||||
"""Create subcloud and notify orchestrators.
|
||||
|
||||
|
@ -897,6 +925,40 @@ class SubcloudManager(manager.Manager):
|
|||
deploy_status=consts.DEPLOY_STATE_CREATE_FAILED)
|
||||
return db_api.subcloud_db_model_to_dict(subcloud)
|
||||
|
||||
def subcloud_deploy_install(self, context, subcloud_id, payload: dict) -> dict:
|
||||
"""Install subcloud
|
||||
|
||||
:param context: request context object
|
||||
:param subcloud_id: subcloud id from db
|
||||
:param payload: subcloud Install
|
||||
"""
|
||||
|
||||
# Retrieve the subcloud details from the database
|
||||
subcloud = db_api.subcloud_get(context, subcloud_id)
|
||||
|
||||
LOG.info("Installing subcloud %s." % subcloud_id)
|
||||
|
||||
try:
|
||||
ansible_subcloud_inventory_file = self._get_ansible_filename(
|
||||
subcloud.name, INVENTORY_FILE_POSTFIX)
|
||||
|
||||
install_command = self._deploy_install_prep(
|
||||
subcloud, payload, ansible_subcloud_inventory_file)
|
||||
|
||||
apply_thread = threading.Thread(
|
||||
target=self.run_deploy_commands,
|
||||
args=(subcloud, payload, context),
|
||||
kwargs={'install_command': install_command})
|
||||
apply_thread.start()
|
||||
return db_api.subcloud_db_model_to_dict(subcloud)
|
||||
except Exception:
|
||||
LOG.exception("Failed to install subcloud %s" % subcloud.name)
|
||||
# If we failed to install the subcloud,
|
||||
# update the deployment status
|
||||
db_api.subcloud_update(
|
||||
context, subcloud_id,
|
||||
deploy_status=consts.DEPLOY_STATE_PRE_INSTALL_FAILED)
|
||||
|
||||
def subcloud_deploy_bootstrap(self, context, subcloud_id, payload):
|
||||
"""Bootstrap subcloud
|
||||
|
||||
|
@ -1658,12 +1720,26 @@ class SubcloudManager(manager.Manager):
|
|||
os.path.join(consts.DC_ANSIBLE_LOG_DIR, subcloud.name)
|
||||
+ "_playbook_output.log"
|
||||
)
|
||||
|
||||
if install_command:
|
||||
install_success = self._run_subcloud_install(
|
||||
context, subcloud, install_command,
|
||||
log_file, payload['install_values'])
|
||||
if not install_success:
|
||||
return
|
||||
db_api.subcloud_update(
|
||||
context, subcloud.id,
|
||||
deploy_status=consts.DEPLOY_STATE_INSTALLED,
|
||||
error_description=consts.ERROR_DESC_EMPTY)
|
||||
|
||||
if apply_command:
|
||||
self._run_subcloud_bootstrap(context, subcloud,
|
||||
apply_command, log_file)
|
||||
|
||||
if deploy_command:
|
||||
self._run_subcloud_config(subcloud, context,
|
||||
deploy_command, log_file)
|
||||
|
||||
except Exception as ex:
|
||||
LOG.exception("run_deploy failed")
|
||||
raise ex
|
||||
|
|
|
@ -192,6 +192,11 @@ class ManagerClient(RPCClient):
|
|||
subcloud_id=subcloud_id,
|
||||
payload=payload))
|
||||
|
||||
def subcloud_deploy_install(self, ctxt, subcloud_id, payload):
|
||||
return self.cast(ctxt, self.make_msg('subcloud_deploy_install',
|
||||
subcloud_id=subcloud_id,
|
||||
payload=payload))
|
||||
|
||||
def subcloud_deploy_bootstrap(self, ctxt, subcloud_id, payload):
|
||||
return self.cast(ctxt, self.make_msg('subcloud_deploy_bootstrap',
|
||||
subcloud_id=subcloud_id,
|
||||
|
|
|
@ -11,6 +11,7 @@ import json
|
|||
import mock
|
||||
from os import path as os_path
|
||||
import six
|
||||
from tsconfig.tsconfig import SW_VERSION
|
||||
import webtest
|
||||
|
||||
from dcmanager.common import consts
|
||||
|
@ -28,10 +29,12 @@ from dcmanager.tests import utils
|
|||
|
||||
FAKE_URL = '/v1.0/phased-subcloud-deploy'
|
||||
|
||||
FAKE_SOFTWARE_VERSION = '21.12'
|
||||
FAKE_TENANT = utils.UUID1
|
||||
|
||||
FAKE_HEADERS = {'X-Tenant-Id': FAKE_TENANT, 'X_ROLE': 'admin,member,reader',
|
||||
'X-Identity-Status': 'Confirmed', 'X-Project-Name': 'admin'}
|
||||
FAKE_SUBCLOUD_INSTALL_VALUES = fake_subcloud.FAKE_SUBCLOUD_INSTALL_VALUES
|
||||
|
||||
|
||||
class FakeRPCClient(object):
|
||||
|
@ -305,3 +308,158 @@ class TestSubcloudDeployConfig(testroot.DCManagerApiTest):
|
|||
self.app.patch_json, FAKE_URL + '/' +
|
||||
str(subcloud.id) + '/configure',
|
||||
headers=FAKE_HEADERS, params=data)
|
||||
|
||||
|
||||
class TestSubcloudDeployInstall(testroot.DCManagerApiTest):
|
||||
def setUp(self):
|
||||
super(TestSubcloudDeployInstall, self).setUp()
|
||||
self.ctx = utils.dummy_context()
|
||||
|
||||
p = mock.patch.object(rpc_client, 'ManagerClient')
|
||||
self.mock_rpc_client = p.start()
|
||||
self.addCleanup(p.stop)
|
||||
|
||||
p = mock.patch.object(dutils, 'get_vault_load_files')
|
||||
self.mock_get_vault_load_files = p.start()
|
||||
self.addCleanup(p.stop)
|
||||
|
||||
p = mock.patch.object(psd_common, 'get_subcloud_db_install_values')
|
||||
self.mock_get_subcloud_db_install_values = p.start()
|
||||
self.addCleanup(p.stop)
|
||||
|
||||
p = mock.patch.object(psd_common, 'validate_k8s_version')
|
||||
self.mock_validate_k8s_version = p.start()
|
||||
self.addCleanup(p.stop)
|
||||
|
||||
p = mock.patch.object(psd_common, 'get_request_data')
|
||||
self.mock_get_request_data = p.start()
|
||||
self.addCleanup(p.stop)
|
||||
|
||||
def test_install_subcloud(self):
|
||||
|
||||
subcloud = fake_subcloud.create_fake_subcloud(
|
||||
self.ctx,
|
||||
deploy_status=consts.DEPLOY_STATE_CREATED)
|
||||
install_data = copy.copy(FAKE_SUBCLOUD_INSTALL_VALUES)
|
||||
install_data.pop('software_version')
|
||||
|
||||
fake_sysadmin_password = base64.b64encode(
|
||||
'testpass'.encode("utf-8")).decode('utf-8')
|
||||
fake_bmc_password = base64.b64encode(
|
||||
'bmc_password'.encode("utf-8")).decode('utf-8')
|
||||
bmc_password = {'bmc_password': fake_bmc_password}
|
||||
install_data.update(bmc_password)
|
||||
install_payload = {'install_values': install_data,
|
||||
'sysadmin_password': fake_sysadmin_password,
|
||||
'bmc_password': fake_bmc_password}
|
||||
self.mock_get_request_data.return_value = install_payload
|
||||
self.mock_get_subcloud_db_install_values.return_value = install_data
|
||||
|
||||
self.mock_rpc_client().subcloud_deploy_install.return_value = True
|
||||
self.mock_get_vault_load_files.return_value = ('iso_file_path', 'sig_file_path')
|
||||
|
||||
response = self.app.patch_json(
|
||||
FAKE_URL + '/' + str(subcloud.id) + '/install',
|
||||
headers=FAKE_HEADERS, params=install_payload)
|
||||
|
||||
self.assertEqual(response.status_int, 200)
|
||||
self.assertEqual(consts.DEPLOY_STATE_PRE_INSTALL,
|
||||
response.json['deploy-status'])
|
||||
self.assertEqual(SW_VERSION, response.json['software-version'])
|
||||
|
||||
def test_install_subcloud_with_release_parameter(self):
|
||||
|
||||
subcloud = fake_subcloud.create_fake_subcloud(
|
||||
self.ctx,
|
||||
deploy_status=consts.DEPLOY_STATE_CREATED)
|
||||
install_data = copy.copy(FAKE_SUBCLOUD_INSTALL_VALUES)
|
||||
install_data.pop('software_version')
|
||||
|
||||
fake_sysadmin_password = base64.b64encode(
|
||||
'testpass'.encode("utf-8")).decode('utf-8')
|
||||
fake_bmc_password = base64.b64encode(
|
||||
'bmc_password'.encode("utf-8")).decode('utf-8')
|
||||
bmc_password = {'bmc_password': fake_bmc_password}
|
||||
install_data.update(bmc_password)
|
||||
install_payload = {'install_values': install_data,
|
||||
'sysadmin_password': fake_sysadmin_password,
|
||||
'bmc_password': fake_bmc_password,
|
||||
'release': FAKE_SOFTWARE_VERSION}
|
||||
self.mock_get_request_data.return_value = install_payload
|
||||
self.mock_get_subcloud_db_install_values.return_value = install_data
|
||||
|
||||
self.mock_rpc_client().subcloud_deploy_install.return_value = True
|
||||
self.mock_get_vault_load_files.return_value = ('iso_file_path', 'sig_file_path')
|
||||
|
||||
response = self.app.patch_json(
|
||||
FAKE_URL + '/' + str(subcloud.id) + '/install',
|
||||
headers=FAKE_HEADERS, params=install_payload)
|
||||
|
||||
self.assertEqual(response.status_int, 200)
|
||||
self.assertEqual(consts.DEPLOY_STATE_PRE_INSTALL,
|
||||
response.json['deploy-status'])
|
||||
self.assertEqual(FAKE_SOFTWARE_VERSION,
|
||||
json.loads(response.json['data_install'])['software_version'])
|
||||
|
||||
def test_install_subcloud_no_body(self):
|
||||
|
||||
subcloud = fake_subcloud.create_fake_subcloud(
|
||||
self.ctx,
|
||||
deploy_status=consts.DEPLOY_STATE_CREATED)
|
||||
|
||||
self.mock_get_request_data.return_value = {}
|
||||
|
||||
six.assertRaisesRegex(self, webtest.app.AppError, "400 *",
|
||||
self.app.patch_json, FAKE_URL + '/' +
|
||||
str(subcloud.id) + '/install',
|
||||
headers=FAKE_HEADERS, params={})
|
||||
|
||||
def test_install_subcloud_no_install_values_on_request_or_db(self):
|
||||
|
||||
subcloud = fake_subcloud.create_fake_subcloud(
|
||||
self.ctx,
|
||||
deploy_status=consts.DEPLOY_STATE_CREATED,
|
||||
data_install='')
|
||||
|
||||
fake_sysadmin_password = base64.b64encode(
|
||||
'testpass'.encode("utf-8")).decode('utf-8')
|
||||
fake_bmc_password = base64.b64encode(
|
||||
'bmc_password'.encode("utf-8")).decode('utf-8')
|
||||
install_payload = {'sysadmin_password': fake_sysadmin_password,
|
||||
'bmc_password': fake_bmc_password}
|
||||
self.mock_get_request_data.return_value = install_payload
|
||||
|
||||
six.assertRaisesRegex(self, webtest.app.AppError, "400 *",
|
||||
self.app.patch_json, FAKE_URL + '/' +
|
||||
str(subcloud.id) + '/install',
|
||||
headers=FAKE_HEADERS, params=install_payload)
|
||||
|
||||
def test_install_subcloud_no_install_values_on_request(self):
|
||||
|
||||
subcloud = fake_subcloud.create_fake_subcloud(
|
||||
self.ctx,
|
||||
deploy_status=consts.DEPLOY_STATE_CREATED)
|
||||
install_data = copy.copy(FAKE_SUBCLOUD_INSTALL_VALUES)
|
||||
install_data.pop('software_version')
|
||||
|
||||
fake_sysadmin_password = base64.b64encode(
|
||||
'testpass'.encode("utf-8")).decode('utf-8')
|
||||
fake_bmc_password = base64.b64encode(
|
||||
'bmc_password'.encode("utf-8")).decode('utf-8')
|
||||
bmc_password = {'bmc_password': fake_bmc_password}
|
||||
install_data.update(bmc_password)
|
||||
install_payload = {'sysadmin_password': fake_sysadmin_password}
|
||||
self.mock_get_request_data.return_value = install_payload
|
||||
self.mock_get_subcloud_db_install_values.return_value = install_data
|
||||
|
||||
self.mock_rpc_client().subcloud_deploy_install.return_value = True
|
||||
self.mock_get_vault_load_files.return_value = ('iso_file_path', 'sig_file_path')
|
||||
|
||||
response = self.app.patch_json(
|
||||
FAKE_URL + '/' + str(subcloud.id) + '/install',
|
||||
headers=FAKE_HEADERS, params=install_payload)
|
||||
|
||||
self.assertEqual(response.status_int, 200)
|
||||
self.assertEqual(consts.DEPLOY_STATE_PRE_INSTALL,
|
||||
response.json['deploy-status'])
|
||||
self.assertEqual(SW_VERSION, response.json['software-version'])
|
||||
|
|
|
@ -425,6 +425,36 @@ class TestSubcloudManager(base.DCManagerTestCase):
|
|||
self.assertEqual('localhost', sm.host)
|
||||
self.assertEqual(self.ctx, sm.context)
|
||||
|
||||
@mock.patch.object(
|
||||
subcloud_manager.SubcloudManager, 'compose_install_command')
|
||||
@mock.patch.object(threading.Thread, 'start')
|
||||
def test_deploy_install_subcloud(self,
|
||||
mock_thread_start,
|
||||
mock_compose_install_command):
|
||||
|
||||
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 = {'bmc_password': 'bmc_pass',
|
||||
'install_values': fake_install_values,
|
||||
'software_version': FAKE_PREVIOUS_SW_VERSION,
|
||||
'sysadmin_password': 'sys_pass'}
|
||||
|
||||
sm = subcloud_manager.SubcloudManager()
|
||||
|
||||
sm.subcloud_deploy_install(self.ctx, subcloud.id, payload=fake_payload)
|
||||
mock_compose_install_command.assert_called_once_with(
|
||||
subcloud_name,
|
||||
sm._get_ansible_filename(subcloud_name, consts.INVENTORY_FILE_POSTFIX),
|
||||
FAKE_PREVIOUS_SW_VERSION)
|
||||
mock_thread_start.assert_called_once()
|
||||
|
||||
@mock.patch.object(subcloud_manager.SubcloudManager,
|
||||
'_create_intermediate_ca_cert')
|
||||
@mock.patch.object(cutils, 'delete_subcloud_inventory')
|
||||
|
|
Loading…
Reference in New Issue