本文主要比较OpenStack中Essex与Folsom版本的Keystone在依赖包、数据库结构、配置方面的差异,为Keystone从Essex向Folsom升级做些前期准备工作。这些比较大部分是在源代码库上通过git命令进行的,首先要clone一个keystone的本地库,命令如下:
git clone git://github.com/openstack/keystone.git cd keystone
tools/pip-requires文件包含keystone的依赖库文件信息,通过以下命令比较发现Folsom版只是添加了一个依赖包iso8601版本须>=0.1.4,该包同时也是nova E版和F版的依赖包,因此依赖包的差异可以忽略,升级Keystone可以不安装其它依赖包。
[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中与数据库相关的源文件,可通过以下命令实现:
[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)
这两个表的定义在源文件keystone/catalog/backends/sql.py中,分别为Service及Endpoint类,从下面的结果可以看出这两个类完全没变,只是在类Catalog中对它们的操作做了些修改,因此在数据库方面这两个表应该是没在差异的。
[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
这几个表的定义均在源文件keystone/identity/backends/sql.py的相关类中。从下面的输出来看,在F版中的主要变化是给表user、tenant、role的name列均添加了not null属性,另外在Identity类中对这些表的操作做了不少修改,但应该不会导致相关表的结构变化。
[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()
该表在源文件keystone/contrib/ec2/backends/sql.py文件中定义,从下面的输出来看该表没有变化。
[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):
该表在源文件keystone/token/backends/sql.py中定义,从下面的输出来看F版中该表添加了一个新的布尔列valid默认值为True。
[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
该表似乎跟Keystone进行数据库升级有关,具体差异不清楚。
从下面的示例配置文件的比较来看主要是增加了几个filter中间件
[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
北方工业大学 | 云计算研究中心 | 姜永