目的

本文主要比较OpenStack中Essex与Folsom版本的Keystone在依赖包、数据库结构、配置方面的差异,为Keystone从Essex向Folsom升级做些前期准备工作。这些比较大部分是在源代码库上通过git命令进行的,首先要clone一个keystone的本地库,命令如下:

[plain] view plaincopyprint?
  1. git clone git://github.com/openstack/keystone.git
  2. 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?
  1. [ugyn@localhost keystone]$ git diff 2012.1:tools/pip-requires 2012.2:tools/pip-requires
  2. diff --git a/2012.1:tools/pip-requires b/2012.2:tools/pip-requires
  3. index 0e13534..ec5562d 100644
  4. --- a/2012.1:tools/pip-requires
  5. +++ b/2012.2:tools/pip-requires
  6. @@ -1,4 +1,4 @@
  7. -# keystonelight dependencies
  8. +# keystone dependencies
  9. pam==0.1.4
  10. WebOb==1.0.8
  11. eventlet
  12. @@ -10,3 +10,4 @@ sqlalchemy
  13. sqlalchemy-migrate
  14. passlib
  15. lxml
  16. +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 dependenciespam==0.1.4WebOb==1.0.8eventlet
@@ -10,3 +10,4 @@ sqlalchemysqlalchemy-migratepassliblxml
+iso8601>=0.1.4

数据库结构的差异

与数据库相关的源文件

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

[plain] view plaincopyprint?
  1. [ugyn@localhost keystone]$ git grep "sql.ModelBase"
  2. keystone/catalog/backends/sql.py:class Service(sql.ModelBase, sql.DictBase):
  3. keystone/catalog/backends/sql.py:class Endpoint(sql.ModelBase, sql.DictBase):
  4. keystone/common/sql/migrate_repo/versions/001_add_initial_tables.py: sql.ModelBase.metadata.create_all(migrate_engine)
  5. keystone/contrib/ec2/backends/sql.py:class Ec2Credential(sql.ModelBase, sql.DictBase):
  6. keystone/identity/backends/sql.py:class User(sql.ModelBase, sql.DictBase):
  7. keystone/identity/backends/sql.py:class Tenant(sql.ModelBase, sql.DictBase):
  8. keystone/identity/backends/sql.py:class Role(sql.ModelBase, sql.DictBase):
  9. keystone/identity/backends/sql.py:class Metadata(sql.ModelBase, sql.DictBase):
  10. keystone/identity/backends/sql.py:class UserTenantMembership(sql.ModelBase, sql.DictBase):
  11. keystone/token/backends/sql.py:class TokenModel(sql.ModelBase, sql.DictBase):
  12. tests/test_backend_sql.py: sql.ModelBase.metadata.bind = engine
  13. 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?
  1. [ugyn@localhost keystone]$ git diff 2012.1:keystone/catalog/backends/sql.py 2012.2:keystone/catalog/backends/sql.py > tables_diff
  2. [ugyn@localhost keystone]$ cat tables_diff
  3. diff --git a/2012.1:keystone/catalog/backends/sql.py b/2012.2:keystone/catalog/backends/sql.py
  4. index 3c553e0..bd0f687 100644
  5. --- a/2012.1:keystone/catalog/backends/sql.py
  6. +++ b/2012.2:keystone/catalog/backends/sql.py
  7. @@ -15,14 +15,11 @@
  8. # License for the specific language governing permissions and limitations
  9. # under the License.
  10. -import sqlalchemy.exc
  11. -import webob.exc
  12. -
  13. from keystone import catalog
  14. -from keystone import config
  15. -from keystone import exception
  16. from keystone.common import sql
  17. from keystone.common.sql import migration
  18. +from keystone import config
  19. +from keystone import exception
  20. CONF = config.CONF
  21. @@ -96,11 +93,9 @@ class Catalog(sql.Base, catalog.Driver):
  22. def delete_service(self, service_id):
  23. session = self.get_session()
  24. - service_ref = session.query(Service).filter_by(id=service_id).first()
  25. - if not service_ref:
  26. - raise exception.ServiceNotFound(service_id=service_id)
  27. with session.begin():
  28. - session.delete(service_ref)
  29. + if not session.query(Service).filter_by(id=service_id).delete():
  30. + raise exception.ServiceNotFound(service_id=service_id)
  31. session.flush()
  32. def create_service(self, service_id, service_ref):
  33. @@ -114,6 +109,7 @@ class Catalog(sql.Base, catalog.Driver):
  34. # Endpoints
  35. def create_endpoint(self, endpoint_id, endpoint_ref):
  36. session = self.get_session()
  37. + self.get_service(endpoint_ref['service_id'])
  38. new_endpoint = Endpoint.from_dict(endpoint_ref)
  39. with session.begin():
  40. session.add(new_endpoint)
  41. @@ -122,18 +118,17 @@ class Catalog(sql.Base, catalog.Driver):
  42. def delete_endpoint(self, endpoint_id):
  43. session = self.get_session()
  44. - endpoint_ref = session.query(Endpoint)
  45. - endpoint_ref = endpoint_ref.filter_by(id=endpoint_id).first()
  46. - if not endpoint_ref:
  47. - raise exception.EndpointNotFound(endpoint_id=endpoint_id)
  48. with session.begin():
  49. - session.delete(endpoint_ref)
  50. + if not session.query(Endpoint).filter_by(id=endpoint_id).delete():
  51. + raise exception.EndpointNotFound(endpoint_id=endpoint_id)
  52. session.flush()
  53. def get_endpoint(self, endpoint_id):
  54. session = self.get_session()
  55. endpoint_ref = session.query(Endpoint)
  56. endpoint_ref = endpoint_ref.filter_by(id=endpoint_id).first()
  57. + if not endpoint_ref:
  58. + raise exception.EndpointNotFound(endpoint_id=endpoint_id)
  59. return endpoint_ref.to_dict()
  60. def list_endpoints(self):
  61. @@ -163,6 +158,7 @@ class Catalog(sql.Base, catalog.Driver):
  62. internal_url = ep['internalurl'].replace('$(', '%(')
  63. public_url = ep['publicurl'].replace('$(', '%(')
  64. admin_url = ep['adminurl'].replace('$(', '%(')
  65. + catalog[region][srv_type]['id'] = ep['id']
  66. catalog[region][srv_type]['name'] = srv_name
  67. catalog[region][srv_type]['publicURL'] = public_url % d
  68. 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 exceptionfrom keystone.common import sqlfrom keystone.common.sql import migration
