设为首页 收藏本站
查看: 651|回复: 0

[经验分享] OpenStack版本比较之Keystone

[复制链接]

尚未签到

发表于 2015-4-12 11:25:37 | 显示全部楼层 |阅读模式
目的
  本文主要比较OpenStack中Essex与Folsom版本的Keystone在依赖包、数据库结构、配置方面的差异,为Keystone从Essex向Folsom升级做些前期准备工作。这些比较大部分是在源代码库上通过git命令进行的,首先要clone一个keystone的本地库,命令如下:






[plain] view plaincopyprint?

  • git clone git://github.com/openstack/keystone.git
  • cd keystone

git clone git://github.com/openstack/keystone.git
cd keystone
依赖包的差异
  tools/pip-requires文件包含keystone的依赖库文件信息,通过以下命令比较发现Folsom版只是添加了一个依赖包iso8601版本须>=0.1.4,该包同时也是nova E版和F版的依赖包,因此依赖包的差异可以忽略,升级Keystone可以不安装其它依赖包。






[plain] view plaincopyprint?

  • [ugyn@localhost keystone]$ git diff 2012.1:tools/pip-requires 2012.2:tools/pip-requires
  • diff --git a/2012.1:tools/pip-requires b/2012.2:tools/pip-requires
  • index 0e13534..ec5562d 100644
  • --- a/2012.1:tools/pip-requires
  • +++ b/2012.2:tools/pip-requires
  • @@ -1,4 +1,4 @@
  • -# keystonelight dependencies
  • +# keystone dependencies
  • pam==0.1.4
  • WebOb==1.0.8
  • eventlet
  • @@ -10,3 +10,4 @@ sqlalchemy
  • sqlalchemy-migrate
  • passlib
  • lxml
  • +iso8601>=0.1.4

[ugyn@localhost keystone]$ git diff 2012.1:tools/pip-requires 2012.2:tools/pip-requires
diff --git a/2012.1:tools/pip-requires b/2012.2:tools/pip-requires
index 0e13534..ec5562d 100644
--- a/2012.1:tools/pip-requires
+++ b/2012.2:tools/pip-requires
@@ -1,4 +1,4 @@
-# keystonelight dependencies
+# keystone dependencies
pam==0.1.4
WebOb==1.0.8
eventlet
@@ -10,3 +10,4 @@ sqlalchemy
sqlalchemy-migrate
passlib
lxml
+iso8601>=0.1.4
数据库结构的差异

与数据库相关的源文件
  要比较数据库结构的差异首先要找到keystone中与数据库相关的源文件,可通过以下命令实现:






[plain] view plaincopyprint?

  • [ugyn@localhost keystone]$ git grep "sql.ModelBase"
  • keystone/catalog/backends/sql.py:class Service(sql.ModelBase, sql.DictBase):
  • keystone/catalog/backends/sql.py:class Endpoint(sql.ModelBase, sql.DictBase):
  • keystone/common/sql/migrate_repo/versions/001_add_initial_tables.py: sql.ModelBase.metadata.create_all(migrate_engine)
  • keystone/contrib/ec2/backends/sql.py:class Ec2Credential(sql.ModelBase, sql.DictBase):
  • keystone/identity/backends/sql.py:class User(sql.ModelBase, sql.DictBase):
  • keystone/identity/backends/sql.py:class Tenant(sql.ModelBase, sql.DictBase):
  • keystone/identity/backends/sql.py:class Role(sql.ModelBase, sql.DictBase):
  • keystone/identity/backends/sql.py:class Metadata(sql.ModelBase, sql.DictBase):
  • keystone/identity/backends/sql.py:class UserTenantMembership(sql.ModelBase, sql.DictBase):
  • keystone/token/backends/sql.py:class TokenModel(sql.ModelBase, sql.DictBase):
  • tests/test_backend_sql.py: sql.ModelBase.metadata.bind = engine
  • tests/test_backend_sql.py: sql.ModelBase.metadata.create_all(engine)


[ugyn@localhost keystone]$ git grep "sql.ModelBase"
keystone/catalog/backends/sql.py:class Service(sql.ModelBase, sql.DictBase):
keystone/catalog/backends/sql.py:class Endpoint(sql.ModelBase, sql.DictBase):
keystone/common/sql/migrate_repo/versions/001_add_initial_tables.py:    sql.ModelBase.metadata.create_all(migrate_engine)
keystone/contrib/ec2/backends/sql.py:class Ec2Credential(sql.ModelBase, sql.DictBase):
keystone/identity/backends/sql.py:class User(sql.ModelBase, sql.DictBase):
keystone/identity/backends/sql.py:class Tenant(sql.ModelBase, sql.DictBase):
keystone/identity/backends/sql.py:class Role(sql.ModelBase, sql.DictBase):
keystone/identity/backends/sql.py:class Metadata(sql.ModelBase, sql.DictBase):
keystone/identity/backends/sql.py:class UserTenantMembership(sql.ModelBase, sql.DictBase):
keystone/token/backends/sql.py:class TokenModel(sql.ModelBase, sql.DictBase):
tests/test_backend_sql.py:        sql.ModelBase.metadata.bind = engine
tests/test_backend_sql.py:        sql.ModelBase.metadata.create_all(engine)

service及endpoint表比较
  这两个表的定义在源文件keystone/catalog/backends/sql.py中,分别为Service及Endpoint类,从下面的结果可以看出这两个类完全没变,只是在类Catalog中对它们的操作做了些修改,因此在数据库方面这两个表应该是没在差异的。






[plain] view plaincopyprint?

  • [ugyn@localhost keystone]$ git diff 2012.1:keystone/catalog/backends/sql.py 2012.2:keystone/catalog/backends/sql.py > tables_diff
  • [ugyn@localhost keystone]$ cat tables_diff
  • diff --git a/2012.1:keystone/catalog/backends/sql.py b/2012.2:keystone/catalog/backends/sql.py
  • index 3c553e0..bd0f687 100644
  • --- a/2012.1:keystone/catalog/backends/sql.py
  • +++ b/2012.2:keystone/catalog/backends/sql.py
  • @@ -15,14 +15,11 @@
  • # License for the specific language governing permissions and limitations
  • # under the License.


  • -import sqlalchemy.exc
  • -import webob.exc
  • -
  • from keystone import catalog
  • -from keystone import config
  • -from keystone import exception
  • from keystone.common import sql
  • from keystone.common.sql import migration
  • +from keystone import config
  • +from keystone import exception



  • CONF = config.CONF
  • @@ -96,11 +93,9 @@ class Catalog(sql.Base, catalog.Driver):

  • def delete_service(self, service_id):
  • session = self.get_session()
  • - service_ref = session.query(Service).filter_by(id=service_id).first()
  • - if not service_ref:
  • - raise exception.ServiceNotFound(service_id=service_id)
  • with session.begin():
  • - session.delete(service_ref)
  • + if not session.query(Service).filter_by(id=service_id).delete():
  • + raise exception.ServiceNotFound(service_id=service_id)
  • session.flush()


  • def create_service(self, service_id, service_ref):
  • @@ -114,6 +109,7 @@ class Catalog(sql.Base, catalog.Driver):
  • # Endpoints
  • def create_endpoint(self, endpoint_id, endpoint_ref):
  • session = self.get_session()
  • + self.get_service(endpoint_ref['service_id'])
  • new_endpoint = Endpoint.from_dict(endpoint_ref)
  • with session.begin():
  • session.add(new_endpoint)
  • @@ -122,18 +118,17 @@ class Catalog(sql.Base, catalog.Driver):

  • def delete_endpoint(self, endpoint_id):
  • session = self.get_session()
  • - endpoint_ref = session.query(Endpoint)
  • - endpoint_ref = endpoint_ref.filter_by(id=endpoint_id).first()
  • - if not endpoint_ref:
  • - raise exception.EndpointNotFound(endpoint_id=endpoint_id)
  • with session.begin():
  • - session.delete(endpoint_ref)
  • + if not session.query(Endpoint).filter_by(id=endpoint_id).delete():
  • + raise exception.EndpointNotFound(endpoint_id=endpoint_id)
  • session.flush()

  • def get_endpoint(self, endpoint_id):
  • session = self.get_session()
  • endpoint_ref = session.query(Endpoint)
  • endpoint_ref = endpoint_ref.filter_by(id=endpoint_id).first()
  • + if not endpoint_ref:
  • + raise exception.EndpointNotFound(endpoint_id=endpoint_id)
  • return endpoint_ref.to_dict()

  • def list_endpoints(self):
  • @@ -163,6 +158,7 @@ class Catalog(sql.Base, catalog.Driver):
  • internal_url = ep['internalurl'].replace('$(', '%(')
  • public_url = ep['publicurl'].replace('$(', '%(')
  • admin_url = ep['adminurl'].replace('$(', '%(')
  • + catalog[region][srv_type]['id'] = ep['id']
  • catalog[region][srv_type]['name'] = srv_name
  • catalog[region][srv_type]['publicURL'] = public_url % d
  • catalog[region][srv_type]['adminURL'] = admin_url % d


