Merge "Add subcloud redeploy option to dcmanager"

This commit is contained in:
Zuul 2023-08-22 17:32:50 +00:00 committed by Gerrit Code Review
commit b6723b8f67
4 changed files with 215 additions and 0 deletions

View File

@ -90,6 +90,21 @@ class subcloud_manager(base.ResourceManager):
resource.append(self.json_to_resource(json_object))
return resource
def subcloud_redeploy(self, url, body, data):
fields = dict()
for k, v in body.items():
fields.update({k: (v, open(v, 'rb'),)})
fields.update(data)
enc = MultipartEncoder(fields=fields)
headers = {'content-type': enc.content_type}
resp = self.http_client.patch(url, enc, headers=headers)
if resp.status_code != 200:
self._raise_api_exception(resp)
json_object = get_json(resp)
resource = list()
resource.append(self.json_to_resource(json_object))
return resource
def _subcloud_prestage(self, url, data):
data = json.dumps(data)
resp = self.http_client.patch(url, data)
@ -169,3 +184,9 @@ class subcloud_manager(base.ResourceManager):
data = kwargs.get('data')
url = '/subclouds/%s/reinstall' % subcloud_ref
return self.subcloud_reinstall(url, files, data)
def redeploy_subcloud(self, subcloud_ref, **kwargs):
files = kwargs.get('files')
data = kwargs.get('data')
url = '/subclouds/%s/redeploy' % subcloud_ref
return self.subcloud_redeploy(url, files, data)

View File

@ -739,6 +739,134 @@ class ReinstallSubcloud(base.DCManagerShowOne):
raise exceptions.DCManagerClientException(msg)
class RedeploySubcloud(base.DCManagerShowOne):
"""Redeploy a subcloud."""
def _get_format_function(self):
return detail_format
def get_parser(self, prog_name):
parser = super(RedeploySubcloud, self).get_parser(prog_name)
parser.add_argument(
'subcloud',
help='Name or ID of the subcloud to redeploy.'
)
parser.add_argument(
'--install-values',
required=False,
help='YAML file containing parameters required for the '
'remote install of the subcloud.'
)
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(
'--deploy-config',
required=False,
help='YAML file containing subcloud variables to be passed to the '
'deploy playbook.'
)
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.subcloud_manager
files = dict()
data = dict()
# Get the install values yaml file
if parsed_args.install_values is not None:
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 bootstrap values yaml file
if parsed_args.bootstrap_values is not None:
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 deploy config yaml file
if parsed_args.deploy_config is not None:
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 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"))
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 is not None:
data['release'] = parsed_args.release
# Require user to type redeploy to confirm
print("WARNING: This will redeploy the subcloud. "
"All applications and data on the subcloud will be lost.")
confirm = six.moves.input(
"Please type \"redeploy\" to confirm: ").strip().lower()
if confirm == 'redeploy':
try:
return dcmanager_client.subcloud_manager.redeploy_subcloud(
subcloud_ref=subcloud_ref, files=files, data=data)
except Exception as e:
print(e)
error_msg = "Unable to redeploy subcloud %s" % (subcloud_ref)
raise exceptions.DCManagerClientException(error_msg)
else:
msg = "Subcloud %s will not be redeployed" % (subcloud_ref)
raise exceptions.DCManagerClientException(msg)
class RestoreSubcloud(base.DCManagerShowOne):
"""Restore a subcloud."""

View File

@ -533,6 +533,7 @@ class DCManagerShell(app.App):
'subcloud update': sm.UpdateSubcloud,
'subcloud reconfig': sm.ReconfigSubcloud,
'subcloud reinstall': sm.ReinstallSubcloud,
'subcloud redeploy': sm.RedeploySubcloud,
'subcloud restore': sm.RestoreSubcloud,
'subcloud prestage': sm.PrestageSubcloud,
'subcloud-backup create': sbm.CreateSubcloudBackup,

View File

@ -266,6 +266,71 @@ class TestCLISubcloudManagerV1(base.BaseCommandTest):
self.assertTrue('bootstrap-values does not exist'
in str(e))
@mock.patch('getpass.getpass', return_value='testpassword')
@mock.patch('six.moves.input', return_value='redeploy')
def test_redeploy_subcloud(self, mock_input, getpass):
self.client.subcloud_manager.redeploy_subcloud. \
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(
subcloud_cmd.RedeploySubcloud, app_args=[
base.NAME,
'--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])
@mock.patch('getpass.getpass', return_value='testpassword')
@mock.patch('six.moves.input', return_value='redeploy')
def test_redeploy_subcloud_no_parameters(self, mock_input, getpass):
self.client.subcloud_manager.redeploy_subcloud.\
return_value = [base.SUBCLOUD_RESOURCE]
actual_call = self.call(
subcloud_cmd.RedeploySubcloud,
app_args=[base.ID])
self.assertEqual(base.SUBCLOUD_FIELD_RESULT_LIST, actual_call[1])
@mock.patch('getpass.getpass', return_value='testpassword')
@mock.patch('six.moves.input', return_value='redeploy')
def test_redeploy_bootstrap_files_does_not_exists(
self, mock_input, getpass):
self.client.subcloud_manager.redeploy_subcloud.\
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)
app_args_install = [base.NAME,
'--install-values', install_file_path]
app_args_bootstrap = [base.NAME,
'--bootstrap-values', bootstrap_file_path]
app_args_config = [base.NAME, '--deploy-config', config_file_path]
args_dict = {'install-values': app_args_install,
'bootstrap-values': app_args_bootstrap,
'deploy-config': app_args_config}
for file in ['install-values', 'bootstrap-values',
'deploy-config']:
e = self.assertRaises(DCManagerClientException,
self.call,
subcloud_cmd.RedeploySubcloud,
app_args=args_dict[file])
self.assertTrue(f'{file} does not exist' in str(e))
@mock.patch('getpass.getpass', return_value='testpassword')
def test_restore_subcloud(self, getpass):
with tempfile.NamedTemporaryFile() as f: