From a8b807798382340746a7ef855b2b4175d96b00a8 Mon Sep 17 00:00:00 2001 From: twang4 Date: Wed, 23 Aug 2023 10:37:01 +0800 Subject: [PATCH] Add subcloud secondary status support and migration Add "subcloud migrate" to rehome a 'secondary' state subcloud. Update "dcmanager subcloud add --secondary" command Update "dcmanager subcloud update --bootstrap-address --bootstrap-values" to save data for day-2's rehome/migrate purpose. Update "dcmanager list subcloud --all" include show 'secondary/secondary-failed' subclouds Update result of "subcloud show", add rehome_data section. Usage: dcmanager subcloud add --secondary --bootstrap-address IP --bootstrap-values [yaml file] dcmanager subcloud update SUBCLOUD --bootstrap-address IP --bootstrap-values [yaml file] dcmanager subcloud list --all dcmanager subcloud show SUBCLOUD dcmanager subcloud migrate SUBCLOUD --sysadmin-password PASSWORD Test Plan: 1. PASS - Verify that the dcmanager help subcloud migrate shows the correct help message; 2. PASS - Run 'dcmanager subcloud add --secondary' and verify that the correct API call is made; 3. PASS - Run 'dcmanager subcloud update --bootstrap-address --bootstrap-values' and verify that the correct API call is made; 4. PASS - Run 'dcmanager subcloud migrate' and verify that the correct API call is made; 5. PASS - Verify that the subcloud name/id parameter is required. Story: 2010852 Task: 48489 Task: 48504 Depends-On: I9a308a4e2cc5057091ba195c4d05e9d1eb4a950c Change-Id: Idd10267370dd2cd562c6eacc0d24c47cf68e9fd5 Signed-off-by: Wang Tao --- .../dcmanagerclient/api/base.py | 5 +- .../api/v1/subcloud_manager.py | 15 ++ .../commands/v1/subcloud_manager.py | 123 +++++++++++++-- .../dcmanagerclient/shell.py | 1 + .../dcmanagerclient/tests/base.py | 48 ++++++ .../tests/v1/test_phased_subcloud_deploy.py | 44 ++++-- .../tests/v1/test_subcloud_group_manager.py | 5 +- .../tests/v1/test_subcloud_manager.py | 146 +++++++++++++----- .../dcmanagerclient/utils.py | 2 + 9 files changed, 327 insertions(+), 62 deletions(-) diff --git a/distributedcloud-client/dcmanagerclient/api/base.py b/distributedcloud-client/dcmanagerclient/api/base.py index d2ca321..d2c85c3 100644 --- a/distributedcloud-client/dcmanagerclient/api/base.py +++ b/distributedcloud-client/dcmanagerclient/api/base.py @@ -46,6 +46,7 @@ class Subcloud(Resource): 'created-at': 'created_at', 'updated-at': 'updated_at', 'group_id': 'group_id', + 'rehome_data': 'rehome_data', 'sync_status': 'sync_status', 'endpoint_sync_status': 'endpoint_sync_status', 'backup-status': 'backup_status', @@ -60,7 +61,8 @@ class Subcloud(Resource): systemcontroller_gateway_ip, created_at, updated_at, group_id, sync_status="unknown", endpoint_sync_status=None, backup_status=None, backup_datetime=None, - error_description=None, prestage_software_version=None): + error_description=None, prestage_software_version=None, + rehome_data=None): if endpoint_sync_status is None: endpoint_sync_status = {} self.manager = manager @@ -83,6 +85,7 @@ class Subcloud(Resource): self.created_at = created_at self.updated_at = updated_at self.group_id = group_id + self.rehome_data = rehome_data self.sync_status = sync_status self.endpoint_sync_status = endpoint_sync_status self.backup_status = backup_status diff --git a/distributedcloud-client/dcmanagerclient/api/v1/subcloud_manager.py b/distributedcloud-client/dcmanagerclient/api/v1/subcloud_manager.py index 114faaa..3848c6a 100644 --- a/distributedcloud-client/dcmanagerclient/api/v1/subcloud_manager.py +++ b/distributedcloud-client/dcmanagerclient/api/v1/subcloud_manager.py @@ -105,6 +105,16 @@ class subcloud_manager(base.ResourceManager): resource.append(self.json_to_resource(json_object)) return resource + def subcloud_migrate(self, url, data): + data = json.dumps(data) + resp = self.http_client.patch(url, data) + if resp.status_code != 200: + self._raise_api_exception(resp) + json_object = get_json(resp) + subcloud = self.resource_class.from_payload(self, json_object) + resource = [subcloud] + return resource + def _subcloud_prestage(self, url, data): data = json.dumps(data) resp = self.http_client.patch(url, data) @@ -190,3 +200,8 @@ class subcloud_manager(base.ResourceManager): data = kwargs.get('data') url = '/subclouds/%s/redeploy' % subcloud_ref return self.subcloud_redeploy(url, files, data) + + def migrate_subcloud(self, subcloud_ref, **kwargs): + data = kwargs.get('data') + url = '/subclouds/%s/migrate' % subcloud_ref + return self.subcloud_migrate(url, data) diff --git a/distributedcloud-client/dcmanagerclient/commands/v1/subcloud_manager.py b/distributedcloud-client/dcmanagerclient/commands/v1/subcloud_manager.py index e244401..6b9a10f 100644 --- a/distributedcloud-client/dcmanagerclient/commands/v1/subcloud_manager.py +++ b/distributedcloud-client/dcmanagerclient/commands/v1/subcloud_manager.py @@ -71,6 +71,7 @@ def detail_format(subcloud=None): 'management_gateway_ip', 'systemcontroller_gateway_ip', 'group_id', + 'rehome_data', 'created_at', 'updated_at', 'backup_status', @@ -93,6 +94,7 @@ def detail_format(subcloud=None): subcloud.management_gateway_ip, subcloud.systemcontroller_gateway_ip, subcloud.group_id, + subcloud.rehome_data, subcloud.created_at, subcloud.updated_at, subcloud.backup_status, @@ -194,6 +196,14 @@ class AddSubcloud(base.DCManagerShowOne): 'the subcloud with. If not specified, the current software ' 'release of the system controller will be used.' ) + + parser.add_argument( + '--secondary', + required=False, + action='store_true', + help='A flag indicating if this subcloud is a placeholder ' + 'for incoming subcloud rehoming.' + ) return parser def _get_resources(self, parsed_args): @@ -223,20 +233,27 @@ class AddSubcloud(base.DCManagerShowOne): error_msg = "migrate with deploy-config is not allowed" raise exceptions.DCManagerClientException(error_msg) + if parsed_args.secondary: + error_msg = "secondary with deploy-config is not allowed" + raise exceptions.DCManagerClientException(error_msg) + 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")) + # To add a secondary subcloud, + # do not need sysadmin_password + if not parsed_args.secondary: + # 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 is not None: if parsed_args.bmc_password is not None: @@ -256,6 +273,9 @@ class AddSubcloud(base.DCManagerShowOne): if parsed_args.release is not None: data['release'] = parsed_args.release + if parsed_args.secondary: + data['secondary'] = 'true' + return dcmanager_client.subcloud_manager.add_subcloud(files=files, data=data) @@ -268,11 +288,26 @@ class ListSubcloud(base.DCManagerLister): def get_parser(self, prog_name): parser = super(ListSubcloud, self).get_parser(prog_name) + parser.add_argument( + '--all', + required=False, + action='store_true', + help='List all subclouds include "secondary" state subclouds' + ) return parser def _get_resources(self, parsed_args): dcmanager_client = self.app.client_manager.subcloud_manager - return dcmanager_client.subcloud_manager.list_subclouds() + subclouds = dcmanager_client.subcloud_manager.list_subclouds() + + # for '--all' parameter, show all subclouds. + # for no parameter, hidden all 'secondary/secondary-failed' + # state subclouds. + if parsed_args.all: + return subclouds + filtered_subclouds = [s for s in subclouds if s.deploy_status not in + ('secondary', 'secondary-failed')] + return filtered_subclouds class ShowSubcloud(base.DCManagerShowOne): @@ -503,6 +538,12 @@ class UpdateSubcloud(base.DCManagerShowOne): 'provided you will be prompted. This parameter is only' ' valid if the --install-values are specified.' ) + parser.add_argument( + '--bootstrap-values', + required=False, + help='YAML file containing subcloud configuration settings. ' + 'Can be either a local file path or a URL.' + ) return parser def _get_resources(self, parsed_args): @@ -535,6 +576,7 @@ class UpdateSubcloud(base.DCManagerShowOne): data.get('management_end_ip'), data.get('bootstrap_address') ] + # Semantic check if the required arguments for updating admin network if all(value is not None for value in subcloud_network_values): # Prompt the user for the subcloud's password if it isn't provided @@ -545,8 +587,14 @@ class UpdateSubcloud(base.DCManagerShowOne): password = utils.prompt_for_password() data["sysadmin_password"] = base64.b64encode( password.encode("utf-8")) - # Not all network values exist - elif any(value is not None for value in subcloud_network_values): + # For subcloud network reconfiguration + # If any management_* presents, need all + # management_subnet/management_gateway_ip/ + # management_start_ip/management_end_ip/bootstrap_address + # presents. + elif any(value is not None and value != parsed_args.bootstrap_address + for value in subcloud_network_values): + # Not all network values exist error_msg = ( "For subcloud network reconfiguration request all the " "following parameters are necessary: --management-subnet, " @@ -569,7 +617,15 @@ class UpdateSubcloud(base.DCManagerShowOne): data["bmc_password"] = base64.b64encode( password.encode("utf-8")) - if not data: + # Update the bootstrap values from 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 + + if not (data or files): error_msg = "Nothing to update" raise exceptions.DCManagerClientException(error_msg) @@ -977,3 +1033,46 @@ class PrestageSubcloud(base.DCManagerShowOne): print(e) error_msg = "Unable to prestage subcloud %s" % (subcloud_ref) raise exceptions.DCManagerClientException(error_msg) + + +class MigrateSubcloud(base.DCManagerShowOne): + """Migrate a secondary status subcloud.""" + def _get_format_function(self): + return detail_format + + def get_parser(self, prog_name): + parser = super(MigrateSubcloud, self).get_parser(prog_name) + + parser.add_argument( + 'subcloud', + help='Name or ID of the subcloud to migrate.' + ) + + 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): + subcloud_ref = parsed_args.subcloud + dcmanager_client = self.app.client_manager.subcloud_manager + data = dict() + if parsed_args.sysadmin_password is not None: + data['sysadmin_password'] = base64.b64encode( + parsed_args.sysadmin_password.encode("utf-8")).decode("utf-8") + else: + password = utils.prompt_for_password() + data["sysadmin_password"] = base64.b64encode( + password.encode("utf-8")).decode("utf-8") + + try: + return dcmanager_client.subcloud_manager.migrate_subcloud( + subcloud_ref=subcloud_ref, data=data) + + except Exception as e: + print(e) + error_msg = "Unable to migrate subcloud %s" % (subcloud_ref) + raise exceptions.DCManagerClientException(error_msg) diff --git a/distributedcloud-client/dcmanagerclient/shell.py b/distributedcloud-client/dcmanagerclient/shell.py index ce3d454..30e3e6c 100644 --- a/distributedcloud-client/dcmanagerclient/shell.py +++ b/distributedcloud-client/dcmanagerclient/shell.py @@ -536,6 +536,7 @@ class DCManagerShell(app.App): 'subcloud redeploy': sm.RedeploySubcloud, 'subcloud restore': sm.RestoreSubcloud, 'subcloud prestage': sm.PrestageSubcloud, + 'subcloud migrate': sm.MigrateSubcloud, 'subcloud-backup create': sbm.CreateSubcloudBackup, 'subcloud-backup delete': sbm.DeleteSubcloudBackup, 'subcloud-backup restore': sbm.RestoreSubcloudBackup, diff --git a/distributedcloud-client/dcmanagerclient/tests/base.py b/distributedcloud-client/dcmanagerclient/tests/base.py index 72d9649..a0bd4c8 100644 --- a/distributedcloud-client/dcmanagerclient/tests/base.py +++ b/distributedcloud-client/dcmanagerclient/tests/base.py @@ -51,6 +51,7 @@ EXTERNAL_OAM_GATEWAY_ADDRESS = "10.10.10.1" EXTERNAL_OAM_FLOATING_ADDRESS = "10.10.10.12" DEFAULT_SUBCLOUD_GROUP_ID = '1' DEPLOY_CONFIG_SYNC_STATUS = 'Deployment: configurations up-to-date' +SUBCLOUD_REHOME_DATA = None BACKUP_STATUS = 'None' BACKUP_DATETIME = 'None' @@ -77,6 +78,29 @@ SUBCLOUD_RESOURCE = api_base.Subcloud( backup_status=BACKUP_STATUS, backup_datetime=BACKUP_DATETIME) +# Subcloud CLI resource object with peerid rehome data +SUBCLOUD_RESOURCE_WITH_PEERID_REHOME_DATA = api_base.Subcloud( + mock, + subcloud_id=ID, + name=NAME, + description=DESCRIPTION, + location=LOCATION, + software_version=SOFTWARE_VERSION, + management_state=MANAGEMENT_STATE, + availability_status=AVAILABILITY_STATUS, + deploy_status=DEPLOY_STATUS, + management_subnet=MANAGEMENT_SUBNET, + management_start_ip=MANAGEMENT_START_IP, + management_end_ip=MANAGEMENT_END_IP, + management_gateway_ip=MANAGEMENT_GATEWAY_IP, + systemcontroller_gateway_ip=SYSTEMCONTROLLER_GATEWAY_IP, + group_id=DEFAULT_SUBCLOUD_GROUP_ID, + rehome_data=SUBCLOUD_REHOME_DATA, + created_at=TIME_NOW, + updated_at=TIME_NOW, + backup_status=BACKUP_STATUS, + backup_datetime=BACKUP_DATETIME) + # Subcloud result values returned from various API calls (e.g. subcloud show) SUBCLOUD_FIELD_RESULT_LIST = ( ID, @@ -99,7 +123,31 @@ SUBCLOUD_FIELD_RESULT_LIST = ( BACKUP_DATETIME ) +# Subcloud result values returned from various API calls (e.g. subcloud show) +SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID_REHOME_DATA = ( + ID, + NAME, + DESCRIPTION, + LOCATION, + SOFTWARE_VERSION, + MANAGEMENT_STATE, + AVAILABILITY_STATUS, + DEPLOY_STATUS, + MANAGEMENT_SUBNET, + MANAGEMENT_START_IP, + MANAGEMENT_END_IP, + MANAGEMENT_GATEWAY_IP, + SYSTEMCONTROLLER_GATEWAY_IP, + DEFAULT_SUBCLOUD_GROUP_ID, + SUBCLOUD_REHOME_DATA, + TIME_NOW, + TIME_NOW, + BACKUP_STATUS, + BACKUP_DATETIME +) EMPTY_SUBCLOUD_FIELD_RESULT = (('',) * len(SUBCLOUD_FIELD_RESULT_LIST),) +EMPTY_SUBCLOUD_FIELD_RESULT_WITH_PEERID_REHOME_DATA = \ + (('',) * len(SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID_REHOME_DATA),) # Subcloud result values returned from subcloud list command SUBCLOUD_LIST_RESULT = ( 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 f2bd568..ac4cc46 100644 --- a/distributedcloud-client/dcmanagerclient/tests/v1/test_phased_subcloud_deploy.py +++ b/distributedcloud-client/dcmanagerclient/tests/v1/test_phased_subcloud_deploy.py @@ -44,7 +44,9 @@ class TestCLIPhasedSubcloudDeployManagerV1(base.BaseCommandTest): '--deploy-config', config_file_path, '--release', base.SOFTWARE_VERSION, ]) - self.assertEqual(base.SUBCLOUD_FIELD_RESULT_LIST, actual_call[1]) + self.assertEqual( + base.SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID_REHOME_DATA, + actual_call[1]) def test_subcloud_deploy_bootstrap(self): self.client.subcloud_deploy_bootstrap.return_value = [ @@ -59,7 +61,9 @@ class TestCLIPhasedSubcloudDeployManagerV1(base.BaseCommandTest): '--bootstrap-address', base.BOOTSTRAP_ADDRESS, '--bootstrap-values', bootstrap_file_path, ]) - self.assertEqual(base.SUBCLOUD_FIELD_RESULT_LIST, actual_call[1]) + self.assertEqual( + base.SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID_REHOME_DATA, + actual_call[1]) def test_install_subcloud(self): self.client.subcloud_deploy_install.return_value = [ @@ -72,7 +76,9 @@ class TestCLIPhasedSubcloudDeployManagerV1(base.BaseCommandTest): cmd.InstallPhasedSubcloudDeploy, app_args=[ base.NAME, '--install-values', file_path, ]) - self.assertEqual(base.SUBCLOUD_FIELD_RESULT_LIST, actual_call[1]) + self.assertEqual( + base.SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID_REHOME_DATA, + actual_call[1]) def test_install_subcloud_with_release(self): self.client.subcloud_deploy_install.return_value = [ @@ -87,7 +93,9 @@ class TestCLIPhasedSubcloudDeployManagerV1(base.BaseCommandTest): '--install-values', file_path, '--release', base.SOFTWARE_VERSION, ]) - self.assertEqual(base.SUBCLOUD_FIELD_RESULT_LIST, actual_call[1]) + self.assertEqual( + base.SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID_REHOME_DATA, + actual_call[1]) def test_install_subcloud_without_install_values(self): self.client.subcloud_deploy_install.return_value = [ @@ -96,7 +104,9 @@ class TestCLIPhasedSubcloudDeployManagerV1(base.BaseCommandTest): actual_call = self.call( cmd.InstallPhasedSubcloudDeploy, app_args=[base.NAME]) - self.assertEqual(base.SUBCLOUD_FIELD_RESULT_LIST, actual_call[1]) + self.assertEqual( + base.SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID_REHOME_DATA, + actual_call[1]) def test_install_file_does_not_exist(self): self.client.subcloud_deploy_install.return_value = [ @@ -122,7 +132,9 @@ class TestCLIPhasedSubcloudDeployManagerV1(base.BaseCommandTest): actual_call = self.call( cmd.ConfigPhasedSubcloudDeploy, app_args=[base.NAME, '--deploy-config', file_path]) - self.assertEqual(base.SUBCLOUD_FIELD_RESULT_LIST, actual_call[1]) + self.assertEqual( + base.SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID_REHOME_DATA, + actual_call[1]) def test_configure_file_does_not_exist(self): self.client.subcloud_deploy_config.return_value = [ @@ -144,7 +156,9 @@ class TestCLIPhasedSubcloudDeployManagerV1(base.BaseCommandTest): actual_call = self.call( cmd.CompletePhasedSubcloudDeploy, app_args=[base.NAME]) - self.assertEqual(base.SUBCLOUD_FIELD_RESULT_LIST, actual_call[1]) + self.assertEqual( + base.SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID_REHOME_DATA, + actual_call[1]) def test_abort_subcloud(self): self.client.subcloud_deploy_abort.return_value = [ @@ -152,7 +166,9 @@ class TestCLIPhasedSubcloudDeployManagerV1(base.BaseCommandTest): actual_call = self.call( cmd.AbortPhasedSubcloudDeploy, app_args=[base.NAME]) - self.assertEqual(base.SUBCLOUD_FIELD_RESULT_LIST, actual_call[1]) + self.assertEqual( + base.SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID_REHOME_DATA, + actual_call[1]) def test_subcloud_deploy_resume_all_parameters(self): self.client.subcloud_deploy_resume.return_value = [ @@ -175,7 +191,9 @@ class TestCLIPhasedSubcloudDeployManagerV1(base.BaseCommandTest): '--deploy-config', config_file_path, '--release', base.SOFTWARE_VERSION, ]) - self.assertEqual(base.SUBCLOUD_FIELD_RESULT_LIST, actual_call[1]) + self.assertEqual( + base.SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID_REHOME_DATA, + actual_call[1]) def test_subcloud_deploy_resume_missing_files(self): self.client.subcloud_deploy_resume.return_value = [ @@ -225,7 +243,9 @@ class TestCLIPhasedSubcloudDeployManagerV1(base.BaseCommandTest): actual_call = self.call( cmd.PhasedSubcloudDeployResume, app_args=[base.NAME]) - self.assertEqual(base.SUBCLOUD_FIELD_RESULT_LIST, actual_call[1]) + self.assertEqual( + base.SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID_REHOME_DATA, + actual_call[1]) def test_subcloud_deploy_resume_no_files_only_release(self): self.client.subcloud_deploy_resume.return_value = [ @@ -236,4 +256,6 @@ class TestCLIPhasedSubcloudDeployManagerV1(base.BaseCommandTest): base.NAME, '--release', base.SOFTWARE_VERSION, ]) - self.assertEqual(base.SUBCLOUD_FIELD_RESULT_LIST, actual_call[1]) + self.assertEqual( + base.SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID_REHOME_DATA, + actual_call[1]) diff --git a/distributedcloud-client/dcmanagerclient/tests/v1/test_subcloud_group_manager.py b/distributedcloud-client/dcmanagerclient/tests/v1/test_subcloud_group_manager.py index 3a87c2a..67b2e08 100644 --- a/distributedcloud-client/dcmanagerclient/tests/v1/test_subcloud_group_manager.py +++ b/distributedcloud-client/dcmanagerclient/tests/v1/test_subcloud_group_manager.py @@ -84,8 +84,9 @@ class TestCLISubcloudGroupManagerV1(base.BaseCommandTest): app_args=[ID]) self.client.subcloud_group_manager.subcloud_group_list_subclouds.\ assert_called_once_with(ID) - self.assertEqual([base.SUBCLOUD_FIELD_RESULT_LIST], - actual_call[1]) + self.assertEqual([ + base.SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID_REHOME_DATA], + actual_call[1]) def test_delete_subcloud_group_by_id(self): self.call(subcloud_group_cmd.DeleteSubcloudGroup, app_args=[ID]) diff --git a/distributedcloud-client/dcmanagerclient/tests/v1/test_subcloud_manager.py b/distributedcloud-client/dcmanagerclient/tests/v1/test_subcloud_manager.py index 9253f2a..95cb2e4 100644 --- a/distributedcloud-client/dcmanagerclient/tests/v1/test_subcloud_manager.py +++ b/distributedcloud-client/dcmanagerclient/tests/v1/test_subcloud_manager.py @@ -53,8 +53,9 @@ class TestCLISubcloudManagerV1(base.BaseCommandTest): self.client.subcloud_manager.subcloud_detail.\ return_value = [base.SUBCLOUD_RESOURCE] actual_call = self.call(subcloud_cmd.ShowSubcloud, app_args=[base.ID]) - self.assertEqual(base.SUBCLOUD_FIELD_RESULT_LIST, - actual_call[1]) + self.assertEqual( + base.SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID_REHOME_DATA, + actual_call[1]) def test_show_subcloud_with_additional_detail(self): SUBCLOUD_WITH_ADDITIONAL_DETAIL = copy.copy(base.SUBCLOUD_RESOURCE) @@ -66,16 +67,18 @@ class TestCLISubcloudManagerV1(base.BaseCommandTest): return_value = [SUBCLOUD_WITH_ADDITIONAL_DETAIL] actual_call = self.call(subcloud_cmd.ShowSubcloud, app_args=[base.ID, '--detail']) - self.assertEqual(base.SUBCLOUD_FIELD_RESULT_LIST + - (base.EXTERNAL_OAM_FLOATING_ADDRESS, - base.DEPLOY_CONFIG_SYNC_STATUS), - actual_call[1]) + self.assertEqual( + base.SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID_REHOME_DATA + + (base.EXTERNAL_OAM_FLOATING_ADDRESS, + base.DEPLOY_CONFIG_SYNC_STATUS), + actual_call[1]) def test_show_subcloud_negative(self): self.client.subcloud_manager.subcloud_detail.return_value = [] actual_call = self.call(subcloud_cmd.ShowSubcloud, app_args=[base.ID]) - self.assertEqual(base.EMPTY_SUBCLOUD_FIELD_RESULT, - actual_call[1]) + self.assertEqual( + base.EMPTY_SUBCLOUD_FIELD_RESULT_WITH_PEERID_REHOME_DATA, + actual_call[1]) @mock.patch('getpass.getpass', return_value='testpassword') def test_add_subcloud(self, getpass): @@ -98,8 +101,12 @@ class TestCLISubcloudManagerV1(base.BaseCommandTest): '--bootstrap-values', file_path, '--release', base.SOFTWARE_VERSION, ]) - self.assertEqual(base.SUBCLOUD_FIELD_RESULT_LIST, actual_call1[1]) - self.assertEqual(base.SUBCLOUD_FIELD_RESULT_LIST, actual_call2[1]) + self.assertEqual( + base.SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID_REHOME_DATA, + actual_call1[1]) + self.assertEqual( + base.SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID_REHOME_DATA, + actual_call2[1]) @mock.patch('getpass.getpass', return_value='testpassword') def test_add_migrate_subcloud(self, getpass): @@ -115,7 +122,9 @@ class TestCLISubcloudManagerV1(base.BaseCommandTest): '--bootstrap-values', file_path, '--migrate', ]) - self.assertEqual(base.SUBCLOUD_FIELD_RESULT_LIST, actual_call[1]) + self.assertEqual( + base.SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID_REHOME_DATA, + actual_call[1]) @mock.patch('getpass.getpass', return_value='testpassword') def test_add_migrate_subcloud_with_deploy_config(self, getpass): @@ -142,7 +151,9 @@ class TestCLISubcloudManagerV1(base.BaseCommandTest): return_value = [base.SUBCLOUD_RESOURCE] actual_call = self.call( subcloud_cmd.UnmanageSubcloud, app_args=[base.ID]) - self.assertEqual(base.SUBCLOUD_FIELD_RESULT_LIST, actual_call[1]) + self.assertEqual( + base.SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID_REHOME_DATA, + actual_call[1]) def test_unmanage_subcloud_without_subcloud_id(self): self.assertRaises(SystemExit, self.call, @@ -153,7 +164,9 @@ class TestCLISubcloudManagerV1(base.BaseCommandTest): return_value = [base.SUBCLOUD_RESOURCE] actual_call = self.call( subcloud_cmd.ManageSubcloud, app_args=[base.ID]) - self.assertEqual(base.SUBCLOUD_FIELD_RESULT_LIST, actual_call[1]) + self.assertEqual( + base.SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID_REHOME_DATA, + actual_call[1]) def test_manage_subcloud_without_subcloud_id(self): self.assertRaises(SystemExit, self.call, @@ -162,18 +175,24 @@ class TestCLISubcloudManagerV1(base.BaseCommandTest): def test_update_subcloud(self): self.client.subcloud_manager.update_subcloud.\ return_value = [base.SUBCLOUD_RESOURCE] - actual_call = self.call( - subcloud_cmd.UpdateSubcloud, - app_args=[base.ID, - '--description', 'subcloud description', - '--location', 'subcloud location', - '--sysadmin-password', 'testpassword', - '--management-subnet', 'subcloud network subnet', - '--management-gateway-ip', 'subcloud network gateway ip', - '--management-start-ip', 'sc network start ip', - '--management-end-ip', 'subcloud network end ip', - '--bootstrap-address', 'subcloud bootstrap address']) - self.assertEqual(base.SUBCLOUD_FIELD_RESULT_LIST, actual_call[1]) + with tempfile.NamedTemporaryFile(mode='w') as f_bootstrap: + bootstrap_file_path = os.path.abspath(f_bootstrap.name) + actual_call = self.call( + subcloud_cmd.UpdateSubcloud, + app_args=[ + base.ID, + '--description', 'subcloud description', + '--location', 'subcloud location', + '--sysadmin-password', 'testpassword', + '--management-subnet', 'subcloud network subnet', + '--management-gateway-ip', 'subcloud network gateway ip', + '--management-start-ip', 'sc network start ip', + '--management-end-ip', 'subcloud network end ip', + '--bootstrap-address', 'subcloud bootstrap address', + '--bootstrap-values', bootstrap_file_path]) + self.assertEqual( + base.SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID_REHOME_DATA, + actual_call[1]) @mock.patch('getpass.getpass', return_value='testpassword') def test_success_reconfigure_subcloud(self, getpass): @@ -189,7 +208,8 @@ class TestCLISubcloudManagerV1(base.BaseCommandTest): app_args=[base.ID, '--deploy-config', file_path]) - expected_result = list(base.SUBCLOUD_FIELD_RESULT_LIST) + expected_result = list( + base.SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID_REHOME_DATA) expected_result[7] = base.DEPLOY_STATE_PRE_DEPLOY self.assertEqual(tuple(expected_result), actual_call[1]) @@ -221,7 +241,9 @@ class TestCLISubcloudManagerV1(base.BaseCommandTest): actual_call = self.call( subcloud_cmd.ReinstallSubcloud, app_args=[base.ID, '--bootstrap-values', file_path]) - self.assertEqual(base.SUBCLOUD_FIELD_RESULT_LIST, actual_call[1]) + self.assertEqual( + base.SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID_REHOME_DATA, + actual_call[1]) @mock.patch('getpass.getpass', return_value='testpassword') @mock.patch('six.moves.input', return_value='reinstall') @@ -245,8 +267,12 @@ class TestCLISubcloudManagerV1(base.BaseCommandTest): '--bootstrap-values', file_path, '--release', base.SOFTWARE_VERSION, ]) - self.assertEqual(base.SUBCLOUD_FIELD_RESULT_LIST, actual_call1[1]) - self.assertEqual(base.SUBCLOUD_FIELD_RESULT_LIST, actual_call2[1]) + self.assertEqual( + base.SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID_REHOME_DATA, + actual_call1[1]) + self.assertEqual( + base.SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID_REHOME_DATA, + actual_call2[1]) @mock.patch('getpass.getpass', return_value='testpassword') @mock.patch('six.moves.input', return_value='reinstall') @@ -288,7 +314,9 @@ class TestCLISubcloudManagerV1(base.BaseCommandTest): '--deploy-config', config_file_path, '--release', base.SOFTWARE_VERSION, ]) - self.assertEqual(base.SUBCLOUD_FIELD_RESULT_LIST, actual_call[1]) + self.assertEqual( + base.SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID_REHOME_DATA, + actual_call[1]) @mock.patch('getpass.getpass', return_value='testpassword') @mock.patch('six.moves.input', return_value='redeploy') @@ -298,7 +326,9 @@ class TestCLISubcloudManagerV1(base.BaseCommandTest): actual_call = self.call( subcloud_cmd.RedeploySubcloud, app_args=[base.ID]) - self.assertEqual(base.SUBCLOUD_FIELD_RESULT_LIST, actual_call[1]) + self.assertEqual( + base.SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID_REHOME_DATA, + actual_call[1]) @mock.patch('getpass.getpass', return_value='testpassword') @mock.patch('six.moves.input', return_value='redeploy') @@ -355,8 +385,9 @@ class TestCLISubcloudManagerV1(base.BaseCommandTest): app_args=[base.ID, '--sysadmin-password', 'testpassword', '--force']) - self.assertEqual(base.SUBCLOUD_FIELD_RESULT_LIST, - actual_call_without_release[1]) + self.assertEqual( + base.SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID_REHOME_DATA, + actual_call_without_release[1]) def test_prestage_without_subcloudID(self): self.assertRaises(SystemExit, self.call, @@ -374,6 +405,49 @@ class TestCLISubcloudManagerV1(base.BaseCommandTest): '--sysadmin-password', 'testpassword', '--force', '--release', base.SOFTWARE_VERSION]) - self.assertEqual(base.SUBCLOUD_FIELD_RESULT_LIST + - (base.SOFTWARE_VERSION,), - actual_call_with_release[1]) + self.assertEqual( + base.SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID_REHOME_DATA + + (base.SOFTWARE_VERSION,), + actual_call_with_release[1]) + + def test_migrate_subcloud(self): + self.client.subcloud_manager.migrate_subcloud. \ + return_value = [base.SUBCLOUD_RESOURCE] + actual_call_without_release = self.call( + subcloud_cmd.MigrateSubcloud, + app_args=[base.ID, + '--sysadmin-password', 'testpassword']) + self.assertEqual( + base.SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID_REHOME_DATA, + actual_call_without_release[1]) + self.assertRaises(SystemExit, self.call, + subcloud_cmd.MigrateSubcloud, app_args=[]) + + def test_add_secondary_subcloud(self): + self.client.subcloud_manager.add_subcloud.\ + return_value = [base.SUBCLOUD_RESOURCE] + + with tempfile.NamedTemporaryFile(mode='w') as f_bootstrap: + bootstrap_file_path = os.path.abspath(f_bootstrap.name) + actual_call = self.call( + subcloud_cmd.AddSubcloud, + app_args=[ + '--bootstrap-address', base.BOOTSTRAP_ADDRESS, + '--bootstrap-values', bootstrap_file_path, + '--sysadmin-password', 'testpassword', + '--secondary', + ]) + self.assertEqual( + base.SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID_REHOME_DATA, + actual_call[1]) + with tempfile.NamedTemporaryFile() as f_config: + config_file_path = os.path.abspath(f_config.name) + self.assertRaises( + DCManagerClientException, self.call, + subcloud_cmd.AddSubcloud, app_args=[ + '--bootstrap-address', base.BOOTSTRAP_ADDRESS, + '--bootstrap-values', bootstrap_file_path, + '--sysadmin-password', 'testpassword', + '--deploy-config', config_file_path, + '--secondary' + ]) diff --git a/distributedcloud-client/dcmanagerclient/utils.py b/distributedcloud-client/dcmanagerclient/utils.py index 701ad3f..1d570dc 100644 --- a/distributedcloud-client/dcmanagerclient/utils.py +++ b/distributedcloud-client/dcmanagerclient/utils.py @@ -120,6 +120,7 @@ def subcloud_detail_format(subcloud=None): 'management_gateway_ip', 'systemcontroller_gateway_ip', 'group_id', + 'rehome_data', 'created_at', 'updated_at', 'backup_status', @@ -142,6 +143,7 @@ def subcloud_detail_format(subcloud=None): subcloud.management_gateway_ip, subcloud.systemcontroller_gateway_ip, subcloud.group_id, + subcloud.rehome_data, subcloud.created_at, subcloud.updated_at, subcloud.backup_status,