您的位置:首页 > 运维架构

OpenStack版本比较之Keystone

2012-11-07 15:48 375 查看

目的

本文主要比较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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: