diff --git a/keystoneclient/tests/functional/v3/client_fixtures.py b/keystoneclient/tests/functional/v3/client_fixtures.py index 9873b26b9..dd7209a6e 100644 --- a/keystoneclient/tests/functional/v3/client_fixtures.py +++ b/keystoneclient/tests/functional/v3/client_fixtures.py @@ -178,6 +178,18 @@ class Endpoint(Base): self.addCleanup(self.client.endpoints.delete, self.entity) +class EndpointGroup(Base): + + def setUp(self): + super(EndpointGroup, self).setUp() + + self.ref = {'name': RESOURCE_NAME_PREFIX + uuid.uuid4().hex, + 'filters': {'interface': 'public'}, + 'description': uuid.uuid4().hex} + self.entity = self.client.endpoint_groups.create(**self.ref) + self.addCleanup(self.client.endpoint_groups.delete, self.entity) + + class Credential(Base): def __init__(self, client, user, type, project=None): diff --git a/keystoneclient/tests/functional/v3/test_endpoint_groups.py b/keystoneclient/tests/functional/v3/test_endpoint_groups.py new file mode 100644 index 000000000..10eccfb29 --- /dev/null +++ b/keystoneclient/tests/functional/v3/test_endpoint_groups.py @@ -0,0 +1,120 @@ +# 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. + +import uuid + +from keystoneauth1.exceptions import http + +from keystoneclient.tests.functional import base +from keystoneclient.tests.functional.v3 import client_fixtures as fixtures + + +class EndpointGroupsTestCase(base.V3ClientTestCase): + + def check_endpoint_group(self, endpoint_group, endpoint_group_ref=None): + self.assertIsNotNone(endpoint_group.id) + self.assertIn('self', endpoint_group.links) + self.assertIn('/endpoint_groups/' + endpoint_group.id, + endpoint_group.links['self']) + + if endpoint_group_ref: + self.assertEqual(endpoint_group_ref['name'], endpoint_group.name) + self.assertEqual(endpoint_group_ref['filters'], + endpoint_group.filters) + + # There is no guarantee description is present in endpoint groups + if hasattr(endpoint_group_ref, 'description'): + self.assertEqual(endpoint_group_ref['description'], + endpoint_group.description) + else: + # Only check remaining mandatory attributes + self.assertIsNotNone(endpoint_group.name) + self.assertIsNotNone(endpoint_group.filters) + + def test_create_endpoint_group(self): + endpoint_group_ref = { + 'name': fixtures.RESOURCE_NAME_PREFIX + uuid.uuid4().hex, + 'filters': {'interface': 'internal'}, + 'description': uuid.uuid4().hex} + endpoint_group = self.client.endpoint_groups.create( + **endpoint_group_ref) + + self.addCleanup(self.client.endpoint_groups.delete, endpoint_group) + self.check_endpoint_group(endpoint_group, endpoint_group_ref) + + def test_get_endpoint_group(self): + endpoint_group = fixtures.EndpointGroup(self.client) + self.useFixture(endpoint_group) + + endpoint_ret = self.client.endpoint_groups.get(endpoint_group.id) + self.check_endpoint_group(endpoint_ret, endpoint_group.ref) + + self.assertRaises(http.NotFound, + self.client.endpoint_groups.get, + uuid.uuid4().hex) + + def test_check_endpoint_group(self): + endpoint_group = fixtures.EndpointGroup(self.client) + self.useFixture(endpoint_group) + + self.client.endpoint_groups.check(endpoint_group.id) + self.assertRaises(http.NotFound, + self.client.endpoint_groups.check, + uuid.uuid4().hex) + + def test_list_endpoint_groups(self): + endpoint_group_one = fixtures.EndpointGroup(self.client) + self.useFixture(endpoint_group_one) + + endpoint_group_two = fixtures.EndpointGroup(self.client) + self.useFixture(endpoint_group_two) + + endpoint_groups = self.client.endpoint_groups.list() + + # All endpoints are valid + for endpoint_group in endpoint_groups: + self.check_endpoint_group(endpoint_group) + + self.assertIn(endpoint_group_one.entity, endpoint_groups) + self.assertIn(endpoint_group_two.entity, endpoint_groups) + + def test_update_endpoint_group(self): + endpoint_group = fixtures.EndpointGroup(self.client) + self.useFixture(endpoint_group) + + new_name = fixtures.RESOURCE_NAME_PREFIX + uuid.uuid4().hex + new_filters = {'interface': 'public'} + new_description = uuid.uuid4().hex + + endpoint_group_ret = self.client.endpoint_groups.update( + endpoint_group, + name=new_name, + filters=new_filters, + description=new_description) + + endpoint_group.ref.update({'name': new_name, 'filters': new_filters, + 'description': new_description}) + self.check_endpoint_group(endpoint_group_ret, endpoint_group.ref) + + def test_delete_endpoint_group(self): + endpoint_group = self.client.endpoint_groups.create( + name=fixtures.RESOURCE_NAME_PREFIX + uuid.uuid4().hex, + filters={'interface': 'admin'}, + description=uuid.uuid4().hex) + + self.client.endpoint_groups.delete(endpoint_group.id) + self.assertRaises(http.NotFound, + self.client.endpoint_groups.check, + endpoint_group.id) + self.assertRaises(http.NotFound, + self.client.endpoint_groups.get, + endpoint_group.id) diff --git a/keystoneclient/tests/unit/v3/test_endpoint_groups.py b/keystoneclient/tests/unit/v3/test_endpoint_groups.py new file mode 100644 index 000000000..364fd53c2 --- /dev/null +++ b/keystoneclient/tests/unit/v3/test_endpoint_groups.py @@ -0,0 +1,34 @@ +# 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. + +import uuid + +from keystoneclient.tests.unit.v3 import utils +from keystoneclient.v3 import endpoint_groups + + +class EndpointGroupTests(utils.ClientTestCase, utils.CrudTests): + + def setUp(self): + super(EndpointGroupTests, self).setUp() + self.key = 'endpoint_group' + self.collection_key = 'endpoint_groups' + self.model = endpoint_groups.EndpointGroup + self.manager = self.client.endpoint_groups + self.path_prefix = 'OS-EP-FILTER' + + def new_ref(self, **kwargs): + kwargs.setdefault('id', uuid.uuid4().hex) + kwargs.setdefault('name', uuid.uuid4().hex) + kwargs.setdefault('filters', '{"interface": "public"}') + kwargs.setdefault('description', uuid.uuid4().hex) + return kwargs diff --git a/keystoneclient/v3/client.py b/keystoneclient/v3/client.py index 181af895f..2ca180a78 100644 --- a/keystoneclient/v3/client.py +++ b/keystoneclient/v3/client.py @@ -33,6 +33,7 @@ from keystoneclient.v3 import credentials from keystoneclient.v3 import domain_configs from keystoneclient.v3 import domains from keystoneclient.v3 import ec2 +from keystoneclient.v3 import endpoint_groups from keystoneclient.v3 import endpoints from keystoneclient.v3 import groups from keystoneclient.v3 import policies @@ -130,6 +131,11 @@ class Client(httpclient.HTTPClient): :py:class:`keystoneclient.v3.contrib.endpoint_filter.\ EndpointFilterManager` + .. py:attribute:: endpoint_groups + + :py:class:`keystoneclient.v3.endpoint_groups.\ + EndpointGroupManager` + .. py:attribute:: endpoint_policy :py:class:`keystoneclient.v3.contrib.endpoint_policy.\ @@ -211,6 +217,8 @@ class Client(httpclient.HTTPClient): self.ec2 = ec2.EC2Manager(self._adapter) self.endpoint_filter = endpoint_filter.EndpointFilterManager( self._adapter) + self.endpoint_groups = endpoint_groups.EndpointGroupManager( + self._adapter) self.endpoint_policy = endpoint_policy.EndpointPolicyManager( self._adapter) self.endpoints = endpoints.EndpointManager(self._adapter) diff --git a/keystoneclient/v3/endpoint_groups.py b/keystoneclient/v3/endpoint_groups.py new file mode 100644 index 000000000..f8b47c4d6 --- /dev/null +++ b/keystoneclient/v3/endpoint_groups.py @@ -0,0 +1,136 @@ +# 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 keystoneclient import base + + +class EndpointGroup(base.Resource): + """Represents an identity endpoint group. + + Attributes: + * id: a UUID that identifies the endpoint group + * name: the endpoint group name + * description: the endpoint group description + * filters: representation of filters in the format of JSON that define + what endpoint entities are part of the group + + """ + + pass + + +class EndpointGroupManager(base.CrudManager): + """Manager class for Endpoint Groups.""" + + resource_class = EndpointGroup + collection_key = 'endpoint_groups' + key = 'endpoint_group' + base_url = 'OS-EP-FILTER' + + def create(self, name, filters, description=None, **kwargs): + """Create an endpoint group. + + :param str name: the name of the endpoint group. + :param str filters: representation of filters in the format of JSON + that define what endpoint entities are part of the + group. + :param str description: a description of the endpoint group. + :param kwargs: any other attribute provided will be passed to the + server. + + :returns: the created endpoint group returned from server. + :rtype: :class:`keystoneclient.v3.endpoint_groups.EndpointGroup` + + """ + return super(EndpointGroupManager, self).create( + name=name, + filters=filters, + description=description, + **kwargs) + + def get(self, endpoint_group): + """Retrieve an endpoint group. + + :param endpoint_group: the endpoint group to be retrieved from the + server. + :type endpoint_group: + str or :class:`keystoneclient.v3.endpoint_groups.EndpointGroup` + + :returns: the specified endpoint group returned from server. + :rtype: :class:`keystoneclient.v3.endpoint_groups.EndpointGroup` + + """ + return super(EndpointGroupManager, self).get( + endpoint_group_id=base.getid(endpoint_group)) + + def check(self, endpoint_group): + """Check if an endpoint group exists. + + :param endpoint_group: the endpoint group to be checked against the + server. + :type endpoint_group: + str or :class:`keystoneclient.v3.endpoint_groups.EndpointGroup` + + :returns: none if the specified endpoint group exists. + + """ + return super(EndpointGroupManager, self).head( + endpoint_group_id=base.getid(endpoint_group)) + + def list(self, **kwargs): + """List endpoint groups. + + Any parameter provided will be passed to the server. + + :returns: a list of endpoint groups. + :rtype: list of + :class:`keystoneclient.v3.endpoint_groups.EndpointGroup`. + + """ + return super(EndpointGroupManager, self).list(**kwargs) + + def update(self, endpoint_group, name=None, filters=None, + description=None, **kwargs): + """Update an endpoint group. + + :param str name: the new name of the endpoint group. + :param str filters: the new representation of filters in the format of + JSON that define what endpoint entities are part of + the group. + :param str description: the new description of the endpoint group. + :param kwargs: any other attribute provided will be passed to the + server. + + :returns: the updated endpoint group returned from server. + :rtype: :class:`keystoneclient.v3.endpoint_groups.EndpointGroup` + + """ + return super(EndpointGroupManager, self).update( + endpoint_group_id=base.getid(endpoint_group), + name=name, + filters=filters, + description=description, + **kwargs) + + def delete(self, endpoint_group): + """Delete an endpoint group. + + :param endpoint_group: the endpoint group to be deleted on the server. + :type endpoint_group: + str or :class:`keystoneclient.v3.endpoint_groups.EndpointGroup` + + :returns: Response object with 204 status. + :rtype: :class:`requests.models.Response` + + """ + return super(EndpointGroupManager, self).delete( + endpoint_group_id=base.getid(endpoint_group))