Keystone resource client plugin and constrains

Adds Keystone client plugin and custom constrains for
role, project, group and domain

Change-Id: I10b4a18c4aa303c72a1e1316b28d738cc44b134c
Implements: blueprint keystone-resources
This commit is contained in:
Kanagaraj Manickam 2015-03-05 08:58:44 +05:30
parent 69a3599767
commit 693b95649d
9 changed files with 329 additions and 0 deletions

View File

@ -0,0 +1,28 @@
Keystone plugin for OpenStack Heat
==================================
This plugin enables Keystone resources in a Heat template for
following resources types:
- Keystone Role (OS::Keystone:Role)
- Keystone Project (OS::Keystone:Project)
- Keystone Group (OS::Keystone:Group)
- Keystone User (OS::Keystone:User)
And it provides Custom Constrains for following keystone entities
- Keystone role
- Keystone domain
- Keystone project
- Keystone group
### 1. Install the Keystone plugin in Heat
NOTE: These instructions assume the value of heat.conf plugin_dirs includes
the default directory /usr/lib/heat.
To install the plugin, from this directory run:
sudo python ./setup.py install
### 2. Restart heat
Only the process "heat-engine" needs to be restarted to load the newly
installed plugin.

View File

@ -0,0 +1,102 @@
#
# 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 exceptions
from keystoneclient import exceptions as keystone_exceptions
from heat.engine.clients.os import keystone
from heat.engine import constraints
class KeystoneClientPlugin(keystone.KeystoneClientPlugin):
def get_role_id(self, role):
try:
role_obj = self.client().client.roles.get(role)
return role_obj.id
except keystone_exceptions.NotFound:
role_list = self.client().client.roles.list(name=role)
for role_obj in role_list:
if role_obj.name == role:
return role_obj.id
raise exceptions.KeystoneRoleNotFound(role_id=role)
def get_project_id(self, project):
try:
project_obj = self.client().client.projects.get(project)
return project_obj.id
except keystone_exceptions.NotFound:
project_list = self.client().client.projects.list(name=project)
for project_obj in project_list:
if project_obj.name == project:
return project_obj.id
raise exceptions.KeystoneProjectNotFound(project_id=project)
def get_domain_id(self, domain):
try:
domain_obj = self.client().client.domains.get(domain)
return domain_obj.id
except keystone_exceptions.NotFound:
domain_list = self.client().client.domains.list(name=domain)
for domain_obj in domain_list:
if domain_obj.name == domain:
return domain_obj.id
raise exceptions.KeystoneDomainNotFound(domain_id=domain)
def get_group_id(self, group):
try:
group_obj = self.client().client.groups.get(group)
return group_obj.id
except keystone_exceptions.NotFound:
group_list = self.client().client.groups.list(name=group)
for group_obj in group_list:
if group_obj.name == group:
return group_obj.id
raise exceptions.KeystoneGroupNotFound(group_id=group)
class KeystoneRoleConstraint(constraints.BaseCustomConstraint):
expected_exceptions = (exceptions.KeystoneRoleNotFound,)
def validate_with_client(self, client, role):
client.client_plugin('keystone').get_role_id(role)
class KeystoneDomainConstraint(constraints.BaseCustomConstraint):
expected_exceptions = (exceptions.KeystoneDomainNotFound,)
def validate_with_client(self, client, domain):
client.client_plugin('keystone').get_domain_id(domain)
class KeystoneProjectConstraint(constraints.BaseCustomConstraint):
expected_exceptions = (exceptions.KeystoneProjectNotFound,)
def validate_with_client(self, client, project):
client.client_plugin('keystone').get_project_id(project)
class KeystoneGroupConstraint(constraints.BaseCustomConstraint):
expected_exceptions = (exceptions.KeystoneGroupNotFound,)
def validate_with_client(self, client, group):
client.client_plugin('keystone').get_group_id(group)

View File

@ -0,0 +1,31 @@
#
# 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 heat.common import exception
from heat.common.i18n import _
class KeystoneRoleNotFound(exception.HeatException):
msg_fmt = _("Keystone role %(role_id)s does not found")
class KeystoneProjectNotFound(exception.HeatException):
msg_fmt = _("Keystone project %(project_id)s does not found")
class KeystoneDomainNotFound(exception.HeatException):
msg_fmt = _("Keystone domain %(domain_id)s does not found")
class KeystoneGroupNotFound(exception.HeatException):
msg_fmt = _("Keystone group %(group_id)s does not found")

View File

