commit e0df0b3bbfc56deb20b3f70c4ce18031beece21f Author: David Ames Date: Mon May 2 15:56:20 2016 -0700 Initial keystone-credentials interface Create the interface for keystone-credentials (identity-credentials) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..172bf57 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.tox diff --git a/copyright b/copyright new file mode 100644 index 0000000..5a49dcb --- /dev/null +++ b/copyright @@ -0,0 +1,21 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0 + +Files: * +Copyright: 2015, Canonical Ltd. +License: Apache-2.0 + +License: Apache-2.0 + 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. + . + On Debian-based systems the full text of the Apache version 2.0 license + can be found in `/usr/share/common-licenses/Apache-2.0'. diff --git a/interface.yaml b/interface.yaml new file mode 100644 index 0000000..b09f69b --- /dev/null +++ b/interface.yaml @@ -0,0 +1,16 @@ +name: keystone-credentials +summary: > + Interface for integrating with Keystone identity credentials + Charms use this relation to obtain keystone credentials + without creating a service catalog entry. Set 'username' + only on the relation and keystone will set defaults and + return authentication details. Possible relation settings: + username: Username to be created. + project: Project (tenant) name to be created. Defaults to services + project. + requested_roles: Comma delimited list of roles to be created + requested_grants: Comma delimited list of roles to be granted. + Defaults to Admin role. + domain: Keystone v3 domain the user will be created in. Defaults + to the Default domain. +maintainer: OpenStack Charmers diff --git a/requires.py b/requires.py new file mode 100644 index 0000000..a412d3c --- /dev/null +++ b/requires.py @@ -0,0 +1,131 @@ +#!/usr/bin/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. + +from charms.reactive import RelationBase +from charms.reactive import hook +from charms.reactive import scopes + + +class KeystoneRequires(RelationBase): + scope = scopes.GLOBAL + + # These remote data fields will be automatically mapped to accessors + # with a basic documentation string provided. + + auto_accessors = ['private-address', 'credentials_host', + 'credentials_protocol', 'credentials_port', + 'credentials_project', 'credentials_username', + 'credentials_password', 'credentials_project_id', + 'api_version', 'auth_host', 'auth_protocol', 'auth_port', + 'region', 'ca_cert', 'https_keystone'] + + @hook('{requires:keystone-credentials}-relation-joined') + def joined(self): + self.set_state('{relation_name}.connected') + self.update_state() + + def update_state(self): + """Update the states of the relations based on the data that the + relation has. + + If the :meth:`base_data_complete` is False then all of the states + are removed. Otherwise, the individual states are set according to + their own data methods. + """ + base_complete = self.base_data_complete() + states = { + '{relation_name}.available': True, + '{relation_name}.available.ssl': self.ssl_data_complete(), + '{relation_name}.available.auth': self.auth_data_complete() + } + for k, v in states.items(): + if base_complete and v: + self.set_state(k) + else: + self.remove_state(k) + + @hook('{requires:keystone-credentials}-relation-changed') + def changed(self): + self.update_state() + + @hook('{requires:keystone-credentials}-relation-{broken,departed}') + def departed(self): + self.update_state() + + def base_data_complete(self): + data = { + 'private-address': self.private_address(), + 'credentials_host': self.credentials_host(), + 'credentials_protocol': self.credentials_protocol(), + 'credentials_port': self.credentials_port(), + 'api_version': self.api_version(), + 'auth_host': self.auth_host(), + 'auth_protocol': self.auth_protocol(), + 'auth_port': self.auth_port(), + } + if all(data.values()): + return True + return False + + def auth_data_complete(self): + data = { + 'credentials_project': self.credentials_project(), + 'credentials_username': self.credentials_username(), + 'credentials_password': self.credentials_password(), + 'credentials_project_id': self.credentials_project_id(), + } + if all(data.values()): + return True + return False + + def ssl_data_complete(self): + data = { + 'https_keystone': self.https_keystone(), + 'ca_cert': self.ca_cert(), + } + for value in data.values(): + if not value or value == '__null__': + return False + return True + + def request_credentials(self, username, project=None, region=None, + requested_roles=None, requested_grants=None, + domain=None): + """ + Request credentials from Keystone + + :side effect: set requested paramaters on the identity-credentials + relation + + Required parameter + :param username: Username to be created. + + Optional parametrs + :param project: Project (tenant) name to be created. Defaults to + services project. + :param requested_roles: Comma delimited list of roles to be created + :param requested_grants: Comma delimited list of roles to be granted. + Defaults to Admin role. + :param domain: Keystone v3 domain the user will be created in. Defaults + to the Default domain. + """ + relation_info = { + 'username': username, + 'project': project, + 'requested_roles': requested_roles, + 'requested_grants': requested_grants, + 'domain': domain, + } + + self.set_local(**relation_info) + self.set_remote(**relation_info) diff --git a/test-requirements.txt b/test-requirements.txt new file mode 100644 index 0000000..10dbed3 --- /dev/null +++ b/test-requirements.txt @@ -0,0 +1,3 @@ +flake8>=2.2.4,<=2.4.1 +os-testr>=0.4.1 +charm-tools diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..c035f1a --- /dev/null +++ b/tox.ini @@ -0,0 +1,25 @@ +[tox] +envlist = lint,py27 +skipsdist = True + +[testenv] +setenv = VIRTUAL_ENV={envdir} + PYTHONHASHSEED=0 +install_command = + pip install --allow-unverified python-apt {opts} {packages} +commands = ostestr {posargs} + +[testenv:py27] +basepython = python2.7 +deps = -r{toxinidir}/test-requirements.txt + +[testenv:lint] +basepython = python2.7 +deps = -r{toxinidir}/test-requirements.txt +commands = flake8 {posargs} + +[testenv:venv] +commands = {posargs} + +[flake8] +ignore = E402,E226