+from keystone import config
+from keystone import exceptionCONF = 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):# Endpointsdef 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_namecatalog[region][srv_type]['publicURL'] = public_url % dcatalog[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?
  1. [ugyn@localhost keystone]$ git diff 2012.1:keystone/identity/backends/sql.py 2012.2:keystone/identity/backends/sql.py > tables_diff
  2. [ugyn@localhost keystone]$ cat tables_diff
  3. diff --git a/2012.1:keystone/identity/backends/sql.py b/2012.2:keystone/identity/backends/sql.py
  4. index e4281a8..a3c8d1f 100644
  5. --- a/2012.1:keystone/identity/backends/sql.py
  6. +++ b/2012.2:keystone/identity/backends/sql.py
  7. @@ -17,11 +17,12 @@
  8. import copy
  9. import functools
  10. -from keystone import identity
  11. -from keystone import exception
  12. +from keystone import clean
  13. from keystone.common import sql
  14. -from keystone.common import utils
  15. from keystone.common.sql import migration
  16. +from keystone.common import utils
  17. +from keystone import exception
  18. +from keystone import identity
  19. def _filter_user(user_ref):
  20. @@ -45,7 +46,7 @@ def handle_conflicts(type='object'):
  21. try:
  22. return method(*args, **kwargs)
  23. except sql.IntegrityError as e:
  24. - raise exception.Conflict(type=type, details=str(e))
  25. + raise exception.Conflict(type=type, details=e.message)
  26. return wrapper
  27. return decorator
  28. @@ -53,7 +54,7 @@ def handle_conflicts(type='object'):
  29. class User(sql.ModelBase, sql.DictBase):
  30. __tablename__ = 'user'
  31. id = sql.Column(sql.String(64), primary_key=True)
  32. - name = sql.Column(sql.String(64), unique=True)
  33. + name = sql.Column(sql.String(64), unique=True, nullable=False)
  34. #password = sql.Column(sql.String(64))
  35. extra = sql.Column(sql.JsonBlob())
  36. @@ -79,7 +80,7 @@ class User(sql.ModelBase, sql.DictBase):
  37. class Tenant(sql.ModelBase, sql.DictBase):
  38. __tablename__ = 'tenant'
  39. id = sql.Column(sql.String(64), primary_key=True)
  40. - name = sql.Column(sql.String(64), unique=True)
  41. + name = sql.Column(sql.String(64), unique=True, nullable=False)
  42. extra = sql.Column(sql.JsonBlob())
  43. @classmethod
  44. @@ -104,7 +105,7 @@ class Tenant(sql.ModelBase, sql.DictBase):
  45. class Role(sql.ModelBase, sql.DictBase):
  46. __tablename__ = 'role'
  47. id = sql.Column(sql.String(64), primary_key=True)
  48. - name = sql.Column(sql.String(64), unique=True)
  49. + name = sql.Column(sql.String(64), unique=True, nullable=False)
  50. class Metadata(sql.ModelBase, sql.DictBase):
  51. @@ -134,6 +135,20 @@ class Identity(sql.Base, identity.Driver):
  52. def db_sync(self):
  53. migration.db_sync()
  54. + def _check_password(self, password, user_ref):
  55. + """Check the specified password against the data store.
  56. +
  57. + This is modeled on ldap/core.py. The idea is to make it easier to
  58. + subclass Identity so that you can still use it to store all the data,
  59. + but use some other means to check the password.
  60. + Note that we'll pass in the entire user_ref in case the subclass
  61. + needs things like user_ref.get('name')
  62. + For further justification, please see the follow up suggestion at
  63. + https://blueprints.launchpad.net/keystone/+spec/sql-identiy-pam
  64. +
  65. + """
  66. + return utils.check_password(password, user_ref.get('password'))
  67. +
  68. # Identity interface
  69. def authenticate(self, user_id=None, tenant_id=None, password=None):
  70. """Authenticate based on a user, tenant and password.
  71. @@ -142,56 +157,69 @@ class Identity(sql.Base, identity.Driver):
  72. in the list of tenants on the user.
  73. """
  74. - user_ref = self._get_user(user_id)
  75. - if (not user_ref
  76. - or not utils.check_password(password, user_ref.get('password'))):
  77. + user_ref = None
  78. + tenant_ref = None
  79. + metadata_ref = {}
  80. +
  81. + try:
  82. + user_ref = self._get_user(user_id)
  83. + except exception.UserNotFound:
  84. raise AssertionError('Invalid user / password')
  85. - tenants = self.get_tenants_for_user(user_id)
  86. - if tenant_id and tenant_id not in tenants:
  87. - raise AssertionError('Invalid tenant')
  88. + if not utils.check_password(password, user_ref.get('password')):
  89. + raise AssertionError('Invalid user / password')
  90. +
  91. + if tenant_id is not None:
  92. + if tenant_id not in self.get_tenants_for_user(user_id):
  93. + raise AssertionError('Invalid tenant')
  94. +
  95. + try:
  96. + tenant_ref = self.get_tenant(tenant_id)
  97. + metadata_ref = self.get_metadata(user_id, tenant_id)
  98. + except exception.TenantNotFound:
  99. + tenant_ref = None
  100. + metadata_ref = {}
  101. + except exception.MetadataNotFound:
  102. + metadata_ref = {}
  103. - tenant_ref = self.get_tenant(tenant_id)
  104. - if tenant_ref:
  105. - metadata_ref = self.get_metadata(user_id, tenant_id)
  106. - else:
  107. - metadata_ref = {}
  108. return (_filter_user(user_ref), tenant_ref, metadata_ref)
  109. def get_tenant(self, tenant_id):
  110. session = self.get_session()
  111. tenant_ref = session.query(Tenant).filter_by(id=tenant_id).first()
  112. - if not tenant_ref:
  113. - return
  114. + if tenant_ref is None:
  115. + raise exception.TenantNotFound(tenant_id=tenant_id)
  116. return tenant_ref.to_dict()
  117. def get_tenant_by_name(self, tenant_name):
  118. session = self.get_session()
  119. tenant_ref = session.query(Tenant).filter_by(name=tenant_name).first()
  120. if not tenant_ref:
  121. - return
  122. + raise exception.TenantNotFound(tenant_id=tenant_name)
  123. return tenant_ref.to_dict()
  124. def get_tenant_users(self, tenant_id):
  125. session = self.get_session()
  126. + self.get_tenant(tenant_id)
  127. user_refs = session.query(User)\
  128. - .join(UserTenantMembership)\
  129. - .filter(UserTenantMembership.tenant_id == tenant_id)\
  130. - .all()
  131. + .join(UserTenantMembership)\
  132. + .filter(UserTenantMembership.tenant_id ==
  133. + tenant_id)\
  134. + .all()
  135. return [_filter_user(user_ref.to_dict()) for user_ref in user_refs]
  136. def _get_user(self, user_id):
  137. session = self.get_session()
  138. user_ref = session.query(User).filter_by(id=user_id).first()
  139. if not user_ref:
  140. - return
  141. + raise exception.UserNotFound(user_id=user_id)
  142. return user_ref.to_dict()
  143. def _get_user_by_name(self, user_name):
  144. session = self.get_session()
  145. user_ref = session.query(User).filter_by(name=user_name).first()
  146. if not user_ref:
  147. - return
  148. + raise exception.UserNotFound(user_id=user_name)
  149. return user_ref.to_dict()
  150. def get_user(self, user_id):
  151. @@ -206,11 +234,16 @@ class Identity(sql.Base, identity.Driver):
  152. .filter_by(user_id=user_id)\
  153. .filter_by(tenant_id=tenant_id)\
  154. .first()
  155. - return getattr(metadata_ref, 'data', {})
  156. + if metadata_ref is None:
  157. + raise exception.MetadataNotFound()
  158. + return metadata_ref.data
  159. def get_role(self, role_id):
  160. session = self.get_session()
  161. - return session.query(Role).filter_by(id=role_id).first()
  162. + role_ref = session.query(Role).filter_by(id=role_id).first()
  163. + if role_ref is None:
  164. + raise exception.RoleNotFound(role_id=role_id)
  165. + return role_ref
  166. def list_users(self):
  167. session = self.get_session()
  168. @@ -225,6 +258,8 @@ class Identity(sql.Base, identity.Driver):
  169. # These should probably be part of the high-level API
  170. def add_user_to_tenant(self, tenant_id, user_id):
  171. session = self.get_session()
  172. + self.get_tenant(tenant_id)
  173. + self.get_user(user_id)
  174. q = session.query(UserTenantMembership)\
  175. .filter_by(user_id=user_id)\
  176. .filter_by(tenant_id=tenant_id)
  177. @@ -239,10 +274,14 @@ class Identity(sql.Base, identity.Driver):
  178. def remove_user_from_tenant(self, tenant_id, user_id):
  179. session = self.get_session()
  180. + self.get_tenant(tenant_id)
  181. + self.get_user(user_id)
  182. membership_ref = session.query(UserTenantMembership)\
  183. .filter_by(user_id=user_id)\
  184. .filter_by(tenant_id=tenant_id)\
  185. .first()
  186. + if membership_ref is None:
  187. + raise exception.NotFound('User not found in tenant')
  188. with session.begin():
  189. session.delete(membership_ref)
  190. session.flush()
  191. @@ -254,37 +293,50 @@ class Identity(sql.Base, identity.Driver):
  192. def get_tenants_for_user(self, user_id):
  193. session = self.get_session()
  194. + self.get_user(user_id)
  195. membership_refs = session.query(UserTenantMembership)\
  196. - .filter_by(user_id=user_id)\
  197. - .all()
  198. + .filter_by(user_id=user_id)\
  199. + .all()
  200. return [x.tenant_id for x in membership_refs]
  201. def get_roles_for_user_and_tenant(self, user_id, tenant_id):
  202. - metadata_ref = self.get_metadata(user_id, tenant_id)
  203. - if not metadata_ref:
  204. + self.get_user(user_id)
  205. + self.get_tenant(tenant_id)
  206. + try:
  207. + metadata_ref = self.get_metadata(user_id, tenant_id)
  208. + except exception.MetadataNotFound:
  209. metadata_ref = {}
  210. return metadata_ref.get('roles', [])
  211. def add_role_to_user_and_tenant(self, user_id, tenant_id, role_id):
  212. - metadata_ref = self.get_metadata(user_id, tenant_id)
  213. - is_new = False
  214. - if not metadata_ref:
  215. - is_new = True
  216. + self.get_user(user_id)
  217. + self.get_tenant(tenant_id)
  218. + self.get_role(role_id)
  219. + try:
  220. + metadata_ref = self.get_metadata(user_id, tenant_id)
  221. + is_new = False
  222. + except exception.MetadataNotFound:
  223. metadata_ref = {}
  224. + is_new = True
  225. roles = set(metadata_ref.get('roles', []))
  226. + if role_id in roles:
  227. + msg = ('User %s already has role %s in tenant %s'
  228. + % (user_id, role_id, tenant_id))
  229. + raise exception.Conflict(type='role grant', details=msg)
  230. roles.add(role_id)
  231. metadata_ref['roles'] = list(roles)
  232. - if not is_new:
  233. - self.update_metadata(user_id, tenant_id, metadata_ref)
  234. - else:
  235. + if is_new:
  236. self.create_metadata(user_id, tenant_id, metadata_ref)
  237. + else:
  238. + self.update_metadata(user_id, tenant_id, metadata_ref)
  239. def remove_role_from_user_and_tenant(self, user_id, tenant_id, role_id):
  240. - metadata_ref = self.get_metadata(user_id, tenant_id)
  241. - is_new = False
  242. - if not metadata_ref:
  243. - is_new = True
  244. + try:
  245. + metadata_ref = self.get_metadata(user_id, tenant_id)
  246. + is_new = False
  247. + except exception.MetadataNotFound:
  248. metadata_ref = {}
  249. + is_new = True
  250. roles = set(metadata_ref.get('roles', []))
  251. if role_id not in roles:
  252. msg = 'Cannot remove role that has not been granted, %s' % role_id
  253. @@ -292,14 +344,15 @@ class Identity(sql.Base, identity.Driver):
  254. roles.remove(role_id)
  255. metadata_ref['roles'] = list(roles)
  256. - if not is_new:
  257. - self.update_metadata(user_id, tenant_id, metadata_ref)
  258. - else:
  259. + if is_new:
  260. self.create_metadata(user_id, tenant_id, metadata_ref)
  261. + else:
  262. + self.update_metadata(user_id, tenant_id, metadata_ref)
  263. # CRUD
  264. @handle_conflicts(type='user')
  265. def create_user(self, user_id, user):
  266. + user['name'] = clean.user_name(user['name'])
  267. user = _ensure_hashed_password(user)
  268. session = self.get_session()
  269. with session.begin():
  270. @@ -310,9 +363,15 @@ class Identity(sql.Base, identity.Driver):
  271. @handle_conflicts(type='user')
  272. def update_user(self, user_id, user):
  273. + if 'name' in user:
  274. + user['name'] = clean.user_name(user['name'])
  275. session = self.get_session()
  276. + if 'id' in user and user_id != user['id']:
  277. + raise exception.ValidationError('Cannot change user ID')
  278. with session.begin():
  279. user_ref = session.query(User).filter_by(id=user_id).first()
  280. + if user_ref is None:
  281. + raise exception.UserNotFound(user_id=user_id)
  282. old_user_dict = user_ref.to_dict()
  283. user = _ensure_hashed_password(user)
  284. for k in user:
  285. @@ -326,21 +385,17 @@ class Identity(sql.Base, identity.Driver):
  286. def delete_user(self, user_id):
  287. session = self.get_session()
  288. - user_ref = session.query(User).filter_by(id=user_id).first()
  289. - membership_refs = session.query(UserTenantMembership)\
  290. - .filter_by(user_id=user_id)\
  291. - .all()
  292. -
  293. with session.begin():
  294. - if membership_refs:
  295. - for membership_ref in membership_refs:
  296. - session.delete(membership_ref)
  297. -
  298. - session.delete(user_ref)
  299. - session.flush()
  300. + session.query(UserTenantMembership)\
  301. + .filter_by(user_id=user_id).delete(False)
  302. + session.query(Metadata)\
  303. + .filter_by(user_id=user_id).delete(False)
  304. + if not session.query(User).filter_by(id=user_id).delete(False):
  305. + raise exception.UserNotFound(user_id=user_id)
  306. @handle_conflicts(type='tenant')
  307. def create_tenant(self, tenant_id, tenant):
  308. + tenant['name'] = clean.tenant_name(tenant['name'])
  309. session = self.get_session()
  310. with session.begin():
  311. tenant_ref = Tenant.from_dict(tenant)
  312. @@ -350,9 +405,13 @@ class Identity(sql.Base, identity.Driver):
  313. @handle_conflicts(type='tenant')
  314. def update_tenant(self, tenant_id, tenant):
  315. + if 'name' in tenant:
  316. + tenant['name'] = clean.tenant_name(tenant['name'])
  317. session = self.get_session()
  318. with session.begin():
  319. tenant_ref = session.query(Tenant).filter_by(id=tenant_id).first()
  320. + if tenant_ref is None:
  321. + raise exception.TenantNotFound(tenant_id=tenant_id)
  322. old_tenant_dict = tenant_ref.to_dict()
  323. for k in tenant:
  324. old_tenant_dict[k] = tenant[k]
  325. @@ -365,10 +424,13 @@ class Identity(sql.Base, identity.Driver):
  326. def delete_tenant(self, tenant_id):
  327. session = self.get_session()
  328. - tenant_ref = session.query(Tenant).filter_by(id=tenant_id).first()
  329. with session.begin():
  330. - session.delete(tenant_ref)
  331. - session.flush()
  332. + session.query(UserTenantMembership)\
  333. + .filter_by(tenant_id=tenant_id).delete(False)
  334. + session.query(Metadata)\
  335. + .filter_by(tenant_id=tenant_id).delete(False)
  336. + if not session.query(Tenant).filter_by(id=tenant_id).delete(False):
  337. + raise exception.TenantNotFound(tenant_id=tenant_id)
  338. @handle_conflicts(type='metadata')
  339. def create_metadata(self, user_id, tenant_id, metadata):
  340. @@ -412,6 +474,8 @@ class Identity(sql.Base, identity.Driver):
  341. session = self.get_session()
  342. with session.begin():
  343. role_ref = session.query(Role).filter_by(id=role_id).first()
  344. + if role_ref is None:
  345. + raise exception.RoleNotFound(role_id=role_id)
  346. for k in role:
  347. role_ref[k] = role[k]
  348. session.flush()
  349. @@ -419,6 +483,7 @@ class Identity(sql.Base, identity.Driver):
  350. def delete_role(self, role_id):
  351. session = self.get_session()
  352. - role_ref = session.query(Role).filter_by(id=role_id).first()
  353. with session.begin():
  354. - session.delete(role_ref)
  355. + if not session.query(Role).filter_by(id=role_id).delete():
  356. + raise exception.RoleNotFound(role_id=role_id)
  357. + 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 copyimport functools-from keystone import identity
