From e403623b8a67f8096bad5c83601f0415bf5ba193 Mon Sep 17 00:00:00 2001 From: Gustavo Pereira Date: Mon, 15 Apr 2024 18:35:31 -0300 Subject: [PATCH] Add subcloud enroll command This commit introduces the enroll command for subclouds. Test Plan PASS: Deploy a system controller and run subcloud add enroll command with migrate command. Verify that an error is thrown informing that enroll and migrate cannot run together. PASS: Deploy a system controller and run subcloud add enroll command. Verify that the dcmanager API returns a success. Change-Id: Icbebdafa746b87cd8b906a9394f161991da9a123 Signed-off-by: Gustavo Pereira --- .../api/v1/phased_subcloud_deploy_manager.py | 6 + .../v1/phased_subcloud_deploy_manager.py | 118 +++++++++++++----- .../commands/v1/subcloud_manager.py | 14 +++ .../dcmanagerclient/shell.py | 1 + .../dcmanagerclient/utils.py | 12 ++ 5 files changed, 123 insertions(+), 28 deletions(-) diff --git a/distributedcloud-client/dcmanagerclient/api/v1/phased_subcloud_deploy_manager.py b/distributedcloud-client/dcmanagerclient/api/v1/phased_subcloud_deploy_manager.py index b327ef4..3e0130f 100644 --- a/distributedcloud-client/dcmanagerclient/api/v1/phased_subcloud_deploy_manager.py +++ b/distributedcloud-client/dcmanagerclient/api/v1/phased_subcloud_deploy_manager.py @@ -79,3 +79,9 @@ class PhasedSubcloudDeployManager(base.ResourceManager): files = kwargs.get("files") url = BASE_URL + f"{subcloud_ref}/resume" return self._deploy_operation(url, files, data, method="patch") + + def subcloud_deploy_enroll(self, subcloud_ref, **kwargs): + data = kwargs.get("data") + files = kwargs.get("files") + url = BASE_URL + f"{subcloud_ref}/enroll" + return self._deploy_operation(url, files, data, method="patch") diff --git a/distributedcloud-client/dcmanagerclient/commands/v1/phased_subcloud_deploy_manager.py b/distributedcloud-client/dcmanagerclient/commands/v1/phased_subcloud_deploy_manager.py index 91f89b2..d803c9b 100644 --- a/distributedcloud-client/dcmanagerclient/commands/v1/phased_subcloud_deploy_manager.py +++ b/distributedcloud-client/dcmanagerclient/commands/v1/phased_subcloud_deploy_manager.py @@ -147,13 +147,7 @@ class PhasedSubcloudDeployResume(base.DCManagerShowOne): files["deploy_config"] = parsed_args.deploy_config # Prompt the user for the subcloud's password if it isn't provided - if parsed_args.sysadmin_password: - data["sysadmin_password"] = base64.b64encode( - parsed_args.sysadmin_password.encode("utf-8") - ) - else: - password = utils.prompt_for_password() - data["sysadmin_password"] = base64.b64encode(password.encode("utf-8")) + utils.set_sysadmin_password(parsed_args, data) if parsed_args.install_values: if parsed_args.bmc_password: @@ -336,13 +330,7 @@ class InstallPhasedSubcloudDeploy(base.DCManagerShowOne): data = {} # Prompt the user for the subcloud's password if it isn't provided - if parsed_args.sysadmin_password is not None: - data["sysadmin_password"] = base64.b64encode( - parsed_args.sysadmin_password.encode("utf-8") - ) - else: - password = utils.prompt_for_password() - data["sysadmin_password"] = base64.b64encode(password.encode("utf-8")) + utils.set_sysadmin_password(parsed_args, data) if parsed_args.install_values is not None: if not os.path.isfile(parsed_args.install_values): @@ -428,13 +416,7 @@ class BootstrapPhasedSubcloudDeploy(base.DCManagerShowOne): files["bootstrap_values"] = parsed_args.bootstrap_values # Prompt the user for the subcloud's password if it isn't provided - if parsed_args.sysadmin_password: - data["sysadmin_password"] = base64.b64encode( - parsed_args.sysadmin_password.encode("utf-8") - ) - else: - password = utils.prompt_for_password() - data["sysadmin_password"] = base64.b64encode(password.encode("utf-8")) + utils.set_sysadmin_password(parsed_args, data) subcloud_ref = parsed_args.subcloud @@ -488,13 +470,7 @@ class ConfigPhasedSubcloudDeploy(base.DCManagerShowOne): files["deploy_config"] = parsed_args.deploy_config # Prompt the user for the subcloud's password if it isn't provided - if parsed_args.sysadmin_password is not None: - data["sysadmin_password"] = base64.b64encode( - parsed_args.sysadmin_password.encode("utf-8") - ) - else: - password = utils.prompt_for_password() - data["sysadmin_password"] = base64.b64encode(password.encode("utf-8")) + utils.set_sysadmin_password(parsed_args, data) try: return phased_subcloud_deploy_manager.subcloud_deploy_config( @@ -537,3 +513,89 @@ class CompletePhasedSubcloudDeploy(base.DCManagerShowOne): f"Unable to complete the deployment of subcloud {subcloud_ref}" ) raise exceptions.DCManagerClientException(error_msg) + + +class EnrollPhasedSubcloudDeploy(base.DCManagerShowOne): + """Enrolls a subcloud.""" + + def _get_format_function(self): + return utils.subcloud_detail_format + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + + parser.add_argument( + "subcloud", help="Name or ID of the subcloud to enroll." + ) + + parser.add_argument( + "--install-values", + required=False, + help="YAML file containing parameters required for the remote " + "install of the subcloud.", + ) + + parser.add_argument( + "--deploy-config", + required=False, + help="YAML file containing parameters required for the initial " + "configuration and unlock of the subcloud.", + ) + + parser.add_argument( + "--bootstrap-address", + required=False, + help="IP address for initial subcloud controller.", + ) + + parser.add_argument( + "--bootstrap-values", + required=False, + help="YAML file containing the parameters required for the " + "subcloud enrollment.", + ) + + parser.add_argument( + "--sysadmin-password", + required=False, + help="sysadmin password of the subcloud to be configured, " + "if not provided you will be prompted.", + ) + + parser.add_argument( + "--bmc-password", + required=False, + help="bmc password of the subcloud to be configured, " + "if not provided you will be prompted. This parameter is only" + " valid if the --install-values are specified.", + ) + + return parser + + def _get_resources(self, parsed_args): + phased_subcloud_deploy_manager = ( + self.app.client_manager.phased_subcloud_deploy_manager + ) + files = {} + data = {} + + if parsed_args.bootstrap_address: + data["bootstrap-address"] = parsed_args.bootstrap_address + + # Get the bootstrap values yaml file + if parsed_args.bootstrap_values: + if not os.path.isfile(parsed_args.bootstrap_values): + error_msg = ( + "bootstrap-values does not exist: " + f"{parsed_args.bootstrap_values}" + ) + raise exceptions.DCManagerClientException(error_msg) + files["bootstrap_values"] = parsed_args.bootstrap_values + + utils.set_sysadmin_password(parsed_args, data) + + subcloud_ref = parsed_args.subcloud + + return phased_subcloud_deploy_manager.subcloud_deploy_enroll( + subcloud_ref, files=files, data=data + ) diff --git a/distributedcloud-client/dcmanagerclient/commands/v1/subcloud_manager.py b/distributedcloud-client/dcmanagerclient/commands/v1/subcloud_manager.py index e23287d..9d1a5c9 100644 --- a/distributedcloud-client/dcmanagerclient/commands/v1/subcloud_manager.py +++ b/distributedcloud-client/dcmanagerclient/commands/v1/subcloud_manager.py @@ -264,6 +264,13 @@ class AddSubcloud(base.DCManagerShowOne): "release of the system controller will be used.", ) + parser.add_argument( + "--enroll", + required=False, + action="store_true", + help="Enroll a subcloud", + ) + return parser def _get_resources(self, parsed_args): @@ -303,6 +310,10 @@ class AddSubcloud(base.DCManagerShowOne): raise exceptions.DCManagerClientException(error_msg) files["deploy_config"] = parsed_args.deploy_config + if parsed_args.migrate and parsed_args.enroll: + error_msg = "cannot run migrate and enroll commands together" + raise exceptions.DCManagerClientException(error_msg) + # Prompt the user for the subcloud's password if it isn't provided if parsed_args.sysadmin_password is not None: data["sysadmin_password"] = base64.b64encode( @@ -327,6 +338,9 @@ class AddSubcloud(base.DCManagerShowOne): if parsed_args.migrate: data["migrate"] = "true" + if parsed_args.enroll: + data["enroll"] = "true" + if parsed_args.release is not None: data["release"] = parsed_args.release diff --git a/distributedcloud-client/dcmanagerclient/shell.py b/distributedcloud-client/dcmanagerclient/shell.py index ffe2299..0b2ffdc 100644 --- a/distributedcloud-client/dcmanagerclient/shell.py +++ b/distributedcloud-client/dcmanagerclient/shell.py @@ -601,6 +601,7 @@ class DCManagerShell(app.App): "subcloud deploy delete": sdm.SubcloudDeployDelete, "subcloud deploy install": psdm.InstallPhasedSubcloudDeploy, "subcloud deploy resume": psdm.PhasedSubcloudDeployResume, + "subcloud deploy enroll": psdm.EnrollPhasedSubcloudDeploy, "subcloud deploy show": sdm.SubcloudDeployShow, "subcloud deploy upload": sdm.SubcloudDeployUpload, "subcloud errors": sm.ShowSubcloudError, diff --git a/distributedcloud-client/dcmanagerclient/utils.py b/distributedcloud-client/dcmanagerclient/utils.py index 97d3fd2..bd61124 100644 --- a/distributedcloud-client/dcmanagerclient/utils.py +++ b/distributedcloud-client/dcmanagerclient/utils.py @@ -19,6 +19,7 @@ import getpass import json import os +import base64 from urllib import parse, request import yaml @@ -166,3 +167,14 @@ def subcloud_detail_format(subcloud=None): data = (("",) * len(columns),) return columns, data + + +def set_sysadmin_password(parsed_args, data): + + if parsed_args.sysadmin_password: + data["sysadmin_password"] = base64.b64encode( + parsed_args.sysadmin_password.encode("utf-8") + ) + else: + password = prompt_for_password() + data["sysadmin_password"] = base64.b64encode(password.encode("utf-8"))