diff --git a/releasenotes/notes/add-identity-v3-clients-for-os-ep-filter-api-endpoint-groups-3518a90bbb731d0f.yaml b/releasenotes/notes/add-identity-v3-clients-for-os-ep-filter-api-endpoint-groups-3518a90bbb731d0f.yaml new file mode 100644 index 0000000000..1dc33aaf9c --- /dev/null +++ b/releasenotes/notes/add-identity-v3-clients-for-os-ep-filter-api-endpoint-groups-3518a90bbb731d0f.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + Defines the identity v3 OS-EP-FILTER EndPoint Groups API client. + This client manages Create, Get, Update, Check, List, and Delete + of EndPoint Group. + diff --git a/tempest/api/identity/admin/v3/test_endpoint_groups.py b/tempest/api/identity/admin/v3/test_endpoint_groups.py new file mode 100644 index 0000000000..5cd456c9cf --- /dev/null +++ b/tempest/api/identity/admin/v3/test_endpoint_groups.py @@ -0,0 +1,157 @@ +# Copyright 2017 AT&T Corporation. +# 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. + +from tempest.api.identity import base +from tempest.lib.common.utils import data_utils +from tempest.lib import decorators + + +class EndPointGroupsTest(base.BaseIdentityV3AdminTest): + + @classmethod + def setup_clients(cls): + super(EndPointGroupsTest, cls).setup_clients() + cls.client = cls.endpoint_groups_client + + @classmethod + def resource_setup(cls): + super(EndPointGroupsTest, cls).resource_setup() + cls.service_ids = list() + cls.endpoint_groups = list() + + # Create endpoint group so as to use it for LIST test + service_id = cls._create_service() + + name = data_utils.rand_name('service_group') + description = data_utils.rand_name('description') + filters = {'service_id': service_id} + + endpoint_group = cls.client.create_endpoint_group( + name=name, + description=description, + filters=filters)['endpoint_group'] + + cls.endpoint_groups.append(endpoint_group) + + @classmethod + def resource_cleanup(cls): + for e in cls.endpoint_groups: + cls.client.delete_endpoint_group(e['id']) + for s in cls.service_ids: + cls.services_client.delete_service(s) + super(EndPointGroupsTest, cls).resource_cleanup() + + @classmethod + def _create_service(self): + s_name = data_utils.rand_name('service') + s_type = data_utils.rand_name('type') + s_description = data_utils.rand_name('description') + service_data = ( + self.services_client.create_service(name=s_name, + type=s_type, + description=s_description)) + + service_id = service_data['service']['id'] + self.service_ids.append(service_id) + return service_id + + @decorators.idempotent_id('7c69e7a1-f865-402d-a2ea-44493017315a') + def test_create_list_show_check_delete_endpoint_group(self): + service_id = self._create_service() + name = data_utils.rand_name('service_group') + description = data_utils.rand_name('description') + filters = {'service_id': service_id} + + endpoint_group = self.client.create_endpoint_group( + name=name, + description=description, + filters=filters)['endpoint_group'] + + self.endpoint_groups.append(endpoint_group) + + # Asserting created endpoint group response body + self.assertIn('id', endpoint_group) + self.assertEqual(name, endpoint_group['name']) + self.assertEqual(description, endpoint_group['description']) + + # Checking if endpoint groups are present in the list of endpoints + # Note that there are two endpoint groups in the list, one created + # in the resource setup, one created in this test case. + fetched_endpoints = \ + self.client.list_endpoint_groups()['endpoint_groups'] + + missing_endpoints = \ + [e for e in self.endpoint_groups if e not in fetched_endpoints] + + # Asserting LIST endpoints + self.assertEmpty(missing_endpoints, + "Failed to find endpoint %s in fetched list" % + ', '.join(str(e) for e in missing_endpoints)) + + # Show endpoint group + fetched_endpoint = self.client.show_endpoint_group( + endpoint_group['id'])['endpoint_group'] + + # Asserting if the attributes of endpoint group are the same + self.assertEqual(service_id, + fetched_endpoint['filters']['service_id']) + for attr in ('id', 'name', 'description'): + self.assertEqual(endpoint_group[attr], fetched_endpoint[attr]) + + # Check endpoint group + self.client.check_endpoint_group(endpoint_group['id']) + + # Deleting the endpoint group created in this method + self.client.delete_endpoint_group(endpoint_group['id']) + self.endpoint_groups.remove(endpoint_group) + + # Checking whether endpoint group is deleted successfully + fetched_endpoints = \ + self.client.list_endpoint_groups()['endpoint_groups'] + fetched_endpoint_ids = [e['id'] for e in fetched_endpoints] + self.assertNotIn(endpoint_group['id'], fetched_endpoint_ids) + + @decorators.idempotent_id('51c8fc38-fa84-4e76-b5b6-6fc37770fb26') + def test_update_endpoint_group(self): + # Creating an endpoint group so as to check update endpoint group + # with new values + service1_id = self._create_service() + name = data_utils.rand_name('service_group') + description = data_utils.rand_name('description') + filters = {'service_id': service1_id} + + endpoint_group = self.client.create_endpoint_group( + name=name, + description=description, + filters=filters)['endpoint_group'] + self.endpoint_groups.append(endpoint_group) + + # Creating new attr values to update endpoint group + service2_id = self._create_service() + name2 = data_utils.rand_name('service_group2') + description2 = data_utils.rand_name('description2') + filters = {'service_id': service2_id} + + # Updating endpoint group with new attr values + updated_endpoint_group = self.client.update_endpoint_group( + endpoint_group['id'], + name=name2, + description=description2, + filters=filters)['endpoint_group'] + + self.assertEqual(name2, updated_endpoint_group['name']) + self.assertEqual(description2, updated_endpoint_group['description']) + self.assertEqual(service2_id, + updated_endpoint_group['filters']['service_id']) diff --git a/tempest/api/identity/base.py b/tempest/api/identity/base.py index 785485b16f..3bc6ce16be 100644 --- a/tempest/api/identity/base.py +++ b/tempest/api/identity/base.py @@ -225,6 +225,7 @@ class BaseIdentityV3AdminTest(BaseIdentityV3Test): cls.oauth_consumers_client = cls.os_admin.oauth_consumers_client cls.domain_config_client = cls.os_admin.domain_config_client cls.endpoint_filter_client = cls.os_admin.endpoint_filter_client + cls.endpoint_groups_client = cls.os_admin.endpoint_groups_client if CONF.identity.admin_domain_scope: # NOTE(andreaf) When keystone policy requires it, the identity diff --git a/tempest/clients.py b/tempest/clients.py index 73a4b20282..5a444646c2 100644 --- a/tempest/clients.py +++ b/tempest/clients.py @@ -233,6 +233,8 @@ class Manager(clients.ServiceClients): **params_v3) self.endpoint_filter_client = \ self.identity_v3.EndPointsFilterClient(**params_v3) + self.endpoint_groups_client = self.identity_v3.EndPointGroupsClient( + **params_v3) # Token clients do not use the catalog. They only need default_params. # They read auth_url, so they should only be set if the corresponding diff --git a/tempest/lib/services/identity/v3/__init__.py b/tempest/lib/services/identity/v3/__init__.py index 6f498d9f67..ce607ffb97 100644 --- a/tempest/lib/services/identity/v3/__init__.py +++ b/tempest/lib/services/identity/v3/__init__.py @@ -19,6 +19,8 @@ from tempest.lib.services.identity.v3.domain_configuration_client \ from tempest.lib.services.identity.v3.domains_client import DomainsClient from tempest.lib.services.identity.v3.endpoint_filter_client import \ EndPointsFilterClient +from tempest.lib.services.identity.v3.endpoint_groups_client import \ + EndPointGroupsClient from tempest.lib.services.identity.v3.endpoints_client import EndPointsClient from tempest.lib.services.identity.v3.groups_client import GroupsClient from tempest.lib.services.identity.v3.identity_client import IdentityClient @@ -39,8 +41,9 @@ from tempest.lib.services.identity.v3.users_client import UsersClient from tempest.lib.services.identity.v3.versions_client import VersionsClient __all__ = ['CredentialsClient', 'DomainsClient', 'DomainConfigurationClient', - 'EndPointsClient', 'EndPointsFilterClient', 'GroupsClient', - 'IdentityClient', 'InheritedRolesClient', 'OAUTHConsumerClient', - 'PoliciesClient', 'ProjectsClient', 'RegionsClient', - 'RoleAssignmentsClient', 'RolesClient', 'ServicesClient', - 'V3TokenClient', 'TrustsClient', 'UsersClient', 'VersionsClient'] + 'EndPointGroupsClient', 'EndPointsClient', 'EndPointsFilterClient', + 'GroupsClient', 'IdentityClient', 'InheritedRolesClient', + 'OAUTHConsumerClient', 'PoliciesClient', 'ProjectsClient', + 'RegionsClient', 'RoleAssignmentsClient', 'RolesClient', + 'ServicesClient', 'V3TokenClient', 'TrustsClient', 'UsersClient', + 'VersionsClient'] diff --git a/tempest/lib/services/identity/v3/endpoint_groups_client.py b/tempest/lib/services/identity/v3/endpoint_groups_client.py new file mode 100644 index 0000000000..723aeaa091 --- /dev/null +++ b/tempest/lib/services/identity/v3/endpoint_groups_client.py @@ -0,0 +1,78 @@ +# Copyright 2017 AT&T Corporation. +# 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. + +from oslo_serialization import jsonutils as json + +from tempest.lib.common import rest_client + + +class EndPointGroupsClient(rest_client.RestClient): + api_version = "v3" + + def create_endpoint_group(self, **kwargs): + """Create endpoint group. + + For a full list of available parameters, please refer to the + official API reference: + https://developer.openstack.org/api-ref/identity/v3-ext/#create-endpoint-group + """ + post_body = json.dumps({'endpoint_group': kwargs}) + resp, body = self.post('OS-EP-FILTER/endpoint_groups', post_body) + self.expected_success(201, resp.status) + body = json.loads(body) + return rest_client.ResponseBody(resp, body) + + def update_endpoint_group(self, endpoint_group_id, **kwargs): + """Update endpoint group. + + For a full list of available parameters, please refer to the + official API reference: + https://developer.openstack.org/api-ref/identity/v3-ext/#update-endpoint-group + """ + post_body = json.dumps({'endpoint_group': kwargs}) + resp, body = self.patch( + 'OS-EP-FILTER/endpoint_groups/%s' % endpoint_group_id, post_body) + self.expected_success(200, resp.status) + body = json.loads(body) + return rest_client.ResponseBody(resp, body) + + def delete_endpoint_group(self, endpoint_group_id): + """Delete endpoint group.""" + resp_header, resp_body = self.delete( + 'OS-EP-FILTER/endpoint_groups/%s' % endpoint_group_id) + self.expected_success(204, resp_header.status) + return rest_client.ResponseBody(resp_header, resp_body) + + def show_endpoint_group(self, endpoint_group_id): + """Get endpoint group.""" + resp_header, resp_body = self.get( + 'OS-EP-FILTER/endpoint_groups/%s' % endpoint_group_id) + self.expected_success(200, resp_header.status) + resp_body = json.loads(resp_body) + return rest_client.ResponseBody(resp_header, resp_body) + + def check_endpoint_group(self, endpoint_group_id): + """Check endpoint group.""" + resp_header, resp_body = self.head( + 'OS-EP-FILTER/endpoint_groups/%s' % endpoint_group_id) + self.expected_success(200, resp_header.status) + return rest_client.ResponseBody(resp_header, resp_body) + + def list_endpoint_groups(self): + """Get endpoint groups.""" + resp_header, resp_body = self.get('OS-EP-FILTER/endpoint_groups') + self.expected_success(200, resp_header.status) + resp_body = json.loads(resp_body) + return rest_client.ResponseBody(resp_header, resp_body) diff --git a/tempest/tests/lib/services/identity/v3/test_endpoint_groups_client.py b/tempest/tests/lib/services/identity/v3/test_endpoint_groups_client.py new file mode 100644 index 0000000000..8b034e6d05 --- /dev/null +++ b/tempest/tests/lib/services/identity/v3/test_endpoint_groups_client.py @@ -0,0 +1,162 @@ +# Copyright 2017 AT&T Corporation. +# 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. + +from tempest.lib.services.identity.v3 import endpoint_groups_client +from tempest.tests.lib import fake_auth_provider +from tempest.tests.lib.services import base + + +class TestEndPointGroupsClient(base.BaseServiceTest): + FAKE_CREATE_ENDPOINT_GROUP = { + "endpoint_group": { + "id": 1, + "name": "FAKE_ENDPOINT_GROUP", + "description": "FAKE SERVICE ENDPOINT GROUP", + "filters": { + "service_id": 1 + } + } + } + + FAKE_ENDPOINT_GROUP_INFO = { + "endpoint_group": { + "id": 1, + "name": "FAKE_ENDPOINT_GROUP", + "description": "FAKE SERVICE ENDPOINT GROUP", + "links": { + "self": "http://example.com/identity/v3/OS-EP-FILTER/" + + "endpoint_groups/1" + }, + "filters": { + "service_id": 1 + } + } + } + + FAKE_LIST_ENDPOINT_GROUPS = { + "endpoint_groups": [ + { + "id": 1, + "name": "SERVICE_GROUP1", + "description": "FAKE SERVICE ENDPOINT GROUP", + "links": { + "self": "http://example.com/identity/v3/OS-EP-FILTER/" + + "endpoint_groups/1" + }, + "filters": { + "service_id": 1 + } + }, + { + "id": 2, + "name": "SERVICE_GROUP2", + "description": "FAKE SERVICE ENDPOINT GROUP", + "links": { + "self": "http://example.com/identity/v3/OS-EP-FILTER/" + + "endpoint_groups/2" + }, + "filters": { + "service_id": 2 + } + } + ] + } + + def setUp(self): + super(TestEndPointGroupsClient, self).setUp() + fake_auth = fake_auth_provider.FakeAuthProvider() + self.client = endpoint_groups_client.EndPointGroupsClient( + fake_auth, 'identity', 'regionOne') + + def _test_create_endpoint_group(self, bytes_body=False): + self.check_service_client_function( + self.client.create_endpoint_group, + 'tempest.lib.common.rest_client.RestClient.post', + self.FAKE_CREATE_ENDPOINT_GROUP, + bytes_body, + status=201, + name="FAKE_ENDPOINT_GROUP", + filters={'service_id': "1"}) + + def _test_show_endpoint_group(self, bytes_body=False): + self.check_service_client_function( + self.client.show_endpoint_group, + 'tempest.lib.common.rest_client.RestClient.get', + self.FAKE_ENDPOINT_GROUP_INFO, + bytes_body, + endpoint_group_id="1") + + def _test_check_endpoint_group(self, bytes_body=False): + self.check_service_client_function( + self.client.check_endpoint_group, + 'tempest.lib.common.rest_client.RestClient.head', + {}, + bytes_body, + status=200, + endpoint_group_id="1") + + def _test_update_endpoint_group(self, bytes_body=False): + self.check_service_client_function( + self.client.update_endpoint_group, + 'tempest.lib.common.rest_client.RestClient.patch', + self.FAKE_ENDPOINT_GROUP_INFO, + bytes_body, + endpoint_group_id="1", + name="NewName") + + def _test_list_endpoint_groups(self, bytes_body=False): + self.check_service_client_function( + self.client.list_endpoint_groups, + 'tempest.lib.common.rest_client.RestClient.get', + self.FAKE_LIST_ENDPOINT_GROUPS, + bytes_body) + + def test_create_endpoint_group_with_str_body(self): + self._test_create_endpoint_group() + + def test_create_endpoint_group_with_bytes_body(self): + self._test_create_endpoint_group(bytes_body=True) + + def test_show_endpoint_group_with_str_body(self): + self._test_show_endpoint_group() + + def test_show_endpoint_group_with_bytes_body(self): + self._test_show_endpoint_group(bytes_body=True) + + def test_check_endpoint_group_with_str_body(self): + self._test_check_endpoint_group() + + def test_check_endpoint_group_with_bytes_body(self): + self._test_check_endpoint_group(bytes_body=True) + + def test_list_endpoint_groups_with_str_body(self): + self._test_list_endpoint_groups() + + def test_list_endpoint_groups_with_bytes_body(self): + self._test_list_endpoint_groups(bytes_body=True) + + def test_update_endpoint_group_with_str_body(self): + self._test_update_endpoint_group() + + def test_update_endpoint_group_with_bytes_body(self): + self._test_update_endpoint_group(bytes_body=True) + + def test_delete_endpoint_group(self): + self.check_service_client_function( + self.client.delete_endpoint_group, + 'tempest.lib.common.rest_client.RestClient.delete', + {}, + endpoint_group_id="1", + status=204)