-from keystone import exception
+from keystone import cleanfrom keystone.common import sql
-from keystone.common import utilsfrom keystone.common.sql import migration
+from keystone.common import utils
+from keystone import exception
+from keystone import identitydef _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 wrapperreturn 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 interfacedef 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.datadef 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_refdef 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 APIdef 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 = Trueroles = 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 = Trueroles = 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?
  1. [ugyn@localhost keystone]$ git diff 2012.1:keystone/contrib/ec2/backends/sql.py 2012.2:keystone/contrib/ec2/backends/sql.py > tables_diff
  2. [ugyn@localhost keystone]$ cat tables_diff
  3. diff --git a/2012.1:keystone/contrib/ec2/backends/sql.py b/2012.2:keystone/contrib/ec2/backends/sql.py
  4. index d84c381..c3af464 100644
  5. --- a/2012.1:keystone/contrib/ec2/backends/sql.py
  6. +++ b/2012.2:keystone/contrib/ec2/backends/sql.py
  7. @@ -15,7 +15,6 @@
  8. # under the License.
  9. from keystone.common import sql
  10. -from keystone.common.sql import migration
  11. 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 migrationclass Ec2Credential(sql.ModelBase, sql.DictBase):

token表的差异

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

[plain] view plaincopyprint?
  1. [ugyn@localhost keystone]$ git diff 2012.1:keystone/token/backends/sql.py 2012.2:keystone/token/backends/sql.py > tables_diff
  2. [ugyn@localhost keystone]$ cat tables_diff
  3. diff --git a/2012.1:keystone/token/backends/sql.py b/2012.2:keystone/token/backends/sql.py
  4. index 7a9a551..02e8947 100644
  5. --- a/2012.1:keystone/token/backends/sql.py
  6. +++ b/2012.2:keystone/token/backends/sql.py
  7. @@ -16,9 +16,12 @@
  8. import copy
  9. import datetime
  10. +import hashlib
  11. +from keystone.common import cms
  12. from keystone.common import sql
  13. from keystone import exception
  14. +from keystone.openstack.common import timeutils
  15. from keystone import token
  16. @@ -27,6 +30,7 @@ class TokenModel(sql.ModelBase, sql.DictBase):
  17. id = sql.Column(sql.String(64), primary_key=True)
  18. expires = sql.Column(sql.DateTime(), default=None)
  19. extra = sql.Column(sql.JsonBlob())
  20. + valid = sql.Column(sql.Boolean(), default=True)
  21. @classmethod
  22. def from_dict(cls, token_dict):
  23. @@ -49,21 +53,31 @@ class Token(sql.Base, token.Driver):
  24. # Public interface
  25. def get_token(self, token_id):
  26. session = self.get_session()
  27. - token_ref = session.query(TokenModel).filter_by(id=token_id).first()
  28. + token_ref = session.query(TokenModel)\
  29. + .filter_by(id=self.token_to_key(token_id),
  30. + valid=True).first()
  31. now = datetime.datetime.utcnow()
  32. if token_ref and (not token_ref.expires or now < token_ref.expires):
  33. return token_ref.to_dict()
  34. else:
  35. raise exception.TokenNotFound(token_id=token_id)
  36. + def token_to_key(self, token_id):
  37. + if len(token_id) > cms.UUID_TOKEN_LENGTH:
  38. + hash = hashlib.md5()
  39. + hash.update(token_id)
  40. + return hash.hexdigest()
  41. + else:
  42. + return token_id
  43. +
  44. def create_token(self, token_id, data):
  45. data_copy = copy.deepcopy(data)
  46. if 'expires' not in data_copy:
  47. data_copy['expires'] = self._get_default_expire_time()
  48. token_ref = TokenModel.from_dict(data_copy)
  49. - token_ref.id = token_id
  50. -
  51. + token_ref.id = self.token_to_key(token_id)
  52. + token_ref.valid = True
  53. session = self.get_session()
  54. with session.begin():
  55. session.add(token_ref)
  56. @@ -72,12 +86,45 @@ class Token(sql.Base, token.Driver):
  57. def delete_token(self, token_id):
  58. session = self.get_session()
  59. - token_ref = session.query(TokenModel)\
  60. - .filter_by(id=token_id)\
  61. - .first()
  62. - if not token_ref:
  63. - raise exception.TokenNotFound(token_id=token_id)
  64. -
  65. + key = self.token_to_key(token_id)
  66. with session.begin():
  67. - session.delete(token_ref)
  68. + token_ref = session.query(TokenModel).filter_by(id=key,
  69. + valid=True).first()
  70. + if not token_ref:
  71. + raise exception.TokenNotFound(token_id=token_id)
  72. + token_ref.valid = False
  73. session.flush()
  74. +
  75. + def list_tokens(self, user_id, tenant_id=None):
  76. + session = self.get_session()
  77. + tokens = []
  78. + now = timeutils.utcnow()
  79. + for token_ref in session.query(TokenModel)\
  80. + .filter(TokenModel.expires > now)\
  81. + .filter_by(valid=True):
  82. + token_ref_dict = token_ref.to_dict()
  83. + if 'user' not in token_ref_dict:
  84. + continue
  85. + if token_ref_dict['user'].get('id') != user_id:
  86. + continue
  87. + if tenant_id is not None:
  88. + if 'tenant' not in token_ref_dict:
  89. + continue
  90. + if token_ref_dict['tenant'].get('id') != tenant_id:
  91. + continue
  92. + tokens.append(token_ref['id'])
  93. + return tokens
  94. +
  95. + def list_revoked_tokens(self):
  96. + session = self.get_session()
  97. + tokens = []
  98. + now = timeutils.utcnow()
  99. + for token_ref in session.query(TokenModel)\
  100. + .filter(TokenModel.expires > now)\
  101. + .filter_by(valid=False):
  102. + record = {
  103. + 'id': token_ref['id'],
  104. + 'expires': token_ref['expires'],
  105. + }
  106. + tokens.append(record)
  107. + 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 copyimport datetime