[ugyn@localhost keystone]$ git diff 2012.1:keystone/catalog/backends/sql.py 2012.2:keystone/catalog/backends/sql.py > tables_diff
[ugyn@localhost keystone]$ cat tables_diff
diff --git a/2012.1:keystone/catalog/backends/sql.py b/2012.2:keystone/catalog/backends/sql.py
index 3c553e0..bd0f687 100644
--- a/2012.1:keystone/catalog/backends/sql.py
+++ b/2012.2:keystone/catalog/backends/sql.py
@@ -15,14 +15,11 @@
# License for the specific language governing permissions and limitations
# under the License.
-import sqlalchemy.exc
-import webob.exc
-
from keystone import catalog
-from keystone import config
-from keystone import exception
from keystone.common import sql
from keystone.common.sql import migration
+from keystone import config
+from keystone import exception

CONF = config.CONF
@@ -96,11 +93,9 @@ class Catalog(sql.Base, catalog.Driver):
def delete_service(self, service_id):
session = self.get_session()
-        service_ref = session.query(Service).filter_by(id=service_id).first()
-        if not service_ref:
-            raise exception.ServiceNotFound(service_id=service_id)
with session.begin():
-            session.delete(service_ref)
+            if not session.query(Service).filter_by(id=service_id).delete():
+                raise exception.ServiceNotFound(service_id=service_id)
session.flush()
def create_service(self, service_id, service_ref):
@@ -114,6 +109,7 @@ class Catalog(sql.Base, catalog.Driver):
# Endpoints
def create_endpoint(self, endpoint_id, endpoint_ref):
session = self.get_session()
+        self.get_service(endpoint_ref['service_id'])
new_endpoint = Endpoint.from_dict(endpoint_ref)
with session.begin():
session.add(new_endpoint)
@@ -122,18 +118,17 @@ class Catalog(sql.Base, catalog.Driver):
def delete_endpoint(self, endpoint_id):
session = self.get_session()
-        endpoint_ref = session.query(Endpoint)
-        endpoint_ref = endpoint_ref.filter_by(id=endpoint_id).first()
-        if not endpoint_ref:
-            raise exception.EndpointNotFound(endpoint_id=endpoint_id)
with session.begin():
-            session.delete(endpoint_ref)
+            if not session.query(Endpoint).filter_by(id=endpoint_id).delete():
+                raise exception.EndpointNotFound(endpoint_id=endpoint_id)
session.flush()
def get_endpoint(self, endpoint_id):
session = self.get_session()
endpoint_ref = session.query(Endpoint)
endpoint_ref = endpoint_ref.filter_by(id=endpoint_id).first()
+        if not endpoint_ref:
+            raise exception.EndpointNotFound(endpoint_id=endpoint_id)
return endpoint_ref.to_dict()
def list_endpoints(self):
@@ -163,6 +158,7 @@ class Catalog(sql.Base, catalog.Driver):
internal_url = ep['internalurl'].replace('$(', '%(')
public_url = ep['publicurl'].replace('$(', '%(')
admin_url = ep['adminurl'].replace('$(', '%(')
+            catalog[region][srv_type]['id'] = ep['id']
catalog[region][srv_type]['name'] = srv_name
catalog[region][srv_type]['publicURL'] = public_url % d
catalog[region][srv_type]['adminURL'] = admin_url % d

user、tenant、role、metadata、user_tenant_membership表的比较
  
  这几个表的定义均在源文件keystone/identity/backends/sql.py的相关类中。从下面的输出来看,在F版中的主要变化是给表user、tenant、role的name列均添加了not null属性,另外在Identity类中对这些表的操作做了不少修改,但应该不会导致相关表的结构变化。






