Merge "Support domain-specific configuration management"

This commit is contained in:
Jenkins
2016-11-02 20:59:13 +00:00
committed by Gerrit Code Review
7 changed files with 358 additions and 0 deletions

View File

@@ -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`

View File

@@ -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)

View 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)

View 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)

View File

@@ -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)

View 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'))

View File

@@ -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``.