From 826e167d5e56baff284d2f3360de457ca5753765 Mon Sep 17 00:00:00 2001 From: albailey Date: Mon, 9 Mar 2020 10:22:47 -0500 Subject: [PATCH] Subcloud Group support for dcmanager CLI Added CLI calls for: dcmanager subcloud-group add subcloud-group delete subcloud-group list subcloud-group show subcloud-group update subcloud-group list-subclouds Update subcloud API calls (create, update and detailed list) to include support for subcloud group. Added unit tests for covering the new and changed CLI commands Change-Id: I943a1ce828a4c7a1ba6092ec49b642f19179a598 Depends-On: https://review.opendev.org/#/c/714759 Story: 2007518 Task: 39302 Signed-off-by: albailey --- .../dcmanagerclient/api/httpclient.py | 4 +- .../dcmanagerclient/api/v1/client.py | 5 +- .../api/v1/subcloud_group_manager.py | 145 +++++++++ .../api/v1/subcloud_manager.py | 49 +-- .../commands/v1/subcloud_group_manager.py | 283 ++++++++++++++++++ .../commands/v1/subcloud_manager.py | 19 ++ .../dcmanagerclient/shell.py | 10 +- .../dcmanagerclient/tests/base.py | 1 + .../dcmanagerclient/tests/test_httpclient.py | 1 + .../tests/v1/test_subcloud_group_manager.py | 161 ++++++++++ .../tests/v1/test_subcloud_manager.py | 44 ++- 11 files changed, 682 insertions(+), 40 deletions(-) create mode 100644 distributedcloud-client/dcmanagerclient/api/v1/subcloud_group_manager.py create mode 100644 distributedcloud-client/dcmanagerclient/commands/v1/subcloud_group_manager.py create mode 100644 distributedcloud-client/dcmanagerclient/tests/v1/test_subcloud_group_manager.py diff --git a/distributedcloud-client/dcmanagerclient/api/httpclient.py b/distributedcloud-client/dcmanagerclient/api/httpclient.py index f68dadb..8c512c3 100644 --- a/distributedcloud-client/dcmanagerclient/api/httpclient.py +++ b/distributedcloud-client/dcmanagerclient/api/httpclient.py @@ -36,8 +36,8 @@ LOG = logging.getLogger(__name__) def log_request(func): def decorator(self, *args, **kwargs): resp = func(self, *args, **kwargs) - LOG.debug("HTTP %s %s %d" % (resp.request.method, resp.url, - resp.status_code)) + LOG.debug("HTTP %s %s %d %s" % (resp.request.method, resp.url, + resp.status_code, resp.text)) return resp return decorator diff --git a/distributedcloud-client/dcmanagerclient/api/v1/client.py b/distributedcloud-client/dcmanagerclient/api/v1/client.py index a29855e..8bbcb3e 100644 --- a/distributedcloud-client/dcmanagerclient/api/v1/client.py +++ b/distributedcloud-client/dcmanagerclient/api/v1/client.py @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # -# Copyright (c) 2017 Wind River Systems, Inc. +# Copyright (c) 2017-2020 Wind River Systems, Inc. # # The right to copy, distribute, modify, or otherwise make use # of this software may be licensed only pursuant to the terms @@ -26,6 +26,7 @@ from keystoneauth1 import session as ks_session from dcmanagerclient.api import httpclient from dcmanagerclient.api.v1 import alarm_manager as am +from dcmanagerclient.api.v1 import subcloud_group_manager as gm from dcmanagerclient.api.v1 import subcloud_manager as sm from dcmanagerclient.api.v1 import sw_update_manager as sum from dcmanagerclient.api.v1 import sw_update_options_manager as suom @@ -95,6 +96,8 @@ class Client(object): # Create all managers self.subcloud_manager = sm.subcloud_manager(self.http_client) + self.subcloud_group_manager = \ + gm.subcloud_group_manager(self.http_client, self.subcloud_manager) self.alarm_manager = am.alarm_manager(self.http_client) self.sw_update_manager = sum.sw_update_manager(self.http_client) self.sw_update_options_manager = \ diff --git a/distributedcloud-client/dcmanagerclient/api/v1/subcloud_group_manager.py b/distributedcloud-client/dcmanagerclient/api/v1/subcloud_group_manager.py new file mode 100644 index 0000000..31b8262 --- /dev/null +++ b/distributedcloud-client/dcmanagerclient/api/v1/subcloud_group_manager.py @@ -0,0 +1,145 @@ +# Copyright (c) 2017 Ericsson AB. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Copyright (c) 2020 Wind River Systems, Inc. +# +# The right to copy, distribute, modify, or otherwise make use +# of this software may be licensed only pursuant to the terms +# of an applicable Wind River license agreement. +# + +import json + +from dcmanagerclient.api import base +from dcmanagerclient.api.base import get_json + + +class SubcloudGroup(base.Resource): + resource_name = 'subcloud_group' + + def __init__(self, + manager, + group_id, + name, + description, + update_apply_type, + max_parallel_subclouds, + created_at, + updated_at): + self.manager = manager + self.group_id = group_id + self.name = name + self.description = description + self.update_apply_type = update_apply_type + self.max_parallel_subclouds = max_parallel_subclouds + self.created_at = created_at + self.updated_at = updated_at + + +class subcloud_group_manager(base.ResourceManager): + resource_class = SubcloudGroup + + def __init__(self, http_client, subcloud_manager): + super(subcloud_group_manager, self).__init__(http_client) + self.subcloud_manager = subcloud_manager + + def _json_to_resource(self, json_object): + return self.resource_class( + self, + group_id=json_object['id'], + name=json_object['name'], + description=json_object['description'], + update_apply_type=json_object['update_apply_type'], + max_parallel_subclouds=json_object['max_parallel_subclouds'], + created_at=json_object['created-at'], + updated_at=json_object['updated-at']) + + def subcloud_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_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 subcloud_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_groups'] + resource = [] + for json_object in json_objects: + resource.append(self._json_to_resource(json_object)) + return resource + + def _subcloud_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 _list_subclouds_for_subcloud_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 = [] + for json_object in json_objects: + resource.append( + self.subcloud_manager.json_to_resource(json_object)) + return resource + + def add_subcloud_group(self, **kwargs): + data = kwargs + url = '/subcloud-groups/' + return self.subcloud_group_create(url, data) + + def list_subcloud_groups(self): + url = '/subcloud-groups/' + return self.subcloud_group_list(url) + + def subcloud_group_list_subclouds(self, subcloud_group_ref): + url = '/subcloud-groups/%s/subclouds' % subcloud_group_ref + return self._list_subclouds_for_subcloud_group(url) + + def subcloud_group_detail(self, subcloud_group_ref): + url = '/subcloud-groups/%s' % subcloud_group_ref + return self._subcloud_group_detail(url) + + def delete_subcloud_group(self, subcloud_group_ref): + url = '/subcloud-groups/%s' % subcloud_group_ref + return self._delete(url) + + def update_subcloud_group(self, subcloud_group_ref, **kwargs): + data = kwargs + url = '/subcloud-groups/%s' % subcloud_group_ref + return self.subcloud_group_update(url, data) diff --git a/distributedcloud-client/dcmanagerclient/api/v1/subcloud_manager.py b/distributedcloud-client/dcmanagerclient/api/v1/subcloud_manager.py index 2403a9e..2688b64 100644 --- a/distributedcloud-client/dcmanagerclient/api/v1/subcloud_manager.py +++ b/distributedcloud-client/dcmanagerclient/api/v1/subcloud_manager.py @@ -34,7 +34,7 @@ class Subcloud(base.Resource): deploy_status, management_subnet, management_start_ip, management_end_ip, management_gateway_ip, systemcontroller_gateway_ip, - created_at, updated_at, sync_status="unknown", + created_at, updated_at, group_id, sync_status="unknown", endpoint_sync_status={}): self.manager = manager self.subcloud_id = subcloud_id @@ -53,6 +53,7 @@ class Subcloud(base.Resource): self.systemcontroller_gateway_ip = systemcontroller_gateway_ip self.created_at = created_at self.updated_at = updated_at + self.group_id = group_id self.sync_status = sync_status self.endpoint_sync_status = endpoint_sync_status @@ -60,6 +61,27 @@ class Subcloud(base.Resource): class subcloud_manager(base.ResourceManager): resource_class = Subcloud + def json_to_resource(self, json_object): + return self.resource_class( + self, + subcloud_id=json_object['id'], + name=json_object['name'], + description=json_object['description'], + location=json_object['location'], + software_version=json_object['software-version'], + management_state=json_object['management-state'], + availability_status=json_object['availability-status'], + deploy_status=json_object['deploy-status'], + management_subnet=json_object['management-subnet'], + management_start_ip=json_object['management-start-ip'], + management_end_ip=json_object['management-end-ip'], + management_gateway_ip=json_object['management-gateway-ip'], + systemcontroller_gateway_ip=json_object[ + 'systemcontroller-gateway-ip'], + created_at=json_object['created-at'], + updated_at=json_object['updated-at'], + group_id=json_object['group_id']) + def subcloud_create(self, url, data): data = json.dumps(data) resp = self.http_client.post(url, data) @@ -67,25 +89,7 @@ class subcloud_manager(base.ResourceManager): self._raise_api_exception(resp) json_object = get_json(resp) resource = list() - resource.append( - self.resource_class( - self, - subcloud_id=json_object['id'], - name=json_object['name'], - description=json_object['description'], - location=json_object['location'], - software_version=json_object['software-version'], - management_state=json_object['management-state'], - availability_status=json_object['availability-status'], - deploy_status=json_object['deploy-status'], - management_subnet=json_object['management-subnet'], - management_start_ip=json_object['management-start-ip'], - management_end_ip=json_object['management-end-ip'], - management_gateway_ip=json_object['management-gateway-ip'], - systemcontroller_gateway_ip=json_object[ - 'systemcontroller-gateway-ip'], - created_at=json_object['created-at'], - updated_at=json_object['updated-at'])) + resource.append(self.json_to_resource(json_object)) return resource def subcloud_update(self, url, data): @@ -113,7 +117,8 @@ class subcloud_manager(base.ResourceManager): systemcontroller_gateway_ip=json_object[ 'systemcontroller-gateway-ip'], created_at=json_object['created-at'], - updated_at=json_object['updated-at'])) + updated_at=json_object['updated-at'], + group_id=json_object['group_id'])) return resource def subcloud_list(self, url): @@ -143,6 +148,7 @@ class subcloud_manager(base.ResourceManager): 'systemcontroller-gateway-ip'], created_at=json_object['created-at'], updated_at=json_object['updated-at'], + group_id=json_object['group_id'], sync_status=json_object['sync_status'], endpoint_sync_status=json_object['endpoint_sync_status'])) return resource @@ -172,6 +178,7 @@ class subcloud_manager(base.ResourceManager): 'systemcontroller-gateway-ip'], created_at=json_object['created-at'], updated_at=json_object['updated-at'], + group_id=json_object['group_id'], endpoint_sync_status=json_object['endpoint_sync_status'])) if detail is not None: resource[0].oam_floating_ip = json_object['oam_floating_ip'] diff --git a/distributedcloud-client/dcmanagerclient/commands/v1/subcloud_group_manager.py b/distributedcloud-client/dcmanagerclient/commands/v1/subcloud_group_manager.py new file mode 100644 index 0000000..6f24c71 --- /dev/null +++ b/distributedcloud-client/dcmanagerclient/commands/v1/subcloud_group_manager.py @@ -0,0 +1,283 @@ +# Copyright (c) 2017 Ericsson AB. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Copyright (c) 2020 Wind River Systems, Inc. +# +# The right to copy, distribute, modify, or otherwise make use +# of this software may be licensed only pursuant to the terms +# of an applicable Wind River license agreement. +# + +from osc_lib.command import command + +from dcmanagerclient.commands.v1 import base +from dcmanagerclient.commands.v1.subcloud_manager import detail_format +from dcmanagerclient import exceptions + + +def group_format(subcloud_group=None): + columns = ( + 'id', + 'name', + 'description', + ) + + if subcloud_group: + data = ( + subcloud_group.group_id, + subcloud_group.name, + subcloud_group.description, + ) + + else: + data = (tuple('' for _ in range(len(columns))),) + + return columns, data + + +def detail_group_format(subcloud_group=None): + # Include all the fields in group_format + # plus some additional fields + columns = ( + 'id', + 'name', + 'description', + 'update apply type', + 'max parallel subclouds', + 'created_at', + 'updated_at', + ) + + if subcloud_group: + data = ( + subcloud_group.group_id, + subcloud_group.name, + subcloud_group.description, + subcloud_group.update_apply_type, + subcloud_group.max_parallel_subclouds, + subcloud_group.created_at, + subcloud_group.updated_at, + ) + else: + data = (tuple('' for _ in range(len(columns))),) + + return columns, data + + +class AddSubcloudGroup(base.DCManagerShowOne): + """Add a new subcloud group.""" + + def _get_format_function(self): + return detail_group_format + + def get_parser(self, prog_name): + parser = super(AddSubcloudGroup, self).get_parser(prog_name) + + parser.add_argument( + '--name', + required=True, + help='Name for the new subcloud group.' + ) + + parser.add_argument( + '--description', + required=False, + default='No description provided', + help='Description of new subcloud group.' + ) + + parser.add_argument( + '--update_apply_type', + required=False, + default='parallel', + help='apply type for the new subcloud group.' + ) + + parser.add_argument( + '--max_parallel_subclouds', + required=False, + default=2, + help='max parallel subclouds for the new subcloud group.' + ) + return parser + + def _get_resources(self, parsed_args): + dcmanager_client = self.app.client_manager.subcloud_group_manager + kwargs = dict() + + if parsed_args.name is not None: + kwargs['name'] = parsed_args.name + + if parsed_args.description is not None: + kwargs['description'] = parsed_args.description + + if parsed_args.update_apply_type is not None: + kwargs['update_apply_type'] = parsed_args.update_apply_type + + if parsed_args.max_parallel_subclouds is not None: + kwargs['max_parallel_subclouds'] = \ + parsed_args.max_parallel_subclouds + return dcmanager_client.subcloud_group_manager.add_subcloud_group( + **kwargs) + + +class ListSubcloudGroup(base.DCManagerLister): + """List subcloud groups.""" + + def _get_format_function(self): + return group_format + + def get_parser(self, prog_name): + parser = super(ListSubcloudGroup, self).get_parser(prog_name) + return parser + + def _get_resources(self, parsed_args): + dcmanager_client = self.app.client_manager.subcloud_group_manager + return dcmanager_client.subcloud_group_manager.list_subcloud_groups() + + +class ListSubcloudGroupSubclouds(base.DCManagerLister): + """List subclouds referencing a subcloud group.""" + + def _get_format_function(self): + return detail_format + + def get_parser(self, prog_name): + parser = super(ListSubcloudGroupSubclouds, self).get_parser(prog_name) + parser.add_argument( + 'group', + help='Name or ID of subcloud group to list associated subclouds.' + ) + return parser + + 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. \ + subcloud_group_list_subclouds(subcloud_group_ref) + + +class ShowSubcloudGroup(base.DCManagerShowOne): + """Show the details of a subcloud group.""" + + def _get_format_function(self): + return detail_group_format + + def get_parser(self, prog_name): + parser = super(ShowSubcloudGroup, self).get_parser(prog_name) + + parser.add_argument( + 'group', + help='Name or ID of subcloud group to view the details.' + ) + + return parser + + 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.\ + subcloud_group_detail(subcloud_group_ref) + + +class DeleteSubcloudGroup(command.Command): + """Delete subcloud group details from the database.""" + + def get_parser(self, prog_name): + parser = super(DeleteSubcloudGroup, self).get_parser(prog_name) + + parser.add_argument( + 'group', + help='Name or ID of the subcloud group to delete.' + ) + return parser + + def take_action(self, parsed_args): + subcloud_group_ref = parsed_args.group + dcmanager_client = self.app.client_manager.subcloud_group_manager + try: + dcmanager_client.subcloud_group_manager.\ + delete_subcloud_group(subcloud_group_ref) + except Exception as e: + print(e) + msg = "Unable to delete subcloud group %s" % (subcloud_group_ref) + raise exceptions.DCManagerClientException(msg) + + +class UpdateSubcloudGroup(base.DCManagerShowOne): + """Update attributes of a subcloud group.""" + + def _get_format_function(self): + return detail_group_format + + def get_parser(self, prog_name): + parser = super(UpdateSubcloudGroup, self).get_parser(prog_name) + + parser.add_argument( + 'group', + help='Name or ID of the subcloud group to update.' + ) + + parser.add_argument( + '--name', + required=False, + help='Name of subcloud group.' + ) + + parser.add_argument( + '--description', + required=False, + help='Description of subcloud group.' + ) + + parser.add_argument( + '--update_apply_type', + required=False, + help='Update apply type of subcloud group.' + ) + + parser.add_argument( + '--max_parallel_subclouds', + type=int, + required=False, + help='max parallel subclouds of subcloud group.' + ) + + return parser + + def _get_resources(self, parsed_args): + subcloud_group_ref = parsed_args.group + dcmanager_client = self.app.client_manager.subcloud_group_manager + kwargs = dict() + if parsed_args.name: + kwargs['name'] = parsed_args.name + if parsed_args.description: + kwargs['description'] = parsed_args.description + if parsed_args.update_apply_type: + kwargs['update_apply_type'] = parsed_args.update_apply_type + if parsed_args.max_parallel_subclouds: + kwargs['max_parallel_subclouds'] = \ + parsed_args.max_parallel_subclouds + if len(kwargs) == 0: + error_msg = "Nothing to update" + raise exceptions.DCManagerClientException(error_msg) + + try: + return dcmanager_client. \ + subcloud_group_manager.update_subcloud_group( + subcloud_group_ref, **kwargs) + except Exception as e: + print(e) + msg = "Unable to update subcloud group %s" % (subcloud_group_ref) + raise exceptions.DCManagerClientException(msg) diff --git a/distributedcloud-client/dcmanagerclient/commands/v1/subcloud_manager.py b/distributedcloud-client/dcmanagerclient/commands/v1/subcloud_manager.py index 44deb9c..ba5adc0 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_end_ip', 'management_gateway_ip', 'systemcontroller_gateway_ip', + 'group_id', 'created_at', 'updated_at', ) @@ -90,6 +91,7 @@ def detail_format(subcloud=None): subcloud.management_end_ip, subcloud.management_gateway_ip, subcloud.systemcontroller_gateway_ip, + subcloud.group_id, subcloud.created_at, subcloud.updated_at, ) @@ -170,6 +172,12 @@ class AddSubcloud(base.DCManagerShowOne): help='bmc password of the subcloud to be configured, ' 'if not provided you will be prompted.' ) + + parser.add_argument( + '--group', + required=False, + help='Name or ID of subcloud group.' + ) return parser def _get_resources(self, parsed_args): @@ -255,6 +263,9 @@ class AddSubcloud(base.DCManagerShowOne): kwargs["bmc_password"] = password break + if parsed_args.group is not None: + kwargs['group_id'] = parsed_args.group + return dcmanager_client.subcloud_manager.add_subcloud(**kwargs) @@ -413,6 +424,12 @@ class UpdateSubcloud(base.DCManagerShowOne): help='Location of subcloud.' ) + parser.add_argument( + '--group', + required=False, + help='Name or ID of subcloud group.' + ) + return parser def _get_resources(self, parsed_args): @@ -423,6 +440,8 @@ class UpdateSubcloud(base.DCManagerShowOne): kwargs['description'] = parsed_args.description if parsed_args.location: kwargs['location'] = parsed_args.location + if parsed_args.group: + kwargs['group_id'] = parsed_args.group if len(kwargs) == 0: error_msg = "Nothing to update" raise exceptions.DCManagerClientException(error_msg) diff --git a/distributedcloud-client/dcmanagerclient/shell.py b/distributedcloud-client/dcmanagerclient/shell.py index 9629496..a2f8c36 100644 --- a/distributedcloud-client/dcmanagerclient/shell.py +++ b/distributedcloud-client/dcmanagerclient/shell.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # -# Copyright (c) 2017 Wind River Systems, Inc. +# Copyright (c) 2017-2020 Wind River Systems, Inc. # # The right to copy, distribute, modify, or otherwise make use # of this software may be licensed only pursuant to the terms @@ -37,6 +37,7 @@ from osc_lib.command import command import argparse from dcmanagerclient.commands.v1 import alarm_manager as am +from dcmanagerclient.commands.v1 import subcloud_group_manager as gm from dcmanagerclient.commands.v1 import subcloud_manager as sm from dcmanagerclient.commands.v1 import sw_update_manager as sum from dcmanagerclient.commands.v1 import sw_update_options_manager as suom @@ -444,6 +445,7 @@ class DCManagerShell(app.App): 'ClientManager', (object,), dict(subcloud_manager=self.client, + subcloud_group_manager=self.client, alarm_manager=self.client, sw_update_manager=self.client, strategy_step_manager=self.client, @@ -480,6 +482,12 @@ class DCManagerShell(app.App): 'subcloud unmanage': sm.UnmanageSubcloud, 'subcloud manage': sm.ManageSubcloud, 'subcloud update': sm.UpdateSubcloud, + 'subcloud-group add': gm.AddSubcloudGroup, + 'subcloud-group delete': gm.DeleteSubcloudGroup, + 'subcloud-group list': gm.ListSubcloudGroup, + 'subcloud-group list-subclouds': gm.ListSubcloudGroupSubclouds, + 'subcloud-group show': gm.ShowSubcloudGroup, + 'subcloud-group update': gm.UpdateSubcloudGroup, 'alarm summary': am.ListAlarmSummary, 'patch-strategy create': sum.CreatePatchStrategy, 'patch-strategy delete': sum.DeletePatchStrategy, diff --git a/distributedcloud-client/dcmanagerclient/tests/base.py b/distributedcloud-client/dcmanagerclient/tests/base.py index c2f9866..8fb0038 100644 --- a/distributedcloud-client/dcmanagerclient/tests/base.py +++ b/distributedcloud-client/dcmanagerclient/tests/base.py @@ -33,6 +33,7 @@ class FakeResponse(object): self.status_code = status_code self.content = content self.headers = {} + self.text = '' def json(self): return json.loads(self.content) diff --git a/distributedcloud-client/dcmanagerclient/tests/test_httpclient.py b/distributedcloud-client/dcmanagerclient/tests/test_httpclient.py index 2955eee..317978c 100644 --- a/distributedcloud-client/dcmanagerclient/tests/test_httpclient.py +++ b/distributedcloud-client/dcmanagerclient/tests/test_httpclient.py @@ -70,6 +70,7 @@ class FakeResponse(object): self.request = FakeRequest(method) self.url = url self.status_code = status_code + self.text = '' class HTTPClientTest(testtools.TestCase): diff --git a/distributedcloud-client/dcmanagerclient/tests/v1/test_subcloud_group_manager.py b/distributedcloud-client/dcmanagerclient/tests/v1/test_subcloud_group_manager.py new file mode 100644 index 0000000..d6d9ad5 --- /dev/null +++ b/distributedcloud-client/dcmanagerclient/tests/v1/test_subcloud_group_manager.py @@ -0,0 +1,161 @@ +# Copyright (c) 2017 Ericsson AB. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Copyright (c) 2017-2020 Wind River Systems, Inc. +# +# The right to copy, distribute, modify, or otherwise make use +# of this software may be licensed only pursuant to the terms +# of an applicable Wind River license agreement. +# + +import copy +import mock + +from oslo_utils import timeutils + +from dcmanagerclient.api.v1 import subcloud_group_manager as zm +from dcmanagerclient.commands.v1 \ + import subcloud_group_manager as subcloud_group_cmd +from dcmanagerclient.tests import base +from dcmanagerclient.tests.v1 import test_subcloud_manager as tsm + + +ID = '2' +NAME = 'GroupX' +DESCRIPTION = 'Custom subcloud group' +APPLY_TYPE = 'parallel' +MAX_PARALLEL_SUBCLOUDS = 3 +TIME_NOW = timeutils.utcnow().isoformat() +NEW_DESCRIPTION = 'Slightly different subcloud group' + +SUBCLOUD_GROUP_DICT = { + 'GROUP_ID': ID, + 'NAME': NAME, + 'DESCRIPTION': DESCRIPTION, + 'APPLY_TYPE': APPLY_TYPE, + 'MAX_PARALLEL_SUBCLOUDS': MAX_PARALLEL_SUBCLOUDS, + 'CREATED_AT': TIME_NOW, + 'UPDATED_AT': TIME_NOW +} + +SUBCLOUD_GROUP = zm.SubcloudGroup( + mock, + group_id=SUBCLOUD_GROUP_DICT['GROUP_ID'], + name=SUBCLOUD_GROUP_DICT['NAME'], + description=SUBCLOUD_GROUP_DICT['DESCRIPTION'], + update_apply_type=SUBCLOUD_GROUP_DICT['APPLY_TYPE'], + max_parallel_subclouds=SUBCLOUD_GROUP_DICT['MAX_PARALLEL_SUBCLOUDS'], + created_at=SUBCLOUD_GROUP_DICT['CREATED_AT'], + updated_at=SUBCLOUD_GROUP_DICT['UPDATED_AT'] +) + + +class TestCLISubcloudGroupManagerV1(base.BaseCommandTest): + + def setUp(self): + super(TestCLISubcloudGroupManagerV1, self).setUp() + # The client is the subcloud_group_manager + self.client = self.app.client_manager.subcloud_group_manager + + def test_list_subcloud_groups(self): + self.client.subcloud_group_manager.\ + list_subcloud_groups.return_value = [SUBCLOUD_GROUP] + actual_call = self.call(subcloud_group_cmd.ListSubcloudGroup) + self.assertEqual([(ID, NAME, DESCRIPTION)], + actual_call[1]) + + def test_list_subcloud_groups_empty(self): + self.client.subcloud_group_manager.\ + list_subcloud_groups.return_value = [] + actual_call = self.call(subcloud_group_cmd.ListSubcloudGroup) + self.assertEqual((('', '', ''),), + actual_call[1]) + + def test_list_subcloud_group_subclouds(self): + self.client.subcloud_group_manager.\ + subcloud_group_list_subclouds.return_value = [tsm.SUBCLOUD] + actual_call = self.call(subcloud_group_cmd.ListSubcloudGroupSubclouds, + app_args=[ID]) + self.client.subcloud_group_manager.subcloud_group_list_subclouds.\ + assert_called_once_with(ID) + self.assertEqual([tsm.DEFAULT_SUBCLOUD_FIELD_RESULT_LIST], + actual_call[1]) + + def test_delete_subcloud_group_by_id(self): + self.call(subcloud_group_cmd.DeleteSubcloudGroup, app_args=[ID]) + self.client.subcloud_group_manager.delete_subcloud_group.\ + assert_called_once_with(ID) + + def test_delete_subcloud_group_without_id(self): + self.assertRaises(SystemExit, self.call, + subcloud_group_cmd.DeleteSubcloudGroup, app_args=[]) + + def test_show_subcloud_group_with_id(self): + self.client.subcloud_group_manager.subcloud_group_detail.\ + return_value = [SUBCLOUD_GROUP] + actual_call = self.call(subcloud_group_cmd.ShowSubcloudGroup, + app_args=[ID]) + self.assertEqual((ID, + NAME, + DESCRIPTION, + APPLY_TYPE, + MAX_PARALLEL_SUBCLOUDS, + TIME_NOW, + TIME_NOW), + actual_call[1]) + + def test_show_subcloud_group_without_id(self): + self.client.subcloud_group_manager.subcloud_group_detail.\ + return_value = [] + actual_call = self.call(subcloud_group_cmd.ShowSubcloudGroup, + app_args=[ID]) + self.assertEqual((('', '', '', '', + '', '', ''),), + actual_call[1]) + + def test_add_subcloud_group(self): + self.client.subcloud_group_manager.add_subcloud_group.\ + return_value = [SUBCLOUD_GROUP] + + actual_call = self.call( + subcloud_group_cmd.AddSubcloudGroup, + app_args=['--name', NAME, + '--description', DESCRIPTION] + ) + self.assertEqual((ID, + NAME, + DESCRIPTION, + APPLY_TYPE, + MAX_PARALLEL_SUBCLOUDS, + TIME_NOW, + TIME_NOW), + actual_call[1]) + + def test_update_subcloud_group(self): + UPDATED_SUBCLOUD = copy.copy(SUBCLOUD_GROUP) + UPDATED_SUBCLOUD.description = NEW_DESCRIPTION + self.client.subcloud_group_manager.update_subcloud_group.\ + return_value = [UPDATED_SUBCLOUD] + actual_call = self.call( + subcloud_group_cmd.UpdateSubcloudGroup, + app_args=[SUBCLOUD_GROUP.group_id, + '--description', NEW_DESCRIPTION]) + self.assertEqual((ID, + NAME, + NEW_DESCRIPTION, + APPLY_TYPE, + MAX_PARALLEL_SUBCLOUDS, + TIME_NOW, + TIME_NOW), + actual_call[1]) diff --git a/distributedcloud-client/dcmanagerclient/tests/v1/test_subcloud_manager.py b/distributedcloud-client/dcmanagerclient/tests/v1/test_subcloud_manager.py index 35dd823..19527d7 100644 --- a/distributedcloud-client/dcmanagerclient/tests/v1/test_subcloud_manager.py +++ b/distributedcloud-client/dcmanagerclient/tests/v1/test_subcloud_manager.py @@ -51,6 +51,7 @@ SYSTEMCONTROLLER_GATEWAY_IP = '192.168.204.101' EXTERNAL_OAM_SUBNET = "10.10.10.0/24" EXTERNAL_OAM_GATEWAY_ADDRESS = "10.10.10.1" EXTERNAL_OAM_FLOATING_ADDRESS = "10.10.10.12" +DEFAULT_SUBCLOUD_GROUP_ID = '1' SUBCLOUD_DICT = { 'SUBCLOUD_ID': ID, @@ -68,6 +69,7 @@ SUBCLOUD_DICT = { 'SYSTEMCONTROLLER_GATEWAY_IP': SYSTEMCONTROLLER_GATEWAY_IP, 'CREATED_AT': TIME_NOW, 'UPDATED_AT': TIME_NOW, + 'GROUP_ID': DEFAULT_SUBCLOUD_GROUP_ID, 'OAM_FLOATING_IP': EXTERNAL_OAM_FLOATING_ADDRESS } @@ -87,7 +89,26 @@ SUBCLOUD = sm.Subcloud( management_gateway_ip=SUBCLOUD_DICT['MANAGEMENT_GATEWAY_IP'], systemcontroller_gateway_ip=SUBCLOUD_DICT['SYSTEMCONTROLLER_GATEWAY_IP'], created_at=SUBCLOUD_DICT['CREATED_AT'], - updated_at=SUBCLOUD_DICT['UPDATED_AT']) + updated_at=SUBCLOUD_DICT['UPDATED_AT'], + group_id=SUBCLOUD_DICT['GROUP_ID']) + +DEFAULT_SUBCLOUD_FIELD_RESULT_LIST = ( + 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, + TIME_NOW, + TIME_NOW) class TestCLISubcloudManagerV1(base.BaseCommandTest): @@ -119,19 +140,7 @@ class TestCLISubcloudManagerV1(base.BaseCommandTest): self.client.subcloud_manager.subcloud_detail.\ return_value = [SUBCLOUD] actual_call = self.call(subcloud_cmd.ShowSubcloud, app_args=[ID]) - self.assertEqual((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, - TIME_NOW, TIME_NOW), + self.assertEqual(DEFAULT_SUBCLOUD_FIELD_RESULT_LIST, actual_call[1]) def test_show_subcloud_with_additional_detail(self): @@ -155,6 +164,7 @@ class TestCLISubcloudManagerV1(base.BaseCommandTest): MANAGEMENT_END_IP, MANAGEMENT_GATEWAY_IP, SYSTEMCONTROLLER_GATEWAY_IP, + DEFAULT_SUBCLOUD_GROUP_ID, TIME_NOW, TIME_NOW, EXTERNAL_OAM_FLOATING_ADDRESS), actual_call[1]) @@ -165,7 +175,7 @@ class TestCLISubcloudManagerV1(base.BaseCommandTest): self.assertEqual((('', '', '', '', '', '', '', '', '', '', '', '', - '', '', ''),), + '', '', '', ''),), actual_call[1]) @mock.patch('getpass.getpass', return_value='testpassword') @@ -200,6 +210,7 @@ class TestCLISubcloudManagerV1(base.BaseCommandTest): MANAGEMENT_SUBNET, MANAGEMENT_START_IP, MANAGEMENT_END_IP, MANAGEMENT_GATEWAY_IP, SYSTEMCONTROLLER_GATEWAY_IP, + DEFAULT_SUBCLOUD_GROUP_ID, TIME_NOW, TIME_NOW), actual_call[1]) def test_unmanage_subcloud(self): @@ -214,6 +225,7 @@ class TestCLISubcloudManagerV1(base.BaseCommandTest): MANAGEMENT_SUBNET, MANAGEMENT_START_IP, MANAGEMENT_END_IP, MANAGEMENT_GATEWAY_IP, SYSTEMCONTROLLER_GATEWAY_IP, + DEFAULT_SUBCLOUD_GROUP_ID, TIME_NOW, TIME_NOW), actual_call[1]) def test_unmanage_subcloud_without_subcloud_id(self): @@ -232,6 +244,7 @@ class TestCLISubcloudManagerV1(base.BaseCommandTest): MANAGEMENT_SUBNET, MANAGEMENT_START_IP, MANAGEMENT_END_IP, MANAGEMENT_GATEWAY_IP, SYSTEMCONTROLLER_GATEWAY_IP, + DEFAULT_SUBCLOUD_GROUP_ID, TIME_NOW, TIME_NOW), actual_call[1]) def test_manage_subcloud_without_subcloud_id(self): @@ -253,4 +266,5 @@ class TestCLISubcloudManagerV1(base.BaseCommandTest): MANAGEMENT_SUBNET, MANAGEMENT_START_IP, MANAGEMENT_END_IP, MANAGEMENT_GATEWAY_IP, SYSTEMCONTROLLER_GATEWAY_IP, + DEFAULT_SUBCLOUD_GROUP_ID, TIME_NOW, TIME_NOW), actual_call[1])