[plain] view plaincopyprint?

  • [ugyn@localhost keystone]$ git diff 2012.1:keystone/identity/backends/sql.py 2012.2:keystone/identity/backends/sql.py > tables_diff
  • [ugyn@localhost keystone]$ cat tables_diff
  • diff --git a/2012.1:keystone/identity/backends/sql.py b/2012.2:keystone/identity/backends/sql.py
  • index e4281a8..a3c8d1f 100644
  • --- a/2012.1:keystone/identity/backends/sql.py
  • +++ b/2012.2:keystone/identity/backends/sql.py
  • @@ -17,11 +17,12 @@
  • import copy
  • import functools


  • -from keystone import identity
  • -from keystone import exception
  • +from keystone import clean
  • from keystone.common import sql
  • -from keystone.common import utils
  • from keystone.common.sql import migration
  • +from keystone.common import utils
  • +from keystone import exception
  • +from keystone import identity



  • def _filter_user(user_ref):
  • @@ -45,7 +46,7 @@ def handle_conflicts(type='object'):
  • try:
  • return method(*args, **kwargs)
  • except sql.IntegrityError as e:
  • - raise exception.Conflict(type=type, details=str(e))
  • + raise exception.Conflict(type=type, details=e.message)
  • return wrapper
  • return decorator

  • @@ -53,7 +54,7 @@ def handle_conflicts(type='object'):
  • class User(sql.ModelBase, sql.DictBase):
  • __tablename__ = 'user'
  • id = sql.Column(sql.String(64), primary_key=True)
  • - name = sql.Column(sql.String(64), unique=True)
  • + name = sql.Column(sql.String(64), unique=True, nullable=False)
  • #password = sql.Column(sql.String(64))
  • extra = sql.Column(sql.JsonBlob())


  • @@ -79,7 +80,7 @@ class User(sql.ModelBase, sql.DictBase):
  • class Tenant(sql.ModelBase, sql.DictBase):
  • __tablename__ = 'tenant'
  • id = sql.Column(sql.String(64), primary_key=True)
  • - name = sql.Column(sql.String(64), unique=True)
  • + name = sql.Column(sql.String(64), unique=True, nullable=False)
  • extra = sql.Column(sql.JsonBlob())


  • @classmethod
  • @@ -104,7 +105,7 @@ class Tenant(sql.ModelBase, sql.DictBase):
  • class Role(sql.ModelBase, sql.DictBase):
  • __tablename__ = 'role'
  • id = sql.Column(sql.String(64), primary_key=True)
  • - name = sql.Column(sql.String(64), unique=True)
  • + name = sql.Column(sql.String(64), unique=True, nullable=False)



  • class Metadata(sql.ModelBase, sql.DictBase):
  • @@ -134,6 +135,20 @@ class Identity(sql.Base, identity.Driver):
  • def db_sync(self):
  • migration.db_sync()


  • + def _check_password(self, password, user_ref):
  • + """Check the specified password against the data store.
  • +
  • + This is modeled on ldap/core.py. The idea is to make it easier to
  • + subclass Identity so that you can still use it to store all the data,
  • + but use some other means to check the password.
  • + Note that we'll pass in the entire user_ref in case the subclass
  • + needs things like user_ref.get('name')
  • + For further justification, please see the follow up suggestion at
  • + https://blueprints.launchpad.net/keystone/+spec/sql-identiy-pam
  • +
  • + """
  • + return utils.check_password(password, user_ref.get('password'))
  • +
  • # Identity interface
  • def authenticate(self, user_id=None, tenant_id=None, password=None):
  • """Authenticate based on a user, tenant and password.
  • @@ -142,56 +157,69 @@ class Identity(sql.Base, identity.Driver):
  • in the list of tenants on the user.


  • """
  • - user_ref = self._get_user(user_id)
  • - if (not user_ref
  • - or not utils.check_password(password, user_ref.get('password'))):
  • + user_ref = None
  • + tenant_ref = None
  • + metadata_ref = {}
  • +
  • + try:
  • + user_ref = self._get_user(user_id)
  • + except exception.UserNotFound:
  • raise AssertionError('Invalid user / password')

  • - tenants = self.get_tenants_for_user(user_id)
  • - if tenant_id and tenant_id not in tenants:
  • - raise AssertionError('Invalid tenant')
  • + if not utils.check_password(password, user_ref.get('password')):
  • + raise AssertionError('Invalid user / password')
  • +
  • + if tenant_id is not None:
  • + if tenant_id not in self.get_tenants_for_user(user_id):
  • + raise AssertionError('Invalid tenant')
  • +
  • + try:
  • + tenant_ref = self.get_tenant(tenant_id)
  • + metadata_ref = self.get_metadata(user_id, tenant_id)
  • + except exception.TenantNotFound:
  • + tenant_ref = None
  • + metadata_ref = {}
  • + except exception.MetadataNotFound:
  • + metadata_ref = {}


  • - tenant_ref = self.get_tenant(tenant_id)
  • - if tenant_ref:
  • - metadata_ref = self.get_metadata(user_id, tenant_id)
  • - else:
  • - metadata_ref = {}
  • return (_filter_user(user_ref), tenant_ref, metadata_ref)

  • def get_tenant(self, tenant_id):
  • session = self.get_session()
  • tenant_ref = session.query(Tenant).filter_by(id=tenant_id).first()
  • - if not tenant_ref:
  • - return
  • + if tenant_ref is None:
  • + raise exception.TenantNotFound(tenant_id=tenant_id)
  • return tenant_ref.to_dict()


  • def get_tenant_by_name(self, tenant_name):
  • session = self.get_session()
  • tenant_ref = session.query(Tenant).filter_by(name=tenant_name).first()
  • if not tenant_ref:
  • - return
  • + raise exception.TenantNotFound(tenant_id=tenant_name)
  • return tenant_ref.to_dict()


  • def get_tenant_users(self, tenant_id):
  • session = self.get_session()
  • + self.get_tenant(tenant_id)
  • user_refs = session.query(User)\
  • - .join(UserTenantMembership)\
  • - .filter(UserTenantMembership.tenant_id == tenant_id)\
  • - .all()
  • + .join(UserTenantMembership)\
  • + .filter(UserTenantMembership.tenant_id ==
  • + tenant_id)\
  • + .all()
  • return [_filter_user(user_ref.to_dict()) for user_ref in user_refs]

  • def _get_user(self, user_id):
  • session = self.get_session()
  • user_ref = session.query(User).filter_by(id=user_id).first()
  • if not user_ref:
  • - return
  • + raise exception.UserNotFound(user_id=user_id)
  • return user_ref.to_dict()

  • def _get_user_by_name(self, user_name):
  • session = self.get_session()
  • user_ref = session.query(User).filter_by(name=user_name).first()
  • if not user_ref:
  • - return
  • + raise exception.UserNotFound(user_id=user_name)
  • return user_ref.to_dict()

  • def get_user(self, user_id):
  • @@ -206,11 +234,16 @@ class Identity(sql.Base, identity.Driver):
  • .filter_by(user_id=user_id)\
  • .filter_by(tenant_id=tenant_id)\
  • .first()
  • - return getattr(metadata_ref, 'data', {})
  • + if metadata_ref is None:
  • + raise exception.MetadataNotFound()
  • + return metadata_ref.data

  • def get_role(self, role_id):
  • session = self.get_session()
  • - return session.query(Role).filter_by(id=role_id).first()
  • + role_ref = session.query(Role).filter_by(id=role_id).first()
  • + if role_ref is None:
  • + raise exception.RoleNotFound(role_id=role_id)
  • + return role_ref

  • def list_users(self):
  • session = self.get_session()
  • @@ -225,6 +258,8 @@ class Identity(sql.Base, identity.Driver):
  • # These should probably be part of the high-level API
  • def add_user_to_tenant(self, tenant_id, user_id):
  • session = self.get_session()
  • + self.get_tenant(tenant_id)
  • + self.get_user(user_id)
  • q = session.query(UserTenantMembership)\
  • .filter_by(user_id=user_id)\
  • .filter_by(tenant_id=tenant_id)
  • @@ -239,10 +274,14 @@ class Identity(sql.Base, identity.Driver):


  • def remove_user_from_tenant(self, tenant_id, user_id):
  • session = self.get_session()

  • + self.get_tenant(tenant_id)
  • + self.get_user(user_id)
  • membership_ref = session.query(UserTenantMembership)\
  • .filter_by(user_id=user_id)\
  • .filter_by(tenant_id=tenant_id)\
  • .first()
  • + if membership_ref is None:
  • + raise exception.NotFound('User not found in tenant')
  • with session.begin():
  • session.delete(membership_ref)
  • session.flush()
  • @@ -254,37 +293,50 @@ class Identity(sql.Base, identity.Driver):


  • def get_tenants_for_user(self, user_id):
  • session = self.get_session()
  • + self.get_user(user_id)
  • membership_refs = session.query(UserTenantMembership)\
  • - .filter_by(user_id=user_id)\
  • - .all()
  • + .filter_by(user_id=user_id)\
  • + .all()
  • return [x.tenant_id for x in membership_refs]


  • def get_roles_for_user_and_tenant(self, user_id, tenant_id):
  • - metadata_ref = self.get_metadata(user_id, tenant_id)
  • - if not metadata_ref:
  • + self.get_user(user_id)
  • + self.get_tenant(tenant_id)
  • + try:
  • + metadata_ref = self.get_metadata(user_id, tenant_id)
  • + except exception.MetadataNotFound:
  • metadata_ref = {}
  • return metadata_ref.get('roles', [])

  • def add_role_to_user_and_tenant(self, user_id, tenant_id, role_id):
  • - metadata_ref = self.get_metadata(user_id, tenant_id)
  • - is_new = False
  • - if not metadata_ref:
  • - is_new = True
  • + self.get_user(user_id)
  • + self.get_tenant(tenant_id)
  • + self.get_role(role_id)
  • + try:
  • + metadata_ref = self.get_metadata(user_id, tenant_id)
  • + is_new = False
  • + except exception.MetadataNotFound:
  • metadata_ref = {}
  • + is_new = True
  • roles = set(metadata_ref.get('roles', []))
  • + if role_id in roles:
  • + msg = ('User %s already has role %s in tenant %s'
  • + % (user_id, role_id, tenant_id))
  • + raise exception.Conflict(type='role grant', details=msg)
  • roles.add(role_id)
  • metadata_ref['roles'] = list(roles)
  • - if not is_new:
  • - self.update_metadata(user_id, tenant_id, metadata_ref)
  • - else:
  • + if is_new:
  • self.create_metadata(user_id, tenant_id, metadata_ref)
  • + else:
  • + self.update_metadata(user_id, tenant_id, metadata_ref)


  • def remove_role_from_user_and_tenant(self, user_id, tenant_id, role_id):
  • - metadata_ref = self.get_metadata(user_id, tenant_id)
  • - is_new = False
  • - if not metadata_ref:
  • - is_new = True
  • + try:
  • + metadata_ref = self.get_metadata(user_id, tenant_id)
  • + is_new = False
  • + except exception.MetadataNotFound:
  • metadata_ref = {}
  • + is_new = True
  • roles = set(metadata_ref.get('roles', []))
  • if role_id not in roles:
  • msg = 'Cannot remove role that has not been granted, %s' % role_id
  • @@ -292,14 +344,15 @@ class Identity(sql.Base, identity.Driver):


  • roles.remove(role_id)
  • metadata_ref['roles'] = list(roles)
  • - if not is_new:
  • - self.update_metadata(user_id, tenant_id, metadata_ref)
  • - else:
  • + if is_new:
  • self.create_metadata(user_id, tenant_id, metadata_ref)
  • + else:
  • + self.update_metadata(user_id, tenant_id, metadata_ref)


  • # CRUD
  • @handle_conflicts(type='user')
  • def create_user(self, user_id, user):
  • + user['name'] = clean.user_name(user['name'])
  • user = _ensure_hashed_password(user)
  • session = self.get_session()
  • with session.begin():
  • @@ -310,9 +363,15 @@ class Identity(sql.Base, identity.Driver):

  • @handle_conflicts(type='user')
  • def update_user(self, user_id, user):
  • + if 'name' in user:
  • + user['name'] = clean.user_name(user['name'])
  • session = self.get_session()
  • + if 'id' in user and user_id != user['id']:
  • + raise exception.ValidationError('Cannot change user ID')
  • with session.begin():
  • user_ref = session.query(User).filter_by(id=user_id).first()
  • + if user_ref is None:
  • + raise exception.UserNotFound(user_id=user_id)
  • old_user_dict = user_ref.to_dict()
  • user = _ensure_hashed_password(user)
  • for k in user:
  • @@ -326,21 +385,17 @@ class Identity(sql.Base, identity.Driver):

  • def delete_user(self, user_id):
  • session = self.get_session()
  • - user_ref = session.query(User).filter_by(id=user_id).first()
  • - membership_refs = session.query(UserTenantMembership)\
  • - .filter_by(user_id=user_id)\
  • - .all()
  • -
  • with session.begin():
  • - if membership_refs:
  • - for membership_ref in membership_refs:
  • - session.delete(membership_ref)
  • -
  • - session.delete(user_ref)
  • - session.flush()
  • + session.query(UserTenantMembership)\
  • + .filter_by(user_id=user_id).delete(False)
  • + session.query(Metadata)\
  • + .filter_by(user_id=user_id).delete(False)
  • + if not session.query(User).filter_by(id=user_id).delete(False):
  • + raise exception.UserNotFound(user_id=user_id)


  • @handle_conflicts(type='tenant')
  • def create_tenant(self, tenant_id, tenant):
  • + tenant['name'] = clean.tenant_name(tenant['name'])
  • session = self.get_session()
  • with session.begin():
  • tenant_ref = Tenant.from_dict(tenant)
  • @@ -350,9 +405,13 @@ class Identity(sql.Base, identity.Driver):


  • @handle_conflicts(type='tenant')
  • def update_tenant(self, tenant_id, tenant):
  • + if 'name' in tenant:
  • + tenant['name'] = clean.tenant_name(tenant['name'])
  • session = self.get_session()
  • with session.begin():
  • tenant_ref = session.query(Tenant).filter_by(id=tenant_id).first()
  • + if tenant_ref is None:
  • + raise exception.TenantNotFound(tenant_id=tenant_id)
  • old_tenant_dict = tenant_ref.to_dict()
  • for k in tenant:
  • old_tenant_dict[k] = tenant[k]
  • @@ -365,10 +424,13 @@ class Identity(sql.Base, identity.Driver):


  • def delete_tenant(self, tenant_id):
  • session = self.get_session()
  • - tenant_ref = session.query(Tenant).filter_by(id=tenant_id).first()
  • with session.begin():
  • - session.delete(tenant_ref)
  • - session.flush()
  • + session.query(UserTenantMembership)\
  • + .filter_by(tenant_id=tenant_id).delete(False)
  • + session.query(Metadata)\
  • + .filter_by(tenant_id=tenant_id).delete(False)
  • + if not session.query(Tenant).filter_by(id=tenant_id).delete(False):
  • + raise exception.TenantNotFound(tenant_id=tenant_id)

  • @handle_conflicts(type='metadata')
  • def create_metadata(self, user_id, tenant_id, metadata):
  • @@ -412,6 +474,8 @@ class Identity(sql.Base, identity.Driver):
  • session = self.get_session()
  • with session.begin():
  • role_ref = session.query(Role).filter_by(id=role_id).first()
  • + if role_ref is None:
  • + raise exception.RoleNotFound(role_id=role_id)
  • for k in role:
  • role_ref[k] = role[k]
  • session.flush()
  • @@ -419,6 +483,7 @@ class Identity(sql.Base, identity.Driver):


  • def delete_role(self, role_id):
  • session = self.get_session()
  • - role_ref = session.query(Role).filter_by(id=role_id).first()
  • with session.begin():
  • - session.delete(role_ref)
  • + if not session.query(Role).filter_by(id=role_id).delete():
  • + raise exception.RoleNotFound(role_id=role_id)
  • + session.flush()