@ -0,0 +1,98 @@
#
# 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 mock
import testtools
from .. import client # noqa
from .. import exceptions # noqa
class KeystoneRoleConstraintTest(testtools.TestCase):
def test_expected_exceptions(self):
self.assertEqual((exceptions.KeystoneRoleNotFound,),
client.KeystoneRoleConstraint.expected_exceptions,
"KeystoneRoleConstraint expected exceptions error")
def test_constrain(self):
constrain = client.KeystoneRoleConstraint()
client_mock = mock.MagicMock()
client_plugin_mock = mock.MagicMock()
client_plugin_mock.get_role_id.return_value = None
client_mock.client_plugin.return_value = client_plugin_mock
self.assertIsNone(constrain.validate_with_client(client_mock,
'role_1'))
client_plugin_mock.get_role_id.assert_called_once_with('role_1')
class KeystoneProjectConstraintTest(testtools.TestCase):
def test_expected_exceptions(self):
self.assertEqual((exceptions.KeystoneProjectNotFound,),
client.KeystoneProjectConstraint.expected_exceptions,
"KeystoneProjectConstraint expected exceptions error")
def test_constrain(self):
constrain = client.KeystoneProjectConstraint()
client_mock = mock.MagicMock()
client_plugin_mock = mock.MagicMock()
client_plugin_mock.get_project_id.return_value = None
client_mock.client_plugin.return_value = client_plugin_mock
self.assertIsNone(constrain.validate_with_client(client_mock,
'project_1'))
client_plugin_mock.get_project_id.assert_called_once_with('project_1')
class KeystoneGroupConstraintTest(testtools.TestCase):
def test_expected_exceptions(self):
self.assertEqual((exceptions.KeystoneGroupNotFound,),
client.KeystoneGroupConstraint.expected_exceptions,
"KeystoneGroupConstraint expected exceptions error")
def test_constrain(self):
constrain = client.KeystoneGroupConstraint()
client_mock = mock.MagicMock()
client_plugin_mock = mock.MagicMock()
client_plugin_mock.get_group_id.return_value = None
client_mock.client_plugin.return_value = client_plugin_mock
self.assertIsNone(constrain.validate_with_client(client_mock,
'group_1'))
client_plugin_mock.get_group_id.assert_called_once_with('group_1')
class KeystoneDomainConstraintTest(testtools.TestCase):
def test_expected_exceptions(self):
self.assertEqual((exceptions.KeystoneDomainNotFound,),
client.KeystoneDomainConstraint.expected_exceptions,
"KeystoneDomainConstraint expected exceptions error")
def test_constrain(self):
constrain = client.KeystoneDomainConstraint()
client_mock = mock.MagicMock()
client_plugin_mock = mock.MagicMock()
client_plugin_mock.get_domain_id.return_value = None
client_mock.client_plugin.return_value = client_plugin_mock
self.assertIsNone(constrain.validate_with_client(client_mock,
'domain_1'))
client_plugin_mock.get_domain_id.assert_called_once_with('domain_1')

View File

@ -0,0 +1,41 @@
[metadata]
name = heat-contrib-keystone
summary = Heat resources for Keystone
description-file =
README.md
author = OpenStack
author-email = openstack-dev@lists.openstack.org
home-page = http://www.openstack.org/
classifier =
Environment :: OpenStack
Intended Audience :: Information Technology
Intended Audience :: System Administrators
License :: OSI Approved :: Apache Software License
Operating System :: POSIX :: Linux
Programming Language :: Python
Programming Language :: Python :: 2
Programming Language :: Python :: 2.7
Programming Language :: Python :: 2.6
[files]
packages =
heat_keystone
# Copy to /usr/lib/heat for plugin loading
data_files =
lib/heat/keystone = heat_keystone/resources/*
[entry_points]
heat.clients =
keystone=heat_keystone.client:KeystoneClientPlugin
heat.constraints =
keystone.role=heat_keystone.client:KeystoneRoleConstraint
keystone.domain=heat_keystone.client:KeystoneDomainConstraint
keystone.project=heat_keystone.client:KeystoneProjectConstraint
keystone.group=heat_keystone.client:KeystoneGroupConstraint
[global]
setup-hooks =
pbr.hooks.setup_hook

View File

@ -0,0 +1,29 @@
#!/usr/bin/env python
#
# 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.
# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
import setuptools
# In python < 2.7.4, a lazy loading of package `pbr` will break
# setuptools if some other modules registered functions in `atexit`.
# solution from: http://bugs.python.org/issue15881#msg170215
try:
import multiprocessing # noqa
except ImportError:
pass
setuptools.setup(
setup_requires=['pbr'],
pbr=True)