Add mandatory/optional parameters in subcloud reinstall API

The subcloud reinstall requires bootstrap values and sysadmin password
which are not stored in the central cloud database for bootstrapping a
subcloud after reinstall. This commit adds these mandatory values to
the subcloud reinstall API, parses these values along with the
existing install values to reinstall a subcloud. In addtion, the
deploy config values are also accepted to deploy the re-installed post
bootstrap.

Tests:
Unhappy path:
1. Reinstall an online subcloud, reinstall rejected.
2. Reinstall a subcloud without mandatory bootstrap value
"system_mode", reinstall rejected.
3. Reinstall a subcloud with "management_start_address" differs from
the value stored in database, reinstall rejected.
4. Reinstall a subcloud without image in data_install, and the
software image is not uploaded in dc-vault, reinstall rejected.

Happy path:
1. Power off a managed subcloud, reinstall this subcloud with correct
bootstrap values and deploy config, the subcloud goes "installing",
"bootstrapping" and turns online and unmanaged after deployment. After
managing this subcloud, it turns in-sync status.
2. Power off a subcloud, reinstall this subcloud with only bootstrap
values offered. After the deploy status changes to "complete", issue a
dcmanager subcloud reconfigure with its deploy config values. The
subcloud will turn online after deployment.
3. Swact the active system controller, power off a subcloud, reinstall
this subcloud on the previous standby system controller. the subcloud
is reinstalled successfully and goes online after deployment.
4. Upgrade the system controllers and subcloud controllers in a DC
system, power off a subcloud after the upgrade, reinstall the subcloud
on the upgraded system controller, the reinstall is successful, and
the subcloud goes online after deployment.
5. Power off a subcloud, manually manipulate the software
version(including the value in data_install), add an image path in
data_install, reinstall this subcloud. The reinstall is successful.
Check the data in database, the software version is corrected and the
image path is changed to the image in dc-vault.

Partial-Bug: 1932034
Signed-off-by: Yuxing Jiang <yuxing.jiang@windriver.com>
Change-Id: I6cdfaa3d476b1c2cdd3970fdfad4a5273d1b1222
This commit is contained in:
Yuxing Jiang 2021-06-21 19:33:03 -04:00 committed by Yuxing Jiang
parent c817259e71
commit df6fa08f77
6 changed files with 451 additions and 155 deletions

View File

@ -642,6 +642,9 @@ serviceUnavailable (503)
:widths: 20, 20, 20, 60
"subcloud", "URI", "xsd:string", "The subcloud reference, name or id."
"sysadmin_password", "plain", "xsd:string", "The sysadmin password of the subcloud. Must be base64 encoded."
"bootstrap_values", "plain", "xsd:string", "The content of a file containing the bootstrap overrides such as subcloud name, management and OAM subnet."
"deploy_config (Optional)", "plain", "xsd:string", "The content of a file containing the resource definitions describing the desired subcloud configuration."
**Response parameters**

View File