[ugyn@localhost keystone]$ git diff 2012.1:keystone/identity/backends/sql.py  2012.2:keystone/identity/backends/sql.py  > tables_diff
[ugyn@localhost keystone]$ cat tables_diff
diff --git a/2012.1:keystone/identity/backends/sql.py b/2012.2:keystone/identity/backends/sql.py
index e4281a8..a3c8d1f 100644
--- a/2012.1:keystone/identity/backends/sql.py
+++ b/2012.2:keystone/identity/backends/sql.py
@@ -17,11 +17,12 @@
import copy
import functools
-from keystone import identity
-from keystone import exception
+from keystone import clean
from keystone.common import sql
-from keystone.common import utils
from keystone.common.sql import migration
+from keystone.common import utils
+from keystone import exception
+from keystone import identity

def _filter_user(user_ref):
@@ -45,7 +46,7 @@ def handle_conflicts(type='object'):
try:
return method(*args, **kwargs)
except sql.IntegrityError as e:
-                raise exception.Conflict(type=type, details=str(e))
+                raise exception.Conflict(type=type, details=e.message)
return wrapper
return decorator
@@ -53,7 +54,7 @@ def handle_conflicts(type='object'):
class User(sql.ModelBase, sql.DictBase):
__tablename__ = 'user'
id = sql.Column(sql.String(64), primary_key=True)
-    name = sql.Column(sql.String(64), unique=True)
+    name = sql.Column(sql.String(64), unique=True, nullable=False)
#password = sql.Column(sql.String(64))
extra = sql.Column(sql.JsonBlob())
@@ -79,7 +80,7 @@ class User(sql.ModelBase, sql.DictBase):
class Tenant(sql.ModelBase, sql.DictBase):
__tablename__ = 'tenant'
id = sql.Column(sql.String(64), primary_key=True)
-    name = sql.Column(sql.String(64), unique=True)
+    name = sql.Column(sql.String(64), unique=True, nullable=False)
extra = sql.Column(sql.JsonBlob())
@classmethod
@@ -104,7 +105,7 @@ class Tenant(sql.ModelBase, sql.DictBase):
class Role(sql.ModelBase, sql.DictBase):
__tablename__ = 'role'
id = sql.Column(sql.String(64), primary_key=True)
-    name = sql.Column(sql.String(64), unique=True)
+    name = sql.Column(sql.String(64), unique=True, nullable=False)

