diff --git a/distributedcloud-client/dcmanagerclient/api/base.py b/distributedcloud-client/dcmanagerclient/api/base.py index d2c85c3..f0a5c64 100644 --- a/distributedcloud-client/dcmanagerclient/api/base.py +++ b/distributedcloud-client/dcmanagerclient/api/base.py @@ -51,7 +51,8 @@ class Subcloud(Resource): 'endpoint_sync_status': 'endpoint_sync_status', 'backup-status': 'backup_status', 'backup-datetime': 'backup_datetime', - 'prestage-software-version': 'prestage_software_version' + 'prestage-software-version': 'prestage_software_version', + 'region-name': 'region_name' } def __init__(self, manager, subcloud_id, name, description, location, @@ -62,7 +63,7 @@ class Subcloud(Resource): group_id, sync_status="unknown", endpoint_sync_status=None, backup_status=None, backup_datetime=None, error_description=None, prestage_software_version=None, - rehome_data=None): + rehome_data=None, region_name=None): if endpoint_sync_status is None: endpoint_sync_status = {} self.manager = manager @@ -91,6 +92,7 @@ class Subcloud(Resource): self.backup_status = backup_status self.backup_datetime = backup_datetime self.prestage_software_version = prestage_software_version + self.region_name = region_name @classmethod def from_payload(cls, manager, payload): diff --git a/distributedcloud-client/dcmanagerclient/api/v1/subcloud_manager.py b/distributedcloud-client/dcmanagerclient/api/v1/subcloud_manager.py index fd73938..2067cf1 100644 --- a/distributedcloud-client/dcmanagerclient/api/v1/subcloud_manager.py +++ b/distributedcloud-client/dcmanagerclient/api/v1/subcloud_manager.py @@ -118,6 +118,8 @@ class subcloud_manager(base.ResourceManager): resource[0].oam_floating_ip = json_object['oam_floating_ip'] resource[0].deploy_config_sync_status = \ json_object['deploy_config_sync_status'] + resource[0].region_name = \ + json_object['region_name'] return resource def add_subcloud(self, **kwargs): diff --git a/distributedcloud-client/dcmanagerclient/commands/v1/subcloud_group_manager.py b/distributedcloud-client/dcmanagerclient/commands/v1/subcloud_group_manager.py index 0d34089..df9337a 100644 --- a/distributedcloud-client/dcmanagerclient/commands/v1/subcloud_group_manager.py +++ b/distributedcloud-client/dcmanagerclient/commands/v1/subcloud_group_manager.py @@ -18,6 +18,8 @@ from osc_lib.command import command from dcmanagerclient.commands.v1 import base from dcmanagerclient.commands.v1.subcloud_manager import detail_format +from dcmanagerclient.commands.v1.subcloud_manager \ + import update_fields_values from dcmanagerclient import exceptions @@ -159,8 +161,10 @@ class ListSubcloudGroupSubclouds(base.DCManagerLister): def _get_resources(self, parsed_args): subcloud_group_ref = parsed_args.group dcmanager_client = self.app.client_manager.subcloud_group_manager - return dcmanager_client.subcloud_group_manager. \ + result = dcmanager_client.subcloud_group_manager. \ subcloud_group_list_subclouds(subcloud_group_ref) + update_fields_values(result) + return result class ShowSubcloudGroup(base.DCManagerShowOne): diff --git a/distributedcloud-client/dcmanagerclient/commands/v1/subcloud_manager.py b/distributedcloud-client/dcmanagerclient/commands/v1/subcloud_manager.py index 9fb8edb..e10dae4 100644 --- a/distributedcloud-client/dcmanagerclient/commands/v1/subcloud_manager.py +++ b/distributedcloud-client/dcmanagerclient/commands/v1/subcloud_manager.py @@ -25,6 +25,11 @@ from dcmanagerclient import exceptions from dcmanagerclient import utils +SET_FIELD_VALUE_DICT = { + "region_name": None +} + + def format(subcloud=None): columns = ( 'id', @@ -119,12 +124,31 @@ def detail_format(subcloud=None): if subcloud.deploy_config_sync_status != "unknown": columns += ('deploy_config_sync_status',) data += (subcloud.deploy_config_sync_status,) + + if subcloud.region_name is not None: + columns += ('region_name',) + data += (subcloud.region_name,) else: data = (tuple('' for _ in range(len(columns))),) return columns, data +# The API is returning the region_name field, however only the list +# and show commands should consider the region name field. +# The other commands do not required it, since the output should +# not show that field +def update_fields_values(result): + + if len(result) == 0: + return + + for i in range(len(result)): + for field, value in SET_FIELD_VALUE_DICT.items(): + if field in dir(result[i]): + setattr(result[i], field, value) + + class AddSubcloud(base.DCManagerShowOne): """Add a new subcloud.""" @@ -134,6 +158,12 @@ class AddSubcloud(base.DCManagerShowOne): def get_parser(self, prog_name): parser = super(AddSubcloud, self).get_parser(prog_name) + parser.add_argument( + '--name', + required=False, + help='Subcloud name' + ) + parser.add_argument( '--bootstrap-address', required=True, @@ -276,8 +306,18 @@ class AddSubcloud(base.DCManagerShowOne): if parsed_args.secondary: data['secondary'] = 'true' - return dcmanager_client.subcloud_manager.add_subcloud(files=files, - data=data) + if parsed_args.name is not None: + if parsed_args.migrate: + data['name'] = parsed_args.name + else: + error_msg = 'The --name option can only be used with \ + --migrate option.' + raise exceptions.DCManagerClientException(error_msg) + + result = dcmanager_client.subcloud_manager.add_subcloud(files=files, + data=data) + update_fields_values(result) + return result class ListSubcloud(base.DCManagerLister): @@ -407,8 +447,10 @@ class UnmanageSubcloud(base.DCManagerShowOne): kwargs = dict() kwargs['management-state'] = 'unmanaged' try: - return dcmanager_client.subcloud_manager.update_subcloud( + result = dcmanager_client.subcloud_manager.update_subcloud( subcloud_ref, files=None, data=kwargs) + update_fields_values(result) + return result except Exception as e: print(e) error_msg = "Unable to unmanage subcloud %s" % (subcloud_ref) @@ -447,8 +489,10 @@ class ManageSubcloud(base.DCManagerShowOne): kwargs['force'] = 'true' try: - return dcmanager_client.subcloud_manager.update_subcloud( + result = dcmanager_client.subcloud_manager.update_subcloud( subcloud_ref, files=None, data=kwargs) + update_fields_values(result) + return result except Exception as e: print(e) error_msg = "Unable to manage subcloud %s" % (subcloud_ref) @@ -469,6 +513,12 @@ class UpdateSubcloud(base.DCManagerShowOne): help='Name or ID of the subcloud to update.' ) + parser.add_argument( + '--name', + required=False, + help='Name of subcloud.' + ) + parser.add_argument( '--description', required=False, @@ -552,6 +602,8 @@ class UpdateSubcloud(base.DCManagerShowOne): files = dict() data = dict() + if parsed_args.name: + data['name'] = parsed_args.name if parsed_args.description: data['description'] = parsed_args.description if parsed_args.location: @@ -630,8 +682,10 @@ class UpdateSubcloud(base.DCManagerShowOne): raise exceptions.DCManagerClientException(error_msg) try: - return dcmanager_client.subcloud_manager.update_subcloud( + result = dcmanager_client.subcloud_manager.update_subcloud( subcloud_ref, files=files, data=data) + update_fields_values(result) + return result except Exception as e: print(e) error_msg = "Unable to update subcloud %s" % (subcloud_ref) @@ -892,9 +946,11 @@ class PrestageSubcloud(base.DCManagerShowOne): data['release'] = parsed_args.release try: - return dcmanager_client.subcloud_manager.\ + result = dcmanager_client.subcloud_manager.\ prestage_subcloud( subcloud_ref=subcloud_ref, data=data) + update_fields_values(result) + return result except Exception as e: print(e) @@ -936,8 +992,10 @@ class MigrateSubcloud(base.DCManagerShowOne): password.encode("utf-8")).decode("utf-8") try: - return dcmanager_client.subcloud_manager.migrate_subcloud( + result = dcmanager_client.subcloud_manager.migrate_subcloud( subcloud_ref=subcloud_ref, data=data) + update_fields_values(result) + return result except Exception as e: print(e) diff --git a/distributedcloud-client/dcmanagerclient/tests/base.py b/distributedcloud-client/dcmanagerclient/tests/base.py index a0bd4c8..da3cb2f 100644 --- a/distributedcloud-client/dcmanagerclient/tests/base.py +++ b/distributedcloud-client/dcmanagerclient/tests/base.py @@ -39,6 +39,7 @@ AVAILABILITY_STATUS = 'offline' DEPLOY_STATUS = 'not-deployed' SYNC_STATUS = 'unknown' ERROR_DESCRIPTION = 'No errors present' +REGION_NAME = '2ec93dfb654846909efe61d1b39dd2ce' DEPLOY_STATE_PRE_DEPLOY = 'pre-deploy' DEPLOY_STATE_PRE_RESTORE = 'pre-restore' MANAGEMENT_SUBNET = '192.168.101.0/24' @@ -55,6 +56,12 @@ SUBCLOUD_REHOME_DATA = None BACKUP_STATUS = 'None' BACKUP_DATETIME = 'None' +# Useful for subcloud name configuration +NAME_SC2 = "subcloud2" +SET_FIELD_VALUE_DICT = { + "region_name": None +} + # Subcloud CLI resource object SUBCLOUD_RESOURCE = api_base.Subcloud( mock, diff --git a/distributedcloud-client/dcmanagerclient/tests/v1/test_subcloud_manager.py b/distributedcloud-client/dcmanagerclient/tests/v1/test_subcloud_manager.py index acd2df9..9c9454d 100644 --- a/distributedcloud-client/dcmanagerclient/tests/v1/test_subcloud_manager.py +++ b/distributedcloud-client/dcmanagerclient/tests/v1/test_subcloud_manager.py @@ -63,6 +63,8 @@ class TestCLISubcloudManagerV1(base.BaseCommandTest): base.EXTERNAL_OAM_FLOATING_ADDRESS SUBCLOUD_WITH_ADDITIONAL_DETAIL.deploy_config_sync_status = \ base.DEPLOY_CONFIG_SYNC_STATUS + SUBCLOUD_WITH_ADDITIONAL_DETAIL.region_name = \ + base.REGION_NAME self.client.subcloud_manager.subcloud_additional_details.\ return_value = [SUBCLOUD_WITH_ADDITIONAL_DETAIL] actual_call = self.call(subcloud_cmd.ShowSubcloud, @@ -70,7 +72,7 @@ class TestCLISubcloudManagerV1(base.BaseCommandTest): self.assertEqual( base.SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID_REHOME_DATA + (base.EXTERNAL_OAM_FLOATING_ADDRESS, - base.DEPLOY_CONFIG_SYNC_STATUS), + base.DEPLOY_CONFIG_SYNC_STATUS, base.REGION_NAME), actual_call[1]) def test_show_subcloud_negative(self): @@ -146,6 +148,70 @@ class TestCLISubcloudManagerV1(base.BaseCommandTest): '--migrate', ]) + @mock.patch('getpass.getpass', return_value='testpassword') + def test_add_migrate_subcloud_with_name_change(self, getpass): + SUBCLOUD_RESOURCE = copy.\ + copy(base.SUBCLOUD_RESOURCE_WITH_PEERID_REHOME_DATA) + SUBCLOUD_RESOURCE.name = base.NAME_SC2 + self.client.subcloud_manager.add_subcloud.\ + return_value = [SUBCLOUD_RESOURCE] + + with tempfile.NamedTemporaryFile(mode='w') as f: + yaml.dump(base.FAKE_BOOTSTRAP_VALUES, f) + file_path = os.path.abspath(f.name) + actual_call = self.call( + subcloud_cmd.AddSubcloud, app_args=[ + '--bootstrap-address', base.BOOTSTRAP_ADDRESS, + '--bootstrap-values', file_path, + '--migrate', + '--name', base.NAME_SC2 + ]) + SUBCLOUD_FIELD_RESULT = base.\ + SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID_REHOME_DATA + RESULT_LIST = list(SUBCLOUD_FIELD_RESULT) + RESULT_LIST[1] = base.NAME_SC2 + self.assertEqual(tuple(RESULT_LIST), actual_call[1]) + + def test_rename_subcloud(self): + SUBCLOUD_RENAMED = copy.\ + copy(base.SUBCLOUD_RESOURCE_WITH_PEERID_REHOME_DATA) + SUBCLOUD_RENAMED.name = base.NAME_SC2 + self.client.subcloud_manager.update_subcloud.\ + return_value = [SUBCLOUD_RENAMED] + + # Rename by id + actual_call1 = self.call( + subcloud_cmd.UpdateSubcloud, + app_args=[base.ID, '--name', base.NAME_SC2]) + results_by_id = \ + list(base.SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID_REHOME_DATA) + results_by_id[1] = base.NAME_SC2 + + # Rename by name + actual_call2 = self.call( + subcloud_cmd.UpdateSubcloud, + app_args=[base.NAME, '--name', base.NAME_SC2]) + + SUBCLOUD_FIELD_RESULT = base.\ + SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID_REHOME_DATA + results_by_name = list(SUBCLOUD_FIELD_RESULT) + results_by_name[1] = base.NAME_SC2 + + self.assertEqual(tuple(results_by_id), actual_call1[1]) + self.assertEqual(tuple(results_by_name), actual_call2[1]) + + def test_update_fields_values(self): + SUBCLOUD_WITH_REGION_DETAIL = copy.copy(base.SUBCLOUD_RESOURCE) + SUBCLOUD_WITH_REGION_DETAIL.region_name = base.REGION_NAME + + SUBCLOUD_WITH_REGION_NONE = copy.copy(base.SUBCLOUD_RESOURCE) + SUBCLOUD_WITH_REGION_NONE.region_name = None + + subcloud_cmd.update_fields_values([SUBCLOUD_WITH_REGION_DETAIL]) + + self.assertEqual(SUBCLOUD_WITH_REGION_DETAIL.region_name, + SUBCLOUD_WITH_REGION_NONE.region_name) + def test_unmanage_subcloud(self): self.client.subcloud_manager.update_subcloud.\ return_value = [base.SUBCLOUD_RESOURCE]