From 860132451a641de26d5b12377df62b783ca32a34 Mon Sep 17 00:00:00 2001 From: Gustavo Herzmann Date: Thu, 25 May 2023 13:42:11 -0300 Subject: [PATCH] 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 [--bootstrap-address ] [--bootstrap-values ] 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 --- .../api/v1/phased_subcloud_deploy_manager.py | 6 ++ .../v1/phased_subcloud_deploy_manager.py | 68 +++++++++++++++++++ .../dcmanagerclient/shell.py | 1 + .../tests/v1/test_phased_subcloud_deploy.py | 19 +++++- 4 files changed, 92 insertions(+), 2 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 1215acc..b9ac971 100644 --- a/distributedcloud-client/dcmanagerclient/api/v1/phased_subcloud_deploy_manager.py +++ b/distributedcloud-client/dcmanagerclient/api/v1/phased_subcloud_deploy_manager.py @@ -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") 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 fe7b5ac..7683f4f 100644 --- a/distributedcloud-client/dcmanagerclient/commands/v1/phased_subcloud_deploy_manager.py +++ b/distributedcloud-client/dcmanagerclient/commands/v1/phased_subcloud_deploy_manager.py @@ -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) diff --git a/distributedcloud-client/dcmanagerclient/shell.py b/distributedcloud-client/dcmanagerclient/shell.py index 57b0768..383454b 100644 --- a/distributedcloud-client/dcmanagerclient/shell.py +++ b/distributedcloud-client/dcmanagerclient/shell.py @@ -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, diff --git a/distributedcloud-client/dcmanagerclient/tests/v1/test_phased_subcloud_deploy.py b/distributedcloud-client/dcmanagerclient/tests/v1/test_phased_subcloud_deploy.py index caa8312..b59da33 100644 --- a/distributedcloud-client/dcmanagerclient/tests/v1/test_phased_subcloud_deploy.py +++ b/distributedcloud-client/dcmanagerclient/tests/v1/test_phased_subcloud_deploy.py @@ -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])