class Metadata(sql.ModelBase, sql.DictBase):
@@ -134,6 +135,20 @@ class Identity(sql.Base, identity.Driver):
def db_sync(self):
migration.db_sync()
+    def _check_password(self, password, user_ref):
+        """Check the specified password against the data store.
+
+        This is modeled on ldap/core.py.  The idea is to make it easier to
+        subclass Identity so that you can still use it to store all the data,
+        but use some other means to check the password.
+        Note that we'll pass in the entire user_ref in case the subclass
+        needs things like user_ref.get('name')
+        For further justification, please see the follow up suggestion at
+        https://blueprints.launchpad.net/keystone/+spec/sql-identiy-pam
+
+        """
+        return utils.check_password(password, user_ref.get('password'))
+
# Identity interface
def authenticate(self, user_id=None, tenant_id=None, password=None):
"""Authenticate based on a user, tenant and password.
@@ -142,56 +157,69 @@ class Identity(sql.Base, identity.Driver):
in the list of tenants on the user.
"""
-        user_ref = self._get_user(user_id)
-        if (not user_ref
-            or not utils.check_password(password, user_ref.get('password'))):
+        user_ref = None
+        tenant_ref = None
+        metadata_ref = {}
+
+        try:
+            user_ref = self._get_user(user_id)
+        except exception.UserNotFound:
raise AssertionError('Invalid user / password')
-        tenants = self.get_tenants_for_user(user_id)
-        if tenant_id and tenant_id not in tenants:
-            raise AssertionError('Invalid tenant')
+        if not utils.check_password(password, user_ref.get('password')):
+            raise AssertionError('Invalid user / password')
+
+        if tenant_id is not None:
+            if tenant_id not in self.get_tenants_for_user(user_id):
+                raise AssertionError('Invalid tenant')
+
+            try:
+                tenant_ref = self.get_tenant(tenant_id)
+                metadata_ref = self.get_metadata(user_id, tenant_id)
+            except exception.TenantNotFound:
+                tenant_ref = None
+                metadata_ref = {}
+            except exception.MetadataNotFound:
+                metadata_ref = {}
-        tenant_ref = self.get_tenant(tenant_id)
-        if tenant_ref:
-            metadata_ref = self.get_metadata(user_id, tenant_id)
-        else:
-            metadata_ref = {}
return (_filter_user(user_ref), tenant_ref, metadata_ref)
def get_tenant(self, tenant_id):
session = self.get_session()
tenant_ref = session.query(Tenant).filter_by(id=tenant_id).first()
-        if not tenant_ref:
-            return
+        if tenant_ref is None:
+            raise exception.TenantNotFound(tenant_id=tenant_id)
return tenant_ref.to_dict()
def get_tenant_by_name(self, tenant_name):
session = self.get_session()
tenant_ref = session.query(Tenant).filter_by(name=tenant_name).first()
if not tenant_ref:
-            return
+            raise exception.TenantNotFound(tenant_id=tenant_name)
return tenant_ref.to_dict()
def get_tenant_users(self, tenant_id):
session = self.get_session()
+        self.get_tenant(tenant_id)
user_refs = session.query(User)\
-                .join(UserTenantMembership)\
-                .filter(UserTenantMembership.tenant_id == tenant_id)\
-                .all()
+            .join(UserTenantMembership)\
+            .filter(UserTenantMembership.tenant_id ==
+                    tenant_id)\
+            .all()
return [_filter_user(user_ref.to_dict()) for user_ref in user_refs]
def _get_user(self, user_id):
session = self.get_session()
user_ref = session.query(User).filter_by(id=user_id).first()
if not user_ref:
-            return
+            raise exception.UserNotFound(user_id=user_id)
return user_ref.to_dict()
def _get_user_by_name(self, user_name):
session = self.get_session()
user_ref = session.query(User).filter_by(name=user_name).first()
if not user_ref:
-            return
+            raise exception.UserNotFound(user_id=user_name)
return user_ref.to_dict()
def get_user(self, user_id):
@@ -206,11 +234,16 @@ class Identity(sql.Base, identity.Driver):
.filter_by(user_id=user_id)\
.filter_by(tenant_id=tenant_id)\
.first()
-        return getattr(metadata_ref, 'data', {})
+        if metadata_ref is None:
+            raise exception.MetadataNotFound()
+        return metadata_ref.data
def get_role(self, role_id):
session = self.get_session()
-        return session.query(Role).filter_by(id=role_id).first()
+        role_ref = session.query(Role).filter_by(id=role_id).first()
+        if role_ref is None:
+            raise exception.RoleNotFound(role_id=role_id)
+        return role_ref
def list_users(self):
session = self.get_session()
@@ -225,6 +258,8 @@ class Identity(sql.Base, identity.Driver):
# These should probably be part of the high-level API
def add_user_to_tenant(self, tenant_id, user_id):
session = self.get_session()
+        self.get_tenant(tenant_id)
+        self.get_user(user_id)
q = session.query(UserTenantMembership)\
.filter_by(user_id=user_id)\
.filter_by(tenant_id=tenant_id)
@@ -239,10 +274,14 @@ class Identity(sql.Base, identity.Driver):
def remove_user_from_tenant(self, tenant_id, user_id):
session = self.get_session()
+        self.get_tenant(tenant_id)
+        self.get_user(user_id)
membership_ref = session.query(UserTenantMembership)\
.filter_by(user_id=user_id)\
.filter_by(tenant_id=tenant_id)\
.first()
+        if membership_ref is None:
+            raise exception.NotFound('User not found in tenant')
with session.begin():
session.delete(membership_ref)
session.flush()
@@ -254,37 +293,50 @@ class Identity(sql.Base, identity.Driver):
def get_tenants_for_user(self, user_id):
session = self.get_session()
+        self.get_user(user_id)
membership_refs = session.query(UserTenantMembership)\
-                          .filter_by(user_id=user_id)\
-                          .all()
+                                 .filter_by(user_id=user_id)\
+                                 .all()
return [x.tenant_id for x in membership_refs]
def get_roles_for_user_and_tenant(self, user_id, tenant_id):
-        metadata_ref = self.get_metadata(user_id, tenant_id)
-        if not metadata_ref:
+        self.get_user(user_id)
+        self.get_tenant(tenant_id)
+        try:
+            metadata_ref = self.get_metadata(user_id, tenant_id)
+        except exception.MetadataNotFound:
metadata_ref = {}
return metadata_ref.get('roles', [])
def add_role_to_user_and_tenant(self, user_id, tenant_id, role_id):
-        metadata_ref = self.get_metadata(user_id, tenant_id)
-        is_new = False
-        if not metadata_ref:
-            is_new = True
+        self.get_user(user_id)
+        self.get_tenant(tenant_id)
+        self.get_role(role_id)
+        try:
+            metadata_ref = self.get_metadata(user_id, tenant_id)
+            is_new = False
+        except exception.MetadataNotFound:
metadata_ref = {}
+            is_new = True
roles = set(metadata_ref.get('roles', []))
+        if role_id in roles:
+            msg = ('User %s already has role %s in tenant %s'
+                   % (user_id, role_id, tenant_id))
+            raise exception.Conflict(type='role grant', details=msg)
roles.add(role_id)
metadata_ref['roles'] = list(roles)
-        if not is_new:
-            self.update_metadata(user_id, tenant_id, metadata_ref)
-        else:
+        if is_new:
self.create_metadata(user_id, tenant_id, metadata_ref)
+        else:
+            self.update_metadata(user_id, tenant_id, metadata_ref)
def remove_role_from_user_and_tenant(self, user_id, tenant_id, role_id):
-        metadata_ref = self.get_metadata(user_id, tenant_id)
-        is_new = False
-        if not metadata_ref:
-            is_new = True
+        try:
+            metadata_ref = self.get_metadata(user_id, tenant_id)
+            is_new = False
+        except exception.MetadataNotFound:
metadata_ref = {}
+            is_new = True
roles = set(metadata_ref.get('roles', []))
if role_id not in roles:
msg = 'Cannot remove role that has not been granted, %s' % role_id
@@ -292,14 +344,15 @@ class Identity(sql.Base, identity.Driver):
roles.remove(role_id)
metadata_ref['roles'] = list(roles)
-        if not is_new:
-            self.update_metadata(user_id, tenant_id, metadata_ref)
-        else:
+        if is_new:
self.create_metadata(user_id, tenant_id, metadata_ref)
+        else:
+            self.update_metadata(user_id, tenant_id, metadata_ref)
# CRUD
@handle_conflicts(type='user')
def create_user(self, user_id, user):
+        user['name'] = clean.user_name(user['name'])
user = _ensure_hashed_password(user)
session = self.get_session()
with session.begin():
@@ -310,9 +363,15 @@ class Identity(sql.Base, identity.Driver):
@handle_conflicts(type='user')
def update_user(self, user_id, user):
+        if 'name' in user:
+            user['name'] = clean.user_name(user['name'])
session = self.get_session()
+        if 'id' in user and user_id != user['id']:
+            raise exception.ValidationError('Cannot change user ID')
with session.begin():
user_ref = session.query(User).filter_by(id=user_id).first()
+            if user_ref is None:
+                raise exception.UserNotFound(user_id=user_id)
old_user_dict = user_ref.to_dict()
user = _ensure_hashed_password(user)
for k in user:
@@ -326,21 +385,17 @@ class Identity(sql.Base, identity.Driver):
def delete_user(self, user_id):
session = self.get_session()
-        user_ref = session.query(User).filter_by(id=user_id).first()
-        membership_refs = session.query(UserTenantMembership)\
-                          .filter_by(user_id=user_id)\
-                          .all()
-
with session.begin():
-            if membership_refs:
-                for membership_ref in membership_refs:
-                    session.delete(membership_ref)
-
-            session.delete(user_ref)
-            session.flush()
+            session.query(UserTenantMembership)\
+                   .filter_by(user_id=user_id).delete(False)
+            session.query(Metadata)\
+                   .filter_by(user_id=user_id).delete(False)
+            if not session.query(User).filter_by(id=user_id).delete(False):
+                raise exception.UserNotFound(user_id=user_id)
@handle_conflicts(type='tenant')
def create_tenant(self, tenant_id, tenant):
+        tenant['name'] = clean.tenant_name(tenant['name'])
session = self.get_session()
with session.begin():
tenant_ref = Tenant.from_dict(tenant)
@@ -350,9 +405,13 @@ class Identity(sql.Base, identity.Driver):
@handle_conflicts(type='tenant')
def update_tenant(self, tenant_id, tenant):
+        if 'name' in tenant:
+            tenant['name'] = clean.tenant_name(tenant['name'])
session = self.get_session()
with session.begin():
tenant_ref = session.query(Tenant).filter_by(id=tenant_id).first()
+            if tenant_ref is None:
+                raise exception.TenantNotFound(tenant_id=tenant_id)
old_tenant_dict = tenant_ref.to_dict()
for k in tenant:
old_tenant_dict[k] = tenant[k]
@@ -365,10 +424,13 @@ class Identity(sql.Base, identity.Driver):
def delete_tenant(self, tenant_id):
session = self.get_session()
-        tenant_ref = session.query(Tenant).filter_by(id=tenant_id).first()
with session.begin():
-            session.delete(tenant_ref)
-            session.flush()
+            session.query(UserTenantMembership)\
+                   .filter_by(tenant_id=tenant_id).delete(False)
+            session.query(Metadata)\
+                   .filter_by(tenant_id=tenant_id).delete(False)
+            if not session.query(Tenant).filter_by(id=tenant_id).delete(False):
+                raise exception.TenantNotFound(tenant_id=tenant_id)
@handle_conflicts(type='metadata')
def create_metadata(self, user_id, tenant_id, metadata):
@@ -412,6 +474,8 @@ class Identity(sql.Base, identity.Driver):
session = self.get_session()
with session.begin():
role_ref = session.query(Role).filter_by(id=role_id).first()
+            if role_ref is None:
+                raise exception.RoleNotFound(role_id=role_id)
for k in role:
role_ref[k] = role[k]
session.flush()
@@ -419,6 +483,7 @@ class Identity(sql.Base, identity.Driver):
def delete_role(self, role_id):
session = self.get_session()
-        role_ref = session.query(Role).filter_by(id=role_id).first()
with session.begin():
-            session.delete(role_ref)
+            if not session.query(Role).filter_by(id=role_id).delete():
+                raise exception.RoleNotFound(role_id=role_id)
+            session.flush()

