Merge "Support domain-specific configuration management"
This commit is contained in:
@@ -8,6 +8,7 @@ Introduction
|
||||
The main concepts in the Identity v3 API are:
|
||||
|
||||
* :py:mod:`~keystoneclient.v3.credentials`
|
||||
* :py:mod:`~keystoneclient.v3.domain_configs`
|
||||
* :py:mod:`~keystoneclient.v3.domains`
|
||||
* :py:mod:`~keystoneclient.v3.endpoints`
|
||||
* :py:mod:`~keystoneclient.v3.groups`
|
||||
|
@@ -219,3 +219,20 @@ class EC2(Base):
|
||||
self.addCleanup(self.client.ec2.delete,
|
||||
self.user_id,
|
||||
self.entity.access)
|
||||
|
||||
|
||||
class DomainConfig(Base):
|
||||
|
||||
def __init__(self, client, domain_id):
|
||||
super(DomainConfig, self).__init__(client, domain_id=domain_id)
|
||||
self.domain_id = domain_id
|
||||
|
||||
def setUp(self):
|
||||
super(DomainConfig, self).setUp()
|
||||
|
||||
self.ref = {'identity': {'driver': uuid.uuid4().hex},
|
||||
'ldap': {'url': uuid.uuid4().hex}}
|
||||
self.entity = self.client.domain_configs.create(
|
||||
self.domain_id, self.ref)
|
||||
self.addCleanup(self.client.domain_configs.delete,
|
||||
self.domain_id)
|
||||
|
101
keystoneclient/tests/functional/v3/test_domain_configs.py
Normal file
101
keystoneclient/tests/functional/v3/test_domain_configs.py
Normal file
@@ -0,0 +1,101 @@
|
||||
# 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 DomainConfigsTestCase(base.V3ClientTestCase):
|
||||
|
||||
def check_domain_config(self, config, config_ref):
|
||||
for attr in config_ref:
|
||||
self.assertEqual(
|
||||
getattr(config, attr),
|
||||
config_ref[attr],
|
||||
'Expected different %s' % attr)
|
||||
|
||||
def _new_ref(self):
|
||||
return {'identity': {'driver': uuid.uuid4().hex},
|
||||
'ldap': {'url': uuid.uuid4().hex}}
|
||||
|
||||
def test_create_domain_config(self):
|
||||
config_ref = self._new_ref()
|
||||
config = self.client.domain_configs.create(
|
||||
self.project_domain_id, config_ref)
|
||||
self.addCleanup(
|
||||
self.client.domain_configs.delete, self.project_domain_id)
|
||||
self.check_domain_config(config, config_ref)
|
||||
|
||||
def test_create_invalid_domain_config(self):
|
||||
invalid_groups_ref = {
|
||||
uuid.uuid4().hex: {uuid.uuid4().hex: uuid.uuid4().hex},
|
||||
uuid.uuid4().hex: {uuid.uuid4().hex: uuid.uuid4().hex}}
|
||||
self.assertRaises(http.Forbidden,
|
||||
self.client.domain_configs.create,
|
||||
self.project_domain_id,
|
||||
invalid_groups_ref)
|
||||
|
||||
invalid_options_ref = {
|
||||
'identity': {uuid.uuid4().hex: uuid.uuid4().hex},
|
||||
'ldap': {uuid.uuid4().hex: uuid.uuid4().hex}}
|
||||
self.assertRaises(http.Forbidden,
|
||||
self.client.domain_configs.create,
|
||||
self.project_domain_id,
|
||||
invalid_options_ref)
|
||||
|
||||
def test_get_domain_config(self):
|
||||
config = fixtures.DomainConfig(self.client, self.project_domain_id)
|
||||
self.useFixture(config)
|
||||
|
||||
config_ret = self.client.domain_configs.get(self.project_domain_id)
|
||||
self.check_domain_config(config_ret, config.ref)
|
||||
|
||||
def test_update_domain_config(self):
|
||||
config = fixtures.DomainConfig(self.client, self.project_domain_id)
|
||||
self.useFixture(config)
|
||||
|
||||
update_config_ref = self._new_ref()
|
||||
config_ret = self.client.domain_configs.update(
|
||||
self.project_domain_id, update_config_ref)
|
||||
self.check_domain_config(config_ret, update_config_ref)
|
||||
|
||||
def test_update_invalid_domain_config(self):
|
||||
config = fixtures.DomainConfig(self.client, self.project_domain_id)
|
||||
self.useFixture(config)
|
||||
|
||||
invalid_groups_ref = {
|
||||
uuid.uuid4().hex: {uuid.uuid4().hex: uuid.uuid4().hex},
|
||||
uuid.uuid4().hex: {uuid.uuid4().hex: uuid.uuid4().hex}}
|
||||
self.assertRaises(http.Forbidden,
|
||||
self.client.domain_configs.update,
|
||||
self.project_domain_id,
|
||||
invalid_groups_ref)
|
||||
|
||||
invalid_options_ref = {
|
||||
'identity': {uuid.uuid4().hex: uuid.uuid4().hex},
|
||||
'ldap': {uuid.uuid4().hex: uuid.uuid4().hex}}
|
||||
self.assertRaises(http.Forbidden,
|
||||
self.client.domain_configs.update,
|
||||
self.project_domain_id,
|
||||
invalid_options_ref)
|
||||
|
||||
def test_domain_config_delete(self):
|
||||
config_ref = self._new_ref()
|
||||
self.client.domain_configs.create(self.project_domain_id, config_ref)
|
||||
|
||||
self.client.domain_configs.delete(self.project_domain_id)
|
||||
self.assertRaises(http.NotFound,
|
||||
self.client.domain_configs.get,
|
||||
self.project_domain_id)
|
96
keystoneclient/tests/unit/v3/test_domain_configs.py
Normal file
96
keystoneclient/tests/unit/v3/test_domain_configs.py
Normal file
@@ -0,0 +1,96 @@
|
||||
# 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 import exceptions
|
||||
from keystoneclient.tests.unit.v3 import utils
|
||||
from keystoneclient.v3 import domain_configs
|
||||
|
||||
|
||||
class DomainConfigsTests(utils.ClientTestCase, utils.CrudTests):
|
||||
"""Test domain config database management."""
|
||||
|
||||
def setUp(self):
|
||||
super(DomainConfigsTests, self).setUp()
|
||||
self.key = 'config'
|
||||
self.model = domain_configs.DomainConfig
|
||||
self.manager = self.client.domain_configs
|
||||
|
||||
def new_ref(self, **kwargs):
|
||||
config_groups = {'identity': {uuid.uuid4().hex: uuid.uuid4().hex},
|
||||
'ldap': {uuid.uuid4().hex: uuid.uuid4().hex}}
|
||||
kwargs.setdefault('config', config_groups)
|
||||
return kwargs
|
||||
|
||||
def _assert_resource_attributes(self, resource, req_ref):
|
||||
for attr in req_ref:
|
||||
self.assertEqual(
|
||||
getattr(resource, attr),
|
||||
req_ref[attr],
|
||||
'Expected different %s' % attr)
|
||||
|
||||
def test_create(self):
|
||||
domain_id = uuid.uuid4().hex
|
||||
config = self.new_ref()
|
||||
|
||||
self.stub_url('PUT',
|
||||
parts=['domains', domain_id, 'config'],
|
||||
json=config, status_code=201)
|
||||
res = self.manager.create(domain_id, config)
|
||||
self._assert_resource_attributes(res, config['config'])
|
||||
self.assertEntityRequestBodyIs(config)
|
||||
|
||||
def test_update(self):
|
||||
domain_id = uuid.uuid4().hex
|
||||
config = self.new_ref()
|
||||
|
||||
self.stub_url('PATCH',
|
||||
parts=['domains', domain_id, 'config'],
|
||||
json=config, status_code=200)
|
||||
res = self.manager.update(domain_id, config)
|
||||
self._assert_resource_attributes(res, config['config'])
|
||||
self.assertEntityRequestBodyIs(config)
|
||||
|
||||
def test_get(self):
|
||||
domain_id = uuid.uuid4().hex
|
||||
config = self.new_ref()
|
||||
config = config['config']
|
||||
|
||||
self.stub_entity('GET',
|
||||
parts=['domains', domain_id, 'config'],
|
||||
entity=config)
|
||||
res = self.manager.get(domain_id)
|
||||
self._assert_resource_attributes(res, config)
|
||||
|
||||
def test_delete(self):
|
||||
domain_id = uuid.uuid4().hex
|
||||
self.stub_url('DELETE',
|
||||
parts=['domains', domain_id, 'config'],
|
||||
status_code=204)
|
||||
self.manager.delete(domain_id)
|
||||
|
||||
def test_list(self):
|
||||
# List not supported for domain config
|
||||
self.assertRaises(exceptions.MethodNotImplemented, self.manager.list)
|
||||
|
||||
def test_list_by_id(self):
|
||||
# List not supported for domain config
|
||||
self.assertRaises(exceptions.MethodNotImplemented, self.manager.list)
|
||||
|
||||
def test_list_params(self):
|
||||
# List not supported for domain config
|
||||
self.assertRaises(exceptions.MethodNotImplemented, self.manager.list)
|
||||
|
||||
def test_find(self):
|
||||
# Find not supported for domain config
|
||||
self.assertRaises(exceptions.MethodNotImplemented, self.manager.find)
|
@@ -30,6 +30,7 @@ from keystoneclient.v3.contrib import oauth1
|
||||
from keystoneclient.v3.contrib import simple_cert
|
||||
from keystoneclient.v3.contrib import trusts
|
||||
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 endpoints
|
||||
@@ -116,6 +117,10 @@ class Client(httpclient.HTTPClient):
|
||||
|
||||
:py:class:`keystoneclient.v3.credentials.CredentialManager`
|
||||
|
||||
.. py:attribute:: domain_configs
|
||||
|
||||
:py:class:`keystoneclient.v3.domain_configs.DomainConfigManager`
|
||||
|
||||
.. py:attribute:: ec2
|
||||
|
||||
:py:class:`keystoneclient.v3.ec2.EC2Manager`
|
||||
@@ -209,6 +214,7 @@ class Client(httpclient.HTTPClient):
|
||||
self.endpoint_policy = endpoint_policy.EndpointPolicyManager(
|
||||
self._adapter)
|
||||
self.endpoints = endpoints.EndpointManager(self._adapter)
|
||||
self.domain_configs = domain_configs.DomainConfigManager(self._adapter)
|
||||
self.domains = domains.DomainManager(self._adapter)
|
||||
self.federation = federation.FederationManager(self._adapter)
|
||||
self.groups = groups.GroupManager(self._adapter)
|
||||
|
130
keystoneclient/v3/domain_configs.py
Normal file
130
keystoneclient/v3/domain_configs.py
Normal file
@@ -0,0 +1,130 @@
|
||||
# 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
|
||||
from keystoneclient import exceptions
|
||||
from keystoneclient.i18n import _
|
||||
|
||||
|
||||
class DomainConfig(base.Resource):
|
||||
"""An object representing a domain config association.
|
||||
|
||||
This resource object does not necessarily contain fixed attributes, as new
|
||||
attributes are added in the server, they are supported here directly.
|
||||
The currently supported configs are `identity` and `ldap`.
|
||||
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class DomainConfigManager(base.Manager):
|
||||
"""Manager class for manipulating domain config associations."""
|
||||
|
||||
resource_class = DomainConfig
|
||||
key = 'config'
|
||||
|
||||
def build_url(self, domain):
|
||||
return '/domains/%s/config' % base.getid(domain)
|
||||
|
||||
def create(self, domain, config):
|
||||
"""Create a config for a domain.
|
||||
|
||||
:param domain: the domain where the config is going to be applied.
|
||||
:type domain: str or :py:class:`keystoneclient.v3.domains.Domain`
|
||||
|
||||
:param dict config: a dictionary of domain configurations.
|
||||
|
||||
Example of the ``config`` parameter::
|
||||
|
||||
{
|
||||
"identity": {
|
||||
"driver": "ldap"
|
||||
},
|
||||
"ldap": {
|
||||
"url": "ldap://myldap.com:389/",
|
||||
"user_tree_dn": "ou=Users,dc=my_new_root,dc=org"
|
||||
}
|
||||
}
|
||||
|
||||
:returns: the created domain config returned from server.
|
||||
:rtype: :class:`keystoneclient.v3.domain_configs.DomainConfig`
|
||||
|
||||
"""
|
||||
base_url = self.build_url(domain)
|
||||
body = {self.key: config}
|
||||
return super(DomainConfigManager, self)._put(
|
||||
base_url, body=body, response_key=self.key)
|
||||
|
||||
def get(self, domain):
|
||||
"""Get a config for a domain.
|
||||
|
||||
:param domain: the domain for which the config is defined.
|
||||
:type domain: str or :py:class:`keystoneclient.v3.domains.Domain`
|
||||
|
||||
:returns: the domain config returned from server.
|
||||
:rtype: :class:`keystoneclient.v3.domain_configs.DomainConfig`
|
||||
|
||||
"""
|
||||
base_url = self.build_url(domain)
|
||||
return super(DomainConfigManager, self)._get(base_url, self.key)
|
||||
|
||||
def update(self, domain, config):
|
||||
"""Update a config for a domain.
|
||||
|
||||
:param domain: the domain where the config is going to be updated.
|
||||
:type domain: str or :py:class:`keystoneclient.v3.domains.Domain`
|
||||
|
||||
:param dict config: a dictionary of domain configurations.
|
||||
|
||||
Example of the ``config`` parameter::
|
||||
|
||||
{
|
||||
"identity": {
|
||||
"driver": "ldap"
|
||||
},
|
||||
"ldap": {
|
||||
"url": "ldap://myldap.com:389/",
|
||||
"user_tree_dn": "ou=Users,dc=my_new_root,dc=org"
|
||||
}
|
||||
}
|
||||
|
||||
:returns: the updated domain config returned from server.
|
||||
:rtype: :class:`keystoneclient.v3.domain_configs.DomainConfig`
|
||||
|
||||
"""
|
||||
base_url = self.build_url(domain)
|
||||
body = {self.key: config}
|
||||
return super(DomainConfigManager, self)._patch(
|
||||
base_url, body=body, response_key=self.key)
|
||||
|
||||
def delete(self, domain):
|
||||
"""Delete a config for a domain.
|
||||
|
||||
:param domain: the domain which the config will be deleted on
|
||||
the server.
|
||||
:type domain: str or :class:`keystoneclient.v3.domains.Domain`
|
||||
|
||||
:returns: Response object with 204 status.
|
||||
:rtype: :class:`requests.models.Response`
|
||||
|
||||
"""
|
||||
base_url = self.build_url(domain)
|
||||
return super(DomainConfigManager, self)._delete(url=base_url)
|
||||
|
||||
def find(self, **kwargs):
|
||||
raise exceptions.MethodNotImplemented(
|
||||
_('Find not supported for domain configs'))
|
||||
|
||||
def list(self, **kwargs):
|
||||
raise exceptions.MethodNotImplemented(
|
||||
_('List not supported for domain configs'))
|
@@ -0,0 +1,7 @@
|
||||
---
|
||||
features:
|
||||
- Added support for ``domain configs``. A user can now
|
||||
upload domain specific configurations to keytone
|
||||
using the client. See ``client.domain_configs.create``,
|
||||
``client.domain_configs.delete``, ``client.domain_configs.get``
|
||||
and ``client.domain_configs.update``.
|
Reference in New Issue
Block a user