From 04d2517df39a7d106a3ec3ac0b0b7fe74576c4e3 Mon Sep 17 00:00:00 2001 From: twang4 Date: Tue, 29 Aug 2023 08:36:16 +0800 Subject: [PATCH] Add Subcloud Peer group management Group of the current managed subclouds which are supposed to be duplicated in a peer site as secondary subclouds. This commit add subcloud-peer-group CMD of create/delete/update/show/list/ list-subclouds of a subcloud-peer-group Update subcloud update --peer-group PEER_GROUP command CLI example: dcmanager subcloud update SUBCLOUD --peer-group PEER_GROUP dcmanager subcloud-peer-group add \ --peer-group-name NAME dcmanager subcloud-peer-group delete PEER_GROUP dcmanager subcloud-peer-group list dcmanager subcloud-peer-group list-subclouds PEER_GROUP dcmanager subcloud-peer-group migrate PEER_GROUP\ --sysadmin-password PASSWORD dcmanager subcloud-peer-group show PEER_GROUP dcmanager subcloud-peer-group status PEER_GROUP dcmanager subcloud-peer-group update PEER_GROUP [--peer-group-name PEER_GROUP_NAME] [--group-priority GROUP_PRIORITY] [--group-state GROUP_STATE] [--max-subcloud-rehoming MAX_SUBCLOUD_REHOMING Test Plan: 1. PASS - Create a subcloud-peer-group 2. PASS - Add a subcloud, update the peer-group-id as a existing subcloud-peer-group successfully; 3. PASS - Verify subcloud-peer-group list-subclouds can get the expected Subcloud above successfully; 4. PASS - dcmanager subcloud-peer-group update PEER_GROUP_NAME --group-priority/--group-state/--max-subcloud-rehoming of a subcloud-peer-group successfully; 5. PASS - 'dcmanager subcloud-peer-group status' and verify the status was retrieved successfully. 6. PASS - 'dcmaanger subcloud-peer-group delete' and verify the peer group is deleted successfully. 7. PASS - Test 'dcmanager subcloud-peer-group show' command 8. PASS - Test 'dcmanager subcloud-peer-group list' command 9. PASS - Test the help output of all commands and verify that the output is correct. 10.PASS - Test the commands 'dcmanager subcloud-peer-group migrate PEER_GROUP' without passing the argument and verify that it prompts for the password. Story: 2010852 Task: 48490 Task: 48491 Depends-On: I93d0808b8cf02eba0e6f687007df42e2d2ea1848 Depends-On: I5f7e8862e543d61f49a5456f989c94689db83318 Change-Id: I57ae769c179dd4e613ce4edd432692540deea678 Signed-off-by: Wang Tao --- .../dcmanagerclient/api/base.py | 4 +- .../dcmanagerclient/api/v1/client.py | 4 + .../api/v1/subcloud_peer_group_manager.py | 168 ++++++++ .../commands/v1/subcloud_manager.py | 9 + .../v1/subcloud_peer_group_manager.py | 365 ++++++++++++++++++ .../dcmanagerclient/shell.py | 13 +- .../dcmanagerclient/tests/base.py | 4 + .../tests/v1/test_subcloud_manager.py | 3 +- .../v1/test_subcloud_peer_group_manager.py | 131 +++++++ .../dcmanagerclient/utils.py | 2 + 10 files changed, 700 insertions(+), 3 deletions(-) create mode 100644 distributedcloud-client/dcmanagerclient/api/v1/subcloud_peer_group_manager.py create mode 100644 distributedcloud-client/dcmanagerclient/commands/v1/subcloud_peer_group_manager.py create mode 100644 distributedcloud-client/dcmanagerclient/tests/v1/test_subcloud_peer_group_manager.py diff --git a/distributedcloud-client/dcmanagerclient/api/base.py b/distributedcloud-client/dcmanagerclient/api/base.py index f0a5c64..2e0f29e 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', + 'peer_group_id': 'peer_group_id', 'rehome_data': 'rehome_data', 'sync_status': 'sync_status', 'endpoint_sync_status': 'endpoint_sync_status', @@ -63,7 +64,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, region_name=None): + peer_group_id=None, rehome_data=None, region_name=None): if endpoint_sync_status is None: endpoint_sync_status = {} self.manager = manager @@ -86,6 +87,7 @@ class Subcloud(Resource): self.created_at = created_at self.updated_at = updated_at self.group_id = group_id + self.peer_group_id = peer_group_id self.rehome_data = rehome_data self.sync_status = sync_status self.endpoint_sync_status = endpoint_sync_status diff --git a/distributedcloud-client/dcmanagerclient/api/v1/client.py b/distributedcloud-client/dcmanagerclient/api/v1/client.py index 4903d73..8c31588 100644 --- a/distributedcloud-client/dcmanagerclient/api/v1/client.py +++ b/distributedcloud-client/dcmanagerclient/api/v1/client.py @@ -32,6 +32,7 @@ from dcmanagerclient.api.v1 import subcloud_backup_manager as sbm from dcmanagerclient.api.v1 import subcloud_deploy_manager as sdm from dcmanagerclient.api.v1 import subcloud_group_manager as gm from dcmanagerclient.api.v1 import subcloud_manager as sm +from dcmanagerclient.api.v1 import subcloud_peer_group_manager as pm from dcmanagerclient.api.v1 import sw_patch_manager as spm from dcmanagerclient.api.v1 import sw_prestage_manager as spr from dcmanagerclient.api.v1 import sw_strategy_manager as sstm @@ -100,6 +101,9 @@ class Client(object): self.subcloud_manager = sm.subcloud_manager(self.http_client) self.subcloud_group_manager = \ gm.subcloud_group_manager(self.http_client, self.subcloud_manager) + self.subcloud_peer_group_manager = \ + pm.subcloud_peer_group_manager(self.http_client, + self.subcloud_manager) self.subcloud_backup_manager = sbm.subcloud_backup_manager( self.http_client) self.subcloud_deploy_manager = sdm.subcloud_deploy_manager( diff --git a/distributedcloud-client/dcmanagerclient/api/v1/subcloud_peer_group_manager.py b/distributedcloud-client/dcmanagerclient/api/v1/subcloud_peer_group_manager.py new file mode 100644 index 0000000..07d0dfe --- /dev/null +++ b/distributedcloud-client/dcmanagerclient/api/v1/subcloud_peer_group_manager.py @@ -0,0 +1,168 @@ +# Copyright (c) 2023 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +import json + +from dcmanagerclient.api import base +from dcmanagerclient.api.base import get_json + +BASE_URL = '/subcloud-peer-groups/' + + +class SubcloudPeerGroup(base.Resource): + resource_name = 'subcloud_peer_group' + + def __init__(self, + manager, + peer_group_id, + peer_group_name, + group_priority, + group_state, + system_leader_id, + system_leader_name, + max_subcloud_rehoming, + created_at, + updated_at): + self.manager = manager + self.id = peer_group_id + self.peer_group_name = peer_group_name + self.group_priority = group_priority + + self.group_state = group_state + self.system_leader_id = system_leader_id + self.system_leader_name = system_leader_name + self.max_subcloud_rehoming = max_subcloud_rehoming + self.created_at = created_at + self.updated_at = updated_at + + +class subcloud_peer_group_manager(base.ResourceManager): + resource_class = SubcloudPeerGroup + + def __init__(self, http_client, subcloud_manager): + super(subcloud_peer_group_manager, self).__init__(http_client) + self.subcloud_manager = subcloud_manager + + def json_to_resource(self, json_object): + return self.resource_class( + self, + peer_group_id=json_object['id'], + peer_group_name=json_object['peer_group_name'], + group_priority=json_object['group_priority'], + group_state=json_object['group_state'], + system_leader_id=json_object['system_leader_id'], + system_leader_name=json_object['system_leader_name'], + max_subcloud_rehoming=json_object['max_subcloud_rehoming'], + created_at=json_object['created-at'], + updated_at=json_object['updated-at']) + + def _subcloud_peer_group_detail(self, url): + resp = self.http_client.get(url) + 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_peer_group_status(self, url): + resp = self.http_client.get(url) + if resp.status_code != 200: + self._raise_api_exception(resp) + json_object = get_json(resp) + resource = list() + resource.append(json_object) + return resource + + def subcloud_peer_group_create(self, url, data): + data = json.dumps(data) + resp = self.http_client.post(url, data) + 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_peer_group_list(self, url): + resp = self.http_client.get(url) + if resp.status_code != 200: + self._raise_api_exception(resp) + json_response_key = get_json(resp) + json_objects = json_response_key['subcloud_peer_groups'] + resource = list() + for json_object in json_objects: + resource.append(self.json_to_resource(json_object)) + return resource + + def subcloud_peer_group_update(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) + resource = list() + resource.append(self.json_to_resource(json_object)) + return resource + + def _list_subclouds_for_subcloud_peer_group(self, url): + resp = self.http_client.get(url) + if resp.status_code != 200: + self._raise_api_exception(resp) + json_response_key = get_json(resp) + json_objects = json_response_key['subclouds'] + resource = list() + for json_object in json_objects: + resource.append( + self.subcloud_manager.json_to_resource(json_object)) + return resource + + def subcloud_peer_group_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_response_key = get_json(resp) + json_objects = json_response_key['subclouds'] + resource = list() + for json_object in json_objects: + resource.append( + self.subcloud_manager.json_to_resource(json_object)) + return resource + + def add_subcloud_peer_group(self, **kwargs): + data = kwargs + url = BASE_URL + return self.subcloud_peer_group_create(url, data) + + def delete_subcloud_peer_group(self, subcloud_peer_group_ref): + url = BASE_URL + subcloud_peer_group_ref + return self._delete(url) + + def subcloud_peer_group_detail(self, subcloud_peer_group_ref): + url = BASE_URL + subcloud_peer_group_ref + return self._subcloud_peer_group_detail(url) + + def list_subcloud_peer_groups(self): + url = BASE_URL + return self.subcloud_peer_group_list(url) + + def update_subcloud_peer_group(self, subcloud_peer_group_ref, **kwargs): + data = kwargs + url = BASE_URL + subcloud_peer_group_ref + return self.subcloud_peer_group_update(url, data) + + def migrate_subcloud_peer_group(self, subcloud_peer_group_ref, **kwargs): + data = kwargs + url = BASE_URL + '%s/migrate' % subcloud_peer_group_ref + return self.subcloud_peer_group_migrate(url, data) + + def subcloud_peer_group_list_subclouds(self, subcloud_peer_group_ref): + url = BASE_URL + '%s/subclouds' % subcloud_peer_group_ref + return self._list_subclouds_for_subcloud_peer_group(url) + + def subcloud_peer_group_status(self, subcloud_peer_group_ref): + url = BASE_URL + '%s/status' % subcloud_peer_group_ref + return self._subcloud_peer_group_status(url) diff --git a/distributedcloud-client/dcmanagerclient/commands/v1/subcloud_manager.py b/distributedcloud-client/dcmanagerclient/commands/v1/subcloud_manager.py index e10dae4..cbead4e 100644 --- a/distributedcloud-client/dcmanagerclient/commands/v1/subcloud_manager.py +++ b/distributedcloud-client/dcmanagerclient/commands/v1/subcloud_manager.py @@ -76,6 +76,7 @@ def detail_format(subcloud=None): 'management_gateway_ip', 'systemcontroller_gateway_ip', 'group_id', + 'peer_group_id', 'rehome_data', 'created_at', 'updated_at', @@ -99,6 +100,7 @@ def detail_format(subcloud=None): subcloud.management_gateway_ip, subcloud.systemcontroller_gateway_ip, subcloud.group_id, + subcloud.peer_group_id, subcloud.rehome_data, subcloud.created_at, subcloud.updated_at, @@ -594,6 +596,11 @@ class UpdateSubcloud(base.DCManagerShowOne): help='YAML file containing subcloud configuration settings. ' 'Can be either a local file path or a URL.' ) + parser.add_argument( + '--peer-group', + required=False, + help='Name or ID of subcloud peer group (for migrate).' + ) return parser def _get_resources(self, parsed_args): @@ -620,6 +627,8 @@ class UpdateSubcloud(base.DCManagerShowOne): data['management_end_ip'] = parsed_args.management_end_ip if parsed_args.bootstrap_address: data['bootstrap_address'] = parsed_args.bootstrap_address + if parsed_args.peer_group: + data['peer_group'] = parsed_args.peer_group subcloud_network_values = [ data.get('management_subnet'), diff --git a/distributedcloud-client/dcmanagerclient/commands/v1/subcloud_peer_group_manager.py b/distributedcloud-client/dcmanagerclient/commands/v1/subcloud_peer_group_manager.py new file mode 100644 index 0000000..4cd1849 --- /dev/null +++ b/distributedcloud-client/dcmanagerclient/commands/v1/subcloud_peer_group_manager.py @@ -0,0 +1,365 @@ +# Copyright (c) 2023 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import base64 + +from osc_lib.command import command + +from dcmanagerclient.commands.v1 import base +from dcmanagerclient import exceptions +from dcmanagerclient import utils + + +def group_format(subcloud_peer_group=None): + columns = ( + 'id', + 'peer_group_name', + 'group_priority', + 'group_state', + 'system_leader_id', + 'system_leader_name', + 'max_subcloud_rehoming', + 'created_at', + 'updated_at', + ) + + if subcloud_peer_group: + data = ( + subcloud_peer_group.id, + subcloud_peer_group.peer_group_name, + subcloud_peer_group.group_priority, + subcloud_peer_group.group_state, + subcloud_peer_group.system_leader_id, + subcloud_peer_group.system_leader_name, + subcloud_peer_group.max_subcloud_rehoming, + subcloud_peer_group.created_at, + subcloud_peer_group.updated_at + ) + + else: + data = (('',) * len(columns),) + + return columns, data + + +class MigrateSubcloudPeerGroup(base.DCManagerLister): + """Migrate subclouds in this peer group.""" + + def _get_format_function(self): + return utils.subcloud_detail_format + + def get_parser(self, prog_name): + parser = super(MigrateSubcloudPeerGroup, self).get_parser(prog_name) + + parser.add_argument( + 'group', + help='Name or ID of the subcloud peer group to migrate.' + ) + + parser.add_argument( + '--sysadmin-password', + required=False, + help='Sysadmin password of the subclouds to be configured, ' + 'if not provided you will be prompted.' + ) + return parser + + def _get_resources(self, parsed_args): + subcloud_peer_group_ref = parsed_args.group + dcmanager_client = self.app.client_manager.subcloud_peer_group_manager + kwargs = dict() + + if parsed_args.sysadmin_password is not None: + kwargs['sysadmin_password'] = base64.b64encode( + parsed_args.sysadmin_password.encode("utf-8")).decode("utf-8") + else: + password = utils.prompt_for_password() + kwargs["sysadmin_password"] = base64.b64encode( + password.encode("utf-8")).decode("utf-8") + + try: + return dcmanager_client. \ + subcloud_peer_group_manager.migrate_subcloud_peer_group( + subcloud_peer_group_ref, **kwargs) + except Exception as e: + print(e) + msg = "Unable to migrate subcloud peer group %s" % ( + subcloud_peer_group_ref) + raise exceptions.DCManagerClientException(msg) + + +class AddSubcloudPeerGroup(base.DCManagerShowOne): + """Add a new subcloud peer group.""" + + def _get_format_function(self): + return group_format + + def get_parser(self, prog_name): + parser = super(AddSubcloudPeerGroup, self).get_parser(prog_name) + + parser.add_argument( + '--peer-group-name', + required=True, + help='Name for the new subcloud peer group.' + ) + + parser.add_argument( + '--group-priority', + required=False, + type=int, + default=0, + help='Assigned priority for the group.' + ) + + parser.add_argument( + '--group-state', + required=False, + choices=['enabled', 'disabled'], + default='enabled', + help='Administrative control of subcloud group.' + ) + + parser.add_argument( + '--max-subcloud-rehoming', + required=False, + type=int, + default=10, + help='Maximum number of subclouds to migrate in parallel' + ) + return parser + + def _get_resources(self, parsed_args): + dcmanager_client = self.app.client_manager.subcloud_peer_group_manager + kwargs = dict() + + kwargs['peer-group-name'] = parsed_args.peer_group_name + + if parsed_args.group_priority is not None: + kwargs['group-priority'] = parsed_args.group_priority + + if parsed_args.group_state is not None: + kwargs['group-state'] = parsed_args.group_state + + if parsed_args.max_subcloud_rehoming is not None: + kwargs['max-subcloud-rehoming'] = \ + parsed_args.max_subcloud_rehoming + + return dcmanager_client.subcloud_peer_group_manager.\ + add_subcloud_peer_group(**kwargs) + + +class DeleteSubcloudPeerGroup(command.Command): + """Delete subcloud peer group details from the database.""" + + def get_parser(self, prog_name): + parser = super(DeleteSubcloudPeerGroup, self).get_parser(prog_name) + + parser.add_argument( + 'group', + help='Name or ID of the subcloud peer group to delete.' + ) + return parser + + def take_action(self, parsed_args): + subcloud_peer_group_ref = parsed_args.group + dcmanager_client = self.app.client_manager.subcloud_peer_group_manager + try: + dcmanager_client.subcloud_peer_group_manager.\ + delete_subcloud_peer_group(subcloud_peer_group_ref) + except Exception as e: + print(e) + msg = "Unable to delete subcloud peer group %s" % ( + subcloud_peer_group_ref) + raise exceptions.DCManagerClientException(msg) + + +class ShowSubcloudPeerGroup(base.DCManagerShowOne): + """Show the details of a subcloud peer group.""" + + def _get_format_function(self): + return group_format + + def get_parser(self, prog_name): + parser = super(ShowSubcloudPeerGroup, self).get_parser(prog_name) + + parser.add_argument( + 'group', + help='Name or ID of subcloud peer group to view the details.' + ) + + return parser + + def _get_resources(self, parsed_args): + subcloud_peer_group_ref = parsed_args.group + dcmanager_client = self.app.client_manager.subcloud_peer_group_manager + return dcmanager_client.subcloud_peer_group_manager.\ + subcloud_peer_group_detail(subcloud_peer_group_ref) + + +class ListSubcloudPeerGroup(base.DCManagerLister): + """List subcloud peer groups.""" + + def _get_format_function(self): + return group_format + + def get_parser(self, prog_name): + parser = super(ListSubcloudPeerGroup, self).get_parser(prog_name) + return parser + + def _get_resources(self, parsed_args): + dcmanager_client = self.app.client_manager.subcloud_peer_group_manager + return dcmanager_client.subcloud_peer_group_manager.\ + list_subcloud_peer_groups() + + +class ListSubcloudPeerGroupSubclouds(base.DCManagerLister): + """List subclouds referencing a subcloud peer group.""" + + def _get_format_function(self): + return utils.subcloud_detail_format + + def get_parser(self, prog_name): + parser = super(ListSubcloudPeerGroupSubclouds, + self).get_parser(prog_name) + parser.add_argument( + 'group', + help="Name or ID of subcloud peer group to list " + "associated subclouds." + ) + return parser + + def _get_resources(self, parsed_args): + subcloud_peer_group_ref = parsed_args.group + dcmanager_client = self.app.client_manager.subcloud_peer_group_manager + return dcmanager_client.subcloud_peer_group_manager. \ + subcloud_peer_group_list_subclouds(subcloud_peer_group_ref) + + +class UpdateSubcloudPeerGroup(base.DCManagerShowOne): + """Update attributes of a subcloud peer group.""" + + def _get_format_function(self): + return group_format + + def get_parser(self, prog_name): + parser = super(UpdateSubcloudPeerGroup, self).get_parser(prog_name) + + parser.add_argument( + 'group', + help='Name or ID of the subcloud peer group to update.' + ) + + parser.add_argument( + '--peer-group-name', + required=False, + help='Name for the new subcloud peer group.' + ) + + parser.add_argument( + '--group-priority', + required=False, + type=int, + help='Assigned priority for the peer group.' + ) + + parser.add_argument( + '--group-state', + required=False, + choices=['enabled', 'disabled'], + help='Administrative control of subcloud peer group.' + ) + + parser.add_argument( + '--max-subcloud-rehoming', + required=False, + type=int, + help='Maximum number of subclouds to migrate in parallel' + ) + return parser + + def _get_resources(self, parsed_args): + subcloud_peer_group_ref = parsed_args.group + dcmanager_client = self.app.client_manager.subcloud_peer_group_manager + kwargs = dict() + + if parsed_args.peer_group_name is not None: + kwargs['peer-group-name'] = parsed_args.peer_group_name + + if parsed_args.group_priority is not None: + kwargs['group-priority'] = parsed_args.group_priority + + if parsed_args.group_state is not None: + kwargs['group-state'] = parsed_args.group_state + + if parsed_args.max_subcloud_rehoming is not None: + kwargs['max-subcloud-rehoming'] = \ + parsed_args.max_subcloud_rehoming + + if len(kwargs) == 0: + error_msg = "Nothing to update" + raise exceptions.DCManagerClientException(error_msg) + + try: + return dcmanager_client. \ + subcloud_peer_group_manager.update_subcloud_peer_group( + subcloud_peer_group_ref, **kwargs) + except Exception as e: + print(e) + msg = "Unable to update subcloud peer group %s" % ( + subcloud_peer_group_ref) + raise exceptions.DCManagerClientException(msg) + + +def detail_status_format(subcloud_peer_group_status=None): + # Include all the fields in peer_group_format + # plus some additional fields + columns = ( + 'peer_group_id', + 'peer_group_name', + 'total_subclouds', + 'complete', + 'waiting_for_migrate', + 'rehoming', + 'rehome_failed', + 'managed', + 'unmanaged', + ) + if subcloud_peer_group_status: + data = ( + subcloud_peer_group_status['peer_group_id'], + subcloud_peer_group_status['peer_group_name'], + subcloud_peer_group_status['total_subclouds'], + subcloud_peer_group_status['complete'], + subcloud_peer_group_status['waiting_for_migrate'], + subcloud_peer_group_status['rehoming'], + subcloud_peer_group_status['rehome_failed'], + subcloud_peer_group_status['managed'], + subcloud_peer_group_status['unmanaged'], + ) + else: + data = (tuple('' for _ in range(len(columns))),) + return columns, data + + +class StatusSubcloudPeerGroup(base.DCManagerShowOne): + """Show a summary of subcloud statuses within a peer group.""" + + def _get_format_function(self): + return detail_status_format + + def get_parser(self, prog_name): + parser = super(StatusSubcloudPeerGroup, self).get_parser(prog_name) + + parser.add_argument( + 'group', + help='Name or ID of subcloud peer group to view the status.' + ) + + return parser + + def _get_resources(self, parsed_args): + subcloud_peer_group_ref = parsed_args.group + dcmanager_client = self.app.client_manager.subcloud_peer_group_manager + return dcmanager_client.subcloud_peer_group_manager.\ + subcloud_peer_group_status(subcloud_peer_group_ref) diff --git a/distributedcloud-client/dcmanagerclient/shell.py b/distributedcloud-client/dcmanagerclient/shell.py index 37e7803..7e27ebf 100644 --- a/distributedcloud-client/dcmanagerclient/shell.py +++ b/distributedcloud-client/dcmanagerclient/shell.py @@ -41,6 +41,7 @@ from dcmanagerclient.commands.v1 import subcloud_backup_manager as sbm from dcmanagerclient.commands.v1 import subcloud_deploy_manager as sdm from dcmanagerclient.commands.v1 import subcloud_group_manager as gm from dcmanagerclient.commands.v1 import subcloud_manager as sm +from dcmanagerclient.commands.v1 import subcloud_peer_group_manager as pm from dcmanagerclient.commands.v1 import sw_patch_manager as spm from dcmanagerclient.commands.v1 import sw_prestage_manager as spr from dcmanagerclient.commands.v1 import sw_update_manager as sum @@ -499,7 +500,8 @@ class DCManagerShell(app.App): kube_upgrade_manager=self.client, kube_rootca_update_manager=self.client, sw_prestage_manager=self.client, - phased_subcloud_deploy_manager=self.client) + phased_subcloud_deploy_manager=self.client, + subcloud_peer_group_manager=self.client) ) self.client_manager = ClientManager() @@ -559,6 +561,15 @@ class DCManagerShell(app.App): 'subcloud deploy show': sdm.SubcloudDeployShow, 'subcloud-deploy upload': sdm.DeprecatedSubcloudDeployUpload, 'subcloud-deploy show': sdm.DeprecatedSubcloudDeployShow, + 'subcloud-peer-group add': pm.AddSubcloudPeerGroup, + 'subcloud-peer-group list': pm.ListSubcloudPeerGroup, + 'subcloud-peer-group list-subclouds': + pm.ListSubcloudPeerGroupSubclouds, + 'subcloud-peer-group show': pm.ShowSubcloudPeerGroup, + 'subcloud-peer-group update': pm.UpdateSubcloudPeerGroup, + 'subcloud-peer-group delete': pm.DeleteSubcloudPeerGroup, + 'subcloud-peer-group migrate': pm.MigrateSubcloudPeerGroup, + 'subcloud-peer-group status': pm.StatusSubcloudPeerGroup, 'system-peer add': sp.AddSystemPeer, 'system-peer list': sp.ListSystemPeer, 'system-peer show': sp.ShowSystemPeer, diff --git a/distributedcloud-client/dcmanagerclient/tests/base.py b/distributedcloud-client/dcmanagerclient/tests/base.py index da3cb2f..8347517 100644 --- a/distributedcloud-client/dcmanagerclient/tests/base.py +++ b/distributedcloud-client/dcmanagerclient/tests/base.py @@ -52,6 +52,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_PEERGROUP_ID = None SUBCLOUD_REHOME_DATA = None BACKUP_STATUS = 'None' BACKUP_DATETIME = 'None' @@ -102,6 +103,7 @@ SUBCLOUD_RESOURCE_WITH_PEERID_REHOME_DATA = api_base.Subcloud( management_gateway_ip=MANAGEMENT_GATEWAY_IP, systemcontroller_gateway_ip=SYSTEMCONTROLLER_GATEWAY_IP, group_id=DEFAULT_SUBCLOUD_GROUP_ID, + peer_group_id=SUBCLOUD_PEERGROUP_ID, rehome_data=SUBCLOUD_REHOME_DATA, created_at=TIME_NOW, updated_at=TIME_NOW, @@ -146,12 +148,14 @@ SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID_REHOME_DATA = ( MANAGEMENT_GATEWAY_IP, SYSTEMCONTROLLER_GATEWAY_IP, DEFAULT_SUBCLOUD_GROUP_ID, + SUBCLOUD_PEERGROUP_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),) diff --git a/distributedcloud-client/dcmanagerclient/tests/v1/test_subcloud_manager.py b/distributedcloud-client/dcmanagerclient/tests/v1/test_subcloud_manager.py index 9c9454d..a3b30fa 100644 --- a/distributedcloud-client/dcmanagerclient/tests/v1/test_subcloud_manager.py +++ b/distributedcloud-client/dcmanagerclient/tests/v1/test_subcloud_manager.py @@ -255,7 +255,8 @@ class TestCLISubcloudManagerV1(base.BaseCommandTest): '--management-start-ip', 'sc network start ip', '--management-end-ip', 'subcloud network end ip', '--bootstrap-address', 'subcloud bootstrap address', - '--bootstrap-values', bootstrap_file_path]) + '--bootstrap-values', bootstrap_file_path, + '--peer-group', 'peer group']) self.assertEqual( base.SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID_REHOME_DATA, actual_call[1]) diff --git a/distributedcloud-client/dcmanagerclient/tests/v1/test_subcloud_peer_group_manager.py b/distributedcloud-client/dcmanagerclient/tests/v1/test_subcloud_peer_group_manager.py new file mode 100644 index 0000000..7a34424 --- /dev/null +++ b/distributedcloud-client/dcmanagerclient/tests/v1/test_subcloud_peer_group_manager.py @@ -0,0 +1,131 @@ +# Copyright (c) 2023 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +import mock +from oslo_utils import timeutils + +from dcmanagerclient.api.v1.subcloud_peer_group_manager \ + import SubcloudPeerGroup as Peergroup +from dcmanagerclient.commands.v1 \ + import subcloud_peer_group_manager as subcloud_peer_group_cmd +from dcmanagerclient.exceptions import DCManagerClientException +from dcmanagerclient.tests import base + +FAKE_MANAGER = None +PG_ID = "1" +PG_NAME = "test-pg-name" +PG_GROUP_PRIORITY = "99" +PG_GROUP_STATE = "disabled" +PG_MAX_SUBCLOUD_REHOMING = "10" +PG_SYSTEM_LEADER_ID = "d9dea83f-f271-470d-9cce-44b0162a800b" +PG_SYSTEM_LEADER_NAME = "dc-1" +TIME_NOW = timeutils.utcnow().isoformat() +PG_CREATED_AT = TIME_NOW +PG_UPDATED_AT = TIME_NOW +SubcloudPeerGroup = Peergroup( + mock, + PG_ID, + PG_NAME, + PG_GROUP_PRIORITY, + PG_GROUP_STATE, + PG_SYSTEM_LEADER_ID, + PG_SYSTEM_LEADER_NAME, + PG_MAX_SUBCLOUD_REHOMING, + PG_CREATED_AT, + PG_UPDATED_AT +) + +PG_TUPLE = (PG_ID, + PG_NAME, + PG_GROUP_PRIORITY, + PG_GROUP_STATE, + PG_SYSTEM_LEADER_ID, + PG_SYSTEM_LEADER_NAME, + PG_MAX_SUBCLOUD_REHOMING, + ) +PG_TUPLE_WITH_DATE = PG_TUPLE + (PG_CREATED_AT, PG_UPDATED_AT) + + +class TestCLISubcloudPeerGroupManager(base.BaseCommandTest): + def setUp(self): + super(TestCLISubcloudPeerGroupManager, self).setUp() + # The client is the subcloud_peer_group_manager + self.client = self.app.client_manager.subcloud_peer_group_manager + + def test_list_subcloud_peer_groups(self): + self.client.subcloud_peer_group_manager.\ + list_subcloud_peer_groups.return_value =\ + [SubcloudPeerGroup] + actual_call = self.call(subcloud_peer_group_cmd.ListSubcloudPeerGroup) + self.assertEqual([PG_TUPLE_WITH_DATE], + actual_call[1]) + + def test_show_subcloud_peer_group(self): + self.client.subcloud_peer_group_manager.\ + subcloud_peer_group_detail.return_value =\ + [SubcloudPeerGroup] + actual_call = self.call(subcloud_peer_group_cmd.ShowSubcloudPeerGroup, + app_args=[PG_ID]) + self.assertEqual(PG_TUPLE_WITH_DATE, actual_call[1]) + + def test_list_subcloud_peer_group_subclouds(self): + self.client.subcloud_peer_group_manager.\ + subcloud_peer_group_list_subclouds.return_value = \ + [base.SUBCLOUD_RESOURCE_WITH_PEERID_REHOME_DATA] + actual_call = self.call( + subcloud_peer_group_cmd.ListSubcloudPeerGroupSubclouds, + app_args=[base.ID]) + self.assertEqual([ + base.SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID_REHOME_DATA], + actual_call[1]) + + def test_add_subcloud_peer_group(self): + self.client.subcloud_peer_group_manager.add_subcloud_peer_group.\ + return_value = [SubcloudPeerGroup] + actual_call1 = self.call( + subcloud_peer_group_cmd.AddSubcloudPeerGroup, app_args=[ + '--peer-group-name', PG_NAME + ]) + + actual_call2 = self.call( + subcloud_peer_group_cmd.AddSubcloudPeerGroup, app_args=[ + '--peer-group-name', PG_NAME, + '--group-priority', PG_GROUP_PRIORITY, + '--group-state', PG_GROUP_STATE, + '--max-subcloud-rehoming', PG_MAX_SUBCLOUD_REHOMING + ]) + self.assertEqual( + PG_TUPLE_WITH_DATE, + actual_call1[1]) + self.assertEqual( + PG_TUPLE_WITH_DATE, + actual_call2[1]) + + def test_delete_subcloud_peer_group(self): + self.call(subcloud_peer_group_cmd.DeleteSubcloudPeerGroup, + app_args=[PG_ID]) + self.client.subcloud_peer_group_manager.delete_subcloud_peer_group.\ + assert_called_once_with(PG_ID) + + def test_update_subcloud_peer_group(self): + self.client.subcloud_peer_group_manager.update_subcloud_peer_group.\ + return_value = [SubcloudPeerGroup] + actual_call = self.call( + subcloud_peer_group_cmd.UpdateSubcloudPeerGroup, + app_args=[ + base.ID, + '--peer-group-name', PG_NAME, + '--group-priority', PG_GROUP_PRIORITY, + '--group-state', PG_GROUP_STATE, + '--max-subcloud-rehoming', PG_MAX_SUBCLOUD_REHOMING]) + self.assertEqual( + (PG_TUPLE_WITH_DATE), + actual_call[1]) + + e = self.assertRaises(DCManagerClientException, + self.call, + subcloud_peer_group_cmd.UpdateSubcloudPeerGroup, + app_args=[base.ID]) + self.assertTrue('Nothing to update' in str(e)) diff --git a/distributedcloud-client/dcmanagerclient/utils.py b/distributedcloud-client/dcmanagerclient/utils.py index 8ff24a6..97abf27 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', + 'peer_group_id', 'rehome_data', 'created_at', 'updated_at', @@ -143,6 +144,7 @@ def subcloud_detail_format(subcloud=None): subcloud.management_gateway_ip, subcloud.systemcontroller_gateway_ip, subcloud.group_id, + subcloud.peer_group_id, subcloud.rehome_data, subcloud.created_at, subcloud.updated_at,