ec2_credential表比较
  该表在源文件keystone/contrib/ec2/backends/sql.py文件中定义,从下面的输出来看该表没有变化。






[plain] view plaincopyprint?

  • [ugyn@localhost keystone]$ git diff 2012.1:keystone/contrib/ec2/backends/sql.py 2012.2:keystone/contrib/ec2/backends/sql.py > tables_diff
  • [ugyn@localhost keystone]$ cat tables_diff
  • diff --git a/2012.1:keystone/contrib/ec2/backends/sql.py b/2012.2:keystone/contrib/ec2/backends/sql.py
  • index d84c381..c3af464 100644
  • --- a/2012.1:keystone/contrib/ec2/backends/sql.py
  • +++ b/2012.2:keystone/contrib/ec2/backends/sql.py
  • @@ -15,7 +15,6 @@
  • # under the License.

  • from keystone.common import sql
  • -from keystone.common.sql import migration



  • class Ec2Credential(sql.ModelBase, sql.DictBase):

[ugyn@localhost keystone]$ git diff 2012.1:keystone/contrib/ec2/backends/sql.py  2012.2:keystone/contrib/ec2/backends/sql.py   > tables_diff
[ugyn@localhost keystone]$ cat tables_diff
diff --git a/2012.1:keystone/contrib/ec2/backends/sql.py b/2012.2:keystone/contrib/ec2/backends/sql.py
index d84c381..c3af464 100644
--- a/2012.1:keystone/contrib/ec2/backends/sql.py
+++ b/2012.2:keystone/contrib/ec2/backends/sql.py
@@ -15,7 +15,6 @@
# under the License.
from keystone.common import sql
-from keystone.common.sql import migration

class Ec2Credential(sql.ModelBase, sql.DictBase):
token表的差异
  该表在源文件keystone/token/backends/sql.py中定义,从下面的输出来看F版中该表添加了一个新的布尔列valid默认值为True。






