Add subcloud deploy resume option to dcmanager
This commit adds the command "subcloud deploy resume" to dcmanager client. Usage: dcmanager subcloud deploy resume <subcloud-name-or-id> --sysadmin-password <password> [--install-values <install-values.yaml>] [--bmc-password <password>] [--bootstrap-address <subcloud-bootstrap-address>] [--bootstrap-values <bootstrap-values.yaml>] [--deploy-config <deployment-config.yaml>] [--release <release>] Test Plan: - PASS: Verify the command works with or without --install-values, --bmc-password, --bootstrap-address, --bootstrap-values, --deploy-config and --release. - PASS: Verify that all provided parameters are successfully passed to the backend. - PASS: Verify that the CLI asks for the sysadmin-password and bmc-password if they are not provided in the command (bmc-password is only prompt if --install-values is also provided). - PASS: Verify that the dcmanager help subcloud deploy resume shows the correct help message containing all options. Depends-on: https://review.opendev.org/c/starlingx/distcloud/+/886104 Story: 2010756 Task: 48317 Change-Id: I8a984626007b43e685f326dd4dd05294cd50fcce Signed-off-by: Victor Romano <victor.gluzromano@windriver.com>
This commit is contained in:
parent
86e34c060b
commit
671db8b561
|
@ -68,3 +68,9 @@ class phased_subcloud_deploy_manager(base.ResourceManager):
|
||||||
files = kwargs.get('files', {})
|
files = kwargs.get('files', {})
|
||||||
url = BASE_URL + "%s/abort" % subcloud_ref
|
url = BASE_URL + "%s/abort" % subcloud_ref
|
||||||
return self._deploy_operation(url, files, data, method='patch')
|
return self._deploy_operation(url, files, data, method='patch')
|
||||||
|
|
||||||
|
def subcloud_deploy_resume(self, subcloud_ref, **kwargs):
|
||||||
|
data = kwargs.get('data')
|
||||||
|
files = kwargs.get('files')
|
||||||
|
url = BASE_URL + "%s/resume" % subcloud_ref
|
||||||
|
return self._deploy_operation(url, files, data, method='patch')
|
||||||
|
|
|
@ -42,6 +42,130 @@ class AbortPhasedSubcloudDeploy(base.DCManagerShowOne):
|
||||||
raise exceptions.DCManagerClientException(error_msg)
|
raise exceptions.DCManagerClientException(error_msg)
|
||||||
|
|
||||||
|
|
||||||
|
class PhasedSubcloudDeployResume(base.DCManagerShowOne):
|
||||||
|
"""Resume the subcloud deployment."""
|
||||||
|
|
||||||
|
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 resume deployment.'
|
||||||
|
)
|
||||||
|
|
||||||
|
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 parameters required for the bootstrap '
|
||||||
|
'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(
|
||||||
|
'--install-values',
|
||||||
|
required=False,
|
||||||
|
help='YAML file containing parameters required for the remote '
|
||||||
|
'install of the subcloud.'
|
||||||
|
)
|
||||||
|
|
||||||
|
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.'
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--release',
|
||||||
|
required=False,
|
||||||
|
help='software release used to install, bootstrap and/or deploy '
|
||||||
|
'the subcloud with. If not specified, the current software '
|
||||||
|
'release of the system controller will be used.'
|
||||||
|
)
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def _get_resources(self, parsed_args):
|
||||||
|
subcloud_ref = parsed_args.subcloud
|
||||||
|
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
|
||||||
|
|
||||||
|
# Get the install values yaml file
|
||||||
|
if parsed_args.install_values:
|
||||||
|
if not os.path.isfile(parsed_args.install_values):
|
||||||
|
error_msg = "install-values does not exist: %s" % \
|
||||||
|
parsed_args.install_values
|
||||||
|
raise exceptions.DCManagerClientException(error_msg)
|
||||||
|
files['install_values'] = parsed_args.install_values
|
||||||
|
|
||||||
|
# Get the deploy config yaml file
|
||||||
|
if parsed_args.deploy_config:
|
||||||
|
if not os.path.isfile(parsed_args.deploy_config):
|
||||||
|
error_msg = "deploy-config does not exist: %s" % \
|
||||||
|
parsed_args.deploy_config
|
||||||
|
raise exceptions.DCManagerClientException(error_msg)
|
||||||
|
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"))
|
||||||
|
|
||||||
|
if parsed_args.install_values:
|
||||||
|
if parsed_args.bmc_password:
|
||||||
|
data['bmc_password'] = base64.b64encode(
|
||||||
|
parsed_args.bmc_password.encode("utf-8"))
|
||||||
|
else:
|
||||||
|
password = utils.prompt_for_password('bmc')
|
||||||
|
data["bmc_password"] = base64.b64encode(
|
||||||
|
password.encode("utf-8"))
|
||||||
|
|
||||||
|
if parsed_args.release:
|
||||||
|
data['release'] = parsed_args.release
|
||||||
|
|
||||||
|
return dcmanager_client.subcloud_deploy_resume(
|
||||||
|
subcloud_ref=subcloud_ref, files=files, data=data)
|
||||||
|
|
||||||
|
|
||||||
class CreatePhasedSubcloudDeploy(base.DCManagerShowOne):
|
class CreatePhasedSubcloudDeploy(base.DCManagerShowOne):
|
||||||
"""Creates a new subcloud."""
|
"""Creates a new subcloud."""
|
||||||
|
|
||||||
|
@ -60,22 +184,22 @@ class CreatePhasedSubcloudDeploy(base.DCManagerShowOne):
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--bootstrap-values',
|
'--bootstrap-values',
|
||||||
required=True,
|
required=True,
|
||||||
help='YAML file containing subcloud configuration settings. '
|
help='YAML file containing parameters required for the bootstrap '
|
||||||
'Can be either a local file path or a URL.'
|
'of the subcloud.'
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--deploy-config',
|
'--deploy-config',
|
||||||
required=False,
|
required=False,
|
||||||
help='YAML file containing subcloud variables to be passed to the '
|
help='YAML file containing parameters required for the initial '
|
||||||
'deploy playbook.'
|
'configuration and unlock of the subcloud.'
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--install-values',
|
'--install-values',
|
||||||
required=False,
|
required=False,
|
||||||
help='YAML file containing parameters required for the '
|
help='YAML file containing parameters required for the remote '
|
||||||
'remote install of the subcloud.'
|
'install of the subcloud.'
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
@ -167,8 +291,8 @@ class InstallPhasedSubcloudDeploy(base.DCManagerShowOne):
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--install-values',
|
'--install-values',
|
||||||
required=False,
|
required=False,
|
||||||
help='YAML file containing parameters required for the '
|
help='YAML file containing parameters required for the remote '
|
||||||
'remote install of the subcloud.'
|
'install of the subcloud.'
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
@ -260,8 +384,8 @@ class BootstrapPhasedSubcloudDeploy(base.DCManagerShowOne):
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--bootstrap-values',
|
'--bootstrap-values',
|
||||||
required=False,
|
required=False,
|
||||||
help='YAML file containing subcloud configuration settings. '
|
help='YAML file containing parameters required for the bootstrap '
|
||||||
'Can be either a local file path or a URL.'
|
'of the subcloud.'
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
@ -322,8 +446,8 @@ class ConfigPhasedSubcloudDeploy(base.DCManagerShowOne):
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--deploy-config',
|
'--deploy-config',
|
||||||
required=False,
|
required=False,
|
||||||
help='YAML file containing subcloud variables to be passed to the '
|
help='YAML file containing parameters required for the initial '
|
||||||
'deploy playbook.'
|
'configuration and unlock of the subcloud.'
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
|
|
@ -141,15 +141,15 @@ class AddSubcloud(base.DCManagerShowOne):
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--bootstrap-values',
|
'--bootstrap-values',
|
||||||
required=True,
|
required=True,
|
||||||
help='YAML file containing subcloud configuration settings. '
|
help='YAML file containing parameters required for the bootstrap '
|
||||||
'Can be either a local file path or a URL.'
|
'of the subcloud.'
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--deploy-config',
|
'--deploy-config',
|
||||||
required=False,
|
required=False,
|
||||||
help='YAML file containing subcloud variables to be passed to the '
|
help='YAML file containing parameters required for the initial '
|
||||||
'deploy playbook.'
|
'configuration and unlock of the subcloud.'
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
@ -599,8 +599,8 @@ class ReconfigSubcloud(base.DCManagerShowOne):
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--deploy-config',
|
'--deploy-config',
|
||||||
required=True,
|
required=True,
|
||||||
help='YAML file containing subcloud variables to be passed to the '
|
help='YAML file containing parameters required for the initial '
|
||||||
'deploy playbook.'
|
'configuration and unlock of the subcloud.'
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
@ -661,15 +661,15 @@ class ReinstallSubcloud(base.DCManagerShowOne):
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--bootstrap-values',
|
'--bootstrap-values',
|
||||||
required=True,
|
required=True,
|
||||||
help='YAML file containing subcloud configuration settings. '
|
help='YAML file containing parameters required for the bootstrap '
|
||||||
'Can be either a local file path or a URL.'
|
'of the subcloud.'
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--deploy-config',
|
'--deploy-config',
|
||||||
required=False,
|
required=False,
|
||||||
help='YAML file containing subcloud variables to be passed to the '
|
help='YAML file containing parameters required for the initial '
|
||||||
'deploy playbook.'
|
'configuration and unlock of the subcloud.'
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
|
|
@ -549,6 +549,7 @@ class DCManagerShell(app.App):
|
||||||
'subcloud deploy bootstrap': psdm.BootstrapPhasedSubcloudDeploy,
|
'subcloud deploy bootstrap': psdm.BootstrapPhasedSubcloudDeploy,
|
||||||
'subcloud deploy config': psdm.ConfigPhasedSubcloudDeploy,
|
'subcloud deploy config': psdm.ConfigPhasedSubcloudDeploy,
|
||||||
'subcloud deploy install': psdm.InstallPhasedSubcloudDeploy,
|
'subcloud deploy install': psdm.InstallPhasedSubcloudDeploy,
|
||||||
|
'subcloud deploy resume': psdm.PhasedSubcloudDeployResume,
|
||||||
'subcloud deploy upload': sdm.SubcloudDeployUpload,
|
'subcloud deploy upload': sdm.SubcloudDeployUpload,
|
||||||
'subcloud deploy show': sdm.SubcloudDeployShow,
|
'subcloud deploy show': sdm.SubcloudDeployShow,
|
||||||
'subcloud-deploy upload': sdm.DeprecatedSubcloudDeployUpload,
|
'subcloud-deploy upload': sdm.DeprecatedSubcloudDeployUpload,
|
||||||
|
|
|
@ -145,3 +145,87 @@ class TestCLIPhasedSubcloudDeployManagerV1(base.BaseCommandTest):
|
||||||
cmd.AbortPhasedSubcloudDeploy,
|
cmd.AbortPhasedSubcloudDeploy,
|
||||||
app_args=[base.NAME])
|
app_args=[base.NAME])
|
||||||
self.assertEqual(base.SUBCLOUD_FIELD_RESULT_LIST, actual_call[1])
|
self.assertEqual(base.SUBCLOUD_FIELD_RESULT_LIST, actual_call[1])
|
||||||
|
|
||||||
|
def test_subcloud_deploy_resume_all_parameters(self):
|
||||||
|
self.client.subcloud_deploy_resume.return_value = [
|
||||||
|
base.SUBCLOUD_RESOURCE]
|
||||||
|
|
||||||
|
with tempfile.NamedTemporaryFile(mode='w') as bootstrap_file,\
|
||||||
|
tempfile.NamedTemporaryFile(mode='w') as config_file,\
|
||||||
|
tempfile.NamedTemporaryFile(mode='w') as install_file:
|
||||||
|
|
||||||
|
bootstrap_file_path = os.path.abspath(bootstrap_file.name)
|
||||||
|
config_file_path = os.path.abspath(config_file.name)
|
||||||
|
install_file_path = os.path.abspath(install_file.name)
|
||||||
|
|
||||||
|
actual_call = self.call(
|
||||||
|
cmd.PhasedSubcloudDeployResume, app_args=[
|
||||||
|
base.NAME,
|
||||||
|
'--bootstrap-address', base.BOOTSTRAP_ADDRESS,
|
||||||
|
'--bootstrap-values', bootstrap_file_path,
|
||||||
|
'--install-values', install_file_path,
|
||||||
|
'--deploy-config', config_file_path,
|
||||||
|
'--release', base.SOFTWARE_VERSION,
|
||||||
|
])
|
||||||
|
self.assertEqual(base.SUBCLOUD_FIELD_RESULT_LIST, actual_call[1])
|
||||||
|
|
||||||
|
def test_subcloud_deploy_resume_missing_files(self):
|
||||||
|
self.client.subcloud_deploy_resume.return_value = [
|
||||||
|
base.SUBCLOUD_RESOURCE]
|
||||||
|
|
||||||
|
with tempfile.NamedTemporaryFile(mode='w') as bootstrap_file,\
|
||||||
|
tempfile.NamedTemporaryFile(mode='w') as config_file,\
|
||||||
|
tempfile.NamedTemporaryFile(mode='w') as install_file:
|
||||||
|
|
||||||
|
bootstrap_file_path = os.path.abspath(bootstrap_file.name)
|
||||||
|
config_file_path = os.path.abspath(config_file.name)
|
||||||
|
install_file_path = os.path.abspath(install_file.name)
|
||||||
|
|
||||||
|
# Missing bootstrap values
|
||||||
|
app_args_bootstrap = [base.NAME,
|
||||||
|
'--bootstrap-address', base.BOOTSTRAP_ADDRESS,
|
||||||
|
'--bootstrap-values', bootstrap_file_path]
|
||||||
|
error_msg_bootstrap = 'bootstrap-values does not exist'
|
||||||
|
call_bootstrap = self.assertRaises(DCManagerClientException,
|
||||||
|
self.call,
|
||||||
|
cmd.PhasedSubcloudDeployResume,
|
||||||
|
app_args=app_args_bootstrap)
|
||||||
|
self.assertTrue(error_msg_bootstrap in str(call_bootstrap))
|
||||||
|
|
||||||
|
# Missing install values
|
||||||
|
app_args_install = [base.NAME, '--install-values', install_file_path]
|
||||||
|
error_msg_install = 'install-values does not exist'
|
||||||
|
call_install = self.assertRaises(DCManagerClientException,
|
||||||
|
self.call,
|
||||||
|
cmd.PhasedSubcloudDeployResume,
|
||||||
|
app_args=app_args_install)
|
||||||
|
self.assertTrue(error_msg_install in str(call_install))
|
||||||
|
|
||||||
|
# Missing deploy config values
|
||||||
|
app_args_config = [base.NAME, '--deploy-config', config_file_path]
|
||||||
|
error_msg_config = 'deploy-config does not exist'
|
||||||
|
call_config = self.assertRaises(DCManagerClientException,
|
||||||
|
self.call,
|
||||||
|
cmd.PhasedSubcloudDeployResume,
|
||||||
|
app_args=app_args_config)
|
||||||
|
self.assertTrue(error_msg_config in str(call_config))
|
||||||
|
|
||||||
|
def test_subcloud_deploy_resume_no_parameters(self):
|
||||||
|
self.client.subcloud_deploy_resume.return_value = [
|
||||||
|
base.SUBCLOUD_RESOURCE]
|
||||||
|
|
||||||
|
actual_call = self.call(
|
||||||
|
cmd.PhasedSubcloudDeployResume,
|
||||||
|
app_args=[base.NAME])
|
||||||
|
self.assertEqual(base.SUBCLOUD_FIELD_RESULT_LIST, actual_call[1])
|
||||||
|
|
||||||
|
def test_subcloud_deploy_resume_no_files_only_release(self):
|
||||||
|
self.client.subcloud_deploy_resume.return_value = [
|
||||||
|
base.SUBCLOUD_RESOURCE]
|
||||||
|
|
||||||
|
actual_call = self.call(
|
||||||
|
cmd.PhasedSubcloudDeployResume, app_args=[
|
||||||
|
base.NAME,
|
||||||
|
'--release', base.SOFTWARE_VERSION,
|
||||||
|
])
|
||||||
|
self.assertEqual(base.SUBCLOUD_FIELD_RESULT_LIST, actual_call[1])
|
||||||
|
|
Loading…
Reference in New Issue