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:
parent
c817259e71
commit
df6fa08f77
@ -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**
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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(
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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(
|
||||
|
Loading…
Reference in New Issue
Block a user