Create V9 Role Driver

In preparation for new role driver methods for implied roles, this
patch creates a new version of the role driver.

Partially Implements@ blueprint implied-roles

Change-Id: I00cc57a77a23ed30d86fce8e309f6b27c4ae26a1
This commit is contained in:
Henry Nash 2015-11-19 21:13:43 +00:00 committed by Adam Young
parent 8913cd8299
commit 3f888260a7
9 changed files with 212 additions and 2 deletions

View File

@ -0,0 +1,80 @@
# 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 keystone import assignment
from keystone.common import sql
from keystone import exception
class Role(assignment.RoleDriverV8):
@sql.handle_conflicts(conflict_type='role')
def create_role(self, role_id, role):
with sql.transaction() as session:
ref = RoleTable.from_dict(role)
session.add(ref)
return ref.to_dict()
@sql.truncated
def list_roles(self, hints):
with sql.transaction() as session:
query = session.query(RoleTable)
refs = sql.filter_limit_query(RoleTable, query, hints)
return [ref.to_dict() for ref in refs]
def list_roles_from_ids(self, ids):
if not ids:
return []
else:
with sql.transaction() as session:
query = session.query(RoleTable)
query = query.filter(RoleTable.id.in_(ids))
role_refs = query.all()
return [role_ref.to_dict() for role_ref in role_refs]
def _get_role(self, session, role_id):
ref = session.query(RoleTable).get(role_id)
if ref is None:
raise exception.RoleNotFound(role_id=role_id)
return ref
def get_role(self, role_id):
with sql.transaction() as session:
return self._get_role(session, role_id).to_dict()
@sql.handle_conflicts(conflict_type='role')
def update_role(self, role_id, role):
with sql.transaction() as session:
ref = self._get_role(session, role_id)
old_dict = ref.to_dict()
for k in role:
old_dict[k] = role[k]
new_role = RoleTable.from_dict(old_dict)
for attr in RoleTable.attributes:
if attr != 'id':
setattr(ref, attr, getattr(new_role, attr))
ref.extra = new_role.extra
return ref.to_dict()
def delete_role(self, role_id):
with sql.transaction() as session:
ref = self._get_role(session, role_id)
session.delete(ref)
class RoleTable(sql.ModelBase, sql.DictBase):
__tablename__ = 'role'
attributes = ['id', 'name']
id = sql.Column(sql.String(64), primary_key=True)
name = sql.Column(sql.String(255), unique=True, nullable=False)
extra = sql.Column(sql.JsonBlob())
__table_args__ = (sql.UniqueConstraint('name'), {})

View File

@ -1329,6 +1329,13 @@ class RoleManager(manager.Manager):
super(RoleManager, self).__init__(role_driver)
# Make sure it is a driver version we support, and if it is a legacy
# driver, then wrap it.
if isinstance(self.driver, RoleDriverV8):
self.driver = V9RoleWrapperForV8Driver(self.driver)
elif not isinstance(self.driver, RoleDriverV9):
raise exception.UnsupportedDriverVersion(driver=role_driver)
@MEMOIZE
def get_role(self, role_id):
return self.driver.get_role(role_id)
@ -1358,8 +1365,15 @@ class RoleManager(manager.Manager):
self.get_role.invalidate(self, role_id)
# The RoleDriverBase class is the set of driver methods from earlier
# drivers that we still support, that have not been removed or modified. This
# class is then used to created the augmented V8 and V9 version abstract driver
# classes, without having to duplicate a lot of abstract method signatures.
# If you remove a method from V9, then move the abstact methods from this Base
# class to the V8 class. Do not modify any of the method signatures in the Base
# class - changes should only be made in the V8 and subsequent classes.
@six.add_metaclass(abc.ABCMeta)
class RoleDriverV8(object):
class RoleDriverBase(object):
def _get_list_limit(self):
return CONF.role.list_limit or CONF.list_limit
@ -1429,4 +1443,77 @@ class RoleDriverV8(object):
raise exception.NotImplemented() # pragma: no cover
class RoleDriverV8(RoleDriverBase):
"""Removed or redefined methods from V8.
Move the abstract methods of any methods removed or modified in later
versions of the driver from RoleDriverBase to here. We maintain this
so that legacy drivers, which will be a subclass of RoleDriverV8, can
still reference them.
"""
pass
class RoleDriverV9(RoleDriverBase):
"""New or redefined methods from V8.
Add any new V9 abstract methods (or those with modified signatures) to
this class.
"""
pass
class V9RoleWrapperForV8Driver(RoleDriverV9):
"""Wrapper class to supported a V8 legacy driver.
In order to support legacy drivers without having to make the manager code
driver-version aware, we wrap legacy drivers so that they look like the
latest version. For the various changes made in a new driver, here are the
actions needed in this wrapper:
Method removed from new driver - remove the call-through method from this
class, since the manager will no longer be
calling it.
Method signature (or meaning) changed - wrap the old method in a new
signature here, and munge the input
and output parameters accordingly.
New method added to new driver - add a method to implement the new
functionality here if possible. If that is
not possible, then return NotImplemented,
since we do not guarantee to support new
functionality with legacy drivers.
"""
@versionutils.deprecated(
as_of=versionutils.deprecated.MITAKA,
what='keystone.assignment.RoleDriverV8',
in_favor_of='keystone.assignment.RoleDriverV9',
remove_in=+2)
def __init__(self, wrapped_driver):
self.driver = wrapped_driver
def create_role(self, role_id, role):
return self.driver.create_role(role_id, role)
def list_roles(self, hints):
return self.driver.list_roles(hints)
def list_roles_from_ids(self, role_ids):
return self.driver.list_roles_from_ids(role_ids)
def get_role(self, role_id):
return self.driver.get_role(role_id)
def update_role(self, role_id, role):
return self.driver.update_role(role_id, role)
def delete_role(self, role_id):
self.driver.delete_role(role_id)
RoleDriver = manager.create_legacy_driver(RoleDriverV8)

View File

@ -15,7 +15,7 @@ from keystone.common import sql
from keystone import exception
class Role(assignment.RoleDriverV8):
class Role(assignment.RoleDriverV9):
@sql.handle_conflicts(conflict_type='role')
def create_role(self, role_id, role):

View File

@ -0,0 +1,30 @@
# 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 keystone.tests.unit import test_backend_sql
class SqlIdentityV8(test_backend_sql.SqlIdentity):
"""Test that a V8 driver still passes the same tests.
We use the SQL driver as an example of a V8 legacy driver.
"""
def config_overrides(self):
super(SqlIdentityV8, self).config_overrides()
# V8 SQL specific driver overrides
self.config_fixture.config(
group='role',
driver='keystone.assignment.V8_role_backends.sql.Role')
self.use_specific_sql_driver_version(
'keystone.assignment', 'role_backends', 'V8_')

View File

@ -0,0 +1,10 @@
---
prelude: >
New Role driver (V9)
upgrade:
- The V8 Role driver interface is deprecated, but still supported in
this release, so any custom drivers based on the V8 interface should still
work.
other:
- Support for the V8 Role driver interface is planned to be removed in
the 'O' release of OpenStack.

View File

@ -75,8 +75,11 @@ deps = -r{toxinidir}/test-requirements.txt
nose
.[ldap,memcache,mongodb]
commands =
# Run each legacy test separately, to avoid SQL model redefinitions
nosetests -v \
keystone/tests/unit/backend/legacy_drivers/assignment/V8/sql.py
nosetests -v \
keystone/tests/unit/backend/legacy_drivers/role/V8/sql.py
[testenv:pep8]
commands =