[plain] view plaincopyprint?

  • [ugyn@localhost keystone]$ git diff 2012.1:keystone/token/backends/sql.py 2012.2:keystone/token/backends/sql.py > tables_diff
  • [ugyn@localhost keystone]$ cat tables_diff
  • diff --git a/2012.1:keystone/token/backends/sql.py b/2012.2:keystone/token/backends/sql.py
  • index 7a9a551..02e8947 100644
  • --- a/2012.1:keystone/token/backends/sql.py
  • +++ b/2012.2:keystone/token/backends/sql.py
  • @@ -16,9 +16,12 @@


  • import copy
  • import datetime
  • +import hashlib


  • +from keystone.common import cms
  • from keystone.common import sql
  • from keystone import exception
  • +from keystone.openstack.common import timeutils
  • from keystone import token



  • @@ -27,6 +30,7 @@ class TokenModel(sql.ModelBase, sql.DictBase):
  • id = sql.Column(sql.String(64), primary_key=True)
  • expires = sql.Column(sql.DateTime(), default=None)
  • extra = sql.Column(sql.JsonBlob())
  • + valid = sql.Column(sql.Boolean(), default=True)

  • @classmethod
  • def from_dict(cls, token_dict):
  • @@ -49,21 +53,31 @@ class Token(sql.Base, token.Driver):
  • # Public interface
  • def get_token(self, token_id):
  • session = self.get_session()
  • - token_ref = session.query(TokenModel).filter_by(id=token_id).first()
  • + token_ref = session.query(TokenModel)\
  • + .filter_by(id=self.token_to_key(token_id),
  • + valid=True).first()
  • now = datetime.datetime.utcnow()
  • if token_ref and (not token_ref.expires or now < token_ref.expires):
  • return token_ref.to_dict()
  • else:
  • raise exception.TokenNotFound(token_id=token_id)

  • + def token_to_key(self, token_id):
  • + if len(token_id) > cms.UUID_TOKEN_LENGTH:
  • + hash = hashlib.md5()
  • + hash.update(token_id)
  • + return hash.hexdigest()
  • + else:
  • + return token_id
  • +
  • def create_token(self, token_id, data):
  • data_copy = copy.deepcopy(data)
  • if 'expires' not in data_copy:
  • data_copy['expires'] = self._get_default_expire_time()


  • token_ref = TokenModel.from_dict(data_copy)
  • - token_ref.id = token_id
  • -
  • + token_ref.id = self.token_to_key(token_id)
  • + token_ref.valid = True
  • session = self.get_session()
  • with session.begin():
  • session.add(token_ref)
  • @@ -72,12 +86,45 @@ class Token(sql.Base, token.Driver):


  • def delete_token(self, token_id):
  • session = self.get_session()
  • - token_ref = session.query(TokenModel)\
  • - .filter_by(id=token_id)\
  • - .first()
  • - if not token_ref:
  • - raise exception.TokenNotFound(token_id=token_id)
  • -
  • + key = self.token_to_key(token_id)
  • with session.begin():
  • - session.delete(token_ref)
  • + token_ref = session.query(TokenModel).filter_by(id=key,
  • + valid=True).first()
  • + if not token_ref:
  • + raise exception.TokenNotFound(token_id=token_id)
  • + token_ref.valid = False
  • session.flush()
  • +
  • + def list_tokens(self, user_id, tenant_id=None):
  • + session = self.get_session()
  • + tokens = []
  • + now = timeutils.utcnow()
  • + for token_ref in session.query(TokenModel)\
  • + .filter(TokenModel.expires > now)\
  • + .filter_by(valid=True):
  • + token_ref_dict = token_ref.to_dict()
  • + if 'user' not in token_ref_dict:
  • + continue
  • + if token_ref_dict['user'].get('id') != user_id:
  • + continue
  • + if tenant_id is not None:
  • + if 'tenant' not in token_ref_dict:
  • + continue
  • + if token_ref_dict['tenant'].get('id') != tenant_id:
  • + continue
  • + tokens.append(token_ref['id'])
  • + return tokens
  • +
  • + def list_revoked_tokens(self):
  • + session = self.get_session()
  • + tokens = []
  • + now = timeutils.utcnow()
  • + for token_ref in session.query(TokenModel)\
  • + .filter(TokenModel.expires > now)\
  • + .filter_by(valid=False):
  • + record = {
  • + 'id': token_ref['id'],
  • + 'expires': token_ref['expires'],
  • + }
  • + tokens.append(record)
  • + return tokens


[ugyn@localhost keystone]$ git diff 2012.1:keystone/token/backends/sql.py  2012.2:keystone/token/backends/sql.py   > tables_diff
[ugyn@localhost keystone]$ cat tables_diff
diff --git a/2012.1:keystone/token/backends/sql.py b/2012.2:keystone/token/backends/sql.py
index 7a9a551..02e8947 100644
--- a/2012.1:keystone/token/backends/sql.py
+++ b/2012.2:keystone/token/backends/sql.py
@@ -16,9 +16,12 @@
import copy
import datetime
+import hashlib
+from keystone.common import cms
from keystone.common import sql
from keystone import exception
+from keystone.openstack.common import timeutils
from keystone import token

@@ -27,6 +30,7 @@ class TokenModel(sql.ModelBase, sql.DictBase):
id = sql.Column(sql.String(64), primary_key=True)
expires = sql.Column(sql.DateTime(), default=None)
extra = sql.Column(sql.JsonBlob())
+    valid = sql.Column(sql.Boolean(), default=True)
@classmethod
def from_dict(cls, token_dict):
@@ -49,21 +53,31 @@ class Token(sql.Base, token.Driver):
# Public interface
def get_token(self, token_id):
session = self.get_session()
-        token_ref = session.query(TokenModel).filter_by(id=token_id).first()
+        token_ref = session.query(TokenModel)\
+            .filter_by(id=self.token_to_key(token_id),
+                       valid=True).first()
now = datetime.datetime.utcnow()
if token_ref and (not token_ref.expires or now < token_ref.expires):
return token_ref.to_dict()
else:
raise exception.TokenNotFound(token_id=token_id)
+    def token_to_key(self, token_id):
+        if len(token_id) > cms.UUID_TOKEN_LENGTH:
+            hash = hashlib.md5()
+            hash.update(token_id)
+            return hash.hexdigest()
+        else:
+            return token_id
+
def create_token(self, token_id, data):
data_copy = copy.deepcopy(data)
if 'expires' not in data_copy:
data_copy['expires'] = self._get_default_expire_time()
token_ref = TokenModel.from_dict(data_copy)
-        token_ref.id = token_id
-
+        token_ref.id = self.token_to_key(token_id)
+        token_ref.valid = True
session = self.get_session()
with session.begin():
session.add(token_ref)
@@ -72,12 +86,45 @@ class Token(sql.Base, token.Driver):
def delete_token(self, token_id):
session = self.get_session()
-        token_ref = session.query(TokenModel)\
-                                .filter_by(id=token_id)\
-                                .first()
-        if not token_ref:
-            raise exception.TokenNotFound(token_id=token_id)
-
+        key = self.token_to_key(token_id)
with session.begin():
-            session.delete(token_ref)
+            token_ref = session.query(TokenModel).filter_by(id=key,
+                                                            valid=True).first()
+            if not token_ref:
+                raise exception.TokenNotFound(token_id=token_id)
+            token_ref.valid = False
session.flush()
+
+    def list_tokens(self, user_id, tenant_id=None):
+        session = self.get_session()
+        tokens = []
+        now = timeutils.utcnow()
+        for token_ref in session.query(TokenModel)\
+                                .filter(TokenModel.expires > now)\
+                                .filter_by(valid=True):
+            token_ref_dict = token_ref.to_dict()
+            if 'user' not in token_ref_dict:
+                continue
+            if token_ref_dict['user'].get('id') != user_id:
+                continue
+            if tenant_id is not None:
+                if 'tenant' not in token_ref_dict:
+                    continue
+                if token_ref_dict['tenant'].get('id') != tenant_id:
+                    continue
+            tokens.append(token_ref['id'])
+        return tokens
+
+    def list_revoked_tokens(self):
+        session = self.get_session()
+        tokens = []
+        now = timeutils.utcnow()
+        for token_ref in session.query(TokenModel)\
+                                .filter(TokenModel.expires > now)\
+                                .filter_by(valid=False):
+            record = {
+                'id': token_ref['id'],
+                'expires': token_ref['expires'],
+            }
+            tokens.append(record)
+        return tokens

migrate_version表的差异
  该表似乎跟Keystone进行数据库升级有关,具体差异不清楚。

配置差异
  从下面的示例配置文件的比较来看主要是增加了几个filter中间件






