Update "dcmanager subcloud add" to use phased operations
This commit updates the subcloud add implementation to use the new phased subcloud deployment operations. This commit also moves the common ManagerClient mock used in the unit tests into the setUp function to reduce duplication. It also renames the compose_apply_command and compose_deploy_command functions to compose_bootstrap_command and compose_config_command, respectively. Test Plan: After running each test case bellow, the resulting deploy status should be equal to 'complete', except for the abort test, where the deploy state should be equal to '<phase>-aborted' (e.g. install-aborted or bootstrap-aborted). 1. PASS - Run subcloud add with bootstrap-values, install-values and deploy-config. Verify that the add operation completes successfully (all phases should run). The final deploy status should be 'complete'; 2. PASS - Run subcloud add with bootstrap-values and install-values. Verify that the add operation completes successfully (the create, install and bootstrap phases should run). The final deploy status should be 'complete'; 3. PASS - Manually install a subcloud and run subcloud add with bootstrap_values. Verify that the add operation completes successfully. The final deploy status should be 'complete'; 4. PASS - Manually install a subcloud and run subcloud add with bootstrap_values and deploy-config. Verify that the add operation completes successfully (the create, bootstrap and config phases should run). The final deploy status should be 'complete'; 5. PASS - Run subcloud add and try to abort it during diferent phases. Verify that the abort operation completes successfully; 6. PASS - Try to resume the deployment after aborting it, verify that it completes successfully; 7. PASS - Run subcloud add with the 'migrate' flag and verify that the rehoming operation completes successfully; 8. PASS - Run subcloud add with --release option and verify that it still works as expected; 9. PASS - Run subcloud reinstall and verify that it still works as expected. The final deploy status should be 'complete'; 10.PASS - Run subcloud update with valid install-values and verify it completes successfully; 11.PASS - Run subcloud update with invalid install-values. Verify that it fails and that the API returns the appropriate HTTP response status code (400); 12.PASS - Run subcloud update without install-values and verify that the operation completes successfully. Story: 2010756 Task: 48336 Change-Id: I45eee98182027f61857992aed7d5cb9a5f6906d3 Signed-off-by: Gustavo Herzmann <gustavo.herzmann@windriver.com>
This commit is contained in:
parent
adc8e48ac9
commit
f6b38600d6
@ -10,7 +10,6 @@ import os
|
||||
from oslo_log import log as logging
|
||||
from oslo_messaging import RemoteError
|
||||
import pecan
|
||||
import tsconfig.tsconfig as tsc
|
||||
import yaml
|
||||
|
||||
from dcmanager.api.controllers import restcomm
|
||||
@ -170,41 +169,19 @@ class PhasedSubcloudDeployController(object):
|
||||
|
||||
payload = get_create_payload(request)
|
||||
|
||||
if not payload:
|
||||
pecan.abort(400, _('Body required'))
|
||||
|
||||
psd_common.validate_bootstrap_values(payload)
|
||||
|
||||
# If a subcloud release is not passed, use the current
|
||||
# system controller software_version
|
||||
payload['software_version'] = payload.get('release', tsc.SW_VERSION)
|
||||
|
||||
psd_common.validate_subcloud_name_availability(context, payload['name'])
|
||||
|
||||
psd_common.validate_system_controller_patch_status("create")
|
||||
|
||||
psd_common.validate_subcloud_config(context, payload)
|
||||
|
||||
psd_common.validate_install_values(payload)
|
||||
|
||||
psd_common.validate_k8s_version(payload)
|
||||
|
||||
psd_common.format_ip_address(payload)
|
||||
|
||||
# Upload the deploy config files if it is included in the request
|
||||
# It has a dependency on the subcloud name, and it is called after
|
||||
# the name has been validated
|
||||
psd_common.upload_deploy_config_file(request, payload)
|
||||
psd_common.pre_deploy_create(payload, context, request)
|
||||
|
||||
try:
|
||||
# Add the subcloud details to the database
|
||||
subcloud = psd_common.add_subcloud_to_database(context, payload)
|
||||
|
||||
# Ask dcmanager-manager to add the subcloud.
|
||||
# Ask dcmanager-manager to create the subcloud.
|
||||
# It will do all the real work...
|
||||
subcloud = self.dcmanager_rpc_client.subcloud_deploy_create(
|
||||
context, subcloud.id, payload)
|
||||
return subcloud
|
||||
|
||||
subcloud_dict = db_api.subcloud_db_model_to_dict(subcloud)
|
||||
return subcloud_dict
|
||||
|
||||
except RemoteError as e:
|
||||
pecan.abort(httpclient.UNPROCESSABLE_ENTITY, e.value)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -151,6 +151,18 @@ def validate_system_controller_patch_status(operation: str):
|
||||
% operation)
|
||||
|
||||
|
||||
def validate_migrate_parameter(payload, request):
|
||||
migrate_str = payload.get('migrate')
|
||||
if migrate_str is not None:
|
||||
if migrate_str not in ["true", "false"]:
|
||||
pecan.abort(400, _('The migrate option is invalid, '
|
||||
'valid options are true and false.'))
|
||||
|
||||
if consts.DEPLOY_CONFIG in request.POST:
|
||||
pecan.abort(400, _('migrate with deploy-config is '
|
||||
'not allowed'))
|
||||
|
||||
|
||||
def validate_subcloud_config(context, payload, operation=None,
|
||||
ignore_conflicts_with=None):
|
||||
"""Check whether subcloud config is valid."""
|
||||
@ -452,7 +464,7 @@ def validate_install_values(payload, subcloud=None):
|
||||
"""
|
||||
install_values = payload.get('install_values')
|
||||
if not install_values:
|
||||
return False
|
||||
return
|
||||
|
||||
original_install_values = None
|
||||
if subcloud:
|
||||
@ -490,6 +502,7 @@ def validate_install_values(payload, subcloud=None):
|
||||
LOG.debug("software_version (%s) is added to install_values" %
|
||||
software_version)
|
||||
payload['install_values'].update({'software_version': software_version})
|
||||
|
||||
if 'persistent_size' in install_values:
|
||||
persistent_size = install_values.get('persistent_size')
|
||||
if not isinstance(persistent_size, int):
|
||||
@ -501,6 +514,7 @@ def validate_install_values(payload, subcloud=None):
|
||||
pecan.abort(400, _("persistent_size of %s MB is less than "
|
||||
"the permitted minimum %s MB ") %
|
||||
(str(persistent_size), consts.DEFAULT_PERSISTENT_SIZE))
|
||||
|
||||
if 'hw_settle' in install_values:
|
||||
hw_settle = install_values.get('hw_settle')
|
||||
if not isinstance(hw_settle, int):
|
||||
@ -510,6 +524,24 @@ def validate_install_values(payload, subcloud=None):
|
||||
pecan.abort(400, _("hw_settle of %s seconds is less than 0") %
|
||||
(str(hw_settle)))
|
||||
|
||||
if 'extra_boot_params' in install_values:
|
||||
# Validate 'extra_boot_params' boot parameter
|
||||
# Note: this must be a single string (no spaces). If
|
||||
# multiple boot parameters are required they can be
|
||||
# separated by commas. They will be split into separate
|
||||
# arguments by the miniboot.cfg kickstart.
|
||||
extra_boot_params = install_values.get('extra_boot_params')
|
||||
if extra_boot_params in ('', None, 'None'):
|
||||
msg = "The install value extra_boot_params must not be empty."
|
||||
pecan.abort(400, _(msg))
|
||||
if ' ' in extra_boot_params:
|
||||
msg = (
|
||||
"Invalid install value 'extra_boot_params="
|
||||
f"{extra_boot_params}'. Spaces are not allowed "
|
||||
"(use ',' to separate multiple arguments)"
|
||||
)
|
||||
pecan.abort(400, _(msg))
|
||||
|
||||
for k in dccommon_consts.MANDATORY_INSTALL_VALUES:
|
||||
if k not in install_values:
|
||||
if original_install_values:
|
||||
@ -592,8 +624,6 @@ def validate_install_values(payload, subcloud=None):
|
||||
LOG.exception(e)
|
||||
pecan.abort(400, _("rd.net.timeout.ipv6dad invalid: %s") % e)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def validate_k8s_version(payload):
|
||||
"""Validate k8s version.
|
||||
@ -677,19 +707,21 @@ def format_ip_address(payload):
|
||||
|
||||
|
||||
def upload_deploy_config_file(request, payload):
|
||||
if consts.DEPLOY_CONFIG in request.POST:
|
||||
file_item = request.POST[consts.DEPLOY_CONFIG]
|
||||
filename = getattr(file_item, 'filename', '')
|
||||
if not filename:
|
||||
pecan.abort(400, _("No %s file uploaded"
|
||||
% consts.DEPLOY_CONFIG))
|
||||
file_item.file.seek(0, os.SEEK_SET)
|
||||
contents = file_item.file.read()
|
||||
# the deploy config needs to upload to the override location
|
||||
fn = get_config_file_path(payload['name'], consts.DEPLOY_CONFIG)
|
||||
upload_config_file(contents, fn, consts.DEPLOY_CONFIG)
|
||||
payload.update({consts.DEPLOY_CONFIG: fn})
|
||||
get_common_deploy_files(payload, payload['software_version'])
|
||||
file_item = request.POST.get(consts.DEPLOY_CONFIG)
|
||||
if file_item is None:
|
||||
return
|
||||
|
||||
filename = getattr(file_item, 'filename', '')
|
||||
if not filename:
|
||||
pecan.abort(400, _("No %s file uploaded" % consts.DEPLOY_CONFIG))
|
||||
|
||||
file_item.file.seek(0, os.SEEK_SET)
|
||||
contents = file_item.file.read()
|
||||
# the deploy config needs to upload to the override location
|
||||
fn = get_config_file_path(payload['name'], consts.DEPLOY_CONFIG)
|
||||
upload_config_file(contents, fn, consts.DEPLOY_CONFIG)
|
||||
payload[consts.DEPLOY_CONFIG] = fn
|
||||
get_common_deploy_files(payload, payload['software_version'])
|
||||
|
||||
|
||||
def get_config_file_path(subcloud_name, config_file_type=None):
|
||||
@ -718,8 +750,7 @@ def upload_config_file(file_item, config_file, config_type):
|
||||
def get_common_deploy_files(payload, software_version):
|
||||
missing_deploy_files = []
|
||||
for f in consts.DEPLOY_COMMON_FILE_OPTIONS:
|
||||
# Skip the prestage_images option as it is
|
||||
# not relevant in this context
|
||||
# Skip the prestage_images option as it is not relevant in this context
|
||||
if f == consts.DEPLOY_PRESTAGE:
|
||||
continue
|
||||
filename = None
|
||||
@ -858,6 +889,35 @@ def populate_payload_with_pre_existing_data(payload: dict,
|
||||
get_common_deploy_files(payload, subcloud.software_version)
|
||||
|
||||
|
||||
def pre_deploy_create(payload: dict, context: RequestContext,
|
||||
request: pecan.Request):
|
||||
if not payload:
|
||||
pecan.abort(400, _('Body required'))
|
||||
|
||||
validate_bootstrap_values(payload)
|
||||
|
||||
# If a subcloud release is not passed, use the current
|
||||
# system controller software_version
|
||||
payload['software_version'] = payload.get('release', tsc.SW_VERSION)
|
||||
|
||||
validate_subcloud_name_availability(context, payload['name'])
|
||||
|
||||
validate_system_controller_patch_status("create")
|
||||
|
||||
validate_subcloud_config(context, payload)
|
||||
|
||||
validate_install_values(payload)
|
||||
|
||||
validate_k8s_version(payload)
|
||||
|
||||
format_ip_address(payload)
|
||||
|
||||
# Upload the deploy config files if it is included in the request
|
||||
# It has a dependency on the subcloud name, and it is called after
|
||||
# the name has been validated
|
||||
upload_deploy_config_file(request, payload)
|
||||
|
||||
|
||||
def pre_deploy_install(payload: dict, validate_password=False):
|
||||
if validate_password:
|
||||
validate_sysadmin_password(payload)
|
||||
|
@ -99,10 +99,10 @@ class DCManagerService(service.Service):
|
||||
super(DCManagerService, self).start()
|
||||
|
||||
@request_context
|
||||
def add_subcloud(self, context, payload):
|
||||
def add_subcloud(self, context, subcloud_id, payload):
|
||||
# Adds a subcloud
|
||||
LOG.info("Handling add_subcloud request for: %s" % payload.get('name'))
|
||||
return self.subcloud_manager.add_subcloud(context, payload)
|
||||
return self.subcloud_manager.add_subcloud(context, subcloud_id, payload)
|
||||
|
||||
@request_context
|
||||
def delete_subcloud(self, context, subcloud_id):
|
||||
|
@ -106,8 +106,10 @@ CERT_NAMESPACE = "dc-cert"
|
||||
TRANSITORY_STATES = {
|
||||
consts.DEPLOY_STATE_NONE: consts.DEPLOY_STATE_DEPLOY_PREP_FAILED,
|
||||
consts.DEPLOY_STATE_PRE_DEPLOY: consts.DEPLOY_STATE_DEPLOY_PREP_FAILED,
|
||||
consts.DEPLOY_STATE_CREATING: consts.DEPLOY_STATE_CREATE_FAILED,
|
||||
consts.DEPLOY_STATE_PRE_INSTALL: consts.DEPLOY_STATE_PRE_INSTALL_FAILED,
|
||||
consts.DEPLOY_STATE_INSTALLING: consts.DEPLOY_STATE_INSTALL_FAILED,
|
||||
consts.DEPLOY_STATE_PRE_BOOTSTRAP: consts.DEPLOY_STATE_PRE_BOOTSTRAP_FAILED,
|
||||
consts.DEPLOY_STATE_BOOTSTRAPPING: consts.DEPLOY_STATE_BOOTSTRAP_FAILED,
|
||||
consts.DEPLOY_STATE_PRE_CONFIG: consts.DEPLOY_STATE_PRE_CONFIG_FAILED,
|
||||
consts.DEPLOY_STATE_CONFIGURING: consts.DEPLOY_STATE_CONFIG_FAILED,
|
||||
@ -246,11 +248,10 @@ class SubcloudManager(manager.Manager):
|
||||
software_version if software_version else SW_VERSION]
|
||||
return install_command
|
||||
|
||||
# TODO(gherzman): rename compose_apply_command to compose_bootstrap_command
|
||||
def compose_apply_command(self, subcloud_name,
|
||||
ansible_subcloud_inventory_file,
|
||||
software_version=None):
|
||||
apply_command = [
|
||||
def compose_bootstrap_command(self, subcloud_name,
|
||||
ansible_subcloud_inventory_file,
|
||||
software_version=None):
|
||||
bootstrap_command = [
|
||||
"ansible-playbook",
|
||||
utils.get_playbook_for_software_version(
|
||||
ANSIBLE_SUBCLOUD_PLAYBOOK, software_version),
|
||||
@ -259,23 +260,22 @@ class SubcloudManager(manager.Manager):
|
||||
]
|
||||
# Add the overrides dir and region_name so the playbook knows
|
||||
# which overrides to load
|
||||
apply_command += [
|
||||
bootstrap_command += [
|
||||
"-e", str("override_files_dir='%s' region_name=%s") % (
|
||||
dccommon_consts.ANSIBLE_OVERRIDES_PATH, subcloud_name),
|
||||
"-e", "install_release_version=%s" %
|
||||
software_version if software_version else SW_VERSION]
|
||||
return apply_command
|
||||
return bootstrap_command
|
||||
|
||||
# TODO(vgluzrom): rename compose_deploy_command to compose_config_command
|
||||
def compose_deploy_command(self, subcloud_name, ansible_subcloud_inventory_file, payload):
|
||||
deploy_command = [
|
||||
def compose_config_command(self, subcloud_name, ansible_subcloud_inventory_file, payload):
|
||||
config_command = [
|
||||
"ansible-playbook", payload[consts.DEPLOY_PLAYBOOK],
|
||||
"-e", "@%s" % dccommon_consts.ANSIBLE_OVERRIDES_PATH + "/" +
|
||||
subcloud_name + '_deploy_values.yml',
|
||||
"-i", ansible_subcloud_inventory_file,
|
||||
"--limit", subcloud_name
|
||||
]
|
||||
return deploy_command
|
||||
return config_command
|
||||
|
||||
def compose_backup_command(self, subcloud_name, ansible_subcloud_inventory_file):
|
||||
backup_command = [
|
||||
@ -332,205 +332,71 @@ class SubcloudManager(manager.Manager):
|
||||
dccommon_consts.ANSIBLE_OVERRIDES_PATH, subcloud_name)]
|
||||
return rehome_command
|
||||
|
||||
def add_subcloud(self, context, payload):
|
||||
def rehome_subcloud(self, context, subcloud, payload):
|
||||
# Ansible inventory filename for the specified subcloud
|
||||
ansible_subcloud_inventory_file = self._get_ansible_filename(
|
||||
subcloud.name, INVENTORY_FILE_POSTFIX)
|
||||
|
||||
rehome_command = self.compose_rehome_command(
|
||||
subcloud.name,
|
||||
ansible_subcloud_inventory_file,
|
||||
subcloud.software_version)
|
||||
|
||||
self.run_deploy_thread(subcloud, payload, context,
|
||||
rehome_command=rehome_command)
|
||||
|
||||
def add_subcloud(self, context, subcloud_id, payload):
|
||||
"""Add subcloud and notify orchestrators.
|
||||
|
||||
:param context: request context object
|
||||
:param subcloud_id: id of the subcloud
|
||||
:param payload: subcloud configuration
|
||||
"""
|
||||
LOG.info("Adding subcloud %s." % payload['name'])
|
||||
subcloud_id = db_api.subcloud_get_by_name(context, payload['name']).id
|
||||
LOG.info(f"Adding subcloud {payload['name']}.")
|
||||
|
||||
# Check the migrate option from payload
|
||||
migrate_str = payload.get('migrate', '')
|
||||
migrate_flag = (migrate_str.lower() == 'true')
|
||||
if migrate_flag:
|
||||
subcloud = db_api.subcloud_update(
|
||||
context, subcloud_id,
|
||||
deploy_status=consts.DEPLOY_STATE_PRE_REHOME)
|
||||
rehoming = payload.get('migrate', '').lower() == "true"
|
||||
payload['ansible_ssh_pass'] = payload['sysadmin_password']
|
||||
|
||||
# Create the subcloud
|
||||
subcloud = self.subcloud_deploy_create(context, subcloud_id,
|
||||
payload, rehoming)
|
||||
|
||||
# Return if create failed
|
||||
if rehoming:
|
||||
success_state = consts.DEPLOY_STATE_PRE_REHOME
|
||||
else:
|
||||
success_state = consts.DEPLOY_STATE_CREATED
|
||||
if subcloud.deploy_status != success_state:
|
||||
return
|
||||
|
||||
# Rehome subcloud
|
||||
if rehoming:
|
||||
self.rehome_subcloud(context, subcloud, payload)
|
||||
return
|
||||
|
||||
# Define which deploy phases should be run
|
||||
phases_to_run = []
|
||||
if consts.INSTALL_VALUES in payload:
|
||||
phases_to_run.append(consts.DEPLOY_PHASE_INSTALL)
|
||||
phases_to_run.append(consts.DEPLOY_PHASE_BOOTSTRAP)
|
||||
if consts.DEPLOY_CONFIG in payload:
|
||||
phases_to_run.append(consts.DEPLOY_PHASE_CONFIG)
|
||||
|
||||
# Finish adding the subcloud by running the deploy phases
|
||||
succeeded = self.run_deploy_phases(
|
||||
context, subcloud_id, payload, phases_to_run)
|
||||
|
||||
if succeeded:
|
||||
subcloud = db_api.subcloud_update(
|
||||
context, subcloud_id,
|
||||
deploy_status=consts.DEPLOY_STATE_PRE_DEPLOY)
|
||||
context, subcloud_id, deploy_status=consts.DEPLOY_STATE_DONE)
|
||||
|
||||
try:
|
||||
# Ansible inventory filename for the specified subcloud
|
||||
ansible_subcloud_inventory_file = self._get_ansible_filename(
|
||||
subcloud.name, INVENTORY_FILE_POSTFIX)
|
||||
|
||||
# Create a new route to this subcloud on the management interface
|
||||
# on both controllers.
|
||||
m_ks_client = OpenStackDriver(
|
||||
region_name=dccommon_consts.DEFAULT_REGION_NAME,
|
||||
region_clients=None).keystone_client
|
||||
subcloud_subnet = netaddr.IPNetwork(utils.get_management_subnet(payload))
|
||||
endpoint = m_ks_client.endpoint_cache.get_endpoint('sysinv')
|
||||
sysinv_client = SysinvClient(dccommon_consts.DEFAULT_REGION_NAME,
|
||||
m_ks_client.session,
|
||||
endpoint=endpoint)
|
||||
LOG.debug("Getting cached regionone data for %s" % subcloud.name)
|
||||
cached_regionone_data = self._get_cached_regionone_data(m_ks_client, sysinv_client)
|
||||
for mgmt_if_uuid in cached_regionone_data['mgmt_interface_uuids']:
|
||||
sysinv_client.create_route(mgmt_if_uuid,
|
||||
str(subcloud_subnet.ip),
|
||||
subcloud_subnet.prefixlen,
|
||||
payload['systemcontroller_gateway_address'],
|
||||
1)
|
||||
|
||||
# Create endpoints to this subcloud on the
|
||||
# management-start-ip of the subcloud which will be allocated
|
||||
# as the floating Management IP of the Subcloud if the
|
||||
# Address Pool is not shared. Incase the endpoint entries
|
||||
# are incorrect, or the management IP of the subcloud is changed
|
||||
# in the future, it will not go managed or will show up as
|
||||
# out of sync. To fix this use Openstack endpoint commands
|
||||
# on the SystemController to change the subcloud endpoints.
|
||||
# The non-identity endpoints are added to facilitate horizon access
|
||||
# from the System Controller to the subcloud.
|
||||
endpoint_config = []
|
||||
endpoint_ip = utils.get_management_start_address(payload)
|
||||
if netaddr.IPAddress(endpoint_ip).version == 6:
|
||||
endpoint_ip = '[' + endpoint_ip + ']'
|
||||
|
||||
for service in m_ks_client.services_list:
|
||||
if service.type == dccommon_consts.ENDPOINT_TYPE_PLATFORM:
|
||||
admin_endpoint_url = "https://{}:6386/v1".format(endpoint_ip)
|
||||
endpoint_config.append({"id": service.id,
|
||||
"admin_endpoint_url": admin_endpoint_url})
|
||||
elif service.type == dccommon_consts.ENDPOINT_TYPE_IDENTITY:
|
||||
admin_endpoint_url = "https://{}:5001/v3".format(endpoint_ip)
|
||||
endpoint_config.append({"id": service.id,
|
||||
"admin_endpoint_url": admin_endpoint_url})
|
||||
elif service.type == dccommon_consts.ENDPOINT_TYPE_PATCHING:
|
||||
admin_endpoint_url = "https://{}:5492".format(endpoint_ip)
|
||||
endpoint_config.append({"id": service.id,
|
||||
"admin_endpoint_url": admin_endpoint_url})
|
||||
elif service.type == dccommon_consts.ENDPOINT_TYPE_FM:
|
||||
admin_endpoint_url = "https://{}:18003".format(endpoint_ip)
|
||||
endpoint_config.append({"id": service.id,
|
||||
"admin_endpoint_url": admin_endpoint_url})
|
||||
elif service.type == dccommon_consts.ENDPOINT_TYPE_NFV:
|
||||
admin_endpoint_url = "https://{}:4546".format(endpoint_ip)
|
||||
endpoint_config.append({"id": service.id,
|
||||
"admin_endpoint_url": admin_endpoint_url})
|
||||
|
||||
if len(endpoint_config) < 5:
|
||||
raise exceptions.BadRequest(
|
||||
resource='subcloud',
|
||||
msg='Missing service in SystemController')
|
||||
|
||||
for endpoint in endpoint_config:
|
||||
try:
|
||||
m_ks_client.keystone_client.endpoints.create(
|
||||
endpoint["id"],
|
||||
endpoint['admin_endpoint_url'],
|
||||
interface=dccommon_consts.KS_ENDPOINT_ADMIN,
|
||||
region=subcloud.name)
|
||||
except Exception as e:
|
||||
# Keystone service must be temporarily busy, retry
|
||||
LOG.error(str(e))
|
||||
m_ks_client.keystone_client.endpoints.create(
|
||||
endpoint["id"],
|
||||
endpoint['admin_endpoint_url'],
|
||||
interface=dccommon_consts.KS_ENDPOINT_ADMIN,
|
||||
region=subcloud.name)
|
||||
|
||||
# Inform orchestrator that subcloud has been added
|
||||
self.dcorch_rpc_client.add_subcloud(
|
||||
context, subcloud.name, subcloud.software_version)
|
||||
|
||||
# create entry into alarm summary table, will get real values later
|
||||
alarm_updates = {'critical_alarms': -1,
|
||||
'major_alarms': -1,
|
||||
'minor_alarms': -1,
|
||||
'warnings': -1,
|
||||
'cloud_status': consts.ALARMS_DISABLED}
|
||||
db_api.subcloud_alarms_create(context, subcloud.name,
|
||||
alarm_updates)
|
||||
|
||||
# Regenerate the addn_hosts_dc file
|
||||
self._create_addn_hosts_dc(context)
|
||||
|
||||
self._populate_payload_with_cached_keystone_data(
|
||||
cached_regionone_data, payload)
|
||||
|
||||
if "install_values" in payload:
|
||||
payload['install_values']['ansible_ssh_pass'] = \
|
||||
payload['sysadmin_password']
|
||||
|
||||
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))
|
||||
|
||||
# Create the ansible inventory for the new subcloud
|
||||
utils.create_subcloud_inventory(payload,
|
||||
ansible_subcloud_inventory_file)
|
||||
|
||||
# create subcloud intermediate certificate and pass in keys
|
||||
self._create_intermediate_ca_cert(payload)
|
||||
|
||||
# Write this subclouds overrides to file
|
||||
# NOTE: This file should not be deleted if subcloud add fails
|
||||
# as it is used for debugging
|
||||
self._write_subcloud_ansible_config(cached_regionone_data, payload)
|
||||
|
||||
if migrate_flag:
|
||||
rehome_command = self.compose_rehome_command(
|
||||
subcloud.name,
|
||||
ansible_subcloud_inventory_file,
|
||||
subcloud.software_version)
|
||||
apply_thread = threading.Thread(
|
||||
target=self.run_deploy_thread,
|
||||
args=(subcloud, payload, context,
|
||||
None, None, None, rehome_command))
|
||||
else:
|
||||
install_command = None
|
||||
if "install_values" in payload:
|
||||
install_command = self.compose_install_command(
|
||||
subcloud.name,
|
||||
ansible_subcloud_inventory_file,
|
||||
subcloud.software_version)
|
||||
apply_command = self.compose_apply_command(
|
||||
subcloud.name,
|
||||
ansible_subcloud_inventory_file,
|
||||
subcloud.software_version)
|
||||
apply_thread = threading.Thread(
|
||||
target=self.run_deploy_thread,
|
||||
args=(subcloud, payload, context,
|
||||
install_command, apply_command, deploy_command))
|
||||
|
||||
apply_thread.start()
|
||||
|
||||
return db_api.subcloud_db_model_to_dict(subcloud)
|
||||
|
||||
except Exception:
|
||||
LOG.exception("Failed to create subcloud %s" % payload['name'])
|
||||
# If we failed to create the subcloud, update the
|
||||
# deployment status
|
||||
if migrate_flag:
|
||||
db_api.subcloud_update(
|
||||
context, subcloud.id,
|
||||
deploy_status=consts.DEPLOY_STATE_REHOME_PREP_FAILED)
|
||||
else:
|
||||
db_api.subcloud_update(
|
||||
context, subcloud.id,
|
||||
deploy_status=consts.DEPLOY_STATE_DEPLOY_PREP_FAILED)
|
||||
LOG.info(f"Finished adding subcloud {subcloud['name']}.")
|
||||
|
||||
def reconfigure_subcloud(self, context, subcloud_id, payload):
|
||||
"""Reconfigure subcloud
|
||||
|
||||
:param context: request context object
|
||||
:param subcloud_id: id of the subcloud
|
||||
:param payload: subcloud configuration
|
||||
"""
|
||||
LOG.info("Reconfiguring subcloud %s." % subcloud_id)
|
||||
@ -543,10 +409,10 @@ class SubcloudManager(manager.Manager):
|
||||
ansible_subcloud_inventory_file = self._get_ansible_filename(
|
||||
subcloud.name, INVENTORY_FILE_POSTFIX)
|
||||
|
||||
deploy_command = None
|
||||
config_command = None
|
||||
if "deploy_playbook" in payload:
|
||||
self._prepare_for_deployment(payload, subcloud.name)
|
||||
deploy_command = self.compose_deploy_command(
|
||||
config_command = self.compose_config_command(
|
||||
subcloud.name,
|
||||
ansible_subcloud_inventory_file,
|
||||
payload)
|
||||
@ -554,7 +420,7 @@ class SubcloudManager(manager.Manager):
|
||||
del payload['sysadmin_password']
|
||||
apply_thread = threading.Thread(
|
||||
target=self.run_deploy_thread,
|
||||
args=(subcloud, payload, context, None, None, deploy_command))
|
||||
args=(subcloud, payload, context, None, None, config_command))
|
||||
apply_thread.start()
|
||||
return db_api.subcloud_db_model_to_dict(subcloud)
|
||||
except Exception:
|
||||
@ -596,10 +462,10 @@ class SubcloudManager(manager.Manager):
|
||||
payload['bootstrap-address'] = \
|
||||
payload['install_values']['bootstrap_address']
|
||||
|
||||
deploy_command = None
|
||||
config_command = None
|
||||
if "deploy_playbook" in payload:
|
||||
self._prepare_for_deployment(payload, subcloud.name)
|
||||
deploy_command = self.compose_deploy_command(
|
||||
config_command = self.compose_config_command(
|
||||
subcloud.name,
|
||||
ansible_subcloud_inventory_file,
|
||||
payload)
|
||||
@ -622,7 +488,7 @@ class SubcloudManager(manager.Manager):
|
||||
subcloud.name,
|
||||
ansible_subcloud_inventory_file,
|
||||
payload['software_version'])
|
||||
apply_command = self.compose_apply_command(
|
||||
bootstrap_command = self.compose_bootstrap_command(
|
||||
subcloud.name,
|
||||
ansible_subcloud_inventory_file,
|
||||
payload['software_version'])
|
||||
@ -631,7 +497,7 @@ class SubcloudManager(manager.Manager):
|
||||
apply_thread = threading.Thread(
|
||||
target=self.run_deploy_thread,
|
||||
args=(subcloud, payload, context,
|
||||
install_command, apply_command, deploy_command,
|
||||
install_command, bootstrap_command, config_command,
|
||||
None, network_reconfig))
|
||||
apply_thread.start()
|
||||
return db_api.subcloud_db_model_to_dict(subcloud)
|
||||
@ -764,12 +630,21 @@ class SubcloudManager(manager.Manager):
|
||||
|
||||
def _deploy_bootstrap_prep(self, context, subcloud, payload: dict,
|
||||
ansible_subcloud_inventory_file):
|
||||
"""Run the preparation steps needed to run the bootstrap operation
|
||||
|
||||
:param context: target request context object
|
||||
:param subcloud: subcloud model object
|
||||
:param payload: bootstrap request parameters
|
||||
:param ansible_subcloud_inventory_file: the ansible inventory file path
|
||||
:return: ansible command needed to run the bootstrap playbook
|
||||
"""
|
||||
management_subnet = utils.get_management_subnet(payload)
|
||||
sys_controller_gw_ip = payload.get(
|
||||
"systemcontroller_gateway_address")
|
||||
|
||||
if (management_subnet != subcloud.management_subnet) or (
|
||||
sys_controller_gw_ip != subcloud.systemcontroller_gateway_ip):
|
||||
sys_controller_gw_ip != subcloud.systemcontroller_gateway_ip
|
||||
):
|
||||
m_ks_client = OpenStackDriver(
|
||||
region_name=dccommon_consts.DEFAULT_REGION_NAME,
|
||||
region_clients=None).keystone_client
|
||||
@ -816,23 +691,37 @@ class SubcloudManager(manager.Manager):
|
||||
utils.create_subcloud_inventory(payload,
|
||||
ansible_subcloud_inventory_file)
|
||||
|
||||
apply_command = self.compose_apply_command(
|
||||
bootstrap_command = self.compose_bootstrap_command(
|
||||
subcloud.name,
|
||||
ansible_subcloud_inventory_file,
|
||||
subcloud.software_version)
|
||||
return apply_command
|
||||
return bootstrap_command
|
||||
|
||||
def _deploy_config_prep(self, subcloud, payload: dict,
|
||||
ansible_subcloud_inventory_file):
|
||||
"""Run the preparation steps needed to run the config operation
|
||||
|
||||
:param subcloud: target subcloud model object
|
||||
:param payload: config request parameters
|
||||
:param ansible_subcloud_inventory_file: the ansible inventory file path
|
||||
:return: ansible command needed to run the config playbook
|
||||
"""
|
||||
self._prepare_for_deployment(payload, subcloud.name)
|
||||
deploy_command = self.compose_deploy_command(
|
||||
config_command = self.compose_config_command(
|
||||
subcloud.name,
|
||||
ansible_subcloud_inventory_file,
|
||||
payload)
|
||||
return deploy_command
|
||||
return config_command
|
||||
|
||||
def _deploy_install_prep(self, subcloud, payload: dict,
|
||||
ansible_subcloud_inventory_file):
|
||||
"""Run the preparation steps needed to run the install operation
|
||||
|
||||
:param subcloud: target subcloud model object
|
||||
:param payload: install request parameters
|
||||
:param ansible_subcloud_inventory_file: the ansible inventory file path
|
||||
:return: ansible command needed to run the install playbook
|
||||
"""
|
||||
payload['install_values']['ansible_ssh_pass'] = \
|
||||
payload['sysadmin_password']
|
||||
payload['install_values']['ansible_become_pass'] = \
|
||||
@ -921,18 +810,25 @@ class SubcloudManager(manager.Manager):
|
||||
self.run_deploy_phases(context, subcloud_id, payload,
|
||||
deploy_states_to_run)
|
||||
|
||||
def subcloud_deploy_create(self, context, subcloud_id, payload):
|
||||
def subcloud_deploy_create(self, context, subcloud_id, payload, rehoming=False):
|
||||
"""Create subcloud and notify orchestrators.
|
||||
|
||||
:param context: request context object
|
||||
:param subcloud_id: subcloud_id from db
|
||||
:param payload: subcloud configuration
|
||||
:param rehoming: flag indicating if this is part of a rehoming operation
|
||||
:return: resulting subcloud DB object
|
||||
"""
|
||||
LOG.info("Creating subcloud %s." % payload['name'])
|
||||
|
||||
if rehoming:
|
||||
deploy_state = consts.DEPLOY_STATE_PRE_REHOME
|
||||
else:
|
||||
deploy_state = consts.DEPLOY_STATE_CREATING
|
||||
|
||||
subcloud = db_api.subcloud_update(
|
||||
context, subcloud_id,
|
||||
deploy_status=consts.DEPLOY_STATE_CREATING)
|
||||
deploy_status=deploy_state)
|
||||
|
||||
try:
|
||||
# Create a new route to this subcloud on the management interface
|
||||
@ -1024,7 +920,7 @@ class SubcloudManager(manager.Manager):
|
||||
self._prepare_for_deployment(payload, subcloud.name,
|
||||
populate_passwords=False)
|
||||
|
||||
payload['users'] = dict()
|
||||
payload['users'] = {}
|
||||
for user in USERS_TO_REPLICATE:
|
||||
payload['users'][user] = \
|
||||
str(keyring.get_password(
|
||||
@ -1046,26 +942,36 @@ class SubcloudManager(manager.Manager):
|
||||
# as it is used for debugging
|
||||
self._write_subcloud_ansible_config(cached_regionone_data, payload)
|
||||
|
||||
if not rehoming:
|
||||
deploy_state = consts.DEPLOY_STATE_CREATED
|
||||
|
||||
subcloud = db_api.subcloud_update(
|
||||
context, subcloud_id,
|
||||
deploy_status=consts.DEPLOY_STATE_CREATED)
|
||||
deploy_status=deploy_state)
|
||||
|
||||
return db_api.subcloud_db_model_to_dict(subcloud)
|
||||
return subcloud
|
||||
|
||||
except Exception:
|
||||
LOG.exception("Failed to create subcloud %s" % payload['name'])
|
||||
# If we failed to create the subcloud, update the deployment status
|
||||
|
||||
if rehoming:
|
||||
deploy_state = consts.DEPLOY_STATE_REHOME_PREP_FAILED
|
||||
else:
|
||||
deploy_state = consts.DEPLOY_STATE_CREATE_FAILED
|
||||
|
||||
subcloud = db_api.subcloud_update(
|
||||
context, subcloud.id,
|
||||
deploy_status=consts.DEPLOY_STATE_CREATE_FAILED)
|
||||
return db_api.subcloud_db_model_to_dict(subcloud)
|
||||
deploy_status=deploy_state)
|
||||
return subcloud
|
||||
|
||||
def subcloud_deploy_install(self, context, subcloud_id, payload: dict):
|
||||
def subcloud_deploy_install(self, context, subcloud_id, payload: dict) -> bool:
|
||||
"""Install subcloud
|
||||
|
||||
:param context: request context object
|
||||
:param subcloud_id: subcloud id from db
|
||||
:param payload: subcloud Install
|
||||
:return: success status
|
||||
"""
|
||||
|
||||
# Retrieve the subcloud details from the database
|
||||
@ -1114,6 +1020,7 @@ class SubcloudManager(manager.Manager):
|
||||
:param context: request context object
|
||||
:param subcloud_id: subcloud_id from db
|
||||
:param payload: subcloud bootstrap configuration
|
||||
:return: success status
|
||||
"""
|
||||
LOG.info("Bootstrapping subcloud %s." % payload['name'])
|
||||
|
||||
@ -1128,11 +1035,11 @@ class SubcloudManager(manager.Manager):
|
||||
ansible_subcloud_inventory_file = self._get_ansible_filename(
|
||||
subcloud.name, INVENTORY_FILE_POSTFIX)
|
||||
|
||||
apply_command = self._deploy_bootstrap_prep(
|
||||
bootstrap_command = self._deploy_bootstrap_prep(
|
||||
context, subcloud, payload,
|
||||
ansible_subcloud_inventory_file)
|
||||
bootstrap_success = self._run_subcloud_bootstrap(
|
||||
context, subcloud, apply_command, log_file)
|
||||
context, subcloud, bootstrap_command, log_file)
|
||||
return bootstrap_success
|
||||
|
||||
except Exception:
|
||||
@ -1142,12 +1049,13 @@ class SubcloudManager(manager.Manager):
|
||||
deploy_status=consts.DEPLOY_STATE_PRE_BOOTSTRAP_FAILED)
|
||||
return False
|
||||
|
||||
def subcloud_deploy_config(self, context, subcloud_id, payload: dict) -> dict:
|
||||
def subcloud_deploy_config(self, context, subcloud_id, payload: dict) -> bool:
|
||||
"""Configure subcloud
|
||||
|
||||
:param context: request context object
|
||||
:param subcloud_id: subcloud_id from db
|
||||
:param payload: subcloud configuration
|
||||
:return: success status
|
||||
"""
|
||||
LOG.info("Configuring subcloud %s." % subcloud_id)
|
||||
|
||||
@ -1164,13 +1072,13 @@ class SubcloudManager(manager.Manager):
|
||||
subcloud.name, INVENTORY_FILE_POSTFIX)
|
||||
|
||||
self._prepare_for_deployment(payload, subcloud.name)
|
||||
deploy_command = self.compose_deploy_command(
|
||||
config_command = self.compose_config_command(
|
||||
subcloud.name,
|
||||
ansible_subcloud_inventory_file,
|
||||
payload)
|
||||
|
||||
config_success = self._run_subcloud_config(subcloud, context,
|
||||
deploy_command, log_file)
|
||||
config_command, log_file)
|
||||
return config_success
|
||||
|
||||
except Exception:
|
||||
@ -1699,21 +1607,21 @@ class SubcloudManager(manager.Manager):
|
||||
LOG.exception(e)
|
||||
|
||||
def run_deploy_thread(self, subcloud, payload, context,
|
||||
install_command=None, apply_command=None,
|
||||
deploy_command=None, rehome_command=None,
|
||||
install_command=None, bootstrap_command=None,
|
||||
config_command=None, rehome_command=None,
|
||||
network_reconfig=None):
|
||||
try:
|
||||
self._run_deploy(subcloud, payload, context,
|
||||
install_command, apply_command,
|
||||
deploy_command, rehome_command,
|
||||
install_command, bootstrap_command,
|
||||
config_command, rehome_command,
|
||||
network_reconfig)
|
||||
except Exception as ex:
|
||||
LOG.exception("run_deploy failed")
|
||||
raise ex
|
||||
|
||||
def _run_deploy(self, subcloud, payload, context,
|
||||
install_command, apply_command,
|
||||
deploy_command, rehome_command,
|
||||
install_command, bootstrap_command,
|
||||
config_command, rehome_command,
|
||||
network_reconfig):
|
||||
log_file = (
|
||||
os.path.join(consts.DC_ANSIBLE_LOG_DIR, subcloud.name)
|
||||
@ -1726,7 +1634,7 @@ class SubcloudManager(manager.Manager):
|
||||
)
|
||||
if not install_success:
|
||||
return
|
||||
if apply_command:
|
||||
if bootstrap_command:
|
||||
try:
|
||||
# Update the subcloud to bootstrapping
|
||||
db_api.subcloud_update(
|
||||
@ -1741,7 +1649,7 @@ class SubcloudManager(manager.Manager):
|
||||
# Run the ansible boostrap-subcloud playbook
|
||||
LOG.info("Starting bootstrap of %s" % subcloud.name)
|
||||
try:
|
||||
run_playbook(log_file, apply_command)
|
||||
run_playbook(log_file, bootstrap_command)
|
||||
except PlaybookExecutionFailed:
|
||||
msg = utils.find_ansible_error_msg(
|
||||
subcloud.name, log_file, consts.DEPLOY_STATE_BOOTSTRAPPING)
|
||||
@ -1752,7 +1660,7 @@ class SubcloudManager(manager.Manager):
|
||||
error_description=msg[0:consts.ERROR_DESCRIPTION_LENGTH])
|
||||
return
|
||||
LOG.info("Successfully bootstrapped %s" % subcloud.name)
|
||||
if deploy_command:
|
||||
if config_command:
|
||||
# Run the custom deploy playbook
|
||||
LOG.info("Starting deploy of %s" % subcloud.name)
|
||||
db_api.subcloud_update(
|
||||
@ -1761,7 +1669,7 @@ class SubcloudManager(manager.Manager):
|
||||
error_description=consts.ERROR_DESC_EMPTY)
|
||||
|
||||
try:
|
||||
run_playbook(log_file, deploy_command)
|
||||
run_playbook(log_file, config_command)
|
||||
except PlaybookExecutionFailed:
|
||||
msg = utils.find_ansible_error_msg(
|
||||
subcloud.name, log_file, consts.DEPLOY_STATE_DEPLOYING)
|
||||
@ -1818,32 +1726,33 @@ class SubcloudManager(manager.Manager):
|
||||
error_description=consts.ERROR_DESC_EMPTY)
|
||||
|
||||
def run_deploy_phases(self, context, subcloud_id, payload,
|
||||
deploy_states_to_run):
|
||||
"""Run individual phases durring deploy operation."""
|
||||
deploy_phases_to_run):
|
||||
"""Run one or more deployment phases, ensuring correct order
|
||||
|
||||
:param context: request context object
|
||||
:param subcloud_id: subcloud id from db
|
||||
:param payload: deploy phases payload
|
||||
:param deploy_phases_to_run: deploy phases that should run
|
||||
"""
|
||||
try:
|
||||
for state in deploy_states_to_run:
|
||||
if state == consts.DEPLOY_PHASE_INSTALL:
|
||||
install_success = self.subcloud_deploy_install(
|
||||
context, subcloud_id, payload)
|
||||
if not install_success:
|
||||
return
|
||||
elif state == consts.DEPLOY_PHASE_BOOTSTRAP:
|
||||
bootstrap_success = self.subcloud_deploy_bootstrap(
|
||||
context, subcloud_id, payload)
|
||||
if not bootstrap_success:
|
||||
return
|
||||
elif state == consts.DEPLOY_PHASE_CONFIG:
|
||||
config_success = self.subcloud_deploy_config(
|
||||
context, subcloud_id, payload)
|
||||
if not config_success:
|
||||
return
|
||||
succeeded = True
|
||||
if consts.DEPLOY_PHASE_INSTALL in deploy_phases_to_run:
|
||||
succeeded = self.subcloud_deploy_install(
|
||||
context, subcloud_id, payload)
|
||||
if succeeded and consts.DEPLOY_PHASE_BOOTSTRAP in deploy_phases_to_run:
|
||||
succeeded = self.subcloud_deploy_bootstrap(
|
||||
context, subcloud_id, payload)
|
||||
if succeeded and consts.DEPLOY_PHASE_CONFIG in deploy_phases_to_run:
|
||||
succeeded = self.subcloud_deploy_config(
|
||||
context, subcloud_id, payload)
|
||||
return succeeded
|
||||
|
||||
except Exception as ex:
|
||||
LOG.exception("run_deploy failed")
|
||||
LOG.exception("run_deploy_phases failed")
|
||||
raise ex
|
||||
|
||||
def _run_subcloud_config(self, subcloud, context,
|
||||
deploy_command, log_file):
|
||||
config_command, log_file):
|
||||
# Run the custom deploy playbook
|
||||
LOG.info("Starting deploy of %s" % subcloud.name)
|
||||
db_api.subcloud_update(
|
||||
@ -1854,7 +1763,7 @@ class SubcloudManager(manager.Manager):
|
||||
try:
|
||||
run_ansible = RunAnsible()
|
||||
aborted = run_ansible.exec_playbook(
|
||||
log_file, deploy_command, subcloud.name)
|
||||
log_file, config_command, subcloud.name)
|
||||
except PlaybookExecutionFailed:
|
||||
msg = utils.find_ansible_error_msg(
|
||||
subcloud.name, log_file, consts.DEPLOY_STATE_CONFIGURING)
|
||||
@ -1923,7 +1832,7 @@ class SubcloudManager(manager.Manager):
|
||||
return True
|
||||
|
||||
def _run_subcloud_bootstrap(self, context, subcloud,
|
||||
apply_command, log_file):
|
||||
bootstrap_command, log_file):
|
||||
# Update the subcloud deploy_status to bootstrapping
|
||||
db_api.subcloud_update(
|
||||
context, subcloud.id,
|
||||
@ -1935,7 +1844,7 @@ class SubcloudManager(manager.Manager):
|
||||
try:
|
||||
run_ansible = RunAnsible()
|
||||
aborted = run_ansible.exec_playbook(
|
||||
log_file, apply_command, subcloud.name)
|
||||
log_file, bootstrap_command, subcloud.name)
|
||||
except PlaybookExecutionFailed:
|
||||
msg = utils.find_ansible_error_msg(
|
||||
subcloud.name, log_file, consts.DEPLOY_STATE_BOOTSTRAPPING)
|
||||
|
@ -124,8 +124,9 @@ class ManagerClient(RPCClient):
|
||||
consts.TOPIC_DC_MANAGER,
|
||||
self.BASE_RPC_API_VERSION)
|
||||
|
||||
def add_subcloud(self, ctxt, payload):
|
||||
def add_subcloud(self, ctxt, subcloud_id, payload):
|
||||
return self.cast(ctxt, self.make_msg('add_subcloud',
|
||||
subcloud_id=subcloud_id,
|
||||
payload=payload))
|
||||
|
||||
def delete_subcloud(self, ctxt, subcloud_id):
|
||||
|
@ -42,7 +42,7 @@ FAKE_SUBCLOUD_INSTALL_VALUES = fake_subcloud.FAKE_SUBCLOUD_INSTALL_VALUES
|
||||
class FakeRPCClient(object):
|
||||
def subcloud_deploy_create(self, context, subcloud_id, _):
|
||||
subcloud = db_api.subcloud_get(context, subcloud_id)
|
||||
return db_api.subcloud_db_model_to_dict(subcloud)
|
||||
return subcloud
|
||||
|
||||
|
||||
# Apply the TestSubcloudPost parameter validation tests to the subcloud deploy
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -78,9 +78,9 @@ class TestDCManagerService(base.DCManagerTestCase):
|
||||
def test_add_subcloud(self, mock_subcloud_manager):
|
||||
self.service_obj.init_managers()
|
||||
self.service_obj.add_subcloud(
|
||||
self.context, payload={'name': 'testname'})
|
||||
self.context, subcloud_id=1, payload={'name': 'testname'})
|
||||
mock_subcloud_manager().add_subcloud.\
|
||||
assert_called_once_with(self.context, mock.ANY)
|
||||
assert_called_once_with(self.context, 1, mock.ANY)
|
||||
|
||||
@mock.patch.object(service, 'SubcloudManager')
|
||||
def test_delete_subcloud(self, mock_subcloud_manager):
|
||||
|
@ -12,6 +12,7 @@
|
||||
# under the License.
|
||||
#
|
||||
import base64
|
||||
import collections
|
||||
import copy
|
||||
import datetime
|
||||
|
||||
@ -28,6 +29,7 @@ sys.modules['fm_core'] = mock.Mock()
|
||||
import threading
|
||||
|
||||
from dccommon import consts as dccommon_consts
|
||||
from dccommon import subcloud_install
|
||||
from dccommon.utils import RunAnsible
|
||||
from dcmanager.common import consts
|
||||
from dcmanager.common import exceptions
|
||||
@ -103,6 +105,7 @@ class FakeProject(object):
|
||||
self.name = projname
|
||||
self.id = projid
|
||||
|
||||
|
||||
FAKE_PROJECTS = [
|
||||
FakeProject(
|
||||
dccommon_consts.ADMIN_PROJECT_NAME,
|
||||
@ -252,7 +255,7 @@ class FakeSysinvClient(object):
|
||||
|
||||
|
||||
class FakeException(Exception):
|
||||
pass
|
||||
pass
|
||||
|
||||
|
||||
FAKE_SUBCLOUD_PRESTAGE_PAYLOAD = {
|
||||
@ -397,6 +400,10 @@ class TestSubcloudManager(base.DCManagerTestCase):
|
||||
self.mock_context.get_admin_context.return_value = self.ctx
|
||||
self.addCleanup(p.stop)
|
||||
|
||||
# Reset the regionone_data cache between tests
|
||||
subcloud_manager.SubcloudManager.regionone_data = \
|
||||
collections.defaultdict(dict)
|
||||
|
||||
@staticmethod
|
||||
def create_subcloud_static(ctxt, **kwargs):
|
||||
values = {
|
||||
@ -453,7 +460,7 @@ class TestSubcloudManager(base.DCManagerTestCase):
|
||||
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),
|
||||
cutils.get_ansible_filename(subcloud_name, consts.INVENTORY_FILE_POSTFIX),
|
||||
FAKE_PREVIOUS_SW_VERSION)
|
||||
|
||||
# Verify subcloud was updated with correct values
|
||||
@ -497,8 +504,8 @@ class TestSubcloudManager(base.DCManagerTestCase):
|
||||
mock_get_cached_regionone_data.return_value = FAKE_CACHED_REGIONONE_DATA
|
||||
|
||||
sm = subcloud_manager.SubcloudManager()
|
||||
subcloud_dict = sm.subcloud_deploy_create(self.ctx, subcloud.id,
|
||||
payload=values)
|
||||
subcloud = sm.subcloud_deploy_create(self.ctx, subcloud.id,
|
||||
payload=values)
|
||||
mock_get_cached_regionone_data.assert_called_once()
|
||||
mock_sysinv_client().create_route.assert_called()
|
||||
self.fake_dcorch_api.add_subcloud.assert_called_once()
|
||||
@ -510,7 +517,7 @@ class TestSubcloudManager(base.DCManagerTestCase):
|
||||
|
||||
# Verify subcloud was updated with correct values
|
||||
self.assertEqual(consts.DEPLOY_STATE_CREATED,
|
||||
subcloud_dict['deploy-status'])
|
||||
subcloud.deploy_status)
|
||||
|
||||
# Verify subcloud was updated with correct values
|
||||
updated_subcloud = db_api.subcloud_get_by_name(self.ctx, values['name'])
|
||||
@ -529,12 +536,12 @@ class TestSubcloudManager(base.DCManagerTestCase):
|
||||
mock_keystone_client.side_effect = FakeException('boom')
|
||||
|
||||
sm = subcloud_manager.SubcloudManager()
|
||||
subcloud_dict = sm.subcloud_deploy_create(self.ctx, subcloud.id,
|
||||
payload=values)
|
||||
subcloud = sm.subcloud_deploy_create(self.ctx, subcloud.id,
|
||||
payload=values)
|
||||
|
||||
# Verify subcloud was updated with correct values
|
||||
self.assertEqual(consts.DEPLOY_STATE_CREATE_FAILED,
|
||||
subcloud_dict['deploy-status'])
|
||||
subcloud.deploy_status)
|
||||
|
||||
# Verify subcloud was updated with correct values
|
||||
updated_subcloud = db_api.subcloud_get_by_name(self.ctx, values['name'])
|
||||
@ -548,7 +555,7 @@ class TestSubcloudManager(base.DCManagerTestCase):
|
||||
@mock.patch.object(RunAnsible, 'exec_playbook')
|
||||
def test_subcloud_deploy_bootstrap(self, mock_exec_playbook, mock_update_yml,
|
||||
mock_get_playbook_for_software_version,
|
||||
mock_keyring, create_subcloud_inventory):
|
||||
mock_keyring, mock_create_subcloud_inventory):
|
||||
mock_get_playbook_for_software_version.return_value = "22.12"
|
||||
mock_keyring.get_password.return_value = "testpass"
|
||||
mock_exec_playbook.return_value = False
|
||||
@ -675,73 +682,72 @@ class TestSubcloudManager(base.DCManagerTestCase):
|
||||
self.assertEqual(consts.DEPLOY_STATE_DONE,
|
||||
updated_subcloud.deploy_status)
|
||||
|
||||
@mock.patch.object(subcloud_install.SubcloudInstall, 'prep')
|
||||
@mock.patch.object(subcloud_install, 'KeystoneClient')
|
||||
@mock.patch.object(subcloud_install, 'SysinvClient')
|
||||
@mock.patch.object(subcloud_manager.SubcloudManager,
|
||||
'compose_apply_command')
|
||||
@mock.patch.object(subcloud_manager.SubcloudManager,
|
||||
'compose_rehome_command')
|
||||
'_write_subcloud_ansible_config')
|
||||
@mock.patch.object(subcloud_manager.SubcloudManager,
|
||||
'_create_intermediate_ca_cert')
|
||||
@mock.patch.object(cutils, 'delete_subcloud_inventory')
|
||||
@mock.patch.object(subcloud_manager.SubcloudManager,
|
||||
'_create_addn_hosts_dc')
|
||||
@mock.patch.object(subcloud_manager, 'OpenStackDriver')
|
||||
@mock.patch.object(subcloud_manager, 'SysinvClient')
|
||||
@mock.patch.object(subcloud_manager.SubcloudManager,
|
||||
'_get_cached_regionone_data')
|
||||
@mock.patch.object(subcloud_manager.SubcloudManager,
|
||||
'_create_addn_hosts_dc')
|
||||
'_write_deploy_files')
|
||||
@mock.patch.object(cutils, 'create_subcloud_inventory')
|
||||
@mock.patch.object(subcloud_manager.SubcloudManager,
|
||||
'_write_subcloud_ansible_config')
|
||||
@mock.patch.object(subcloud_manager,
|
||||
'keyring')
|
||||
@mock.patch.object(threading.Thread,
|
||||
'start')
|
||||
def test_add_subcloud(self, mock_thread_start, mock_keyring,
|
||||
mock_write_subcloud_ansible_config,
|
||||
mock_create_subcloud_inventory,
|
||||
mock_create_addn_hosts,
|
||||
mock_get_cached_regionone_data,
|
||||
mock_sysinv_client,
|
||||
mock_keystone_client,
|
||||
mock_delete_subcloud_inventory,
|
||||
@mock.patch.object(subcloud_manager, 'keyring')
|
||||
@mock.patch.object(cutils, 'get_playbook_for_software_version')
|
||||
@mock.patch.object(cutils, 'update_values_on_yaml_file')
|
||||
@mock.patch.object(RunAnsible, 'exec_playbook')
|
||||
def test_add_subcloud(self, mock_exec_playbook, mock_update_yml,
|
||||
mock_get_playbook_for_software_version,
|
||||
mock_keyring, mock_create_subcloud_inventory,
|
||||
mock_write_deploy_files, mock_sysinv_client,
|
||||
mock_openstack_driver, mock_create_addn_hosts,
|
||||
mock_create_intermediate_ca_cert,
|
||||
mock_compose_rehome_command,
|
||||
mock_compose_apply_command):
|
||||
values = utils.create_subcloud_dict(base.SUBCLOUD_SAMPLE_DATA_0)
|
||||
values['deploy_status'] = consts.DEPLOY_STATE_NONE
|
||||
mock_write_subcloud_ansible_config,
|
||||
mock_install_ks_client, mock_install_sysinvclient,
|
||||
mock_install_prep):
|
||||
# Prepare the payload
|
||||
install_values = copy.copy(fake_subcloud.FAKE_SUBCLOUD_INSTALL_VALUES)
|
||||
install_values['software_version'] = SW_VERSION
|
||||
payload = {**fake_subcloud.FAKE_BOOTSTRAP_VALUE,
|
||||
**fake_subcloud.FAKE_BOOTSTRAP_FILE_DATA,
|
||||
"sysadmin_password": "testpass",
|
||||
'bmc_password': 'bmc_pass',
|
||||
'install_values': install_values,
|
||||
'software_version': FAKE_PREVIOUS_SW_VERSION,
|
||||
"deploy_playbook": "test_playbook.yaml",
|
||||
"deploy_overrides": "test_overrides.yaml",
|
||||
"deploy_chart": "test_chart.yaml",
|
||||
"deploy_config": "subcloud1.yaml"}
|
||||
|
||||
# dcmanager add_subcloud queries the data from the db
|
||||
subcloud = self.create_subcloud_static(self.ctx, name=values['name'])
|
||||
# Create subcloud in DB
|
||||
subcloud = self.create_subcloud_static(self.ctx, name=payload['name'])
|
||||
|
||||
mock_keystone_client().keystone_client = FakeKeystoneClient()
|
||||
mock_keyring.get_password.return_value = "testpassword"
|
||||
mock_get_cached_regionone_data.return_value = FAKE_CACHED_REGIONONE_DATA
|
||||
# Mock return values
|
||||
mock_get_playbook_for_software_version.return_value = SW_VERSION
|
||||
mock_keyring.get_password.return_value = payload['sysadmin_password']
|
||||
mock_exec_playbook.return_value = False
|
||||
mock_openstack_driver().keystone_client = FakeKeystoneClient()
|
||||
|
||||
# Call the add method
|
||||
sm = subcloud_manager.SubcloudManager()
|
||||
subcloud_dict = sm.add_subcloud(self.ctx, payload=values)
|
||||
mock_get_cached_regionone_data.assert_called_once()
|
||||
mock_sysinv_client().create_route.assert_called()
|
||||
self.fake_dcorch_api.add_subcloud.assert_called_once()
|
||||
mock_create_addn_hosts.assert_called_once()
|
||||
mock_create_subcloud_inventory.assert_called_once()
|
||||
mock_write_subcloud_ansible_config.assert_called_once()
|
||||
mock_keyring.get_password.assert_called()
|
||||
mock_thread_start.assert_called_once()
|
||||
mock_create_intermediate_ca_cert.assert_called_once()
|
||||
mock_compose_rehome_command.assert_not_called()
|
||||
mock_compose_apply_command.assert_called_once_with(
|
||||
values['name'],
|
||||
sm._get_ansible_filename(values['name'], consts.INVENTORY_FILE_POSTFIX),
|
||||
subcloud['software_version'])
|
||||
sm.add_subcloud(self.ctx, subcloud.id, payload)
|
||||
|
||||
# Verify subcloud was updated with correct values
|
||||
self.assertEqual(consts.DEPLOY_STATE_PRE_DEPLOY,
|
||||
subcloud_dict['deploy-status'])
|
||||
|
||||
# Verify subcloud was updated with correct values
|
||||
updated_subcloud = db_api.subcloud_get_by_name(self.ctx, values['name'])
|
||||
self.assertEqual(consts.DEPLOY_STATE_PRE_DEPLOY,
|
||||
# Verify results
|
||||
updated_subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud.name)
|
||||
self.assertEqual(consts.DEPLOY_STATE_DONE,
|
||||
updated_subcloud.deploy_status)
|
||||
|
||||
mock_write_deploy_files.assert_called()
|
||||
mock_keyring.get_password.assert_called()
|
||||
mock_update_yml.assert_called()
|
||||
mock_create_subcloud_inventory.assert_called()
|
||||
mock_get_playbook_for_software_version.assert_called_once()
|
||||
self.assertEqual(mock_exec_playbook.call_count, 3)
|
||||
|
||||
@mock.patch.object(subcloud_manager.SubcloudManager,
|
||||
'compose_rehome_command')
|
||||
@mock.patch.object(subcloud_manager.SubcloudManager,
|
||||
@ -758,10 +764,8 @@ class TestSubcloudManager(base.DCManagerTestCase):
|
||||
'_write_subcloud_ansible_config')
|
||||
@mock.patch.object(subcloud_manager,
|
||||
'keyring')
|
||||
@mock.patch.object(threading.Thread,
|
||||
'start')
|
||||
def test_add_subcloud_with_migration_option(
|
||||
self, mock_thread_start, mock_keyring,
|
||||
self, mock_keyring,
|
||||
mock_write_subcloud_ansible_config,
|
||||
mock_create_subcloud_inventory,
|
||||
mock_create_addn_hosts,
|
||||
@ -783,24 +787,22 @@ class TestSubcloudManager(base.DCManagerTestCase):
|
||||
mock_get_cached_regionone_data.return_value = FAKE_CACHED_REGIONONE_DATA
|
||||
|
||||
sm = subcloud_manager.SubcloudManager()
|
||||
subcloud_dict = sm.add_subcloud(self.ctx, payload=values)
|
||||
with mock.patch.object(sm, 'run_deploy_thread') as mock_run_deploy:
|
||||
sm.add_subcloud(self.ctx, subcloud.id, payload=values)
|
||||
|
||||
mock_get_cached_regionone_data.assert_called_once()
|
||||
mock_sysinv_client().create_route.assert_called()
|
||||
self.fake_dcorch_api.add_subcloud.assert_called_once()
|
||||
mock_create_addn_hosts.assert_called_once()
|
||||
mock_create_subcloud_inventory.assert_called_once()
|
||||
mock_write_subcloud_ansible_config.assert_called_once()
|
||||
mock_thread_start.assert_called_once()
|
||||
mock_run_deploy.assert_called_once()
|
||||
mock_create_intermediate_ca_cert.assert_called_once()
|
||||
mock_compose_rehome_command.assert_called_once_with(
|
||||
values['name'],
|
||||
sm._get_ansible_filename(values['name'], consts.INVENTORY_FILE_POSTFIX),
|
||||
subcloud['software_version'])
|
||||
|
||||
# Verify subcloud was updated with correct values
|
||||
self.assertEqual(consts.DEPLOY_STATE_PRE_REHOME,
|
||||
subcloud_dict['deploy-status'])
|
||||
|
||||
# Verify subcloud was updated with correct values
|
||||
updated_subcloud = db_api.subcloud_get_by_name(self.ctx, values['name'])
|
||||
self.assertEqual(consts.DEPLOY_STATE_PRE_REHOME,
|
||||
@ -809,28 +811,28 @@ class TestSubcloudManager(base.DCManagerTestCase):
|
||||
@mock.patch.object(subcloud_manager, 'OpenStackDriver')
|
||||
@mock.patch.object(subcloud_manager, 'SysinvClient')
|
||||
@mock.patch.object(subcloud_manager.SubcloudManager, '_get_cached_regionone_data')
|
||||
def test_add_subcloud_deploy_prep_failed(self,
|
||||
mock_get_cached_regionone_data,
|
||||
mock_sysinv_client,
|
||||
mock_keystone_client):
|
||||
def test_add_subcloud_create_failed(self,
|
||||
mock_get_cached_regionone_data,
|
||||
mock_sysinv_client,
|
||||
mock_keystone_client):
|
||||
values = utils.create_subcloud_dict(base.SUBCLOUD_SAMPLE_DATA_0)
|
||||
services = FAKE_SERVICES
|
||||
|
||||
# dcmanager add_subcloud queries the data from the db
|
||||
self.create_subcloud_static(self.ctx, name=values['name'])
|
||||
subcloud = self.create_subcloud_static(self.ctx, name=values['name'])
|
||||
|
||||
self.fake_dcorch_api.add_subcloud.side_effect = FakeException('boom')
|
||||
mock_get_cached_regionone_data.return_value = FAKE_CACHED_REGIONONE_DATA
|
||||
mock_keystone_client().services_list = services
|
||||
|
||||
sm = subcloud_manager.SubcloudManager()
|
||||
sm.add_subcloud(self.ctx, payload=values)
|
||||
sm.add_subcloud(self.ctx, subcloud.id, payload=values)
|
||||
mock_get_cached_regionone_data.assert_called_once()
|
||||
mock_sysinv_client().create_route.assert_called()
|
||||
|
||||
# Verify subcloud was updated with correct values
|
||||
subcloud = db_api.subcloud_get_by_name(self.ctx, values['name'])
|
||||
self.assertEqual(consts.DEPLOY_STATE_DEPLOY_PREP_FAILED,
|
||||
self.assertEqual(consts.DEPLOY_STATE_CREATE_FAILED,
|
||||
subcloud.deploy_status)
|
||||
|
||||
@mock.patch.object(subcloud_manager, 'OpenStackDriver')
|
||||
@ -843,14 +845,14 @@ class TestSubcloudManager(base.DCManagerTestCase):
|
||||
services = FAKE_SERVICES
|
||||
|
||||
# dcmanager add_subcloud queries the data from the db
|
||||
self.create_subcloud_static(self.ctx, name=values['name'])
|
||||
subcloud = self.create_subcloud_static(self.ctx, name=values['name'])
|
||||
|
||||
self.fake_dcorch_api.add_subcloud.side_effect = FakeException('boom')
|
||||
mock_get_cached_regionone_data.return_value = FAKE_CACHED_REGIONONE_DATA
|
||||
mock_keystone_client().services_list = services
|
||||
|
||||
sm = subcloud_manager.SubcloudManager()
|
||||
sm.add_subcloud(self.ctx, payload=values)
|
||||
sm.add_subcloud(self.ctx, subcloud.id, payload=values)
|
||||
mock_get_cached_regionone_data.assert_called_once()
|
||||
mock_sysinv_client().create_route.assert_called()
|
||||
|
||||
@ -1642,9 +1644,8 @@ class TestSubcloudManager(base.DCManagerTestCase):
|
||||
mock_prepare_for_deployment.assert_called_once()
|
||||
|
||||
def test_get_ansible_filename(self):
|
||||
sm = subcloud_manager.SubcloudManager()
|
||||
filename = sm._get_ansible_filename('subcloud1',
|
||||
consts.INVENTORY_FILE_POSTFIX)
|
||||
filename = cutils.get_ansible_filename('subcloud1',
|
||||
consts.INVENTORY_FILE_POSTFIX)
|
||||
self.assertEqual(filename,
|
||||
f'{dccommon_consts.ANSIBLE_OVERRIDES_PATH}/subcloud1_inventory.yml')
|
||||
|
||||
@ -1667,15 +1668,15 @@ class TestSubcloudManager(base.DCManagerTestCase):
|
||||
)
|
||||
|
||||
@mock.patch('os.path.isfile')
|
||||
def test_compose_apply_command(self, mock_isfile):
|
||||
def test_compose_bootstrap_command(self, mock_isfile):
|
||||
mock_isfile.return_value = True
|
||||
sm = subcloud_manager.SubcloudManager()
|
||||
apply_command = sm.compose_apply_command(
|
||||
bootstrap_command = sm.compose_bootstrap_command(
|
||||
'subcloud1',
|
||||
f'{dccommon_consts.ANSIBLE_OVERRIDES_PATH}/subcloud1_inventory.yml',
|
||||
FAKE_PREVIOUS_SW_VERSION)
|
||||
self.assertEqual(
|
||||
apply_command,
|
||||
bootstrap_command,
|
||||
[
|
||||
'ansible-playbook',
|
||||
cutils.get_playbook_for_software_version(
|
||||
@ -1688,19 +1689,19 @@ class TestSubcloudManager(base.DCManagerTestCase):
|
||||
]
|
||||
)
|
||||
|
||||
def test_compose_deploy_command(self):
|
||||
def test_compose_config_command(self):
|
||||
sm = subcloud_manager.SubcloudManager()
|
||||
fake_payload = {"sysadmin_password": "testpass",
|
||||
"deploy_playbook": "test_playbook.yaml",
|
||||
"deploy_overrides": "test_overrides.yaml",
|
||||
"deploy_chart": "test_chart.yaml",
|
||||
"deploy_config": "subcloud1.yaml"}
|
||||
deploy_command = sm.compose_deploy_command(
|
||||
config_command = sm.compose_config_command(
|
||||
'subcloud1',
|
||||
f'{dccommon_consts.ANSIBLE_OVERRIDES_PATH}/subcloud1_inventory.yml',
|
||||
fake_payload)
|
||||
self.assertEqual(
|
||||
deploy_command,
|
||||
config_command,
|
||||
[
|
||||
'ansible-playbook', 'test_playbook.yaml', '-e',
|
||||
f'@{dccommon_consts.ANSIBLE_OVERRIDES_PATH}/subcloud1_deploy_values.yml', '-i',
|
||||
@ -1739,7 +1740,7 @@ class TestSubcloudManager(base.DCManagerTestCase):
|
||||
@mock.patch.object(
|
||||
subcloud_manager.SubcloudManager, 'compose_install_command')
|
||||
@mock.patch.object(
|
||||
subcloud_manager.SubcloudManager, 'compose_apply_command')
|
||||
subcloud_manager.SubcloudManager, 'compose_bootstrap_command')
|
||||
@mock.patch.object(cutils, 'create_subcloud_inventory')
|
||||
@mock.patch.object(subcloud_manager.SubcloudManager, '_get_cached_regionone_data')
|
||||
@mock.patch.object(subcloud_manager, 'OpenStackDriver')
|
||||
@ -1748,7 +1749,7 @@ class TestSubcloudManager(base.DCManagerTestCase):
|
||||
def test_reinstall_subcloud(
|
||||
self, mock_keyring, mock_thread_start,
|
||||
mock_keystone_client, mock_get_cached_regionone_data, mock_create_subcloud_inventory,
|
||||
mock_compose_apply_command, mock_compose_install_command,
|
||||
mock_compose_bootstrap_command, mock_compose_install_command,
|
||||
mock_create_intermediate_ca_cert, mock_write_subcloud_ansible_config):
|
||||
|
||||
subcloud_name = 'subcloud1'
|
||||
@ -1778,11 +1779,11 @@ class TestSubcloudManager(base.DCManagerTestCase):
|
||||
mock_write_subcloud_ansible_config.assert_called_once()
|
||||
mock_compose_install_command.assert_called_once_with(
|
||||
subcloud_name,
|
||||
sm._get_ansible_filename(subcloud_name, consts.INVENTORY_FILE_POSTFIX),
|
||||
cutils.get_ansible_filename(subcloud_name, consts.INVENTORY_FILE_POSTFIX),
|
||||
FAKE_PREVIOUS_SW_VERSION)
|
||||
mock_compose_apply_command.assert_called_once_with(
|
||||
mock_compose_bootstrap_command.assert_called_once_with(
|
||||
subcloud_name,
|
||||
sm._get_ansible_filename(subcloud_name, consts.INVENTORY_FILE_POSTFIX),
|
||||
cutils.get_ansible_filename(subcloud_name, consts.INVENTORY_FILE_POSTFIX),
|
||||
FAKE_PREVIOUS_SW_VERSION)
|
||||
mock_thread_start.assert_called_once()
|
||||
|
||||
@ -2305,13 +2306,10 @@ class TestSubcloudManager(base.DCManagerTestCase):
|
||||
self.assertEqual(mock_run_ansible.call_count, 2)
|
||||
# Verify the "image_list_file" was passed to the prestage image playbook
|
||||
# for the remote prestage
|
||||
self.assertTrue(
|
||||
'image_list_file' in mock_run_ansible.call_args_list[1].args[1][5])
|
||||
self.assertIn('image_list_file', mock_run_ansible.call_args_list[1].args[1][5])
|
||||
# Verify the prestage request release was passed to the playbooks
|
||||
self.assertTrue(
|
||||
FAKE_PRESTAGE_RELEASE in mock_run_ansible.call_args_list[0].args[1][5])
|
||||
self.assertTrue(
|
||||
FAKE_PRESTAGE_RELEASE in mock_run_ansible.call_args_list[1].args[1][5])
|
||||
self.assertIn(FAKE_PRESTAGE_RELEASE, mock_run_ansible.call_args_list[0].args[1][5])
|
||||
self.assertIn(FAKE_PRESTAGE_RELEASE, mock_run_ansible.call_args_list[1].args[1][5])
|
||||
|
||||
@mock.patch.object(prestage, '_run_ansible')
|
||||
def test_prestage_local_pass(self, mock_run_ansible):
|
||||
@ -2333,10 +2331,8 @@ class TestSubcloudManager(base.DCManagerTestCase):
|
||||
# Verify both of prestage package and image ansible playbooks were called
|
||||
self.assertEqual(mock_run_ansible.call_count, 2)
|
||||
# Verify the prestage request release was passed to the playbooks
|
||||
self.assertTrue(
|
||||
FAKE_PRESTAGE_RELEASE in mock_run_ansible.call_args_list[0].args[1][5])
|
||||
self.assertTrue(
|
||||
FAKE_PRESTAGE_RELEASE in mock_run_ansible.call_args_list[1].args[1][5])
|
||||
self.assertIn(FAKE_PRESTAGE_RELEASE, mock_run_ansible.call_args_list[0].args[1][5])
|
||||
self.assertIn(FAKE_PRESTAGE_RELEASE, mock_run_ansible.call_args_list[1].args[1][5])
|
||||
|
||||
@mock.patch.object(prestage, 'prestage_images')
|
||||
@mock.patch.object(prestage, 'prestage_packages')
|
||||
|
Loading…
Reference in New Issue
Block a user