diff --git a/releasenotes/notes/add-identity-v3-clients-for-os-ep-filter-api-extensions-9cfd217fd2c6a61f.yaml b/releasenotes/notes/add-identity-v3-clients-for-os-ep-filter-api-extensions-9cfd217fd2c6a61f.yaml new file mode 100644 index 0000000000..69320fb3e3 --- /dev/null +++ b/releasenotes/notes/add-identity-v3-clients-for-os-ep-filter-api-extensions-9cfd217fd2c6a61f.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Defines the identity v3 OS-EP-FILTER extension API client. + This client manages associations between endpoints, projects + along with groups. diff --git a/tempest/api/identity/base.py b/tempest/api/identity/base.py index 5dc88b8fe5..a998dcfcfb 100644 --- a/tempest/api/identity/base.py +++ b/tempest/api/identity/base.py @@ -223,6 +223,7 @@ class BaseIdentityV3AdminTest(BaseIdentityV3Test): cls.role_assignments = cls.os_admin.role_assignments_client cls.oauth_consumers_client = cls.os_adm.oauth_consumers_client cls.domain_config_client = cls.os_adm.domain_config_client + cls.endpoint_filter_client = cls.os_adm.endpoint_filter_client if CONF.identity.admin_domain_scope: # NOTE(andreaf) When keystone policy requires it, the identity # admin clients for these tests shall use 'domain' scoped tokens. diff --git a/tempest/clients.py b/tempest/clients.py index 4ef6872183..e000a7481c 100644 --- a/tempest/clients.py +++ b/tempest/clients.py @@ -231,6 +231,8 @@ class Manager(clients.ServiceClients): **params_v3) self.domain_config_client = self.identity_v3.DomainConfigurationClient( **params_v3) + self.endpoint_filter_client = \ + self.identity_v3.EndPointsFilterClient(**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 f2f3391970..6f498d9f67 100644 --- a/tempest/lib/services/identity/v3/__init__.py +++ b/tempest/lib/services/identity/v3/__init__.py @@ -17,6 +17,8 @@ from tempest.lib.services.identity.v3.credentials_client import \ from tempest.lib.services.identity.v3.domain_configuration_client \ import DomainConfigurationClient 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.endpoints_client import EndPointsClient from tempest.lib.services.identity.v3.groups_client import GroupsClient from tempest.lib.services.identity.v3.identity_client import IdentityClient @@ -37,8 +39,8 @@ 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', 'GroupsClient', 'IdentityClient', - 'InheritedRolesClient', 'OAUTHConsumerClient', 'PoliciesClient', - 'ProjectsClient', 'RegionsClient', 'RoleAssignmentsClient', - 'RolesClient', 'ServicesClient', 'V3TokenClient', 'TrustsClient', - 'UsersClient', 'VersionsClient'] + '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_filter_client.py b/tempest/lib/services/identity/v3/endpoint_filter_client.py new file mode 100644 index 0000000000..a8cd7222e3 --- /dev/null +++ b/tempest/lib/services/identity/v3/endpoint_filter_client.py @@ -0,0 +1,68 @@ +# Copyright 2017 AT&T Corp. +# 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. + +""" +https://developer.openstack.org/api-ref/identity/v3-ext/#os-ep-filter-api +""" + +from oslo_serialization import jsonutils as json + +from tempest.lib.common import rest_client + + +class EndPointsFilterClient(rest_client.RestClient): + api_version = "v3" + ep_filter = "OS-EP-FILTER" + + def list_projects_for_endpoint(self, endpoint_id): + """List all projects that are associated with the endpoint.""" + resp, body = self.get(self.ep_filter + '/endpoints/%s/projects' % + endpoint_id) + self.expected_success(200, resp.status) + body = json.loads(body) + return rest_client.ResponseBody(resp, body) + + def add_endpoint_to_project(self, project_id, endpoint_id): + """Add association between project and endpoint. """ + body = None + resp, body = self.put( + self.ep_filter + '/projects/%s/endpoints/%s' % + (project_id, endpoint_id), body) + self.expected_success(204, resp.status) + return rest_client.ResponseBody(resp, body) + + def check_endpoint_in_project(self, project_id, endpoint_id): + """Check association of Project with Endpoint.""" + resp, body = self.head( + self.ep_filter + '/projects/%s/endpoints/%s' % + (project_id, endpoint_id), None) + self.expected_success(204, resp.status) + return rest_client.ResponseBody(resp, body) + + def list_endpoints_in_project(self, project_id): + """List Endpoints associated with Project.""" + resp, body = self.get(self.ep_filter + '/projects/%s/endpoints' + % project_id) + self.expected_success(200, resp.status) + body = json.loads(body) + return rest_client.ResponseBody(resp, body) + + def delete_endpoint_from_project(self, project_id, endpoint_id): + """Delete association between project and endpoint.""" + resp, body = self.delete( + self.ep_filter + '/projects/%s/endpoints/%s' + % (project_id, endpoint_id)) + self.expected_success(204, resp.status) + return rest_client.ResponseBody(resp, body) diff --git a/tempest/tests/lib/services/identity/v3/test_endpoint_filter_client.py b/tempest/tests/lib/services/identity/v3/test_endpoint_filter_client.py new file mode 100644 index 0000000000..7faf6a02f1 --- /dev/null +++ b/tempest/tests/lib/services/identity/v3/test_endpoint_filter_client.py @@ -0,0 +1,165 @@ +# Copyright 2017 AT&T Corp. +# 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_filter_client +from tempest.tests.lib import fake_auth_provider +from tempest.tests.lib.services import base + + +class TestEndPointsFilterClient(base.BaseServiceTest): + FAKE_LIST_PROJECTS_FOR_ENDPOINTS = { + "projects": [ + { + "domain_id": "1777c7", + "enabled": True, + "id": "1234ab1", + "type": "compute", + "links": { + "self": "http://example.com/identity/v3/projects/1234ab1" + }, + "name": "Project 1", + "description": "Project 1 description", + }, + { + "domain_id": "1777c7", + "enabled": True, + "id": "5678cd2", + "type": "compute", + "links": { + "self": "http://example.com/identity/v3/projects/5678cd2" + }, + "name": "Project 2", + "description": "Project 2 description", + } + ], + "links": { + "self": "http://example.com/identity/v3/OS-EP-FILTER/endpoints/\ + u6ay5u/projects", + "previous": None, + "next": None + } + } + + FAKE_LIST_ENDPOINTS_FOR_PROJECTS = { + "endpoints": [ + { + "id": "u6ay5u", + "interface": "public", + "url": "http://example.com/identity/", + "region": "north", + "links": { + "self": "http://example.com/identity/v3/endpoints/u6ay5u" + }, + "service_id": "5um4r", + }, + { + "id": "u6ay5u", + "interface": "internal", + "url": "http://example.com/identity/", + "region": "south", + "links": { + "self": "http://example.com/identity/v3/endpoints/u6ay5u" + }, + "service_id": "5um4r", + }, + ], + "links": { + "self": "http://example.com/identity/v3/OS-EP-FILTER/projects/\ + 1234ab1/endpoints", + "previous": None, + "next": None + } + } + + def setUp(self): + super(TestEndPointsFilterClient, self).setUp() + fake_auth = fake_auth_provider.FakeAuthProvider() + self.client = endpoint_filter_client.EndPointsFilterClient( + fake_auth, 'identity', 'regionOne') + + def _test_add_endpoint_to_project(self, bytes_body=False): + self.check_service_client_function( + self.client.add_endpoint_to_project, + 'tempest.lib.common.rest_client.RestClient.put', + {}, + bytes_body, + status=204, + project_id=3, + endpoint_id=4) + + def _test_check_endpoint_in_project(self, bytes_body=False): + self.check_service_client_function( + self.client.check_endpoint_in_project, + 'tempest.lib.common.rest_client.RestClient.head', + {}, + bytes_body, + status=204, + project_id=3, + endpoint_id=4) + + def _test_list_projects_for_endpoint(self, bytes_body=False): + self.check_service_client_function( + self.client.list_projects_for_endpoint, + 'tempest.lib.common.rest_client.RestClient.get', + self.FAKE_LIST_PROJECTS_FOR_ENDPOINTS, + bytes_body, + status=200, + endpoint_id=3) + + def _test_list_endpoints_in_project(self, bytes_body=False): + self.check_service_client_function( + self.client.list_endpoints_in_project, + 'tempest.lib.common.rest_client.RestClient.get', + self.FAKE_LIST_ENDPOINTS_FOR_PROJECTS, + bytes_body, + status=200, + project_id=4) + + def _test_delete_endpoint_from_project(self, bytes_body=False): + self.check_service_client_function( + self.client.delete_endpoint_from_project, + 'tempest.lib.common.rest_client.RestClient.delete', + {}, + bytes_body, + status=204, + project_id=3, + endpoint_id=4) + + def test_add_endpoint_to_project_with_str_body(self): + self._test_add_endpoint_to_project() + + def test_add_endpoint_to_project_with_bytes_body(self): + self._test_add_endpoint_to_project(bytes_body=True) + + def test_check_endpoint_in_project_with_str_body(self): + self._test_check_endpoint_in_project() + + def test_check_endpoint_in_project_with_bytes_body(self): + self._test_check_endpoint_in_project(bytes_body=True) + + def test_list_projects_for_endpoint_with_str_body(self): + self._test_list_projects_for_endpoint() + + def test_list_projects_for_endpoint_with_bytes_body(self): + self._test_list_projects_for_endpoint(bytes_body=True) + + def test_list_endpoints_in_project_with_str_body(self): + self._test_list_endpoints_in_project() + + def test_list_endpoints_in_project_with_bytes_body(self): + self._test_list_endpoints_in_project(bytes_body=True) + + def test_delete_endpoint_from_project(self): + self._test_delete_endpoint_from_project()