Add 'subcloud deploy bootstrap' command to dcmanager

Adds the subcloud deploy bootstrap command to dcmanager client. It only
performs the bootstrap phase, where all parameters are validated and
the bootstrap playbook is executed.

Usage:
dcmanager subcloud deploy bootstrap
          --sysadmin-password <password>
          [--bootstrap-address <subcloud-bootstrap-address>]
          [--bootstrap-values <bootstrap-values.yaml>]
          subcloud-name

Test Plan:
1. PASS - Bootstrap a subcloud using all the parameters and verify that
          the correct API call is made;
2. PASS - Bootstrap a subcloud using only the required parameters
          (--sysadmin-password and subcloud-name) and verify that the
          correct API call is made;
3. PASS - Verify that the CLI asks for the sysadmin-password if it's
          not provided through the --sysadmin-password parameter;
4. PASS - Verify that it's not possible to bootstrap a subcloud without
          the required parameters (--sysadmin-password and
          subcloud-name);
5. PASS - Verify that the dcmanager help subcloud deploy bootstrap
          shows the correct help message containing all options.

Story: 2010756
Task: 48104

Change-Id: I7033e8247bdcfae243c6a86c0f632b88ef206ce9
Signed-off-by: Gustavo Herzmann <gustavo.herzmann@windriver.com>
This commit is contained in:
Gustavo Herzmann 2023-05-25 13:42:11 -03:00
parent dbb950d3ff
commit 860132451a
4 changed files with 92 additions and 2 deletions

View File

@ -42,3 +42,9 @@ class phased_subcloud_deploy_manager(base.ResourceManager):
data = kwargs.get('data')
files = kwargs.get('files')
return self._deploy_operation(BASE_URL, files, data)
def subcloud_deploy_bootstrap(self, subcloud_ref, **kwargs):
data = kwargs.get('data')
files = kwargs.get('files')
url = BASE_URL + "%s/bootstrap" % subcloud_ref
return self._deploy_operation(url, files, data, "patch")

View File

@ -118,3 +118,71 @@ class CreatePhasedSubcloudDeploy(base.DCManagerShowOne):
return dcmanager_client.subcloud_deploy_create(
files=files, data=data)
class BootstrapPhasedSubcloudDeploy(base.DCManagerShowOne):
"""Bootstrap 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 bootstrap.'
)
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 subcloud configuration settings. '
'Can be either a local file path or a URL.'
)
parser.add_argument(
'--sysadmin-password',
required=False,
help='sysadmin password of the subcloud to be configured, '
'if not provided you will be prompted.'
)
return parser
def _get_resources(self, parsed_args):
dcmanager_client = self.app.client_manager.\
phased_subcloud_deploy_manager.phased_subcloud_deploy_manager
files = dict()
data = dict()
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: %s" % \
parsed_args.bootstrap_values
raise exceptions.DCManagerClientException(error_msg)
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"))
subcloud_ref = parsed_args.subcloud
return dcmanager_client.subcloud_deploy_bootstrap(
subcloud_ref, files=files, data=data)

View File

@ -545,6 +545,7 @@ class DCManagerShell(app.App):
'subcloud-group show': gm.ShowSubcloudGroup,
'subcloud-group update': gm.UpdateSubcloudGroup,
'subcloud deploy create': psdm.CreatePhasedSubcloudDeploy,
'subcloud deploy bootstrap': psdm.BootstrapPhasedSubcloudDeploy,
'subcloud-deploy upload': sdm.SubcloudDeployUpload,
'subcloud-deploy show': sdm.SubcloudDeployShow,
'alarm summary': am.ListAlarmSummary,

View File

@ -12,6 +12,7 @@ from dcmanagerclient.commands.v1 import phased_subcloud_deploy_manager as cmd
from dcmanagerclient.tests import base
@mock.patch('getpass.getpass', new=mock.Mock(return_value='testpassword'))
class TestCLIPhasedSubcloudDeployManagerV1(base.BaseCommandTest):
def setUp(self):
@ -20,8 +21,7 @@ class TestCLIPhasedSubcloudDeployManagerV1(base.BaseCommandTest):
self.client = self.app.client_manager.phased_subcloud_deploy_manager.\
phased_subcloud_deploy_manager
@mock.patch('getpass.getpass', return_value='testpassword')
def test_subcloud_deploy_create(self, getpass):
def test_subcloud_deploy_create(self):
self.client.subcloud_deploy_create.return_value = [
base.SUBCLOUD_RESOURCE]
@ -42,3 +42,18 @@ class TestCLIPhasedSubcloudDeployManagerV1(base.BaseCommandTest):
'--release', base.SOFTWARE_VERSION,
])
self.assertEqual(base.SUBCLOUD_FIELD_RESULT_LIST, actual_call[1])
def test_subcloud_deploy_bootstrap(self):
self.client.subcloud_deploy_bootstrap.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.BootstrapPhasedSubcloudDeploy, app_args=[
base.ID,
'--bootstrap-address', base.BOOTSTRAP_ADDRESS,
'--bootstrap-values', bootstrap_file_path,
])
self.assertEqual(base.SUBCLOUD_FIELD_RESULT_LIST, actual_call[1])