[plain] view plaincopyprint?

  • [ugyn@localhost keystone]$ git diff 2012.1:etc/keystone.conf 2012.2:etc/keystone.conf.sample > tables_diff
  • [ugyn@localhost keystone]$ cat tables_diff
  • diff --git a/2012.1:etc/keystone.conf b/2012.2:etc/keystone.conf.sample
  • index 3ecf641..1d48676 100644
  • --- a/2012.1:etc/keystone.conf
  • +++ b/2012.2:etc/keystone.conf.sample
  • @@ -1,53 +1,128 @@
  • [DEFAULT]
  • -#bind_host = 0.0.0.0
  • -public_port = 5000
  • -admin_port = 35357
  • -admin_token = ADMIN
  • -compute_port = 8774
  • -verbose = True
  • -debug = True
  • -#log_config = ./etc/logging.conf.sample
  • -
  • -# ================= Syslog Options ============================
  • -# Send logs to syslog (/dev/log) instead of to file specified
  • -# by `log-file`
  • -use_syslog = False
  • -
  • -# Facility to use. If unset defaults to LOG_USER.
  • -# syslog_log_facility = LOG_LOCAL0
  • +# A "shared secret" between keystone and other openstack services
  • +# admin_token = ADMIN
  • +
  • +# The IP address of the network interface to listen on
  • +# bind_host = 0.0.0.0
  • +
  • +# The port number which the public service listens on
  • +# public_port = 5000
  • +
  • +# The port number which the public admin listens on
  • +# admin_port = 35357
  • +
  • +# The port number which the OpenStack Compute service listens on
  • +# compute_port = 8774
  • +
  • +# === Logging Options ===
  • +# Print debugging output
  • +# verbose = False
  • +
  • +# Print more verbose output
  • +# (includes plaintext request logging, potentially including passwords)
  • +# debug = False
  • +
  • +# Name of log file to output to. If not set, logging will go to stdout.
  • +# log_file = keystone.log
  • +
  • +# The directory to keep log files in (will be prepended to --logfile)
  • +# log_dir = /var/log/keystone
  • +
  • +# Use syslog for logging.
  • +# use_syslog = False
  • +
  • +# syslog facility to receive log lines
  • +# syslog_log_facility = LOG_USER
  • +
  • +# If this option is specified, the logging configuration file specified is
  • +# used and overrides any other logging options specified. Please see the
  • +# Python logging module documentation for details on logging configuration
  • +# files.
  • +# log_config = logging.conf
  • +
  • +# A logging.Formatter log message format string which may use any of the
  • +# available logging.LogRecord attributes.
  • +# log_format = %(asctime)s %(levelname)8s [%(name)s] %(message)s
  • +
  • +# Format string for %(asctime)s in log records.
  • +# log_date_format = %Y-%m-%d %H:%M:%S
  • +
  • +# onready allows you to send a notification when the process is ready to serve
  • +# For example, to have it notify using systemd, one could set shell command:
  • +# onready = systemd-notify --ready
  • +# or a module with notify() method:
  • +# onready = keystone.common.systemd


  • [sql]
  • -connection = sqlite:///keystone.db
  • -idle_timeout = 200
  • +# The SQLAlchemy connection string used to connect to the database
  • +# connection = sqlite:///keystone.db


  • -[ldap]
  • -#url = ldap://localhost
  • -#tree_dn = dc=example,dc=com
  • -#user_tree_dn = ou=Users,dc=example,dc=com
  • -#role_tree_dn = ou=Roles,dc=example,dc=com
  • -#tenant_tree_dn = ou=Groups,dc=example,dc=com
  • -#user = dc=Manager,dc=example,dc=com
  • -#password = freeipa4all
  • -#suffix = cn=example,cn=com
  • +# the timeout before idle sql connections are reaped
  • +# idle_timeout = 200


  • [identity]
  • -driver = keystone.identity.backends.sql.Identity
  • +# driver = keystone.identity.backends.sql.Identity


  • [catalog]
  • -driver = keystone.catalog.backends.templated.TemplatedCatalog
  • -template_file = ./etc/default_catalog.templates
  • +# dynamic, sql-based backend (supports API/CLI-based management commands)
  • +# driver = keystone.catalog.backends.sql.Catalog
  • +
  • +# static, file-based backend (does *NOT* support any management commands)
  • +# driver = keystone.catalog.backends.templated.TemplatedCatalog
  • +
  • +# template_file = default_catalog.templates

  • [token]
  • -driver = keystone.token.backends.kvs.Token
  • +# driver = keystone.token.backends.kvs.Token

  • # Amount of time a token should remain valid (in seconds)
  • -expiration = 86400
  • +# expiration = 86400

  • [policy]
  • -driver = keystone.policy.backends.rules.Policy
  • +# driver = keystone.policy.backends.rules.Policy

  • [ec2]
  • -driver = keystone.contrib.ec2.backends.kvs.Ec2
  • +# driver = keystone.contrib.ec2.backends.kvs.Ec2
  • +
  • +[ssl]
  • +#enable = True
  • +#certfile = /etc/keystone/ssl/certs/keystone.pem
  • +#keyfile = /etc/keystone/ssl/private/keystonekey.pem
  • +#ca_certs = /etc/keystone/ssl/certs/ca.pem
  • +#cert_required = True
  • +
  • +[signing]
  • +#token_format = UUID
  • +#certfile = /etc/keystone/ssl/certs/signing_cert.pem
  • +#keyfile = /etc/keystone/ssl/private/signing_key.pem
  • +#ca_certs = /etc/keystone/ssl/certs/ca.pem
  • +#key_size = 1024
  • +#valid_days = 3650
  • +#ca_password = None
  • +#token_format = PKI
  • +
  • +[ldap]
  • +# url = ldap://localhost
  • +# user = dc=Manager,dc=example,dc=com
  • +# password = None
  • +# suffix = cn=example,cn=com
  • +# use_dumb_member = False
  • +
  • +# user_tree_dn = ou=Users,dc=example,dc=com
  • +# user_objectclass = inetOrgPerson
  • +# user_id_attribute = cn
  • +# user_name_attribute = sn
  • +
  • +# tenant_tree_dn = ou=Groups,dc=example,dc=com
  • +# tenant_objectclass = groupOfNames
  • +# tenant_id_attribute = cn
  • +# tenant_member_attribute = member
  • +# tenant_name_attribute = ou
  • +
  • +# role_tree_dn = ou=Roles,dc=example,dc=com
  • +# role_objectclass = organizationalRole
  • +# role_id_attribute = cn
  • +# role_member_attribute = roleOccupant

  • [filter:debug]
  • paste.filter_factory = keystone.common.wsgi:Debug.factory
  • @@ -64,12 +139,27 @@ paste.filter_factory = keystone.middleware:XmlBodyMiddleware.factory
  • [filter:json_body]
  • paste.filter_factory = keystone.middleware:JsonBodyMiddleware.factory

  • +[filter:user_crud_extension]
  • +paste.filter_factory = keystone.contrib.user_crud:CrudExtension.factory
  • +
  • [filter:crud_extension]
  • paste.filter_factory = keystone.contrib.admin_crud:CrudExtension.factory

  • [filter:ec2_extension]
  • paste.filter_factory = keystone.contrib.ec2:Ec2Extension.factory


  • +[filter:s3_extension]
  • +paste.filter_factory = keystone.contrib.s3:S3Extension.factory
  • +
  • +[filter:url_normalize]
  • +paste.filter_factory = keystone.middleware:NormalizingFilter.factory
  • +
  • +[filter:stats_monitoring]
  • +paste.filter_factory = keystone.contrib.stats:StatsMiddleware.factory
  • +
  • +[filter:stats_reporting]
  • +paste.filter_factory = keystone.contrib.stats:StatsExtension.factory
  • +
  • [app:public_service]
  • paste.app_factory = keystone.service:public_app_factory

  • @@ -77,10 +167,10 @@ paste.app_factory = keystone.service:public_app_factory
  • paste.app_factory = keystone.service:admin_app_factory


  • [pipeline:public_api]
  • -pipeline = token_auth admin_token_auth xml_body json_body debug ec2_extension public_service
  • +pipeline = stats_monitoring url_normalize token_auth admin_token_auth xml_body json_body debug ec2_extension user_crud_extension public_service


  • [pipeline:admin_api]
  • -pipeline = token_auth admin_token_auth xml_body json_body debug ec2_extension crud_extension admin_service
  • +pipeline = stats_monitoring url_normalize token_auth admin_token_auth xml_body json_body debug stats_reporting ec2_extension s3_extension crud_extension admin_service


  • [app:public_version_service]
  • paste.app_factory = keystone.service:public_version_app_factory
  • @@ -89,10 +179,10 @@ paste.app_factory = keystone.service:public_version_app_factory
  • paste.app_factory = keystone.service:admin_version_app_factory

  • [pipeline:public_version_api]
  • -pipeline = xml_body public_version_service
  • +pipeline = stats_monitoring url_normalize xml_body public_version_service

  • [pipeline:admin_version_api]
  • -pipeline = xml_body admin_version_service
  • +pipeline = stats_monitoring url_normalize xml_body admin_version_service

  • [composite:main]
  • use = egg:Paste#urlmap

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-56243-1-1.html 上篇帖子: 译:块设备和 OpenStack 下篇帖子: openstack中iptables的使用
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表