+import hashlib+from keystone.common import cmsfrom keystone.common import sqlfrom keystone import exception
+from keystone.openstack.common import timeutilsfrom 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)@classmethoddef from_dict(cls, token_dict):
@@ -49,21 +53,31 @@ class Token(sql.Base, token.Driver):# Public interfacedef 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 = Truesession = 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 = Falsesession.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?
  1. [ugyn@localhost keystone]$ git diff 2012.1:etc/keystone.conf 2012.2:etc/keystone.conf.sample > tables_diff
  2. [ugyn@localhost keystone]$ cat tables_diff
  3. diff --git a/2012.1:etc/keystone.conf b/2012.2:etc/keystone.conf.sample
  4. index 3ecf641..1d48676 100644
  5. --- a/2012.1:etc/keystone.conf
  6. +++ b/2012.2:etc/keystone.conf.sample
  7. @@ -1,53 +1,128 @@
  8. [DEFAULT]
  9. -#bind_host = 0.0.0.0
  10. -public_port = 5000
  11. -admin_port = 35357
  12. -admin_token = ADMIN
  13. -compute_port = 8774
  14. -verbose = True
  15. -debug = True
  16. -#log_config = ./etc/logging.conf.sample
  17. -
  18. -# ================= Syslog Options ============================
  19. -# Send logs to syslog (/dev/log) instead of to file specified
  20. -# by `log-file`
  21. -use_syslog = False
  22. -
  23. -# Facility to use. If unset defaults to LOG_USER.
  24. -# syslog_log_facility = LOG_LOCAL0
  25. +# A "shared secret" between keystone and other openstack services
  26. +# admin_token = ADMIN
  27. +
  28. +# The IP address of the network interface to listen on
  29. +# bind_host = 0.0.0.0
  30. +
  31. +# The port number which the public service listens on
  32. +# public_port = 5000
  33. +
  34. +# The port number which the public admin listens on
  35. +# admin_port = 35357
  36. +
  37. +# The port number which the OpenStack Compute service listens on
  38. +# compute_port = 8774
  39. +
  40. +# === Logging Options ===
  41. +# Print debugging output
  42. +# verbose = False
  43. +
  44. +# Print more verbose output
  45. +# (includes plaintext request logging, potentially including passwords)
  46. +# debug = False
  47. +
  48. +# Name of log file to output to. If not set, logging will go to stdout.
  49. +# log_file = keystone.log
  50. +
  51. +# The directory to keep log files in (will be prepended to --logfile)
  52. +# log_dir = /var/log/keystone
  53. +
  54. +# Use syslog for logging.
  55. +# use_syslog = False
  56. +
  57. +# syslog facility to receive log lines
  58. +# syslog_log_facility = LOG_USER
  59. +
  60. +# If this option is specified, the logging configuration file specified is
  61. +# used and overrides any other logging options specified. Please see the
  62. +# Python logging module documentation for details on logging configuration
  63. +# files.
  64. +# log_config = logging.conf
  65. +
  66. +# A logging.Formatter log message format string which may use any of the
  67. +# available logging.LogRecord attributes.
  68. +# log_format = %(asctime)s %(levelname)8s [%(name)s] %(message)s
  69. +
  70. +# Format string for %(asctime)s in log records.
  71. +# log_date_format = %Y-%m-%d %H:%M:%S
  72. +
  73. +# onready allows you to send a notification when the process is ready to serve
  74. +# For example, to have it notify using systemd, one could set shell command:
  75. +# onready = systemd-notify --ready
  76. +# or a module with notify() method:
  77. +# onready = keystone.common.systemd
  78. [sql]
  79. -connection = sqlite:///keystone.db
  80. -idle_timeout = 200
  81. +# The SQLAlchemy connection string used to connect to the database
  82. +# connection = sqlite:///keystone.db
  83. -[ldap]
  84. -#url = ldap://localhost
  85. -#tree_dn = dc=example,dc=com
  86. -#user_tree_dn = ou=Users,dc=example,dc=com
  87. -#role_tree_dn = ou=Roles,dc=example,dc=com
  88. -#tenant_tree_dn = ou=Groups,dc=example,dc=com
  89. -#user = dc=Manager,dc=example,dc=com
  90. -#password = freeipa4all
  91. -#suffix = cn=example,cn=com
  92. +# the timeout before idle sql connections are reaped
  93. +# idle_timeout = 200
  94. [identity]
  95. -driver = keystone.identity.backends.sql.Identity
  96. +# driver = keystone.identity.backends.sql.Identity
  97. [catalog]
  98. -driver = keystone.catalog.backends.templated.TemplatedCatalog
  99. -template_file = ./etc/default_catalog.templates
  100. +# dynamic, sql-based backend (supports API/CLI-based management commands)
  101. +# driver = keystone.catalog.backends.sql.Catalog
  102. +
  103. +# static, file-based backend (does *NOT* support any management commands)
  104. +# driver = keystone.catalog.backends.templated.TemplatedCatalog
  105. +
  106. +# template_file = default_catalog.templates
  107. [token]
  108. -driver = keystone.token.backends.kvs.Token
  109. +# driver = keystone.token.backends.kvs.Token
  110. # Amount of time a token should remain valid (in seconds)
  111. -expiration = 86400
  112. +# expiration = 86400
  113. [policy]
  114. -driver = keystone.policy.backends.rules.Policy
  115. +# driver = keystone.policy.backends.rules.Policy
  116. [ec2]
  117. -driver = keystone.contrib.ec2.backends.kvs.Ec2
  118. +# driver = keystone.contrib.ec2.backends.kvs.Ec2
  119. +
  120. +[ssl]
  121. +#enable = True
  122. +#certfile = /etc/keystone/ssl/certs/keystone.pem
  123. +#keyfile = /etc/keystone/ssl/private/keystonekey.pem
  124. +#ca_certs = /etc/keystone/ssl/certs/ca.pem
  125. +#cert_required = True
  126. +
  127. +[signing]
  128. +#token_format = UUID
  129. +#certfile = /etc/keystone/ssl/certs/signing_cert.pem
  130. +#keyfile = /etc/keystone/ssl/private/signing_key.pem
  131. +#ca_certs = /etc/keystone/ssl/certs/ca.pem
  132. +#key_size = 1024
  133. +#valid_days = 3650
  134. +#ca_password = None
  135. +#token_format = PKI
  136. +
  137. +[ldap]
  138. +# url = ldap://localhost
  139. +# user = dc=Manager,dc=example,dc=com
  140. +# password = None
  141. +# suffix = cn=example,cn=com
  142. +# use_dumb_member = False
  143. +
  144. +# user_tree_dn = ou=Users,dc=example,dc=com
  145. +# user_objectclass = inetOrgPerson
  146. +# user_id_attribute = cn
  147. +# user_name_attribute = sn
  148. +
  149. +# tenant_tree_dn = ou=Groups,dc=example,dc=com
  150. +# tenant_objectclass = groupOfNames
  151. +# tenant_id_attribute = cn
  152. +# tenant_member_attribute = member
  153. +# tenant_name_attribute = ou
  154. +
  155. +# role_tree_dn = ou=Roles,dc=example,dc=com
  156. +# role_objectclass = organizationalRole
  157. +# role_id_attribute = cn
  158. +# role_member_attribute = roleOccupant
  159. [filter:debug]
  160. paste.filter_factory = keystone.common.wsgi:Debug.factory
  161. @@ -64,12 +139,27 @@ paste.filter_factory = keystone.middleware:XmlBodyMiddleware.factory
  162. [filter:json_body]
  163. paste.filter_factory = keystone.middleware:JsonBodyMiddleware.factory
  164. +[filter:user_crud_extension]
  165. +paste.filter_factory = keystone.contrib.user_crud:CrudExtension.factory
  166. +
  167. [filter:crud_extension]
  168. paste.filter_factory = keystone.contrib.admin_crud:CrudExtension.factory
  169. [filter:ec2_extension]
  170. paste.filter_factory = keystone.contrib.ec2:Ec2Extension.factory
  171. +[filter:s3_extension]
  172. +paste.filter_factory = keystone.contrib.s3:S3Extension.factory
  173. +
  174. +[filter:url_normalize]
  175. +paste.filter_factory = keystone.middleware:NormalizingFilter.factory
  176. +
  177. +[filter:stats_monitoring]
  178. +paste.filter_factory = keystone.contrib.stats:StatsMiddleware.factory
  179. +
  180. +[filter:stats_reporting]
  181. +paste.filter_factory = keystone.contrib.stats:StatsExtension.factory
  182. +
  183. [app:public_service]
  184. paste.app_factory = keystone.service:public_app_factory
  185. @@ -77,10 +167,10 @@ paste.app_factory = keystone.service:public_app_factory
  186. paste.app_factory = keystone.service:admin_app_factory
  187. [pipeline:public_api]
  188. -pipeline = token_auth admin_token_auth xml_body json_body debug ec2_extension public_service
  189. +pipeline = stats_monitoring url_normalize token_auth admin_token_auth xml_body json_body debug ec2_extension user_crud_extension public_service
  190. [pipeline:admin_api]
  191. -pipeline = token_auth admin_token_auth xml_body json_body debug ec2_extension crud_extension admin_service
  192. +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
  193. [app:public_version_service]
  194. paste.app_factory = keystone.service:public_version_app_factory
  195. @@ -89,10 +179,10 @@ paste.app_factory = keystone.service:public_version_app_factory
  196. paste.app_factory = keystone.service:admin_version_app_factory
  197. [pipeline:public_version_api]
  198. -pipeline = xml_body public_version_service
  199. +pipeline = stats_monitoring url_normalize xml_body public_version_service
  200. [pipeline:admin_version_api]
  201. -pipeline = xml_body admin_version_service
  202. +pipeline = stats_monitoring url_normalize xml_body admin_version_service
  203. [composite:main]
  204. use = egg:Paste#urlmap