@ -428,6 +428,19 @@ class SubcloudsController(object):
"%(end)s") %
{'start': mgmt_address_start, 'end': mgmt_address_end})
self._validate_oam_network_config(external_oam_subnet_str,
external_oam_gateway_address_str,
external_oam_floating_address_str,
subcloud_subnets)
self._validate_group_id(context, group_id)
def _validate_oam_network_config(self,
external_oam_subnet_str,
external_oam_gateway_address_str,
external_oam_floating_address_str,
existing_networks):
"""validate whether oam network configuration is valid"""
# Parse/validate the oam subnet
MIN_OAM_SUBNET_SIZE = 3
oam_subnet = None
@ -435,7 +448,7 @@ class SubcloudsController(object):
oam_subnet = utils.validate_network_str(
external_oam_subnet_str,
minimum_size=MIN_OAM_SUBNET_SIZE,
existing_networks=subcloud_subnets)
existing_networks=existing_networks)
except exceptions.ValidateFail as e:
LOG.exception(e)
pecan.abort(400, _("external_oam_subnet invalid: %s") % e)
@ -454,7 +467,6 @@ class SubcloudsController(object):
except exceptions.ValidateFail as e:
LOG.exception(e)
pecan.abort(400, _("oam_floating_address invalid: %s") % e)
self._validate_group_id(context, group_id)
def _format_ip_address(self, payload):
"""Format IP addresses in 'bootstrap_values' and 'install_values'.
@ -1080,23 +1092,154 @@ class SubcloudsController(object):
LOG.exception("Unable to reconfigure subcloud %s" % subcloud.name)
pecan.abort(500, _('Unable to reconfigure subcloud'))
elif verb == "reinstall":
payload = self._get_request_data(request)
install_values = self._get_subcloud_db_install_values(subcloud)
payload = db_api.subcloud_db_model_to_dict(subcloud)
for k in ['data_install', 'data_upgrade', 'created-at', 'updated-at']:
if k in payload:
del payload[k]
if subcloud.availability_status == consts.AVAILABILITY_ONLINE:
msg = _('Cannot re-install an online subcloud')
LOG.exception(msg)
pecan.abort(400, msg)
# Validate the bootstrap values with the data in central cloud.
# Stop the process if the boostrap value is not equal to the data
# in DB. Re-use the data from DB if it is not passed.
name = payload.get('name', subcloud.name)
if name != subcloud.name:
pecan.abort(400, _('name is incorrect for the subcloud'))
else:
payload['name'] = name
system_mode = payload.get('system_mode')
if not system_mode:
pecan.abort(400, _('system_mode required'))
management_subnet = payload.get('management_subnet',
subcloud.management_subnet)
if management_subnet != subcloud.management_subnet:
pecan.abort(400, _('management_subnet is incorrect for subcloud'))
else:
payload['management_subnet'] = management_subnet
management_start_ip = payload.get('management_start_address',
subcloud.management_start_ip)
if management_start_ip != subcloud.management_start_ip:
pecan.abort(400, _('management_start_address is incorrect for '
'the subcloud'))
else:
payload['management_start_address'] = management_start_ip
management_end_ip = payload.get('management_end_address',
subcloud.management_end_ip)
if management_end_ip != subcloud.management_end_ip:
pecan.abort(400, _('management_end_address is incorrect for '
'the subcloud'))
else:
payload['management_end_address'] = management_end_ip
management_gateway_ip = payload.get('management_gateway_address',
subcloud.management_gateway_ip)
if management_gateway_ip != subcloud.management_gateway_ip:
pecan.abort(400, _('management_gateway_address is incorrect for '
'the subcloud'))
else:
payload['management_gateway_address'] = management_gateway_ip
systemcontroller_gateway_ip = \
payload.get('systemcontroller_gateway_address',
subcloud.systemcontroller_gateway_ip)
if systemcontroller_gateway_ip != subcloud.systemcontroller_gateway_ip:
pecan.abort(400, _('systemcontroller_gateway_address is incorrect '
'for the subcloud'))
else:
payload['systemcontroller_gateway_address'] = \
systemcontroller_gateway_ip
external_oam_subnet = payload.get('external_oam_subnet')
if not external_oam_subnet:
pecan.abort(400, _('external_oam_subnet required'))
external_oam_gateway_ip = payload.get('external_oam_gateway_address')
if not external_oam_gateway_ip:
pecan.abort(400, _('external_oam_gateway_address required'))
external_oam_floating_ip = \
payload.get('external_oam_floating_address')
if not external_oam_floating_ip:
pecan.abort(400, _('external_oam_floating_address required'))
sysadmin_password = payload.get('sysadmin_password')
if not sysadmin_password:
pecan.abort(400, _('subcloud sysadmin_password required'))
try:
payload['sysadmin_password'] = base64.b64decode(
sysadmin_password).decode('utf-8')
except Exception:
msg = _('Failed to decode subcloud sysadmin_password, '
'verify the password is base64 encoded')
LOG.exception(msg)
pecan.abort(400, msg)
# Search existing subcloud subnets in db
subcloud_subnets = []
subclouds = db_api.subcloud_get_all(context)
for k in subclouds:
subcloud_subnets.append(IPNetwork(k.management_subnet))
self._validate_oam_network_config(external_oam_subnet,
external_oam_gateway_ip,
external_oam_floating_ip,
subcloud_subnets)
# If the software version of the subcloud is different from the
# central cloud, update the software version in install valuse and
# delete the image path in install values, then the subcloud will
# be reinstalled using the image in dc_vault.
if install_values.get('software_version') != tsc.SW_VERSION:
install_values['software_version'] = tsc.SW_VERSION
install_values.pop('image', None)
# Confirm the active system controller load is still in dc-vault if
# image not in install values, add the matching image into the
# install values.
if 'image' not in install_values:
matching_iso, matching_sig = \
SubcloudsController.verify_active_load_in_vault()
LOG.info("image was not in install_values: will reference %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
self._upload_deploy_config_file(request, payload)
try:
# Align the software version of the subcloud with the central
# cloud. Update description, location and group id if offered,
# update the deploy status as pre-install.
db_api.subcloud_update(
context,
subcloud_id,
description=payload.get('description', subcloud.description),
location=payload.get('location', subcloud.location),
software_version=tsc.SW_VERSION,
management_state=consts.MANAGEMENT_UNMANAGED,
deploy_status=consts.DEPLOY_STATE_PRE_INSTALL,
data_install=data_install)
self.rpc_client.reinstall_subcloud(
context, subcloud_id, payload)
# Return deploy_status as pre-install
subcloud.deploy_status = consts.DEPLOY_STATE_PRE_INSTALL
return db_api.subcloud_db_model_to_dict(subcloud)
except RemoteError as e:
pecan.abort(422, e.value)

View File

@ -392,38 +392,7 @@ class SubcloudManager(manager.Manager):
# Query system controller keystone admin user/project IDs,
# services project id, sysinv and dcmanager user id and store in
# payload so they get copied to the override file
admin_user_id = None
sysinv_user_id = None
dcmanager_user_id = None
admin_project_id = None
services_project_id = None
user_list = m_ks_client.get_enabled_users(id_only=False)
for user in user_list:
if user.name == dccommon_consts.ADMIN_USER_NAME:
admin_user_id = user.id
elif user.name == dccommon_consts.SYSINV_USER_NAME:
sysinv_user_id = user.id
elif user.name == dccommon_consts.DCMANAGER_USER_NAME:
dcmanager_user_id = user.id
project_list = m_ks_client.get_enabled_projects(id_only=False)
for project in project_list:
if project.name == dccommon_consts.ADMIN_PROJECT_NAME:
admin_project_id = project.id
elif project.name == dccommon_consts.SERVICES_USER_NAME:
services_project_id = project.id
payload['system_controller_keystone_admin_user_id'] = \
admin_user_id
payload['system_controller_keystone_admin_project_id'] = \
admin_project_id
payload['system_controller_keystone_services_project_id'] = \
services_project_id
payload['system_controller_keystone_sysinv_user_id'] = \
sysinv_user_id
payload['system_controller_keystone_dcmanager_user_id'] = \
dcmanager_user_id
self._get_keystone_ids(m_ks_client, payload)
# Add the admin and service user passwords to the payload so they
# get copied to the override file
@ -550,6 +519,45 @@ class SubcloudManager(manager.Manager):
context, subcloud_id,
deploy_status=consts.DEPLOY_STATE_DEPLOY_PREP_FAILED)
def _get_keystone_ids(self, keystone_client, payload):
"""Get keystone user_id and project_id
:param keystone_client: keystone client
:param payload: subcloud configuration
"""
admin_user_id = None
sysinv_user_id = None
dcmanager_user_id = None
admin_project_id = None
services_project_id = None
user_list = keystone_client.get_enabled_users(id_only=False)
for user in user_list:
if user.name == dccommon_consts.ADMIN_USER_NAME:
admin_user_id = user.id
elif user.name == dccommon_consts.SYSINV_USER_NAME:
sysinv_user_id = user.id
elif user.name == dccommon_consts.DCMANAGER_USER_NAME:
dcmanager_user_id = user.id
project_list = keystone_client.get_enabled_projects(id_only=False)
for project in project_list:
if project.name == dccommon_consts.ADMIN_PROJECT_NAME:
admin_project_id = project.id
elif project.name == dccommon_consts.SERVICES_USER_NAME:
services_project_id = project.id
payload['system_controller_keystone_admin_user_id'] = \
admin_user_id
payload['system_controller_keystone_admin_project_id'] = \
admin_project_id
payload['system_controller_keystone_services_project_id'] = \
services_project_id
payload['system_controller_keystone_sysinv_user_id'] = \
sysinv_user_id
payload['system_controller_keystone_dcmanager_user_id'] = \
dcmanager_user_id
def reinstall_subcloud(self, context, subcloud_id, payload):
"""Reinstall subcloud
@ -561,51 +569,50 @@ class SubcloudManager(manager.Manager):
# Retrieve the subcloud details from the database
subcloud = db_api.subcloud_get(context, subcloud_id)
# Semantic checking
if subcloud.availability_status == \
consts.AVAILABILITY_ONLINE:
raise exceptions.SubcloudNotOffline()
software_version = str(payload['install_values'].get('software_version'))
LOG.info("The type of sw version is %s" % type(SW_VERSION))
if software_version != SW_VERSION:
raise exceptions.BadRequest(
resource='subcloud',
msg='Software version should match the system controller')
if 'image' not in payload['install_values']:
matching_iso, matching_sig = utils.get_vault_load_files(
SW_VERSION)
payload['install_values'].update({'image': matching_iso})
LOG.info("Reinstalling subcloud %s." % subcloud_id)
subcloud = db_api.subcloud_update(
context, subcloud_id,
software_version=SW_VERSION,
deploy_status=consts.DEPLOY_STATE_PRE_INSTALL)
try:
ansible_subcloud_inventory_file = self._get_ansible_filename(
subcloud.name, INVENTORY_FILE_POSTFIX)
m_ks_client = OpenStackDriver(
region_name=consts.DEFAULT_REGION_NAME,
region_clients=None).keystone_client
self._get_keystone_ids(m_ks_client, payload)
payload['admin_password'] = str(
keyring.get_password('CGCS', 'admin'))
payload['ansible_become_pass'] = payload['admin_password']
payload['ansible_ssh_pass'] = payload['admin_password']
payload['ansible_become_pass'] = payload['sysadmin_password']
payload['ansible_ssh_pass'] = payload['sysadmin_password']
payload['install_values']['ansible_ssh_pass'] = \
payload['admin_password']
payload['sysadmin_password']
payload['install_values']['ansible_become_pass'] = \
payload['admin_password']
payload['sysadmin_password']
payload['bootstrap-address'] = \
payload['install_values']['bootstrap_address']
deploy_command = None
if "deploy_playbook" in payload:
self._prepare_for_deployment(payload, subcloud.name)
deploy_command = self.compose_deploy_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(context, payload)
install_command = self.compose_install_command(
subcloud.name,
ansible_subcloud_inventory_file)
@ -615,7 +622,7 @@ class SubcloudManager(manager.Manager):
apply_thread = threading.Thread(
target=self.run_deploy,
args=(subcloud, payload, context,
install_command, apply_command))
install_command, apply_command, deploy_command))
apply_thread.start()
return db_api.subcloud_db_model_to_dict(subcloud)
except Exception:
@ -624,7 +631,7 @@ class SubcloudManager(manager.Manager):
# deployment status
db_api.subcloud_update(
context, subcloud_id,
deploy_status=consts.DEPLOY_STATE_DEPLOY_PREP_FAILED)
deploy_status=consts.DEPLOY_STATE_PRE_INSTALL_FAILED)
def _create_check_target_override_file(self, payload, subcloud_name):
check_target_override_file = os.path.join(

View File

@ -53,6 +53,7 @@ FAKE_HEADERS = fake_subcloud.FAKE_HEADERS
FAKE_SUBCLOUD_DATA = fake_subcloud.FAKE_SUBCLOUD_DATA
FAKE_BOOTSTRAP_VALUE = fake_subcloud.FAKE_BOOTSTRAP_VALUE
FAKE_SUBCLOUD_INSTALL_VALUES = fake_subcloud.FAKE_SUBCLOUD_INSTALL_VALUES
FAKE_SUBCLOUD_BOOTSTRAP_PAYLOAD = fake_subcloud.FAKE_SUBCLOUD_BOOTSTRAP_PAYLOAD
class Subcloud(object):
@ -1266,31 +1267,186 @@ class TestSubcloudAPIOther(testroot.DCManagerApiTest):
@mock.patch.object(cutils, 'get_vault_load_files')
@mock.patch.object(rpc_client, 'ManagerClient')
@mock.patch.object(subclouds.SubcloudsController, '_get_subcloud_db_install_values')
@mock.patch.object(subclouds.SubcloudsController, '_validate_install_values')
@mock.patch.object(subclouds.SubcloudsController, '_validate_oam_network_config')
@mock.patch.object(subclouds.SubcloudsController, '_get_request_data')
def test_reinstall_subcloud(
self, moc_validate_install_values, mock_get_subcloud_db_install_values,
mock_rpc_client, mock_get_vault_load_files):
self, mock_get_request_data, mock_validate_oam_network_config,
mock_get_subcloud_db_install_values, mock_rpc_client,
mock_get_vault_load_files):
subcloud = fake_subcloud.create_fake_subcloud(self.ctx)
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_get_subcloud_db_install_values.return_value = install_data
mock_rpc_client().reinstall_subcloud.return_value = True
mock_get_vault_load_files.return_value = ('iso_file_path', 'sig_file_path')
response = self.app.patch_json(
FAKE_URL + '/' + str(subcloud.id) + '/reinstall',
headers=FAKE_HEADERS)
headers=FAKE_HEADERS, params=reinstall_data)
mock_validate_oam_network_config.assert_called_once()
mock_rpc_client().reinstall_subcloud.assert_called_once_with(
mock.ANY,
subcloud.id,
mock.ANY)
self.assertEqual(response.status_int, 200)
@mock.patch.object(cutils, 'get_vault_load_files')
@mock.patch.object(rpc_client, 'ManagerClient')
@mock.patch.object(subclouds.SubcloudsController, '_get_subcloud_db_install_values')
@mock.patch.object(subclouds.SubcloudsController, '_validate_oam_network_config')
@mock.patch.object(subclouds.SubcloudsController, '_get_request_data')
def test_reinstall_subcloud_no_body(
self, mock_get_request_data, mock_validate_oam_network_config,
mock_get_subcloud_db_install_values, mock_rpc_client,
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_oam_network_config.assert_not_called()
mock_get_subcloud_db_install_values.return_value = install_data
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(rpc_client, 'ManagerClient')
@mock.patch.object(subclouds.SubcloudsController, '_get_subcloud_db_install_values')
@mock.patch.object(subclouds.SubcloudsController, '_validate_oam_network_config')
@mock.patch.object(subclouds.SubcloudsController, '_get_request_data')
def test_reinstall_online_subcloud(
self, mock_get_request_data, mock_validate_oam_network_config,
mock_get_subcloud_db_install_values, mock_rpc_client,
mock_get_vault_load_files):
subcloud = fake_subcloud.create_fake_subcloud(self.ctx)
db_api.subcloud_update(self.ctx,
subcloud.id,
availability_status=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_oam_network_config.assert_not_called()
mock_get_subcloud_db_install_values.return_value = install_data
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(rpc_client, 'ManagerClient')
@mock.patch.object(subclouds.SubcloudsController, '_get_subcloud_db_install_values')
@mock.patch.object(subclouds.SubcloudsController, '_get_request_data')
def test_reinstall_subcloud_missing_required_value(
self, mock_get_request_data, mock_get_subcloud_db_install_values,
mock_rpc_client):
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
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(cutils, 'get_vault_load_files')
@mock.patch.object(rpc_client, 'ManagerClient')
@mock.patch.object(subclouds.SubcloudsController, '_get_subcloud_db_install_values')
@mock.patch.object(subclouds.SubcloudsController, '_validate_oam_network_config')
@mock.patch.object(subclouds.SubcloudsController, '_get_request_data')
def test_reinstall_subcloud_missing_stored_value(
self, mock_get_request_data, mock_validate_oam_network_config,
mock_get_subcloud_db_install_values, mock_rpc_client,
mock_get_vault_load_files):
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
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(cutils, 'get_vault_load_files')
@mock.patch.object(rpc_client, 'ManagerClient')
@mock.patch.object(subclouds.SubcloudsController, '_get_subcloud_db_install_values')
@mock.patch.object(subclouds.SubcloudsController, '_validate_subcloud_config')
@mock.patch.object(subclouds.SubcloudsController, '_get_request_data')
def test_reinstall_subcloud_stored_value_not_match(
self, mock_get_request_data, mock_validate_subcloud_config,
mock_get_subcloud_db_install_values, mock_rpc_client,
mock_get_vault_load_files):
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
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)
reinstall_data[k] = 'wrong_value'
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(rpc_client, 'ManagerClient')
@mock.patch.object(subclouds.SubcloudsController, '_get_restore_payload')
def test_restore_subcloud_no_body(self, mock_get_restore_payload,

View File

@ -40,9 +40,27 @@ FAKE_BOOTSTRAP_VALUE = {
'sysadmin_password': base64.b64encode('testpass'.encode("utf-8"))
}
FAKE_SUBCLOUD_BOOTSTRAP_PAYLOAD = {
'bootstrap-address': '10.10.10.12',
"system_mode": "simplex",
"name": "subcloud1",
"description": "subcloud1 description",
"location": "subcloud1 location",
"management_subnet": "192.168.101.0/24",
"management_gateway_address": "192.168.101.1",
"management_start_address": "192.168.101.2",
"management_end_address": "192.168.101.50",
"systemcontroller_gateway_address": "192.168.204.101",
"external_oam_subnet": "10.10.10.0/24",
"external_oam_gateway_address": "10.10.10.1",
"external_oam_floating_address": "10.10.10.12",
'sysadmin_password':
(base64.b64encode('testpass'.encode("utf-8"))).decode('ascii'),
}
FAKE_SUBCLOUD_INSTALL_VALUES = {
"image": "http://192.168.101.2:8080/iso/bootimage.iso",
"software_version": "12.34",
"software_version": "18.03",
"bootstrap_interface": "eno1",
"bootstrap_address": "128.224.151.183",
"bootstrap_address_prefix": 23,
@ -68,8 +86,8 @@ def create_fake_subcloud(ctxt, **kwargs):
'software_version': "18.03",
"management_subnet": "192.168.101.0/24",
"management_gateway_ip": "192.168.101.1",
"management_start_ip": "192.168.101.3",
"management_end_ip": "192.168.101.4",
"management_start_ip": "192.168.101.2",
"management_end_ip": "192.168.101.50",
"systemcontroller_gateway_ip": "192.168.204.101",
'deploy_status': consts.DEPLOY_STATE_DONE,
'openstack_installed': False,

View File

@ -17,6 +17,7 @@
# of an applicable Wind River license agreement.
#
import copy
import mock
from oslo_concurrency import lockutils
@ -1169,6 +1170,8 @@ 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(
@ -1176,104 +1179,70 @@ class TestSubcloudManager(base.DCManagerTestCase):
@mock.patch.object(
subcloud_manager.SubcloudManager, 'compose_apply_command')
@mock.patch.object(cutils, 'create_subcloud_inventory')
@mock.patch.object(subcloud_manager.SubcloudManager, '_get_keystone_ids')
@mock.patch.object(subcloud_manager, 'OpenStackDriver')
@mock.patch.object(threading.Thread, 'start')
@mock.patch.object(subcloud_manager, 'keyring')
def test_reinstall_subcloud_with_image(
def test_reinstall_subcloud(
self, mock_keyring, mock_thread_start,
mock_create_subcloud_inventory,
mock_keystone_client, mock_get_keystone_ids, mock_create_subcloud_inventory,
mock_compose_apply_command, mock_compose_install_command,
mock_create_intermediate_ca_cert):
mock_create_intermediate_ca_cert, mock_write_subcloud_ansible_config):
subcloud = self.create_subcloud_static(
self.ctx,
name='subcloud1',
deploy_status=consts.DEPLOY_STATE_PRE_DEPLOY)
deploy_status=consts.DEPLOY_STATE_PRE_INSTALL)
fake_install_values = fake_subcloud.FAKE_SUBCLOUD_INSTALL_VALUES
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}
fake_payload = copy.copy(fake_subcloud.FAKE_SUBCLOUD_BOOTSTRAP_PAYLOAD)
fake_payload.update({
'bmc_password': 'bmc_pass',
'software_version': SW_VERSION,
'install_values': fake_install_values})
sm = subcloud_manager.SubcloudManager()
mock_keyring.get_password.return_value = "testpassword"
sm.reinstall_subcloud(self.ctx, subcloud.id, payload=fake_payload)
mock_keyring.get_password.assert_called_once()
mock_keystone_client.assert_called_once()
mock_get_keystone_ids.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()
mock_compose_apply_command.assert_called_once()
mock_thread_start.assert_called_once()
@mock.patch.object(
subcloud_manager.SubcloudManager, '_create_intermediate_ca_cert')
@mock.patch.object(cutils, "get_vault_load_files")
@mock.patch.object(
subcloud_manager.SubcloudManager, 'compose_install_command')
@mock.patch.object(
subcloud_manager.SubcloudManager, 'compose_apply_command')
@mock.patch.object(cutils, 'create_subcloud_inventory')
@mock.patch.object(threading.Thread, 'start')
@mock.patch.object(subcloud_manager, 'keyring')
def test_reinstall_subcloud_without_image(
self, mock_keyring, mock_thread_start, mock_create_subcloud_inventory,
mock_compose_apply_command, mock_compose_install_command,
mock_get_vault_load_files, mock_create_intermediate_ca_cert):
subcloud = self.create_subcloud_static(
self.ctx,
name='subcloud1',
deploy_status=consts.DEPLOY_STATE_PRE_DEPLOY)
fake_install_values = fake_subcloud.FAKE_SUBCLOUD_INSTALL_VALUES
fake_install_values['software_version'] = SW_VERSION
del fake_install_values['image']
fake_payload = {
"bmc_password": "bmc_pass",
"install_values": fake_install_values}
def test_get_keystone_get_keystone_ids(self):
keystone_client = FakeKeystoneClient()
payload = dict()
sm = subcloud_manager.SubcloudManager()
mock_keyring.get_password.return_value = "testpassword"
mock_get_vault_load_files.return_value = ("iso file path", "sig file path")
sm.reinstall_subcloud(self.ctx, subcloud.id, payload=fake_payload)
mock_keyring.get_password.assert_called_once()
mock_create_subcloud_inventory.assert_called_once()
mock_compose_install_command.assert_called_once()
mock_compose_apply_command.assert_called_once()
mock_thread_start.assert_called_once()
def test_reinstall_online_subcloud(self):
subcloud = self.create_subcloud_static(
self.ctx,
name='subcloud1',
deploy_status=consts.DEPLOY_STATE_PRE_DEPLOY)
db_api.subcloud_update(self.ctx,
subcloud.id,
availability_status=consts.AVAILABILITY_ONLINE)
data = utils.create_subcloud_dict(base.SUBCLOUD_SAMPLE_DATA_0)
sm = subcloud_manager.SubcloudManager()
self.assertRaises(exceptions.SubcloudNotOffline,
sm.reinstall_subcloud, self.ctx,
subcloud.id, data)
def test_reinstall_subcloud_software_not_match(self):
subcloud = self.create_subcloud_static(
self.ctx,
name='subcloud1',
deploy_status=consts.DEPLOY_STATE_PRE_DEPLOY)
db_api.subcloud_update(self.ctx,
subcloud.id,
availability_status=consts.AVAILABILITY_OFFLINE)
fake_install_values = fake_subcloud.FAKE_SUBCLOUD_INSTALL_VALUES
data = utils.create_subcloud_dict(base.SUBCLOUD_SAMPLE_DATA_0)
data.update({'install_values': fake_install_values})
sm = subcloud_manager.SubcloudManager()
self.assertRaises(exceptions.BadRequest,
sm.reinstall_subcloud, self.ctx,
subcloud.id, data)
sm._get_keystone_ids(keystone_client, payload)
for fake_user in FAKE_USERS:
if fake_user.name == dccommon_consts.ADMIN_USER_NAME:
self.assertEqual(
payload['system_controller_keystone_admin_user_id'],
fake_user.id)
elif fake_user.name == dccommon_consts.SYSINV_USER_NAME:
self.assertEqual(
payload['system_controller_keystone_sysinv_user_id'],
fake_user.id)
elif fake_user.name == dccommon_consts.DCMANAGER_USER_NAME:
self.assertEqual(
payload['system_controller_keystone_dcmanager_user_id'],
fake_user.id)
for fake_project in FAKE_PROJECTS:
if fake_project.name == dccommon_consts.ADMIN_PROJECT_NAME:
self.assertEqual(
payload['system_controller_keystone_admin_project_id'],
fake_project.id)
elif fake_project.name == dccommon_consts.SERVICES_USER_NAME:
self.assertEqual(
payload['system_controller_keystone_services_project_id'],
fake_project.id)
def test_handle_subcloud_operations_in_progress(self):
subcloud1 = self.create_subcloud_static(