Implement subcloud deploy backend skeleton
This commit creates the structure to handle all enroll related tasks. Test Plan: PASS: Create a subcloud with add command passing --enroll as a parameter. Verify that the subcloud deploy status is set as enroll-complete. PASS: Create a subcloud with subcloud deploy create command and run deploy enroll command after subcloud is created. Verify that subcloud deploy status is set as enroll-complete. PASS: Run subcloud deploy create with install and bootstrap values, then run subcloud deploy enroll without providing install and bootstrap values. Verify the deploy status is set to enroll-complete. Story: 2011100 Task: 50137 Change-Id: I835253ade3d2ac0ff7aa2f1d08768269adc61d41 Signed-off-by: Gustavo Pereira <gustavo.lyrapereira@windriver.com>
This commit is contained in:
@@ -210,3 +210,9 @@ ANSIBLE_SUBCLOUD_INSTALL_PLAYBOOK = \
|
|||||||
'/usr/share/ansible/stx-ansible/playbooks/install.yml'
|
'/usr/share/ansible/stx-ansible/playbooks/install.yml'
|
||||||
|
|
||||||
ENROLL_INIT_SEED_ISO_NAME = 'seed.iso'
|
ENROLL_INIT_SEED_ISO_NAME = 'seed.iso'
|
||||||
|
|
||||||
|
ANSIBLE_SUBCLOUD_ENROLL_PLAYBOOK = \
|
||||||
|
"/usr/share/ansible/stx-ansible/playbooks/enroll_subcloud.yml"
|
||||||
|
|
||||||
|
# Sysinv client default timeout
|
||||||
|
SYSINV_CLIENT_REST_DEFAULT_TIMEOUT = 600
|
||||||
|
@@ -100,8 +100,6 @@ KUBE_ROOTCA_UPDATING_HOST_TRUSTNEWCA_FAILED = 'updating-host-trust-new-ca-failed
|
|||||||
# by dcmanager upgrade orchestration for the load import operations.
|
# by dcmanager upgrade orchestration for the load import operations.
|
||||||
HOST_FS_NAME_SCRATCH = 'scratch'
|
HOST_FS_NAME_SCRATCH = 'scratch'
|
||||||
|
|
||||||
SYSINV_CLIENT_REST_DEFAULT_TIMEOUT = 600
|
|
||||||
|
|
||||||
|
|
||||||
def make_sysinv_patch(update_dict):
|
def make_sysinv_patch(update_dict):
|
||||||
patch = []
|
patch = []
|
||||||
@@ -122,7 +120,7 @@ class SysinvClient(base.DriverBase):
|
|||||||
"""Sysinv V1 driver."""
|
"""Sysinv V1 driver."""
|
||||||
|
|
||||||
def __init__(self, region, session,
|
def __init__(self, region, session,
|
||||||
timeout=SYSINV_CLIENT_REST_DEFAULT_TIMEOUT,
|
timeout=consts.SYSINV_CLIENT_REST_DEFAULT_TIMEOUT,
|
||||||
endpoint_type=consts.KS_ENDPOINT_ADMIN,
|
endpoint_type=consts.KS_ENDPOINT_ADMIN,
|
||||||
endpoint=None):
|
endpoint=None):
|
||||||
try:
|
try:
|
||||||
|
@@ -69,12 +69,12 @@ class SubcloudEnrollmentInit(object):
|
|||||||
network_cloud_config = [
|
network_cloud_config = [
|
||||||
{
|
{
|
||||||
'type': 'physical',
|
'type': 'physical',
|
||||||
'name': iso_values['bootstrap_interface'],
|
'name': iso_values['install_values']['bootstrap_interface'],
|
||||||
'subnets': [
|
'subnets': [
|
||||||
{
|
{
|
||||||
'type': 'static',
|
'type': 'static',
|
||||||
'address': iso_values['external_oam_floating_address'],
|
'address': iso_values['external_oam_floating_address'],
|
||||||
'netmask': iso_values['network_mask'],
|
'netmask': iso_values['install_values']['network_mask'],
|
||||||
'gateway': iso_values['external_oam_gateway_address'],
|
'gateway': iso_values['external_oam_gateway_address'],
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@@ -58,6 +58,7 @@ SUBCLOUD_BOOTSTRAP_GET_FILE_CONTENTS = (
|
|||||||
|
|
||||||
SUBCLOUD_ENROLL_GET_FILE_CONTENTS = (
|
SUBCLOUD_ENROLL_GET_FILE_CONTENTS = (
|
||||||
consts.BOOTSTRAP_VALUES,
|
consts.BOOTSTRAP_VALUES,
|
||||||
|
consts.INSTALL_VALUES
|
||||||
)
|
)
|
||||||
|
|
||||||
SUBCLOUD_CONFIG_GET_FILE_CONTENTS = (
|
SUBCLOUD_CONFIG_GET_FILE_CONTENTS = (
|
||||||
@@ -102,7 +103,7 @@ VALID_STATES_FOR_DEPLOY_ENROLL = (
|
|||||||
consts.DEPLOY_STATE_ENROLL_FAILED,
|
consts.DEPLOY_STATE_ENROLL_FAILED,
|
||||||
consts.DEPLOY_STATE_ENROLLED,
|
consts.DEPLOY_STATE_ENROLLED,
|
||||||
consts.DEPLOY_STATE_PRE_ENROLL,
|
consts.DEPLOY_STATE_PRE_ENROLL,
|
||||||
consts.DEPLOY_STATE_ENROLLING,
|
consts.DEPLOY_STATE_INIT_ENROLL_FAILED
|
||||||
)
|
)
|
||||||
|
|
||||||
FILES_FOR_RESUME_INSTALL = \
|
FILES_FOR_RESUME_INSTALL = \
|
||||||
@@ -531,21 +532,28 @@ class PhasedSubcloudDeployController(object):
|
|||||||
payload = psd_common.get_request_data(
|
payload = psd_common.get_request_data(
|
||||||
request, subcloud, SUBCLOUD_ENROLL_GET_FILE_CONTENTS)
|
request, subcloud, SUBCLOUD_ENROLL_GET_FILE_CONTENTS)
|
||||||
|
|
||||||
psd_common.validate_enroll_parameter(payload, request)
|
|
||||||
|
|
||||||
# Try to load the existing override values
|
# Try to load the existing override values
|
||||||
override_file = psd_common.get_config_file_path(subcloud.name)
|
override_file = psd_common.get_config_file_path(subcloud.name)
|
||||||
if os.path.exists(override_file):
|
if os.path.exists(override_file):
|
||||||
if not has_bootstrap_values:
|
if not has_bootstrap_values:
|
||||||
psd_common.populate_payload_with_pre_existing_data(
|
psd_common.populate_payload_with_pre_existing_data(
|
||||||
payload, subcloud, SUBCLOUD_BOOTSTRAP_GET_FILE_CONTENTS)
|
payload, subcloud, SUBCLOUD_ENROLL_GET_FILE_CONTENTS)
|
||||||
elif not has_bootstrap_values:
|
elif not has_bootstrap_values:
|
||||||
msg = ("Required bootstrap-values file was not provided and it was "
|
msg = ("Required bootstrap-values file was not provided and it was "
|
||||||
f"not previously available at {override_file}")
|
f"not previously available at {override_file}")
|
||||||
pecan.abort(400, _(msg))
|
pecan.abort(400, _(msg))
|
||||||
|
|
||||||
|
psd_common.validate_enroll_parameter(payload)
|
||||||
|
|
||||||
payload['software_version'] = subcloud.software_version
|
payload['software_version'] = subcloud.software_version
|
||||||
|
|
||||||
|
# Use bootstrap file verification
|
||||||
|
psd_common.pre_deploy_bootstrap(context, payload, subcloud,
|
||||||
|
has_bootstrap_values)
|
||||||
|
|
||||||
|
self.dcmanager_rpc_client.subcloud_deploy_enroll(
|
||||||
|
context, subcloud.id, payload)
|
||||||
|
|
||||||
pecan.abort(400, "subcloud deploy enrollment is not "
|
pecan.abort(400, "subcloud deploy enrollment is not "
|
||||||
"available yet")
|
"available yet")
|
||||||
|
|
||||||
|
@@ -536,7 +536,8 @@ class SubcloudsController(object):
|
|||||||
|
|
||||||
psd_common.validate_secondary_parameter(payload, request)
|
psd_common.validate_secondary_parameter(payload, request)
|
||||||
|
|
||||||
psd_common.validate_enroll_parameter(payload, request)
|
if payload.get('enroll'):
|
||||||
|
psd_common.validate_enroll_parameter(payload)
|
||||||
|
|
||||||
# Compares to match both supplied and bootstrap name param
|
# Compares to match both supplied and bootstrap name param
|
||||||
# of the subcloud if migrate is on
|
# of the subcloud if migrate is on
|
||||||
|
@@ -24,7 +24,6 @@ from oslo_log import log as logging
|
|||||||
from tsconfig.tsconfig import CONFIG_PATH
|
from tsconfig.tsconfig import CONFIG_PATH
|
||||||
|
|
||||||
from dccommon import consts as dccommon_consts
|
from dccommon import consts as dccommon_consts
|
||||||
from dccommon.drivers.openstack import sysinv_v1
|
|
||||||
from dcmanager.audit import firmware_audit
|
from dcmanager.audit import firmware_audit
|
||||||
from dcmanager.audit import kube_rootca_update_audit
|
from dcmanager.audit import kube_rootca_update_audit
|
||||||
from dcmanager.audit import kubernetes_audit
|
from dcmanager.audit import kubernetes_audit
|
||||||
@@ -448,7 +447,7 @@ class SubcloudAuditManager(manager.Manager):
|
|||||||
# an extra audit interval.
|
# an extra audit interval.
|
||||||
last_audit_fixup_threshold = current_time - datetime.timedelta(
|
last_audit_fixup_threshold = current_time - datetime.timedelta(
|
||||||
seconds=(
|
seconds=(
|
||||||
sysinv_v1.SYSINV_CLIENT_REST_DEFAULT_TIMEOUT
|
dccommon_consts.SYSINV_CLIENT_REST_DEFAULT_TIMEOUT
|
||||||
+ CONF.scheduler.subcloud_audit_interval
|
+ CONF.scheduler.subcloud_audit_interval
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@@ -233,7 +233,9 @@ DEPLOY_STATE_PRE_REHOME = 'pre-rehome'
|
|||||||
DEPLOY_STATE_PRE_ENROLL = 'pre-enroll'
|
DEPLOY_STATE_PRE_ENROLL = 'pre-enroll'
|
||||||
DEPLOY_STATE_PRE_ENROLL_FAILED = 'pre-enroll-failed'
|
DEPLOY_STATE_PRE_ENROLL_FAILED = 'pre-enroll-failed'
|
||||||
DEPLOY_STATE_PRE_ENROLL_COMPLETE = 'pre-enroll-complete'
|
DEPLOY_STATE_PRE_ENROLL_COMPLETE = 'pre-enroll-complete'
|
||||||
DEPLOY_STATE_INIT_ENROLL = 'init-enroll'
|
DEPLOY_STATE_PRE_INIT_ENROLL = 'pre-init-enroll'
|
||||||
|
DEPLOY_STATE_PRE_INIT_ENROLL_FAILED = 'pre-init-enroll-failed'
|
||||||
|
DEPLOY_STATE_INITIATING_ENROLL = 'initiating-enroll'
|
||||||
DEPLOY_STATE_INIT_ENROLL_FAILED = 'init-enroll-failed'
|
DEPLOY_STATE_INIT_ENROLL_FAILED = 'init-enroll-failed'
|
||||||
DEPLOY_STATE_INIT_ENROLL_COMPLETE = 'init-enroll-complete'
|
DEPLOY_STATE_INIT_ENROLL_COMPLETE = 'init-enroll-complete'
|
||||||
# If any of the following rehoming or secondary statuses
|
# If any of the following rehoming or secondary statuses
|
||||||
|
@@ -162,11 +162,15 @@ def validate_migrate_parameter(payload, request):
|
|||||||
'not allowed'))
|
'not allowed'))
|
||||||
|
|
||||||
|
|
||||||
def validate_enroll_parameter(payload, request):
|
def validate_enroll_parameter(payload):
|
||||||
enroll_str = payload.get('enroll')
|
install_values = payload.get('install_values')
|
||||||
if enroll_str and enroll_str not in ["true", "false"]:
|
if not 'install_values':
|
||||||
pecan.abort(400, _('The enroll option is invalid, '
|
pecan.abort(400, _("Install values is necessary for "
|
||||||
'valid options are true and false.'))
|
"subcloud enrollment"))
|
||||||
|
|
||||||
|
# Update the install values in payload
|
||||||
|
if not payload.get('bmc_password'):
|
||||||
|
payload.update({'bmc_password': install_values.get('bmc_password')})
|
||||||
|
|
||||||
|
|
||||||
def validate_secondary_parameter(payload, request):
|
def validate_secondary_parameter(payload, request):
|
||||||
|
@@ -36,6 +36,7 @@ from oslo_config import cfg
|
|||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
from oslo_serialization import base64
|
from oslo_serialization import base64
|
||||||
import pecan
|
import pecan
|
||||||
|
import requests
|
||||||
import six.moves
|
import six.moves
|
||||||
import tsconfig.tsconfig as tsc
|
import tsconfig.tsconfig as tsc
|
||||||
import yaml
|
import yaml
|
||||||
@@ -677,6 +678,9 @@ def is_subcloud_name_format_valid(name):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
# TODO(glyraper): Replace get_region_from_subcloud_address()
|
||||||
|
# with get_region_name once all the subclouds support
|
||||||
|
# '/v1/isystems/region_id' API
|
||||||
def get_region_from_subcloud_address(payload):
|
def get_region_from_subcloud_address(payload):
|
||||||
"""Retrieves the current region from the subcloud being migrated
|
"""Retrieves the current region from the subcloud being migrated
|
||||||
|
|
||||||
@@ -763,6 +767,24 @@ def get_region_from_subcloud_address(payload):
|
|||||||
return (subcloud_region, err_cause)
|
return (subcloud_region, err_cause)
|
||||||
|
|
||||||
|
|
||||||
|
def get_region_name(endpoint,
|
||||||
|
timeout=dccommon_consts.SYSINV_CLIENT_REST_DEFAULT_TIMEOUT):
|
||||||
|
url = endpoint + '/v1/isystems/region_id'
|
||||||
|
response = requests.get(url, timeout=timeout)
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
data = response.json()
|
||||||
|
if 'region_name' not in data:
|
||||||
|
raise exceptions.NotFound
|
||||||
|
|
||||||
|
region_name = data['region_name']
|
||||||
|
return region_name
|
||||||
|
else:
|
||||||
|
msg = f'GET region_name from {url} FAILED WITH RC {response.status_code}'
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exceptions.ServiceUnavailable
|
||||||
|
|
||||||
|
|
||||||
def find_ansible_error_msg(subcloud_name, log_file, stage=None):
|
def find_ansible_error_msg(subcloud_name, log_file, stage=None):
|
||||||
"""Find errors into ansible logs.
|
"""Find errors into ansible logs.
|
||||||
|
|
||||||
|
@@ -201,7 +201,7 @@ def subcloud_update(
|
|||||||
group_id=None, data_install=None, data_upgrade=None,
|
group_id=None, data_install=None, data_upgrade=None,
|
||||||
first_identity_sync_complete=None, systemcontroller_gateway_ip=None,
|
first_identity_sync_complete=None, systemcontroller_gateway_ip=None,
|
||||||
peer_group_id=None, rehome_data=None, rehomed=None,
|
peer_group_id=None, rehome_data=None, rehomed=None,
|
||||||
prestage_status=None, prestage_versions=None
|
prestage_status=None, prestage_versions=None, region_name=None
|
||||||
):
|
):
|
||||||
"""Update a subcloud or raise if it does not exist."""
|
"""Update a subcloud or raise if it does not exist."""
|
||||||
return IMPL.subcloud_update(
|
return IMPL.subcloud_update(
|
||||||
@@ -211,7 +211,7 @@ def subcloud_update(
|
|||||||
audit_fail_count, deploy_status, backup_status, backup_datetime,
|
audit_fail_count, deploy_status, backup_status, backup_datetime,
|
||||||
error_description, openstack_installed, group_id, data_install, data_upgrade,
|
error_description, openstack_installed, group_id, data_install, data_upgrade,
|
||||||
first_identity_sync_complete, systemcontroller_gateway_ip, peer_group_id,
|
first_identity_sync_complete, systemcontroller_gateway_ip, peer_group_id,
|
||||||
rehome_data, rehomed, prestage_status, prestage_versions
|
rehome_data, rehomed, prestage_status, prestage_versions, region_name
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@@ -443,7 +443,8 @@ def subcloud_update(context, subcloud_id, management_state=None,
|
|||||||
systemcontroller_gateway_ip=None,
|
systemcontroller_gateway_ip=None,
|
||||||
peer_group_id=None,
|
peer_group_id=None,
|
||||||
rehome_data=None, rehomed=None,
|
rehome_data=None, rehomed=None,
|
||||||
prestage_status=None, prestage_versions=None):
|
prestage_status=None, prestage_versions=None,
|
||||||
|
region_name=None):
|
||||||
with write_session() as session:
|
with write_session() as session:
|
||||||
subcloud_ref = subcloud_get(context, subcloud_id)
|
subcloud_ref = subcloud_get(context, subcloud_id)
|
||||||
if management_state is not None:
|
if management_state is not None:
|
||||||
@@ -502,6 +503,8 @@ def subcloud_update(context, subcloud_id, management_state=None,
|
|||||||
subcloud_ref.prestage_status = prestage_status
|
subcloud_ref.prestage_status = prestage_status
|
||||||
if prestage_versions is not None:
|
if prestage_versions is not None:
|
||||||
subcloud_ref.prestage_versions = prestage_versions
|
subcloud_ref.prestage_versions = prestage_versions
|
||||||
|
if region_name is not None:
|
||||||
|
subcloud_ref.region_name = region_name
|
||||||
subcloud_ref.save(session)
|
subcloud_ref.save(session)
|
||||||
return subcloud_ref
|
return subcloud_ref
|
||||||
|
|
||||||
|
@@ -284,6 +284,14 @@ class DCManagerService(service.Service):
|
|||||||
return self.subcloud_manager.subcloud_deploy_install(
|
return self.subcloud_manager.subcloud_deploy_install(
|
||||||
context, subcloud_id, payload, initial_deployment)
|
context, subcloud_id, payload, initial_deployment)
|
||||||
|
|
||||||
|
@run_in_thread
|
||||||
|
@request_context
|
||||||
|
def subcloud_deploy_enroll(self, context, subcloud_id, payload):
|
||||||
|
# Enroll a subcloud
|
||||||
|
LOG.info(f'Handling subcloud_deploy_enroll request for: {subcloud_id}')
|
||||||
|
return self.subcloud_manager.subcloud_deploy_enroll(
|
||||||
|
context, subcloud_id, payload)
|
||||||
|
|
||||||
@request_context
|
@request_context
|
||||||
def subcloud_deploy_complete(self, context, subcloud_id):
|
def subcloud_deploy_complete(self, context, subcloud_id):
|
||||||
# Complete the subcloud deployment
|
# Complete the subcloud deployment
|
||||||
|
@@ -48,6 +48,7 @@ from dccommon.drivers.openstack.sysinv_v1 import SysinvClient
|
|||||||
from dccommon.exceptions import PlaybookExecutionFailed
|
from dccommon.exceptions import PlaybookExecutionFailed
|
||||||
from dccommon.exceptions import SubcloudNotFound
|
from dccommon.exceptions import SubcloudNotFound
|
||||||
from dccommon import kubeoperator
|
from dccommon import kubeoperator
|
||||||
|
from dccommon.subcloud_enrollment import SubcloudEnrollmentInit
|
||||||
from dccommon.subcloud_install import SubcloudInstall
|
from dccommon.subcloud_install import SubcloudInstall
|
||||||
from dccommon.utils import AnsiblePlaybook
|
from dccommon.utils import AnsiblePlaybook
|
||||||
from dccommon.utils import LAST_SW_VERSION_IN_CENTOS
|
from dccommon.utils import LAST_SW_VERSION_IN_CENTOS
|
||||||
@@ -70,7 +71,6 @@ from dcmanager.manager.system_peer_manager import SystemPeerManager
|
|||||||
from dcmanager.rpc import client as dcmanager_rpc_client
|
from dcmanager.rpc import client as dcmanager_rpc_client
|
||||||
from dcorch.rpc import client as dcorch_rpc_client
|
from dcorch.rpc import client as dcorch_rpc_client
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
|
|
||||||
@@ -140,6 +140,10 @@ TRANSITORY_STATES = {
|
|||||||
# TODO(gherzman): remove states when they are no longer needed
|
# TODO(gherzman): remove states when they are no longer needed
|
||||||
consts.DEPLOY_STATE_PRE_DEPLOY: consts.DEPLOY_STATE_PRE_CONFIG_FAILED,
|
consts.DEPLOY_STATE_PRE_DEPLOY: consts.DEPLOY_STATE_PRE_CONFIG_FAILED,
|
||||||
consts.DEPLOY_STATE_DEPLOYING: consts.DEPLOY_STATE_CONFIG_FAILED,
|
consts.DEPLOY_STATE_DEPLOYING: consts.DEPLOY_STATE_CONFIG_FAILED,
|
||||||
|
consts.DEPLOY_STATE_PRE_ENROLL: consts.DEPLOY_STATE_PRE_ENROLL_FAILED,
|
||||||
|
consts.DEPLOY_STATE_ENROLLING: consts.DEPLOY_STATE_ENROLL_FAILED,
|
||||||
|
consts.DEPLOY_STATE_PRE_INIT_ENROLL: consts.DEPLOY_STATE_PRE_INIT_ENROLL_FAILED,
|
||||||
|
consts.DEPLOY_STATE_INITIATING_ENROLL: consts.DEPLOY_STATE_INIT_ENROLL_FAILED
|
||||||
}
|
}
|
||||||
|
|
||||||
TRANSITORY_BACKUP_STATES = {
|
TRANSITORY_BACKUP_STATES = {
|
||||||
@@ -330,6 +334,31 @@ class SubcloudManager(manager.Manager):
|
|||||||
dccommon_consts.RVMC_CONFIG_FILE_NAME)]
|
dccommon_consts.RVMC_CONFIG_FILE_NAME)]
|
||||||
return install_command
|
return install_command
|
||||||
|
|
||||||
|
# TODO(glyraper): software_version will be used in the future
|
||||||
|
def compose_enroll_command(self, subcloud_name,
|
||||||
|
subcloud_region,
|
||||||
|
ansible_subcloud_inventory_file,
|
||||||
|
software_version,
|
||||||
|
state):
|
||||||
|
|
||||||
|
if state == "init":
|
||||||
|
LOG.info(f'Initiating enroll for subcloud: {subcloud_name}')
|
||||||
|
return True
|
||||||
|
elif state == "enroll":
|
||||||
|
extra_vars = "override_files_dir='%s' region_name=%s" % (
|
||||||
|
dccommon_consts.ANSIBLE_OVERRIDES_PATH, subcloud_region)
|
||||||
|
|
||||||
|
enroll_command = [
|
||||||
|
"ansible-playbook",
|
||||||
|
dccommon_consts.ANSIBLE_SUBCLOUD_ENROLL_PLAYBOOK,
|
||||||
|
"-i", ansible_subcloud_inventory_file,
|
||||||
|
"--limit", subcloud_name,
|
||||||
|
"-e", extra_vars]
|
||||||
|
|
||||||
|
return enroll_command
|
||||||
|
else:
|
||||||
|
raise exceptions.InvalidInputError
|
||||||
|
|
||||||
def compose_bootstrap_command(self, subcloud_name,
|
def compose_bootstrap_command(self, subcloud_name,
|
||||||
subcloud_region,
|
subcloud_region,
|
||||||
ansible_subcloud_inventory_file,
|
ansible_subcloud_inventory_file,
|
||||||
@@ -846,13 +875,14 @@ class SubcloudManager(manager.Manager):
|
|||||||
rehoming = payload.get('migrate', '').lower() == "true"
|
rehoming = payload.get('migrate', '').lower() == "true"
|
||||||
secondary = (payload.get('secondary', '').lower() == "true")
|
secondary = (payload.get('secondary', '').lower() == "true")
|
||||||
enroll = payload.get('enroll', '').lower() == "true"
|
enroll = payload.get('enroll', '').lower() == "true"
|
||||||
initial_deployment = True if not rehoming else False
|
initial_deployment = True if not (rehoming or enroll) else False
|
||||||
|
|
||||||
# Create the subcloud
|
# Create the subcloud
|
||||||
subcloud = self.subcloud_deploy_create(context, subcloud_id,
|
subcloud = self.subcloud_deploy_create(context, subcloud_id,
|
||||||
payload, rehoming,
|
payload, rehoming,
|
||||||
initial_deployment,
|
initial_deployment,
|
||||||
return_as_dict=False)
|
return_as_dict=False,
|
||||||
|
enroll=enroll)
|
||||||
|
|
||||||
# return if 'secondary' subcloud
|
# return if 'secondary' subcloud
|
||||||
if secondary:
|
if secondary:
|
||||||
@@ -873,9 +903,9 @@ class SubcloudManager(manager.Manager):
|
|||||||
|
|
||||||
# Define which deploy phases should be run
|
# Define which deploy phases should be run
|
||||||
phases_to_run = []
|
phases_to_run = []
|
||||||
if consts.INSTALL_VALUES in payload:
|
if consts.INSTALL_VALUES in payload and not enroll:
|
||||||
phases_to_run.append(consts.DEPLOY_PHASE_INSTALL)
|
phases_to_run.append(consts.DEPLOY_PHASE_INSTALL)
|
||||||
if enroll and consts.INSTALL_VALUES not in payload:
|
if enroll and consts.INSTALL_VALUES in payload:
|
||||||
phases_to_run.append(consts.DEPLOY_PHASE_ENROLL)
|
phases_to_run.append(consts.DEPLOY_PHASE_ENROLL)
|
||||||
else:
|
else:
|
||||||
phases_to_run.append(consts.DEPLOY_PHASE_BOOTSTRAP)
|
phases_to_run.append(consts.DEPLOY_PHASE_BOOTSTRAP)
|
||||||
@@ -930,7 +960,7 @@ class SubcloudManager(manager.Manager):
|
|||||||
group_id = payload.get('group')
|
group_id = payload.get('group')
|
||||||
|
|
||||||
# Retrieve either a single subcloud or all subclouds in a group
|
# Retrieve either a single subcloud or all subclouds in a group
|
||||||
subclouds = [db_api.subcloud_get(context, subcloud_id)] if subcloud_id\
|
subclouds = [db_api.subcloud_get(context, subcloud_id)] if subcloud_id \
|
||||||
else db_api.subcloud_get_for_group(context, group_id)
|
else db_api.subcloud_get_for_group(context, group_id)
|
||||||
|
|
||||||
self._filter_subclouds_with_ongoing_backup(subclouds)
|
self._filter_subclouds_with_ongoing_backup(subclouds)
|
||||||
@@ -1147,14 +1177,16 @@ class SubcloudManager(manager.Manager):
|
|||||||
|
|
||||||
def _deploy_install_prep(self, subcloud, payload: dict,
|
def _deploy_install_prep(self, subcloud, payload: dict,
|
||||||
ansible_subcloud_inventory_file,
|
ansible_subcloud_inventory_file,
|
||||||
initial_deployment=False):
|
initial_deployment=False, init_enroll=False):
|
||||||
"""Run the preparation steps needed to run the install operation
|
"""Run preparation steps for install or init enroll operations
|
||||||
|
|
||||||
:param subcloud: target subcloud model object
|
:param subcloud: target subcloud model object
|
||||||
:param payload: install request parameters
|
:param payload: install request parameters
|
||||||
:param ansible_subcloud_inventory_file: the ansible inventory file path
|
:param ansible_subcloud_inventory_file: the ansible inventory file path
|
||||||
:param initial_deployment: initial_deployment flag from subcloud inventory
|
:param initial_deployment: initial_deployment flag from subcloud inventory
|
||||||
|
:param init_enroll: which operation should be run, install or init-enroll
|
||||||
:return: ansible command needed to run the install playbook
|
:return: ansible command needed to run the install playbook
|
||||||
|
|
||||||
"""
|
"""
|
||||||
payload['install_values']['ansible_ssh_pass'] = \
|
payload['install_values']['ansible_ssh_pass'] = \
|
||||||
payload['sysadmin_password']
|
payload['sysadmin_password']
|
||||||
@@ -1181,12 +1213,20 @@ class SubcloudManager(manager.Manager):
|
|||||||
utils.create_subcloud_inventory(subcloud_params,
|
utils.create_subcloud_inventory(subcloud_params,
|
||||||
ansible_subcloud_inventory_file,
|
ansible_subcloud_inventory_file,
|
||||||
initial_deployment)
|
initial_deployment)
|
||||||
|
if init_enroll:
|
||||||
|
init_enroll_command = self.compose_enroll_command(
|
||||||
|
subcloud.name,
|
||||||
|
subcloud.region_name,
|
||||||
|
ansible_subcloud_inventory_file,
|
||||||
|
subcloud.software_version,
|
||||||
|
state="init"
|
||||||
|
)
|
||||||
|
return init_enroll_command
|
||||||
|
|
||||||
install_command = self.compose_install_command(
|
install_command = self.compose_install_command(
|
||||||
subcloud.name,
|
subcloud.name,
|
||||||
ansible_subcloud_inventory_file,
|
ansible_subcloud_inventory_file,
|
||||||
payload['software_version'])
|
payload['software_version'])
|
||||||
|
|
||||||
return install_command
|
return install_command
|
||||||
|
|
||||||
def subcloud_deploy_abort(self, context, subcloud_id, deploy_status):
|
def subcloud_deploy_abort(self, context, subcloud_id, deploy_status):
|
||||||
@@ -1318,7 +1358,7 @@ class SubcloudManager(manager.Manager):
|
|||||||
|
|
||||||
def subcloud_deploy_create(self, context, subcloud_id, payload,
|
def subcloud_deploy_create(self, context, subcloud_id, payload,
|
||||||
rehoming=False, initial_deployment=True,
|
rehoming=False, initial_deployment=True,
|
||||||
return_as_dict=True):
|
return_as_dict=True, enroll=False):
|
||||||
"""Create subcloud and notify orchestrators.
|
"""Create subcloud and notify orchestrators.
|
||||||
|
|
||||||
:param context: request context object
|
:param context: request context object
|
||||||
@@ -1327,6 +1367,7 @@ class SubcloudManager(manager.Manager):
|
|||||||
:param rehoming: flag indicating if this is part of a rehoming operation
|
:param rehoming: flag indicating if this is part of a rehoming operation
|
||||||
:param initial_deployment: initial_deployment flag from subcloud inventory
|
:param initial_deployment: initial_deployment flag from subcloud inventory
|
||||||
:param return_as_dict: converts the subcloud DB object to a dict before
|
:param return_as_dict: converts the subcloud DB object to a dict before
|
||||||
|
:param enroll: define steps to run when running enroll operation
|
||||||
returning
|
returning
|
||||||
:return: resulting subcloud DB object or dictionary
|
:return: resulting subcloud DB object or dictionary
|
||||||
"""
|
"""
|
||||||
@@ -1374,57 +1415,11 @@ class SubcloudManager(manager.Manager):
|
|||||||
payload['systemcontroller_gateway_address'],
|
payload['systemcontroller_gateway_address'],
|
||||||
1)
|
1)
|
||||||
|
|
||||||
# Create endpoints to this subcloud on the
|
if not enroll:
|
||||||
# management-start-ip of the subcloud which will be allocated
|
self._create_subcloud_endpoints(m_ks_client=m_ks_client,
|
||||||
# as the floating Management IP of the Subcloud if the
|
payload=payload,
|
||||||
# Address Pool is not shared. Incase the endpoint entries
|
subcloud=subcloud,
|
||||||
# are incorrect, or the management IP of the subcloud is changed
|
context=context)
|
||||||
# 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:
|
|
||||||
admin_endpoint_url = ENDPOINT_URLS.get(service.type, None)
|
|
||||||
if admin_endpoint_url:
|
|
||||||
admin_endpoint_url = admin_endpoint_url.format(endpoint_ip)
|
|
||||||
endpoint_config.append(
|
|
||||||
{"id": service.id,
|
|
||||||
"admin_endpoint_url": admin_endpoint_url})
|
|
||||||
|
|
||||||
if len(endpoint_config) < len(ENDPOINT_URLS):
|
|
||||||
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.region_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.region_name)
|
|
||||||
|
|
||||||
# Inform orchestrator that subcloud has been added
|
|
||||||
self.dcorch_rpc_client.add_subcloud(
|
|
||||||
context,
|
|
||||||
subcloud.region_name,
|
|
||||||
subcloud.software_version,
|
|
||||||
subcloud.management_start_ip
|
|
||||||
)
|
|
||||||
|
|
||||||
# create entry into alarm summary table, will get real values later
|
# create entry into alarm summary table, will get real values later
|
||||||
alarm_updates = {'critical_alarms': -1,
|
alarm_updates = {'critical_alarms': -1,
|
||||||
@@ -1468,6 +1463,7 @@ class SubcloudManager(manager.Manager):
|
|||||||
ansible_subcloud_inventory_file,
|
ansible_subcloud_inventory_file,
|
||||||
initial_deployment)
|
initial_deployment)
|
||||||
|
|
||||||
|
if not enroll:
|
||||||
# create subcloud intermediate certificate and pass in keys
|
# create subcloud intermediate certificate and pass in keys
|
||||||
self._create_intermediate_ca_cert(payload)
|
self._create_intermediate_ca_cert(payload)
|
||||||
|
|
||||||
@@ -1575,10 +1571,72 @@ class SubcloudManager(manager.Manager):
|
|||||||
deploy_status=consts.DEPLOY_STATE_PRE_INSTALL_FAILED)
|
deploy_status=consts.DEPLOY_STATE_PRE_INSTALL_FAILED)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def subcloud_deploy_enroll(self, context, subcloud_id, payload,
|
def subcloud_deploy_enroll(self, context, subcloud_id, payload: dict):
|
||||||
initial_deployment=False):
|
|
||||||
|
|
||||||
raise NotImplementedError
|
db_api.subcloud_update(
|
||||||
|
context,
|
||||||
|
subcloud_id,
|
||||||
|
deploy_status=consts.DEPLOY_STATE_PRE_INIT_ENROLL
|
||||||
|
)
|
||||||
|
|
||||||
|
subcloud = db_api.subcloud_get(context, subcloud_id)
|
||||||
|
enrollment = SubcloudEnrollmentInit(subcloud.name)
|
||||||
|
enrollment.prep(dccommon_consts.ANSIBLE_OVERRIDES_PATH, payload)
|
||||||
|
|
||||||
|
if self.subcloud_init_enroll(context, subcloud.id, payload):
|
||||||
|
try:
|
||||||
|
|
||||||
|
db_api.subcloud_update(
|
||||||
|
context,
|
||||||
|
subcloud_id,
|
||||||
|
deploy_status=consts.DEPLOY_STATE_PRE_ENROLL
|
||||||
|
)
|
||||||
|
|
||||||
|
endpoint = ("https://" +
|
||||||
|
payload.get("external_oam_floating_address") + ":6385")
|
||||||
|
subcloud_region_name = utils.get_region_name(endpoint)
|
||||||
|
subcloud.region_name = subcloud_region_name
|
||||||
|
|
||||||
|
m_ks_client = OpenStackDriver(
|
||||||
|
region_name=dccommon_consts.DEFAULT_REGION_NAME,
|
||||||
|
region_clients=None).keystone_client
|
||||||
|
|
||||||
|
self._create_subcloud_endpoints(m_ks_client=m_ks_client,
|
||||||
|
payload=payload,
|
||||||
|
subcloud=subcloud,
|
||||||
|
context=context)
|
||||||
|
self._create_intermediate_ca_cert(payload=payload)
|
||||||
|
|
||||||
|
log_file = (
|
||||||
|
os.path.join(consts.DC_ANSIBLE_LOG_DIR, subcloud.name)
|
||||||
|
+ "_playbook_output.log"
|
||||||
|
)
|
||||||
|
ansible_subcloud_inventory_file = self._get_ansible_filename(
|
||||||
|
subcloud.name, INVENTORY_FILE_POSTFIX)
|
||||||
|
|
||||||
|
enroll_playbook_command = self.compose_enroll_command(
|
||||||
|
subcloud.name,
|
||||||
|
subcloud.region_name,
|
||||||
|
ansible_subcloud_inventory_file,
|
||||||
|
subcloud.software_version,
|
||||||
|
state="enroll"
|
||||||
|
)
|
||||||
|
self._run_subcloud_enroll(context,
|
||||||
|
subcloud,
|
||||||
|
enroll_playbook_command,
|
||||||
|
log_file,
|
||||||
|
region_name=subcloud_region_name)
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
LOG.exception(f'Failed to enroll subcloud {subcloud.name}')
|
||||||
|
db_api.subcloud_update(
|
||||||
|
context, subcloud_id,
|
||||||
|
deploy_status=consts.DEPLOY_STATE_PRE_ENROLL_FAILED)
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
LOG.error(f'Initial enrollment failed for subcloud {subcloud.name}')
|
||||||
|
|
||||||
|
return subcloud
|
||||||
|
|
||||||
def subcloud_deploy_bootstrap(self, context, subcloud_id, payload,
|
def subcloud_deploy_bootstrap(self, context, subcloud_id, payload,
|
||||||
initial_deployment=False):
|
initial_deployment=False):
|
||||||
@@ -1729,6 +1787,56 @@ class SubcloudManager(manager.Manager):
|
|||||||
|
|
||||||
return valid_subclouds, invalid_subclouds
|
return valid_subclouds, invalid_subclouds
|
||||||
|
|
||||||
|
def subcloud_init_enroll(self, context, subcloud_id, payload: dict) -> bool:
|
||||||
|
"""Init subcloud enroll
|
||||||
|
|
||||||
|
:param context: request context object
|
||||||
|
:param subcloud_id: subcloud id from db
|
||||||
|
:param payload: subcloud Install
|
||||||
|
:param initial_deployment: initial_deployment flag from subcloud inventory
|
||||||
|
:return: success status
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Retrieve the subcloud details from the database
|
||||||
|
subcloud = db_api.subcloud_update(
|
||||||
|
context,
|
||||||
|
subcloud_id,
|
||||||
|
deploy_status=consts.DEPLOY_STATE_INITIATING_ENROLL,
|
||||||
|
data_install=json.dumps(payload['install_values']))
|
||||||
|
|
||||||
|
LOG.info("Initiating subcloud %s enrollment." % subcloud.name)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# TODO(glyraper): log_file to be used in the playbook execution
|
||||||
|
# log_file = (
|
||||||
|
# os.path.join(consts.DC_ANSIBLE_LOG_DIR, subcloud.name)
|
||||||
|
# + "_playbook_output.log"
|
||||||
|
# )
|
||||||
|
ansible_subcloud_inventory_file = self._get_ansible_filename(
|
||||||
|
subcloud.name, INVENTORY_FILE_POSTFIX)
|
||||||
|
init_enroll_command = self._deploy_install_prep(
|
||||||
|
subcloud, payload, ansible_subcloud_inventory_file,
|
||||||
|
init_enroll=True)
|
||||||
|
if init_enroll_command:
|
||||||
|
LOG.info('Subcloud enrollment initial phase successful '
|
||||||
|
f'for subcloud {subcloud.name}')
|
||||||
|
|
||||||
|
db_api.subcloud_update(
|
||||||
|
context,
|
||||||
|
subcloud_id,
|
||||||
|
deploy_status=consts.DEPLOY_STATE_INIT_ENROLL_COMPLETE,
|
||||||
|
error_description=consts.ERROR_DESC_EMPTY)
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
LOG.exception("Failed to enroll subcloud %s" % subcloud.name)
|
||||||
|
# If we failed to initiate the subcloud enroll,
|
||||||
|
# update the deployment status
|
||||||
|
db_api.subcloud_update(
|
||||||
|
context, subcloud_id,
|
||||||
|
deploy_status=consts.DEPLOY_STATE_PRE_INIT_ENROLL_FAILED)
|
||||||
|
return False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _mark_invalid_subclouds_for_backup(context, invalid_subclouds):
|
def _mark_invalid_subclouds_for_backup(context, invalid_subclouds):
|
||||||
try:
|
try:
|
||||||
@@ -1765,7 +1873,7 @@ class SubcloudManager(manager.Manager):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def _update_backup_status(context, subclouds, backup_status):
|
def _update_backup_status(context, subclouds, backup_status):
|
||||||
subcloud_ids = [subcloud.id for subcloud in subclouds]
|
subcloud_ids = [subcloud.id for subcloud in subclouds]
|
||||||
return SubcloudManager.\
|
return SubcloudManager. \
|
||||||
_update_backup_status_by_ids(context, subcloud_ids,
|
_update_backup_status_by_ids(context, subcloud_ids,
|
||||||
backup_status)
|
backup_status)
|
||||||
|
|
||||||
@@ -1963,7 +2071,8 @@ class SubcloudManager(manager.Manager):
|
|||||||
failed_subcloud_names = [subcloud.name for subcloud in failed_subclouds]
|
failed_subcloud_names = [subcloud.name for subcloud in failed_subclouds]
|
||||||
|
|
||||||
notice = (
|
notice = (
|
||||||
"Subcloud backup %s operation completed with warnings:\n" % operation)
|
"Subcloud backup %s operation completed with warnings:\n"
|
||||||
|
% operation)
|
||||||
if invalid_subclouds:
|
if invalid_subclouds:
|
||||||
notice += ("The following subclouds were skipped for local backup "
|
notice += ("The following subclouds were skipped for local backup "
|
||||||
"%s operation: %s."
|
"%s operation: %s."
|
||||||
@@ -1974,6 +2083,61 @@ class SubcloudManager(manager.Manager):
|
|||||||
% (operation, ' ,'.join(failed_subcloud_names)))
|
% (operation, ' ,'.join(failed_subcloud_names)))
|
||||||
return notice
|
return notice
|
||||||
|
|
||||||
|
def _create_subcloud_endpoints(self, m_ks_client, payload, subcloud,
|
||||||
|
context):
|
||||||
|
|
||||||
|
# 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:
|
||||||
|
admin_endpoint_url = ENDPOINT_URLS.get(service.type, None)
|
||||||
|
if admin_endpoint_url:
|
||||||
|
admin_endpoint_url = admin_endpoint_url.format(endpoint_ip)
|
||||||
|
endpoint_config.append(
|
||||||
|
{"id": service.id,
|
||||||
|
"admin_endpoint_url": admin_endpoint_url})
|
||||||
|
|
||||||
|
if len(endpoint_config) < len(ENDPOINT_URLS):
|
||||||
|
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.region_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.region_name)
|
||||||
|
|
||||||
|
# Inform orchestrator that subcloud has been added
|
||||||
|
self.dcorch_rpc_client.add_subcloud(
|
||||||
|
context,
|
||||||
|
subcloud.region_name,
|
||||||
|
subcloud.software_version,
|
||||||
|
subcloud.management_start_ip
|
||||||
|
)
|
||||||
|
|
||||||
def _create_subcloud_inventory_file(self, subcloud, bootstrap_address=None,
|
def _create_subcloud_inventory_file(self, subcloud, bootstrap_address=None,
|
||||||
initial_deployment=False):
|
initial_deployment=False):
|
||||||
# Ansible inventory filename for the specified subcloud
|
# Ansible inventory filename for the specified subcloud
|
||||||
@@ -1985,7 +2149,7 @@ class SubcloudManager(manager.Manager):
|
|||||||
keystone_client = OpenStackDriver(
|
keystone_client = OpenStackDriver(
|
||||||
region_name=subcloud.region_name,
|
region_name=subcloud.region_name,
|
||||||
region_clients=None).keystone_client
|
region_clients=None).keystone_client
|
||||||
bootstrap_address = utils.get_oam_addresses(subcloud, keystone_client)\
|
bootstrap_address = utils.get_oam_addresses(subcloud, keystone_client) \
|
||||||
.oam_floating_ip
|
.oam_floating_ip
|
||||||
|
|
||||||
# Add parameters used to generate inventory
|
# Add parameters used to generate inventory
|
||||||
@@ -2242,7 +2406,8 @@ class SubcloudManager(manager.Manager):
|
|||||||
context, subcloud_id, payload, initial_deployment)
|
context, subcloud_id, payload, initial_deployment)
|
||||||
if succeeded and consts.DEPLOY_PHASE_ENROLL in deploy_phases_to_run:
|
if succeeded and consts.DEPLOY_PHASE_ENROLL in deploy_phases_to_run:
|
||||||
succeeded = self.subcloud_deploy_enroll(
|
succeeded = self.subcloud_deploy_enroll(
|
||||||
context, subcloud_id, payload, initial_deployment)
|
context, subcloud_id, payload)
|
||||||
|
raise NotImplementedError
|
||||||
if succeeded and consts.DEPLOY_PHASE_BOOTSTRAP in deploy_phases_to_run:
|
if succeeded and consts.DEPLOY_PHASE_BOOTSTRAP in deploy_phases_to_run:
|
||||||
succeeded = self.subcloud_deploy_bootstrap(
|
succeeded = self.subcloud_deploy_bootstrap(
|
||||||
context, subcloud_id, payload, initial_deployment)
|
context, subcloud_id, payload, initial_deployment)
|
||||||
@@ -2338,6 +2503,41 @@ class SubcloudManager(manager.Manager):
|
|||||||
LOG.info("Successfully installed %s" % subcloud.name)
|
LOG.info("Successfully installed %s" % subcloud.name)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def _run_subcloud_enroll(self, context, subcloud, enroll_command,
|
||||||
|
log_file, region_name):
|
||||||
|
|
||||||
|
# Update the subcloud deploy_status to enrolling
|
||||||
|
db_api.subcloud_update(
|
||||||
|
context, subcloud.id,
|
||||||
|
deploy_status=consts.DEPLOY_STATE_ENROLLING,
|
||||||
|
error_description=consts.ERROR_DESC_EMPTY)
|
||||||
|
|
||||||
|
LOG.info(f'Starting enroll of subcloud {subcloud.name}')
|
||||||
|
try:
|
||||||
|
ansible = AnsiblePlaybook(subcloud.name)
|
||||||
|
ansible.run_playbook(log_file, enroll_command)
|
||||||
|
except PlaybookExecutionFailed:
|
||||||
|
msg = utils.find_ansible_error_msg(
|
||||||
|
subcloud.name, log_file, consts.DEPLOY_STATE_ENROLLING)
|
||||||
|
LOG.error(f'Enroll failed for subcloud {subcloud.name}: {msg}')
|
||||||
|
db_api.subcloud_update(
|
||||||
|
context, subcloud.id,
|
||||||
|
deploy_status=consts.DEPLOY_STATE_ENROLL_FAILED,
|
||||||
|
error_description=msg[0:consts.ERROR_DESCRIPTION_LENGTH])
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Ensure rehomed=False after bootstrapped from central cloud, it
|
||||||
|
# applies on both initial deployment and re-deployment.
|
||||||
|
db_api.subcloud_update(
|
||||||
|
context, subcloud.id,
|
||||||
|
deploy_status=consts.DEPLOY_STATE_ENROLLED,
|
||||||
|
error_description=consts.ERROR_DESC_EMPTY,
|
||||||
|
region_name=region_name,
|
||||||
|
rehomed=False)
|
||||||
|
|
||||||
|
LOG.info(f'Successfully enrolled {subcloud.name}')
|
||||||
|
return True
|
||||||
|
|
||||||
def _run_subcloud_bootstrap(self, context, subcloud,
|
def _run_subcloud_bootstrap(self, context, subcloud,
|
||||||
bootstrap_command, log_file):
|
bootstrap_command, log_file):
|
||||||
# Update the subcloud deploy_status to bootstrapping
|
# Update the subcloud deploy_status to bootstrapping
|
||||||
@@ -2432,8 +2632,9 @@ class SubcloudManager(manager.Manager):
|
|||||||
"""Create the deploy value files for the subcloud"""
|
"""Create the deploy value files for the subcloud"""
|
||||||
|
|
||||||
deploy_values_file = os.path.join(
|
deploy_values_file = os.path.join(
|
||||||
dccommon_consts.ANSIBLE_OVERRIDES_PATH, subcloud_name +
|
dccommon_consts.ANSIBLE_OVERRIDES_PATH,
|
||||||
'_deploy_values.yml')
|
subcloud_name + '_deploy_values.yml'
|
||||||
|
)
|
||||||
|
|
||||||
with open(deploy_values_file, 'w') as f_out_deploy_values_file:
|
with open(deploy_values_file, 'w') as f_out_deploy_values_file:
|
||||||
json.dump(payload['deploy_values'], f_out_deploy_values_file)
|
json.dump(payload['deploy_values'], f_out_deploy_values_file)
|
||||||
@@ -2613,7 +2814,6 @@ class SubcloudManager(manager.Manager):
|
|||||||
|
|
||||||
if mkey in data and 'hosts' in data[mkey] and \
|
if mkey in data and 'hosts' in data[mkey] and \
|
||||||
cur_sc_name in data[mkey]['hosts']:
|
cur_sc_name in data[mkey]['hosts']:
|
||||||
|
|
||||||
data[mkey]['hosts'][new_sc_name] = \
|
data[mkey]['hosts'][new_sc_name] = \
|
||||||
data[mkey]['hosts'].pop(cur_sc_name)
|
data[mkey]['hosts'].pop(cur_sc_name)
|
||||||
|
|
||||||
@@ -2706,7 +2906,7 @@ class SubcloudManager(manager.Manager):
|
|||||||
:param new_subcloud_name: new subcloud name
|
:param new_subcloud_name: new subcloud name
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
subcloud = db_api.\
|
subcloud = db_api. \
|
||||||
subcloud_get_by_name_or_region_name(context,
|
subcloud_get_by_name_or_region_name(context,
|
||||||
new_subcloud_name)
|
new_subcloud_name)
|
||||||
except exceptions.SubcloudNameOrRegionNameNotFound:
|
except exceptions.SubcloudNameOrRegionNameNotFound:
|
||||||
@@ -3232,8 +3432,9 @@ class SubcloudManager(manager.Manager):
|
|||||||
def _create_subcloud_update_overrides_file(
|
def _create_subcloud_update_overrides_file(
|
||||||
self, payload, subcloud_name, filename_suffix):
|
self, payload, subcloud_name, filename_suffix):
|
||||||
update_overrides_file = os.path.join(
|
update_overrides_file = os.path.join(
|
||||||
dccommon_consts.ANSIBLE_OVERRIDES_PATH, subcloud_name + '_' +
|
dccommon_consts.ANSIBLE_OVERRIDES_PATH,
|
||||||
filename_suffix + '.yml')
|
subcloud_name + '_' + filename_suffix + '.yml'
|
||||||
|
)
|
||||||
|
|
||||||
self._update_override_values(payload)
|
self._update_override_values(payload)
|
||||||
|
|
||||||
@@ -3364,8 +3565,9 @@ class SubcloudManager(manager.Manager):
|
|||||||
@utils.synchronized("regionone-data-cache", external=False)
|
@utils.synchronized("regionone-data-cache", external=False)
|
||||||
def _get_cached_regionone_data(
|
def _get_cached_regionone_data(
|
||||||
self, regionone_keystone_client, regionone_sysinv_client=None):
|
self, regionone_keystone_client, regionone_sysinv_client=None):
|
||||||
if (not SubcloudManager.regionone_data or SubcloudManager.regionone_data[
|
if (not SubcloudManager.regionone_data or
|
||||||
'expiry'] <= datetime.datetime.utcnow()):
|
SubcloudManager.regionone_data['expiry'] <=
|
||||||
|
datetime.datetime.utcnow()):
|
||||||
user_list = regionone_keystone_client.get_enabled_users(id_only=False)
|
user_list = regionone_keystone_client.get_enabled_users(id_only=False)
|
||||||
for user in user_list:
|
for user in user_list:
|
||||||
if user.name == dccommon_consts.ADMIN_USER_NAME:
|
if user.name == dccommon_consts.ADMIN_USER_NAME:
|
||||||
|
@@ -221,6 +221,11 @@ class ManagerClient(RPCClient):
|
|||||||
payload=payload,
|
payload=payload,
|
||||||
initial_deployment=initial_deployment))
|
initial_deployment=initial_deployment))
|
||||||
|
|
||||||
|
def subcloud_deploy_enroll(self, ctxt, subcloud_id, payload):
|
||||||
|
return self.cast(ctxt, self.make_msg('subcloud_deploy_enroll',
|
||||||
|
subcloud_id=subcloud_id,
|
||||||
|
payload=payload))
|
||||||
|
|
||||||
def subcloud_deploy_bootstrap(self, ctxt, subcloud_id, payload,
|
def subcloud_deploy_bootstrap(self, ctxt, subcloud_id, payload,
|
||||||
initial_deployment):
|
initial_deployment):
|
||||||
return self.cast(ctxt, self.make_msg('subcloud_deploy_bootstrap',
|
return self.cast(ctxt, self.make_msg('subcloud_deploy_bootstrap',
|
||||||
|
@@ -1107,11 +1107,17 @@ class TestPhasedSubcloudDeployPatchEnroll(BaseTestPhasedSubcloudDeployPatch):
|
|||||||
deploy_status=consts.DEPLOY_STATE_CREATED, software_version=SW_VERSION
|
deploy_status=consts.DEPLOY_STATE_CREATED, software_version=SW_VERSION
|
||||||
)
|
)
|
||||||
|
|
||||||
modified_bootstrap_data = copy.copy(fake_subcloud.FAKE_BOOTSTRAP_FILE_DATA)
|
modified_bootstrap_data = copy.copy(
|
||||||
|
fake_subcloud.FAKE_SUBCLOUD_BOOTSTRAP_PAYLOAD)
|
||||||
|
modified_bootstrap_data.update({"name": "fake subcloud1"})
|
||||||
|
modified_install_data = copy.copy(fake_subcloud.FAKE_SUBCLOUD_INSTALL_VALUES)
|
||||||
fake_content = json.dumps(modified_bootstrap_data).encode("utf-8")
|
fake_content = json.dumps(modified_bootstrap_data).encode("utf-8")
|
||||||
|
install_fake_content = json.dumps(modified_install_data).encode("utf-8")
|
||||||
|
|
||||||
self.upload_files = \
|
self.upload_files = [(
|
||||||
[("bootstrap_values", "bootstrap_fake_filename", fake_content)]
|
"bootstrap_values", "bootstrap_fake_filename", fake_content),
|
||||||
|
("install_values", "install_values_fake_filename", install_fake_content)
|
||||||
|
]
|
||||||
|
|
||||||
def test_patch_enroll_fails(self):
|
def test_patch_enroll_fails(self):
|
||||||
"""Test patch enroll fails"""
|
"""Test patch enroll fails"""
|
||||||
|
@@ -384,8 +384,11 @@ class BaseTestSubcloudManager(base.DCManagerTestCase):
|
|||||||
self.peer_group = self.create_subcloud_peer_group_static(self.ctx)
|
self.peer_group = self.create_subcloud_peer_group_static(self.ctx)
|
||||||
|
|
||||||
self.mock_keyring.get_password.return_value = "testpassword"
|
self.mock_keyring.get_password.return_value = "testpassword"
|
||||||
self.fake_install_values = \
|
self.fake_install_values = copy.copy(
|
||||||
copy.copy(fake_subcloud.FAKE_SUBCLOUD_INSTALL_VALUES)
|
fake_subcloud.FAKE_SUBCLOUD_INSTALL_VALUES)
|
||||||
|
self.fake_bootstrap_values = copy.copy(
|
||||||
|
fake_subcloud.FAKE_BOOTSTRAP_FILE_DATA)
|
||||||
|
|
||||||
self.fake_payload = {"sysadmin_password": "testpass",
|
self.fake_payload = {"sysadmin_password": "testpass",
|
||||||
"deploy_playbook": "test_playbook.yaml",
|
"deploy_playbook": "test_playbook.yaml",
|
||||||
"deploy_overrides": "test_overrides.yaml",
|
"deploy_overrides": "test_overrides.yaml",
|
||||||
@@ -395,10 +398,29 @@ class BaseTestSubcloudManager(base.DCManagerTestCase):
|
|||||||
'install_values': self.fake_install_values,
|
'install_values': self.fake_install_values,
|
||||||
'software_version': SW_VERSION,
|
'software_version': SW_VERSION,
|
||||||
'sysadmin_password': 'sys_pass'}
|
'sysadmin_password': 'sys_pass'}
|
||||||
|
self.fake_payload_enroll = {'bmc_password': 'bmc_pass',
|
||||||
|
'install_values': self.fake_install_values,
|
||||||
|
'software_version': SW_VERSION,
|
||||||
|
'sysadmin_password': 'sys_pass',
|
||||||
|
'admin_password': 'sys_pass'
|
||||||
|
}
|
||||||
|
self.fake_payload_enroll = dict(self.fake_payload_enroll,
|
||||||
|
**self.fake_bootstrap_values,
|
||||||
|
**self.fake_install_values)
|
||||||
|
|
||||||
|
rel_version = self.fake_payload_enroll.get('software_version')
|
||||||
|
|
||||||
|
self.iso_dir = (f'/opt/platform/iso/{rel_version}/'
|
||||||
|
f'nodes/{self.subcloud.name}')
|
||||||
|
self.iso_file = f'{self.iso_dir}/seed.iso'
|
||||||
|
|
||||||
# Reset the regionone_data cache between tests
|
# Reset the regionone_data cache between tests
|
||||||
subcloud_manager.SubcloudManager.regionone_data = \
|
subcloud_manager.SubcloudManager.regionone_data = \
|
||||||
collections.defaultdict(dict)
|
collections.defaultdict(dict)
|
||||||
|
|
||||||
|
def patched_isdir(self, path):
|
||||||
|
return path != self.iso_dir
|
||||||
|
|
||||||
def _mock_dcorch_api(self):
|
def _mock_dcorch_api(self):
|
||||||
"""Mock the DCOrch API"""
|
"""Mock the DCOrch API"""
|
||||||
|
|
||||||
@@ -481,6 +503,13 @@ class BaseTestSubcloudManager(base.DCManagerTestCase):
|
|||||||
mock_patch = mock.patch.object(ostree_mount,
|
mock_patch = mock.patch.object(ostree_mount,
|
||||||
'validate_ostree_iso_mount')
|
'validate_ostree_iso_mount')
|
||||||
self.mock_validate_ostree_iso_mount = mock_patch.start()
|
self.mock_validate_ostree_iso_mount = mock_patch.start()
|
||||||
|
|
||||||
|
def _mock_subcloud_manager_run_subcloud_enroll(self):
|
||||||
|
"""Mock subcloud manager's _run_subcloud_enroll"""
|
||||||
|
|
||||||
|
mock_patch = mock.patch.object(subcloud_manager.SubcloudManager,
|
||||||
|
'_run_subcloud_enroll')
|
||||||
|
self.mock_run_subcloud_enroll = mock_patch.start()
|
||||||
self.addCleanup(mock_patch.stop)
|
self.addCleanup(mock_patch.stop)
|
||||||
|
|
||||||
def _mock_subcloud_manager_create_intermediate_ca_cert(self):
|
def _mock_subcloud_manager_create_intermediate_ca_cert(self):
|
||||||
@@ -499,6 +528,14 @@ class BaseTestSubcloudManager(base.DCManagerTestCase):
|
|||||||
self.mock_compose_install_command = mock_patch.start()
|
self.mock_compose_install_command = mock_patch.start()
|
||||||
self.addCleanup(mock_patch.stop)
|
self.addCleanup(mock_patch.stop)
|
||||||
|
|
||||||
|
def _mock_subcloud_manager_compose_enroll_command(self):
|
||||||
|
"""Mock subcloud manager compose_enroll_command"""
|
||||||
|
|
||||||
|
mock_patch = mock.patch.object(subcloud_manager.SubcloudManager,
|
||||||
|
'compose_enroll_command')
|
||||||
|
self.mock_compose_enroll_command = mock_patch.start()
|
||||||
|
self.addCleanup(mock_patch.stop)
|
||||||
|
|
||||||
def _mock_netaddr_ipaddress(self):
|
def _mock_netaddr_ipaddress(self):
|
||||||
"""Mock netaddr's IPAddress"""
|
"""Mock netaddr's IPAddress"""
|
||||||
|
|
||||||
@@ -1247,6 +1284,139 @@ class TestSubcloudDeploy(BaseTestSubcloudManager):
|
|||||||
)
|
)
|
||||||
self.assertEqual(consts.DEPLOY_STATE_DONE, ret.deploy_status)
|
self.assertEqual(consts.DEPLOY_STATE_DONE, ret.deploy_status)
|
||||||
|
|
||||||
|
@mock.patch.object(cutils, 'get_region_name')
|
||||||
|
@mock.patch.object(subcloud_enrollment.SubcloudEnrollmentInit, 'prep')
|
||||||
|
def test_deploy_subcloud_enroll(
|
||||||
|
self, mock_subcloud_enrollment_prep, mock_get_region_name):
|
||||||
|
|
||||||
|
mock_run_patch_patch = mock.patch('eventlet.green.subprocess.run')
|
||||||
|
mock_mkdtemp_patch = mock.patch('tempfile.mkdtemp')
|
||||||
|
mock_makedirs_patch = mock.patch('os.makedirs')
|
||||||
|
mock_rmtree_patch = mock.patch('shutil.rmtree')
|
||||||
|
|
||||||
|
self.seed_data_dir = '/temp/seed_data'
|
||||||
|
mock_get_region_name.return_value = '11111'
|
||||||
|
|
||||||
|
self.mock_run = mock_run_patch_patch.start()
|
||||||
|
self.mock_mkdtemp = mock_mkdtemp_patch.start()
|
||||||
|
self.mock_makedirs = mock_makedirs_patch.start()
|
||||||
|
self.mock_rmtree = mock_rmtree_patch.start()
|
||||||
|
|
||||||
|
self.addCleanup(mock_run_patch_patch.stop)
|
||||||
|
self.addCleanup(mock_mkdtemp_patch.stop)
|
||||||
|
self.addCleanup(mock_makedirs_patch.stop)
|
||||||
|
self.addCleanup(mock_rmtree_patch.stop)
|
||||||
|
|
||||||
|
self._mock_builtins_open()
|
||||||
|
|
||||||
|
self.mock_builtins_open.side_effect = mock.mock_open()
|
||||||
|
self.mock_os_path_exists.return_value = True
|
||||||
|
self.mock_mkdtemp.return_value = self.seed_data_dir
|
||||||
|
self.mock_os_path_isdir.return_value = True
|
||||||
|
self.mock_run.return_value = mock.MagicMock(returncode=0,
|
||||||
|
stdout=b'Success')
|
||||||
|
|
||||||
|
self._mock_subcloud_manager_compose_enroll_command()
|
||||||
|
self.fake_payload_enroll['software_version'] = FAKE_PREVIOUS_SW_VERSION
|
||||||
|
self.subcloud['deploy_status'] = consts.DEPLOY_STATE_PRE_INIT_ENROLL
|
||||||
|
self.fake_payload_enroll['software_version'] = SW_VERSION
|
||||||
|
|
||||||
|
with mock.patch('os.path.isdir', side_effect=self.patched_isdir):
|
||||||
|
self.sm.subcloud_deploy_enroll(
|
||||||
|
self.ctx, self.subcloud.id, payload=self.fake_payload_enroll)
|
||||||
|
|
||||||
|
# Verify subcloud was updated with correct values
|
||||||
|
updated_subcloud = db_api.subcloud_get_by_name(self.ctx,
|
||||||
|
self.subcloud.name)
|
||||||
|
self.assertEqual(consts.DEPLOY_STATE_ENROLLED,
|
||||||
|
updated_subcloud.deploy_status)
|
||||||
|
|
||||||
|
@mock.patch.object(subcloud_enrollment.SubcloudEnrollmentInit,
|
||||||
|
'prep')
|
||||||
|
@mock.patch.object(subcloud_manager.SubcloudManager,
|
||||||
|
'_deploy_install_prep')
|
||||||
|
def test_subcloud_deploy_pre_init_enroll_failed(
|
||||||
|
self, mock_deploy_install_prep, mock_subcloud_enrollment_prep):
|
||||||
|
|
||||||
|
mock_deploy_install_prep.side_effect = base.FakeException('boom')
|
||||||
|
|
||||||
|
subcloud = fake_subcloud.create_fake_subcloud(
|
||||||
|
self.ctx,
|
||||||
|
name=fake_subcloud.FAKE_BOOTSTRAP_FILE_DATA["name"],
|
||||||
|
deploy_status=consts.DEPLOY_STATE_CREATED,
|
||||||
|
data_install=json.dumps(self.fake_payload_enroll['install_values'])
|
||||||
|
)
|
||||||
|
|
||||||
|
self.sm.subcloud_init_enroll(
|
||||||
|
self.ctx, subcloud.id, self.fake_payload_enroll)
|
||||||
|
|
||||||
|
# Verify subcloud was updated with correct values
|
||||||
|
updated_subcloud = db_api.subcloud_get_by_name(self.ctx,
|
||||||
|
self.payload['name'])
|
||||||
|
|
||||||
|
self.assertEqual(consts.DEPLOY_STATE_PRE_INIT_ENROLL_FAILED,
|
||||||
|
updated_subcloud.deploy_status)
|
||||||
|
|
||||||
|
@mock.patch.object(subcloud_enrollment.SubcloudEnrollmentInit,
|
||||||
|
'prep')
|
||||||
|
@mock.patch.object(subcloud_manager.SubcloudManager,
|
||||||
|
'_create_subcloud_endpoints')
|
||||||
|
def test_subcloud_deploy_enroll_failed(
|
||||||
|
self, mock_create_subcloud_endpoints, mock_subcloud_enrollment_prep):
|
||||||
|
mock_create_subcloud_endpoints.side_effect = base.FakeException('boom')
|
||||||
|
|
||||||
|
subcloud = fake_subcloud.create_fake_subcloud(
|
||||||
|
self.ctx,
|
||||||
|
name=fake_subcloud.FAKE_BOOTSTRAP_FILE_DATA["name"],
|
||||||
|
deploy_status=consts.DEPLOY_STATE_INIT_ENROLL_COMPLETE,
|
||||||
|
data_install=json.dumps(self.fake_payload_enroll['install_values'])
|
||||||
|
)
|
||||||
|
|
||||||
|
self.sm.subcloud_deploy_enroll(
|
||||||
|
self.ctx, subcloud.id, self.fake_payload_enroll)
|
||||||
|
|
||||||
|
# Verify subcloud was updated with correct values
|
||||||
|
updated_subcloud = db_api.subcloud_get_by_name(self.ctx,
|
||||||
|
self.payload['name'])
|
||||||
|
|
||||||
|
self.assertEqual(consts.DEPLOY_STATE_PRE_ENROLL_FAILED,
|
||||||
|
updated_subcloud.deploy_status)
|
||||||
|
|
||||||
|
@mock.patch.object(subcloud_enrollment.SubcloudEnrollmentInit,
|
||||||
|
'prep')
|
||||||
|
@mock.patch.object(cutils, 'get_region_name')
|
||||||
|
def test_subcloud_deploy_enroll_run_playbook_failed(
|
||||||
|
self, mock_get_region_name, mock_subcloud_enrollment_prep):
|
||||||
|
|
||||||
|
self.mock_ansible_run_playbook.side_effect = PlaybookExecutionFailed()
|
||||||
|
mock_get_region_name.return_value = "11111"
|
||||||
|
|
||||||
|
subcloud = fake_subcloud.create_fake_subcloud(
|
||||||
|
self.ctx,
|
||||||
|
name=fake_subcloud.FAKE_BOOTSTRAP_FILE_DATA["name"],
|
||||||
|
deploy_status=consts.DEPLOY_STATE_PRE_ENROLL_COMPLETE,
|
||||||
|
data_install=json.dumps(self.fake_payload_enroll['install_values'])
|
||||||
|
)
|
||||||
|
|
||||||
|
self.sm.subcloud_deploy_enroll(
|
||||||
|
self.ctx, subcloud.id, self.fake_payload_enroll)
|
||||||
|
|
||||||
|
self.mock_ansible_run_playbook.assert_called_once()
|
||||||
|
|
||||||
|
# Verify subcloud was updated with correct values
|
||||||
|
updated_subcloud = db_api.subcloud_get_by_name(self.ctx,
|
||||||
|
self.payload['name'])
|
||||||
|
self.assertEqual(consts.DEPLOY_STATE_ENROLL_FAILED,
|
||||||
|
updated_subcloud.deploy_status)
|
||||||
|
# Verify the subcloud rehomed flag is False after bootstrapped
|
||||||
|
self.assertFalse(updated_subcloud.rehomed)
|
||||||
|
self.mock_log.error.assert_called_once_with(
|
||||||
|
'Enroll failed for subcloud fake subcloud1: '
|
||||||
|
'FAILED enrolling playbook of (fake subcloud1).'
|
||||||
|
'\ncheck individual log at '
|
||||||
|
'/var/log/dcmanager/ansible/fake '
|
||||||
|
'subcloud1_playbook_output.log for detailed output ')
|
||||||
|
|
||||||
|
|
||||||
class TestSubcloudAdd(BaseTestSubcloudManager):
|
class TestSubcloudAdd(BaseTestSubcloudManager):
|
||||||
"""Test class for testing subcloud add"""
|
"""Test class for testing subcloud add"""
|
||||||
@@ -4082,6 +4252,8 @@ class TestSubcloudEnrollment(BaseTestSubcloudManager):
|
|||||||
self.seed_data_dir = '/temp/seed_data'
|
self.seed_data_dir = '/temp/seed_data'
|
||||||
self.enroll_init = subcloud_enrollment.\
|
self.enroll_init = subcloud_enrollment.\
|
||||||
SubcloudEnrollmentInit(self.subcloud_name)
|
SubcloudEnrollmentInit(self.subcloud_name)
|
||||||
|
self.fake_install_values = \
|
||||||
|
copy.copy(fake_subcloud.FAKE_SUBCLOUD_INSTALL_VALUES)
|
||||||
|
|
||||||
self.iso_values = {
|
self.iso_values = {
|
||||||
'software_version': self.rel_version,
|
'software_version': self.rel_version,
|
||||||
@@ -4090,6 +4262,7 @@ class TestSubcloudEnrollment(BaseTestSubcloudManager):
|
|||||||
'external_oam_floating_address': '10.10.10.2',
|
'external_oam_floating_address': '10.10.10.2',
|
||||||
'network_mask': '255.255.255.0',
|
'network_mask': '255.255.255.0',
|
||||||
'external_oam_gateway_address': '10.10.10.1',
|
'external_oam_gateway_address': '10.10.10.1',
|
||||||
|
'install_values': self.fake_install_values
|
||||||
}
|
}
|
||||||
|
|
||||||
mock_run_patch_patch = mock.patch('eventlet.green.subprocess.run')
|
mock_run_patch_patch = mock.patch('eventlet.green.subprocess.run')
|
||||||
|
Reference in New Issue
Block a user