转载于:https://www.cnblogs.com/wencangz/archive/2012/11/07/2758937.html

OpenStack版本比较之Keystone相关推荐

  1. (原创)OpenStack服务如何使用Keystone(三)---详细配置Keystone中间件

    (一)Keystone端的操作 (二)如何在OpenStack服务上部署Keystone中间件 (三)详细配置keystonemiddleware 前文我们介绍了如何部署Keystone中间件以及中间 ...

  2. OpenStack实践系列②认证服务Keystone

    OpenStack实践系列②认证服务Keystone 三.实战OpenStack之控制节点 3.1 CentOS7的时间同步服务器chrony 下载chrony # yum install -y ch ...

  3. 查看OpenStack版本

    可以根据nova版本反查openstack版本 1.查看nova版本 cd /usr/lib/python2.7/dist-packagesls -a | grep nova 我的是这样的,nova版 ...

  4. OpenStack(二)——Keystone组件

    OpenStack(二)--Keystone组件 一.OpenStack组件之间的通信关系 二.OpenStack物理构架 三.Keystone组件 1.Keystone身份服务 2.管理对象 3.K ...

  5. OpenStack集群部署——Keystone部署(二)

    三.Keyston-认证服务 3.1 Keyston介绍 Keyston介绍 补充 3.2 安装时间同步器 ---------------------------------------------- ...

  6. Openstack组件实现原理 — Keystone认证功能

    前言 Keystone实现始终围绕着Keystone所实现的功能来展开,所以在理解其实现之前,建议大家尝试通过安装Keystone这一个过程来感受Keystone在Openstack架构中所充当的角色 ...

  7. 【原创-长文】openstack 版本D安装配置及本次安装中遇到的问题

    openstack配置 一.硬件及操作系统要求 硬件:IBM服务器R410 两台.网线.显示器.键盘若干,100M光纤(硬性要求) 操作系统:两台服务器均安装Ubuntu server 12.04 L ...

  8. OpenStack手动分布式部署Keystone【Queens版】

    目录 Keystone简介 1.登录数据库配置(在controller执行) 1.1登录数据库 1.2数据库里创建keystone 1.3授权对keystone数据库的正确访问 1.4退出数据库 2. ...

  9. OpenStack版本 - 发布时间 - 及其组件

    Austin(2010.10):Nova.Glance.Keystone.Horizon.Swift.Quantum Bexar(2011.02):Nova.Glance.Keystone.Horiz ...

