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 <gustavo.lyrapereira@windriver.com>
This commit is contained in:
Gustavo Pereira 2024-04-15 18:35:31 -03:00
parent 622b38f552
commit 1c69d6e558
7 changed files with 162 additions and 28 deletions

View File

@ -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")

View File

@ -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
)

View File

@ -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

View File

@ -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,

View File

@ -271,3 +271,21 @@ class TestCLIPhasedSubcloudDeployManagerV1(base.BaseCommandTest):
],
)
self.assertEqual(base.SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID, actual_call[1])
def test_subcloud_deploy_enroll(self):
self.client.subcloud_deploy_enroll.return_value = [base.SUBCLOUD_RESOURCE]
with tempfile.NamedTemporaryFile(mode="w") as bootstrap_file:
bootstrap_file_path = os.path.abspath(bootstrap_file.name)
actual_call = self.call(
cmd.EnrollPhasedSubcloudDeploy,
app_args=[
base.ID,
"--bootstrap-address",
base.BOOTSTRAP_ADDRESS,
"--bootstrap-values",
bootstrap_file_path,
],
)
self.assertEqual(base.SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID, actual_call[1])

View File

@ -240,6 +240,27 @@ class TestCLISubcloudManagerV1(base.BaseCommandTest):
result_list[1] = base.NAME_SC2
self.assertEqual(tuple(result_list), actual_call[1])
@mock.patch("getpass.getpass", return_value="testpassword")
def test_add_enroll_subcloud(self, _mock_getpass):
self.client.subcloud_manager.add_subcloud.return_value = [
self.subcloud_resource
]
with tempfile.NamedTemporaryFile(mode="w") as f:
yaml.dump(base.FAKE_BOOTSTRAP_VALUES, f)
file_path = os.path.abspath(f.name)
actual_call = self.call(
subcloud_cmd.AddSubcloud,
app_args=[
"--bootstrap-address",
base.BOOTSTRAP_ADDRESS,
"--bootstrap-values",
file_path,
"--enroll",
],
)
self.assertEqual(base.SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID, actual_call[1])
def test_rename_subcloud(self):
subcloud_renamed = copy.copy(base.SUBCLOUD_RESOURCE_WITH_PEERID)
subcloud_renamed.name = base.NAME_SC2

View File

@ -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 = (("<none>",) * 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"))