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