最新文章

  1. 常用Intent调用(摘自网络)
  2. javascript 将内容复制到剪贴板
  3. P2408 不同子串个数
  4. Kali Linux 自定义分辨率
  5. button标签设置隐藏和显示_让你更高效的功能——设置、预设的妙用【动态数学软件GeoGebra】...
  6. web与app开发java_移动web开发和移动app开发的区分
  7. excel转html不显示图片,excel表格怎么把0显示出来/为什么在excel插入背景后预览看不到图片...
  8. 大学生计算机系统推荐,上大学买电脑?最适合大学生学习用的Windows笔记本电脑推荐!...
  9. 前端面试题 ~ 移动端注意
  10. 我做了一款iOS12捷径市场,也许是目前最好看也是最具技术含量的ShortCuts小程序
  11. 虚拟机介绍与使用(VMware Workstation)
  12. jmap常用命令以及dump文件查看
  13. 爬虫内容学习-工具类---xpath-1
  14. 项目4:后台管理的开发和使用(前端)
  15. 小萌库- 新海诚那些唯美感人的动漫
  16. VR全景在线虚拟展厅实现全方位沉浸式互动体验
  17. Mysql数据库经验总结
  18. 设置邮件规则,轻松整理你的收件箱!
  19. Kepware通过OPCUA链接WinCC(Kepware作为客户端)
  20. CoreAnimation动画(CALayer动画)

热门文章

  1. 06-02 Jenkins job 机制
  2. 金融数据分析与挖掘实战练习-1.9
  3. 零基础学web前端难吗?新手该怎么学?
  4. 现在转行前端,该怎么学习呢?怎么学好基础html、css、js
  5. 如何成为一名Web前端开发人员?入行学习完整指南
  6. 我可能不适合做前端,你呢?
  7. java des 0填充方式_DES填充方式与初始向量IV的作用
  8. 前窗玻璃膜贴了一周还有气泡_汽车玻璃膜贴全部好还是贴局部好?
  9. 业余学python 树莓派_厉害了!小伙自学Python一个月,利用树莓派制作了黑客优盘工具!...
  10. php 类自动执行,php类中的魔术方法及类的自动加载