Add enroll command to backend
This commit introduces the enroll command endpoint. Test Plan: PASS: Deploy a system controller and run subcloud add enroll without bootstrap-values. Verify that the API returns an error. PASS: Deploy a system controller and run subcloud add enroll passing all required parameters. Verify in dcmanager log that the API returned a success code. Change-Id: I525d26166dbb7d7afcb26b96191b5045eee7b52d Signed-off-by: Gustavo Pereira <gustavo.lyrapereira@windriver.com>
This commit is contained in:
parent
9bf56e9e30
commit
1a569cd926
|
@ -34,6 +34,7 @@ CONFIG = consts.DEPLOY_PHASE_CONFIG
|
||||||
COMPLETE = consts.DEPLOY_PHASE_COMPLETE
|
COMPLETE = consts.DEPLOY_PHASE_COMPLETE
|
||||||
ABORT = consts.DEPLOY_PHASE_ABORT
|
ABORT = consts.DEPLOY_PHASE_ABORT
|
||||||
RESUME = consts.DEPLOY_PHASE_RESUME
|
RESUME = consts.DEPLOY_PHASE_RESUME
|
||||||
|
ENROLL = consts.DEPLOY_PHASE_ENROLL
|
||||||
|
|
||||||
SUBCLOUD_CREATE_REQUIRED_PARAMETERS = (
|
SUBCLOUD_CREATE_REQUIRED_PARAMETERS = (
|
||||||
consts.BOOTSTRAP_VALUES,
|
consts.BOOTSTRAP_VALUES,
|
||||||
|
@ -55,6 +56,10 @@ SUBCLOUD_BOOTSTRAP_GET_FILE_CONTENTS = (
|
||||||
consts.BOOTSTRAP_VALUES,
|
consts.BOOTSTRAP_VALUES,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
SUBCLOUD_ENROLL_GET_FILE_CONTENTS = (
|
||||||
|
consts.BOOTSTRAP_VALUES,
|
||||||
|
)
|
||||||
|
|
||||||
SUBCLOUD_CONFIG_GET_FILE_CONTENTS = (
|
SUBCLOUD_CONFIG_GET_FILE_CONTENTS = (
|
||||||
consts.DEPLOY_CONFIG,
|
consts.DEPLOY_CONFIG,
|
||||||
)
|
)
|
||||||
|
@ -92,6 +97,12 @@ VALID_STATES_FOR_DEPLOY_ABORT = (
|
||||||
consts.DEPLOY_STATE_CONFIGURING
|
consts.DEPLOY_STATE_CONFIGURING
|
||||||
)
|
)
|
||||||
|
|
||||||
|
VALID_STATES_FOR_DEPLOY_ENROLL = (
|
||||||
|
consts.DEPLOY_STATE_CREATED,
|
||||||
|
consts.DEPLOY_STATE_ENROLL_FAILED,
|
||||||
|
consts.DEPLOY_STATE_ENROLLED,
|
||||||
|
)
|
||||||
|
|
||||||
FILES_FOR_RESUME_INSTALL = \
|
FILES_FOR_RESUME_INSTALL = \
|
||||||
SUBCLOUD_INSTALL_GET_FILE_CONTENTS + \
|
SUBCLOUD_INSTALL_GET_FILE_CONTENTS + \
|
||||||
SUBCLOUD_BOOTSTRAP_GET_FILE_CONTENTS + \
|
SUBCLOUD_BOOTSTRAP_GET_FILE_CONTENTS + \
|
||||||
|
@ -506,6 +517,39 @@ class PhasedSubcloudDeployController(object):
|
||||||
LOG.exception("Unable to resume subcloud %s deployment" % subcloud.name)
|
LOG.exception("Unable to resume subcloud %s deployment" % subcloud.name)
|
||||||
pecan.abort(500, _('Unable to resume subcloud deployment'))
|
pecan.abort(500, _('Unable to resume subcloud deployment'))
|
||||||
|
|
||||||
|
def _deploy_enroll(self, context: RequestContext,
|
||||||
|
request: pecan.Request, subcloud: models.Subcloud):
|
||||||
|
if subcloud.deploy_status not in VALID_STATES_FOR_DEPLOY_ENROLL:
|
||||||
|
valid_states_str = ', '.join(VALID_STATES_FOR_DEPLOY_ENROLL)
|
||||||
|
msg = f'Subcloud deploy status must be either: {valid_states_str}'
|
||||||
|
pecan.abort(400, _(msg))
|
||||||
|
|
||||||
|
has_bootstrap_values = consts.BOOTSTRAP_VALUES in request.POST
|
||||||
|
|
||||||
|
payload = psd_common.get_request_data(
|
||||||
|
request, subcloud, SUBCLOUD_ENROLL_GET_FILE_CONTENTS)
|
||||||
|
|
||||||
|
psd_common.validate_enroll_parameter(payload, request)
|
||||||
|
|
||||||
|
# Try to load the existing override values
|
||||||
|
override_file = psd_common.get_config_file_path(subcloud.name)
|
||||||
|
if os.path.exists(override_file):
|
||||||
|
if not has_bootstrap_values:
|
||||||
|
psd_common.populate_payload_with_pre_existing_data(
|
||||||
|
payload, subcloud, SUBCLOUD_BOOTSTRAP_GET_FILE_CONTENTS)
|
||||||
|
elif not has_bootstrap_values:
|
||||||
|
msg = ("Required bootstrap-values file was not provided and it was "
|
||||||
|
f"not previously available at {override_file}")
|
||||||
|
pecan.abort(400, _(msg))
|
||||||
|
|
||||||
|
payload['software_version'] = subcloud.software_version
|
||||||
|
|
||||||
|
pecan.abort(400, "subcloud deploy enrollment is not "
|
||||||
|
"available yet")
|
||||||
|
# TODO(glyraper): Enroll function in development
|
||||||
|
# TODO(glyraper): Create cover tests
|
||||||
|
return ""
|
||||||
|
|
||||||
@pecan.expose(generic=True, template='json')
|
@pecan.expose(generic=True, template='json')
|
||||||
def index(self):
|
def index(self):
|
||||||
# Route the request to specific methods with parameters
|
# Route the request to specific methods with parameters
|
||||||
|
@ -555,6 +599,8 @@ class PhasedSubcloudDeployController(object):
|
||||||
subcloud = self._deploy_config(context, pecan.request, subcloud)
|
subcloud = self._deploy_config(context, pecan.request, subcloud)
|
||||||
elif verb == COMPLETE:
|
elif verb == COMPLETE:
|
||||||
subcloud = self._deploy_complete(context, subcloud)
|
subcloud = self._deploy_complete(context, subcloud)
|
||||||
|
elif verb == ENROLL:
|
||||||
|
subcloud = self._deploy_enroll(context, pecan.request, subcloud)
|
||||||
else:
|
else:
|
||||||
pecan.abort(400, _('Invalid request'))
|
pecan.abort(400, _('Invalid request'))
|
||||||
|
|
||||||
|
|
|
@ -561,6 +561,9 @@ class SubcloudsController(object):
|
||||||
|
|
||||||
psd_common.validate_secondary_parameter(payload, request)
|
psd_common.validate_secondary_parameter(payload, request)
|
||||||
|
|
||||||
|
if payload.get('enroll'):
|
||||||
|
psd_common.validate_enroll_parameter(payload, request)
|
||||||
|
|
||||||
# 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
|
||||||
if payload.get('migrate') == 'true' and bootstrap_sc_name is not None:
|
if payload.get('migrate') == 'true' and bootstrap_sc_name is not None:
|
||||||
|
|
|
@ -40,6 +40,7 @@ DEPLOY_PHASE_CONFIG = 'configure'
|
||||||
DEPLOY_PHASE_COMPLETE = 'complete'
|
DEPLOY_PHASE_COMPLETE = 'complete'
|
||||||
DEPLOY_PHASE_ABORT = 'abort'
|
DEPLOY_PHASE_ABORT = 'abort'
|
||||||
DEPLOY_PHASE_RESUME = 'resume'
|
DEPLOY_PHASE_RESUME = 'resume'
|
||||||
|
DEPLOY_PHASE_ENROLL = 'enroll'
|
||||||
|
|
||||||
# Admin status for hosts
|
# Admin status for hosts
|
||||||
ADMIN_LOCKED = 'locked'
|
ADMIN_LOCKED = 'locked'
|
||||||
|
@ -217,6 +218,8 @@ DEPLOY_STATE_INSTALL_ABORTED = 'install-aborted'
|
||||||
DEPLOY_STATE_ABORTING_BOOTSTRAP = 'aborting-bootstrap'
|
DEPLOY_STATE_ABORTING_BOOTSTRAP = 'aborting-bootstrap'
|
||||||
DEPLOY_STATE_ABORTING_CONFIG = 'aborting-config'
|
DEPLOY_STATE_ABORTING_CONFIG = 'aborting-config'
|
||||||
DEPLOY_STATE_CONFIG_ABORTED = 'config-aborted'
|
DEPLOY_STATE_CONFIG_ABORTED = 'config-aborted'
|
||||||
|
DEPLOY_STATE_ENROLLED = 'enroll-complete'
|
||||||
|
DEPLOY_STATE_ENROLL_FAILED = 'enroll-failed'
|
||||||
DEPLOY_STATE_MIGRATING_DATA = 'migrating-data'
|
DEPLOY_STATE_MIGRATING_DATA = 'migrating-data'
|
||||||
DEPLOY_STATE_DATA_MIGRATION_FAILED = 'data-migration-failed'
|
DEPLOY_STATE_DATA_MIGRATION_FAILED = 'data-migration-failed'
|
||||||
DEPLOY_STATE_MIGRATED = 'migrated'
|
DEPLOY_STATE_MIGRATED = 'migrated'
|
||||||
|
@ -226,6 +229,7 @@ DEPLOY_STATE_RESTORE_PREP_FAILED = 'restore-prep-failed'
|
||||||
DEPLOY_STATE_RESTORING = 'restoring'
|
DEPLOY_STATE_RESTORING = 'restoring'
|
||||||
DEPLOY_STATE_RESTORE_FAILED = 'restore-failed'
|
DEPLOY_STATE_RESTORE_FAILED = 'restore-failed'
|
||||||
DEPLOY_STATE_PRE_REHOME = 'pre-rehome'
|
DEPLOY_STATE_PRE_REHOME = 'pre-rehome'
|
||||||
|
DEPLOY_STATE_PRE_ENROLL = 'pre-enroll'
|
||||||
# If any of the following rehoming or secondary statuses
|
# If any of the following rehoming or secondary statuses
|
||||||
# are modified, cert-mon code will need to be updated.
|
# are modified, cert-mon code will need to be updated.
|
||||||
DEPLOY_STATE_REHOMING = 'rehoming'
|
DEPLOY_STATE_REHOMING = 'rehoming'
|
||||||
|
|
|
@ -162,6 +162,13 @@ def validate_migrate_parameter(payload, request):
|
||||||
'not allowed'))
|
'not allowed'))
|
||||||
|
|
||||||
|
|
||||||
|
def validate_enroll_parameter(payload, request):
|
||||||
|
enroll_str = payload.get('enroll')
|
||||||
|
if enroll_str and enroll_str not in ["true", "false"]:
|
||||||
|
pecan.abort(400, _('The enroll option is invalid, '
|
||||||
|
'valid options are true and false.'))
|
||||||
|
|
||||||
|
|
||||||
def validate_secondary_parameter(payload, request):
|
def validate_secondary_parameter(payload, request):
|
||||||
secondary_str = payload.get('secondary')
|
secondary_str = payload.get('secondary')
|
||||||
migrate_str = payload.get('migrate')
|
migrate_str = payload.get('migrate')
|
||||||
|
|
|
@ -844,6 +844,7 @@ 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"
|
||||||
initial_deployment = True if not rehoming else False
|
initial_deployment = True if not rehoming else False
|
||||||
|
|
||||||
# Create the subcloud
|
# Create the subcloud
|
||||||
|
@ -873,8 +874,11 @@ class SubcloudManager(manager.Manager):
|
||||||
phases_to_run = []
|
phases_to_run = []
|
||||||
if consts.INSTALL_VALUES in payload:
|
if consts.INSTALL_VALUES in payload:
|
||||||
phases_to_run.append(consts.DEPLOY_PHASE_INSTALL)
|
phases_to_run.append(consts.DEPLOY_PHASE_INSTALL)
|
||||||
phases_to_run.append(consts.DEPLOY_PHASE_BOOTSTRAP)
|
if enroll:
|
||||||
if consts.DEPLOY_CONFIG in payload:
|
phases_to_run.append(consts.DEPLOY_PHASE_ENROLL)
|
||||||
|
else:
|
||||||
|
phases_to_run.append(consts.DEPLOY_PHASE_BOOTSTRAP)
|
||||||
|
if not enroll and consts.DEPLOY_CONFIG in payload:
|
||||||
phases_to_run.append(consts.DEPLOY_PHASE_CONFIG)
|
phases_to_run.append(consts.DEPLOY_PHASE_CONFIG)
|
||||||
else:
|
else:
|
||||||
phases_to_run.append(consts.DEPLOY_PHASE_COMPLETE)
|
phases_to_run.append(consts.DEPLOY_PHASE_COMPLETE)
|
||||||
|
@ -1566,6 +1570,11 @@ 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,
|
||||||
|
initial_deployment=False):
|
||||||
|
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
def subcloud_deploy_bootstrap(self, context, subcloud_id, payload,
|
def subcloud_deploy_bootstrap(self, context, subcloud_id, payload,
|
||||||
initial_deployment=False):
|
initial_deployment=False):
|
||||||
"""Bootstrap subcloud
|
"""Bootstrap subcloud
|
||||||
|
@ -2226,6 +2235,9 @@ class SubcloudManager(manager.Manager):
|
||||||
if consts.DEPLOY_PHASE_INSTALL in deploy_phases_to_run:
|
if consts.DEPLOY_PHASE_INSTALL in deploy_phases_to_run:
|
||||||
succeeded = self.subcloud_deploy_install(
|
succeeded = self.subcloud_deploy_install(
|
||||||
context, subcloud_id, payload, initial_deployment)
|
context, subcloud_id, payload, initial_deployment)
|
||||||
|
if succeeded and consts.DEPLOY_PHASE_ENROLL in deploy_phases_to_run:
|
||||||
|
succeeded = self.subcloud_deploy_enroll(
|
||||||
|
context, subcloud_id, payload, initial_deployment)
|
||||||
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)
|
||||||
|
|
|
@ -1075,3 +1075,32 @@ class TestPhasedSubcloudDeployPatchResume(BaseTestPhasedSubcloudDeployPatch):
|
||||||
"Unable to resume subcloud deployment"
|
"Unable to resume subcloud deployment"
|
||||||
)
|
)
|
||||||
self.mock_rpc_client().subcloud_deploy_resume.assert_called_once()
|
self.mock_rpc_client().subcloud_deploy_resume.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
class TestSubcloudDeployEnroll(BaseTestPhasedSubcloudDeployPatch):
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
|
||||||
|
self.url = f"{self.url}/enroll"
|
||||||
|
|
||||||
|
self._update_subcloud(
|
||||||
|
deploy_status=consts.DEPLOY_STATE_CREATED, software_version=SW_VERSION
|
||||||
|
)
|
||||||
|
|
||||||
|
modified_bootstrap_data = copy.copy(fake_subcloud.FAKE_BOOTSTRAP_FILE_DATA)
|
||||||
|
fake_content = json.dumps(modified_bootstrap_data).encode("utf-8")
|
||||||
|
|
||||||
|
self.upload_files = \
|
||||||
|
[("bootstrap_values", "bootstrap_fake_filename", fake_content)]
|
||||||
|
|
||||||
|
def test_patch_enroll_succeeds(self):
|
||||||
|
"""Test patch enroll fails"""
|
||||||
|
|
||||||
|
response = self._send_request()
|
||||||
|
|
||||||
|
# self._assert_response(response)
|
||||||
|
# self._assert_payload()
|
||||||
|
self._assert_pecan_and_response(
|
||||||
|
response, http.client.BAD_REQUEST, "subcloud deploy enrollment is not "
|
||||||
|
"available yet"
|
||||||
|
)
|
||||||
|
|
Loading…
Reference in New Issue