diff options
-rw-r--r-- | nova/db/api.py | 126 | ||||
-rw-r--r-- | nova/db/sqlalchemy/api.py | 239 | ||||
-rw-r--r-- | nova/db/sqlalchemy/migrate_repo/versions/109_drop_dns_domains_project_id_fkey.py | 64 | ||||
-rw-r--r-- | nova/db/sqlalchemy/migrate_repo/versions/109_sqlite_downgrade.sql | 53 | ||||
-rw-r--r-- | nova/db/sqlalchemy/migrate_repo/versions/109_sqlite_upgrade.sql | 52 | ||||
-rw-r--r-- | nova/db/sqlalchemy/migrate_repo/versions/110_drop_deprecated_auth.py | 189 | ||||
-rw-r--r-- | nova/db/sqlalchemy/models.py | 81 |
7 files changed, 358 insertions, 446 deletions
diff --git a/nova/db/api.py b/nova/db/api.py index fd4babb55..be1a20a10 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -905,29 +905,6 @@ def iscsi_target_create_safe(context, values): ############### -def auth_token_destroy(context, token_id): - """Destroy an auth token.""" - return IMPL.auth_token_destroy(context, token_id) - - -def auth_token_get(context, token_hash): - """Retrieves a token given the hash representing it.""" - return IMPL.auth_token_get(context, token_hash) - - -def auth_token_update(context, token_hash, values): - """Updates a token given the hash representing it.""" - return IMPL.auth_token_update(context, token_hash, values) - - -def auth_token_create(context, token): - """Creates a new token.""" - return IMPL.auth_token_create(context, token) - - -################### - - def quota_create(context, project_id, resource, limit): """Create a quota for the given project and resource.""" return IMPL.quota_create(context, project_id, resource, limit) @@ -1370,109 +1347,6 @@ def provider_fw_rule_destroy(context, rule_id): ################### -def user_get(context, id): - """Get user by id.""" - return IMPL.user_get(context, id) - - -def user_get_by_access_key(context, access_key): - """Get user by access key.""" - return IMPL.user_get_by_access_key(context, access_key) - - -def user_create(context, values): - """Create a new user.""" - return IMPL.user_create(context, values) - - -def user_delete(context, id): - """Delete a user.""" - return IMPL.user_delete(context, id) - - -def user_get_all(context): - """Create a new user.""" - return IMPL.user_get_all(context) - - -def user_add_role(context, user_id, role): - """Add another global role for user.""" - return IMPL.user_add_role(context, user_id, role) - - -def user_remove_role(context, user_id, role): - """Remove global role from user.""" - return IMPL.user_remove_role(context, user_id, role) - - -def user_get_roles(context, user_id): - """Get global roles for user.""" - return IMPL.user_get_roles(context, user_id) - - -def user_add_project_role(context, user_id, project_id, role): - """Add project role for user.""" - return IMPL.user_add_project_role(context, user_id, project_id, role) - - -def user_remove_project_role(context, user_id, project_id, role): - """Remove project role from user.""" - return IMPL.user_remove_project_role(context, user_id, project_id, role) - - -def user_get_roles_for_project(context, user_id, project_id): - """Return list of roles a user holds on project.""" - return IMPL.user_get_roles_for_project(context, user_id, project_id) - - -def user_update(context, user_id, values): - """Update user.""" - return IMPL.user_update(context, user_id, values) - - -################### - - -def project_get(context, id): - """Get project by id.""" - return IMPL.project_get(context, id) - - -def project_create(context, values): - """Create a new project.""" - return IMPL.project_create(context, values) - - -def project_add_member(context, project_id, user_id): - """Add user to project.""" - return IMPL.project_add_member(context, project_id, user_id) - - -def project_get_all(context): - """Get all projects.""" - return IMPL.project_get_all(context) - - -def project_get_by_user(context, user_id): - """Get all projects of which the given user is a member.""" - return IMPL.project_get_by_user(context, user_id) - - -def project_remove_member(context, project_id, user_id): - """Remove the given user from the given project.""" - return IMPL.project_remove_member(context, project_id, user_id) - - -def project_update(context, project_id, values): - """Update Remove the given user from the given project.""" - return IMPL.project_update(context, project_id, values) - - -def project_delete(context, project_id): - """Delete project.""" - return IMPL.project_delete(context, project_id) - - def project_get_networks(context, project_id, associate=True): """Return the network associated with the project. diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 54d70b14f..597e4f835 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2296,46 +2296,6 @@ def iscsi_target_create_safe(context, values): ################### -@require_admin_context -def auth_token_destroy(context, token_id): - session = get_session() - with session.begin(): - token_ref = auth_token_get(context, token_id, session=session) - token_ref.delete(session=session) - - -@require_admin_context -def auth_token_get(context, token_hash, session=None): - result = model_query(context, models.AuthToken, session=session).\ - filter_by(token_hash=token_hash).\ - first() - - if not result: - raise exception.AuthTokenNotFound(token=token_hash) - - return result - - -@require_admin_context -def auth_token_update(context, token_hash, values): - session = get_session() - with session.begin(): - token_ref = auth_token_get(context, token_hash, session=session) - token_ref.update(values) - token_ref.save(session=session) - - -@require_admin_context -def auth_token_create(context, token): - tk = models.AuthToken() - tk.update(token) - tk.save() - return tk - - -################### - - @require_context def quota_get(context, project_id, resource, session=None): result = model_query(context, models.Quota, session=session, @@ -3614,205 +3574,6 @@ def provider_fw_rule_destroy(context, rule_id): ################### -@require_admin_context -def user_get(context, id, session=None): - result = model_query(context, models.User, session=session).\ - filter_by(id=id).\ - first() - - if not result: - raise exception.UserNotFound(user_id=id) - - return result - - -@require_admin_context -def user_get_by_access_key(context, access_key, session=None): - result = model_query(context, models.User, session=session).\ - filter_by(access_key=access_key).\ - first() - - if not result: - raise exception.AccessKeyNotFound(access_key=access_key) - - return result - - -@require_admin_context -def user_create(context, values): - user_ref = models.User() - user_ref.update(values) - user_ref.save() - return user_ref - - -@require_admin_context -def user_delete(context, id): - session = get_session() - with session.begin(): - session.query(models.UserProjectAssociation).\ - filter_by(user_id=id).\ - delete() - session.query(models.UserRoleAssociation).\ - filter_by(user_id=id).\ - delete() - session.query(models.UserProjectRoleAssociation).\ - filter_by(user_id=id).\ - delete() - user_ref = user_get(context, id, session=session) - session.delete(user_ref) - - -def user_get_all(context): - return model_query(context, models.User).all() - - -def user_get_roles(context, user_id): - session = get_session() - with session.begin(): - user_ref = user_get(context, user_id, session=session) - return [role.role for role in user_ref['roles']] - - -def user_get_roles_for_project(context, user_id, project_id): - session = get_session() - with session.begin(): - res = session.query(models.UserProjectRoleAssociation).\ - filter_by(user_id=user_id).\ - filter_by(project_id=project_id).\ - all() - return [association.role for association in res] - - -def user_remove_project_role(context, user_id, project_id, role): - session = get_session() - with session.begin(): - session.query(models.UserProjectRoleAssociation).\ - filter_by(user_id=user_id).\ - filter_by(project_id=project_id).\ - filter_by(role=role).\ - delete() - - -def user_remove_role(context, user_id, role): - session = get_session() - with session.begin(): - res = session.query(models.UserRoleAssociation).\ - filter_by(user_id=user_id).\ - filter_by(role=role).\ - all() - for role in res: - session.delete(role) - - -def user_add_role(context, user_id, role): - session = get_session() - with session.begin(): - user_ref = user_get(context, user_id, session=session) - models.UserRoleAssociation(user=user_ref, role=role).\ - save(session=session) - - -def user_add_project_role(context, user_id, project_id, role): - session = get_session() - with session.begin(): - user_ref = user_get(context, user_id, session=session) - project_ref = project_get(context, project_id, session=session) - models.UserProjectRoleAssociation(user_id=user_ref['id'], - project_id=project_ref['id'], - role=role).save(session=session) - - -def user_update(context, user_id, values): - session = get_session() - with session.begin(): - user_ref = user_get(context, user_id, session=session) - user_ref.update(values) - user_ref.save(session=session) - - -################### - - -def project_create(context, values): - project_ref = models.Project() - project_ref.update(values) - project_ref.save() - return project_ref - - -def project_add_member(context, project_id, user_id): - session = get_session() - with session.begin(): - project_ref = project_get(context, project_id, session=session) - user_ref = user_get(context, user_id, session=session) - - project_ref.members += [user_ref] - project_ref.save(session=session) - - -def project_get(context, id, session=None): - result = model_query(context, models.Project, session=session, - read_deleted="no").\ - filter_by(id=id).\ - options(joinedload_all('members')).\ - first() - - if not result: - raise exception.ProjectNotFound(project_id=id) - - return result - - -def project_get_all(context): - return model_query(context, models.Project).\ - options(joinedload_all('members')).\ - all() - - -def project_get_by_user(context, user_id): - user = model_query(context, models.User).\ - filter_by(id=user_id).\ - options(joinedload_all('projects')).\ - first() - - if not user: - raise exception.UserNotFound(user_id=user_id) - - return user.projects - - -def project_remove_member(context, project_id, user_id): - session = get_session() - project = project_get(context, project_id, session=session) - user = user_get(context, user_id, session=session) - - if user in project.members: - project.members.remove(user) - project.save(session=session) - - -def project_update(context, project_id, values): - session = get_session() - with session.begin(): - project_ref = project_get(context, project_id, session=session) - project_ref.update(values) - project_ref.save(session=session) - - -def project_delete(context, id): - session = get_session() - with session.begin(): - session.query(models.UserProjectAssociation).\ - filter_by(project_id=id).\ - delete() - session.query(models.UserProjectRoleAssociation).\ - filter_by(project_id=id).\ - delete() - project_ref = project_get(context, id, session=session) - session.delete(project_ref) - - @require_context def project_get_networks(context, project_id, associate=True): # NOTE(tr3buchet): as before this function will associate diff --git a/nova/db/sqlalchemy/migrate_repo/versions/109_drop_dns_domains_project_id_fkey.py b/nova/db/sqlalchemy/migrate_repo/versions/109_drop_dns_domains_project_id_fkey.py new file mode 100644 index 000000000..16489b068 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/109_drop_dns_domains_project_id_fkey.py @@ -0,0 +1,64 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2012 OpenStack LLC. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from migrate import ForeignKeyConstraint +from sqlalchemy import MetaData, Table +from sqlalchemy import Column, ForeignKey, Integer + +from nova.openstack.common import log as logging + + +LOG = logging.getLogger(__name__) + + +def upgrade(migrate_engine): + meta = MetaData() + meta.bind = migrate_engine + + dns_domains = Table('dns_domains', meta, autoload=True) + projects = Table('projects', meta, autoload=True) + + fkeys = list(dns_domains.c.project_id.foreign_keys) + if fkeys: + try: + fkey_name = fkeys[0].constraint.name + ForeignKeyConstraint( + columns=[dns_domains.c.project_id], + refcolumns=[projects.c.id], + name=fkey_name).drop() + except Exception: + LOG.error(_("foreign key constraint couldn't be removed")) + raise + + +def downgrade(migrate_engine): + meta = MetaData() + meta.bind = migrate_engine + + dns_domains = Table('dns_domains', meta, autoload=True) + projects = Table('projects', meta, autoload=True) + + kwargs = { + 'columns': [dns_domains.c.project_id], + 'refcolumns': [projects.c.id], + } + + if migrate_engine.name == 'mysql': + # For MySQL we name our fkeys explicitly so they match Essex + kwargs['name'] = 'dns_domains_ibfk_1' + + ForeignKeyConstraint(**kwargs).create() diff --git a/nova/db/sqlalchemy/migrate_repo/versions/109_sqlite_downgrade.sql b/nova/db/sqlalchemy/migrate_repo/versions/109_sqlite_downgrade.sql new file mode 100644 index 000000000..ffb4d132e --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/109_sqlite_downgrade.sql @@ -0,0 +1,53 @@ +BEGIN TRANSACTION; + CREATE TEMPORARY TABLE dns_domains_backup ( + created_at DATETIME, + updated_at DATETIME, + deleted_at DATETIME, + deleted BOOLEAN, + domain VARCHAR(512) NOT NULL, + scope VARCHAR(255), + availability_zone VARCHAR(255), + project_id VARCHAR(255), + PRIMARY KEY (domain) + ); + + INSERT INTO dns_domains_backup + SELECT created_at, + updated_at, + deleted_at, + deleted, + domain, + scope, + availability_zone, + project_id + FROM dns_domains; + + DROP TABLE dns_domains; + + CREATE TABLE dns_domains ( + created_at DATETIME, + updated_at DATETIME, + deleted_at DATETIME, + deleted BOOLEAN, + domain VARCHAR(512) NOT NULL, + scope VARCHAR(255), + availability_zone VARCHAR(255), + project_id VARCHAR(255), + PRIMARY KEY (domain), + FOREIGN KEY (project_id) REFERENCES projects (id) + ); + + INSERT INTO dns_domains + SELECT created_at, + updated_at, + deleted_at, + deleted, + domain, + scope, + availability_zone, + project_id + FROM dns_domains_backup; + + DROP TABLE dns_domains_backup; + +COMMIT; diff --git a/nova/db/sqlalchemy/migrate_repo/versions/109_sqlite_upgrade.sql b/nova/db/sqlalchemy/migrate_repo/versions/109_sqlite_upgrade.sql new file mode 100644 index 000000000..eeb481658 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/109_sqlite_upgrade.sql @@ -0,0 +1,52 @@ +BEGIN TRANSACTION; + CREATE TEMPORARY TABLE dns_domains_backup ( + created_at DATETIME, + updated_at DATETIME, + deleted_at DATETIME, + deleted BOOLEAN, + domain VARCHAR(512) NOT NULL, + scope VARCHAR(255), + availability_zone VARCHAR(255), + project_id VARCHAR(255), + PRIMARY KEY (domain) + ); + + INSERT INTO dns_domains_backup + SELECT created_at, + updated_at, + deleted_at, + deleted, + domain, + scope, + availability_zone, + project_id + FROM dns_domains; + + DROP TABLE dns_domains; + + CREATE TABLE dns_domains ( + created_at DATETIME, + updated_at DATETIME, + deleted_at DATETIME, + deleted BOOLEAN, + domain VARCHAR(512) NOT NULL, + scope VARCHAR(255), + availability_zone VARCHAR(255), + project_id VARCHAR(255), + PRIMARY KEY (domain) + ); + + INSERT INTO dns_domains + SELECT created_at, + updated_at, + deleted_at, + deleted, + domain, + scope, + availability_zone, + project_id + FROM dns_domains_backup; + + DROP TABLE dns_domains_backup; + +COMMIT; diff --git a/nova/db/sqlalchemy/migrate_repo/versions/110_drop_deprecated_auth.py b/nova/db/sqlalchemy/migrate_repo/versions/110_drop_deprecated_auth.py new file mode 100644 index 000000000..734a3729f --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/110_drop_deprecated_auth.py @@ -0,0 +1,189 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2012 OpenStack LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from migrate import ForeignKeyConstraint +from sqlalchemy import (Boolean, Column, DateTime, ForeignKey, + Index, MetaData, String, Table) + +from nova.openstack.common import log as logging + +LOG = logging.getLogger(__name__) + + +def upgrade(migrate_engine): + meta = MetaData() + meta.bind = migrate_engine + + tables = ( + "user_project_role_association", + "user_project_association", + "user_role_association", + "projects", + "users", + "auth_tokens", + ) + for table_name in tables: + Table(table_name, meta, autoload=True).drop() + + +def downgrade(migrate_engine): + meta = MetaData() + meta.bind = migrate_engine + + auth_tokens = Table('auth_tokens', meta, + Column('created_at', DateTime), + Column('updated_at', DateTime), + Column('deleted_at', DateTime), + Column('deleted', Boolean), + Column('token_hash', String(length=255), primary_key=True, + nullable=False), + Column('user_id', String(length=255)), + Column('server_management_url', String(length=255)), + Column('storage_url', String(length=255)), + Column('cdn_management_url', String(length=255)), + mysql_engine='InnoDB', + ) + + projects = Table('projects', meta, + Column('created_at', DateTime), + Column('updated_at', DateTime), + Column('deleted_at', DateTime), + Column('deleted', Boolean), + Column('id', String(length=255), primary_key=True, nullable=False), + Column('name', String(length=255)), + Column('description', String(length=255)), + Column('project_manager', String(length=255), ForeignKey('users.id')), + mysql_engine='InnoDB', + ) + + user_project_association = Table('user_project_association', meta, + Column('created_at', DateTime), + Column('updated_at', DateTime), + Column('deleted_at', DateTime), + Column('deleted', Boolean), + Column('user_id', String(length=255), primary_key=True, + nullable=False), + Column('project_id', String(length=255), primary_key=True, + nullable=False), + mysql_engine='InnoDB', + ) + + user_project_role_association = \ + Table('user_project_role_association', meta, + Column('created_at', DateTime), + Column('updated_at', DateTime), + Column('deleted_at', DateTime), + Column('deleted', Boolean), + Column('user_id', String(length=255), primary_key=True, + nullable=False), + Column('project_id', String(length=255), primary_key=True, + nullable=False), + Column('role', String(length=255), primary_key=True, nullable=False), + mysql_engine='InnoDB', + ) + + user_role_association = Table('user_role_association', meta, + Column('created_at', DateTime), + Column('updated_at', DateTime), + Column('deleted_at', DateTime), + Column('deleted', Boolean), + Column('user_id', String(length=255), ForeignKey('users.id'), + primary_key=True, nullable=False), + Column('role', String(length=255), primary_key=True, nullable=False), + mysql_engine='InnoDB', + ) + + users = Table('users', meta, + Column('created_at', DateTime), + Column('updated_at', DateTime), + Column('deleted_at', DateTime), + Column('deleted', Boolean), + Column('id', String(length=255), primary_key=True, nullable=False), + Column('name', String(length=255)), + Column('access_key', String(length=255)), + Column('secret_key', String(length=255)), + Column('is_admin', Boolean), + mysql_engine='InnoDB', + ) + + tables = [users, projects, user_project_association, + auth_tokens, user_project_role_association, + user_role_association] + + for table in tables: + try: + table.create() + except Exception: + LOG.exception('Exception while creating table.') + raise + + if migrate_engine.name == 'mysql': + index = Index('project_id', user_project_association.c.project_id) + index.create(migrate_engine) + + fkeys = [ + [ + [user_project_role_association.c.user_id, + user_project_role_association.c.project_id], + [user_project_association.c.user_id, + user_project_association.c.project_id], + 'user_project_role_association_ibfk_1', + ], + [ + [user_project_association.c.user_id], + [users.c.id], + 'user_project_association_ibfk_1', + ], + [ + [user_project_association.c.project_id], + [projects.c.id], + 'user_project_association_ibfk_2', + ], + ] + + for fkey_pair in fkeys: + if migrate_engine.name == 'mysql': + # For MySQL we name our fkeys explicitly so they match Essex + fkey = ForeignKeyConstraint(columns=fkey_pair[0], + refcolumns=fkey_pair[1], + name=fkey_pair[2]) + fkey.create() + elif migrate_engine.name == 'postgresql': + fkey = ForeignKeyConstraint(columns=fkey_pair[0], + refcolumns=fkey_pair[1]) + fkey.create() + + # Hopefully this entire loop to set the charset can go away during + # the "E" release compaction. See the notes on the dns_domains + # table above for why this is required vs. setting mysql_charset inline. + if migrate_engine.name == "mysql": + tables = [ + # tables that are FK parents, must be converted early + "projects", + "user_project_association", + "users", + # those that are children and others later + "auth_tokens", + "user_project_role_association", + "user_role_association", + ] + sql = "SET foreign_key_checks = 0;" + for table in tables: + sql += "ALTER TABLE %s CONVERT TO CHARACTER SET utf8;" % table + sql += "SET foreign_key_checks = 1;" + sql += "ALTER DATABASE %s DEFAULT CHARACTER SET utf8;" \ + % migrate_engine.url.database + migrate_engine.execute(sql) diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index d117d9361..ebcdcdde6 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -26,7 +26,6 @@ from sqlalchemy.exc import IntegrityError from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import ForeignKey, DateTime, Boolean, Text, Float from sqlalchemy.orm import relationship, backref, object_mapper -from sqlalchemy.schema import ForeignKeyConstraint from nova.db.sqlalchemy.session import get_session from nova import exception @@ -211,7 +210,6 @@ class Instance(BASE, NovaBase): # ramdisk_id = Column(Integer, ForeignKey('images.id'), nullable=True) # ramdisk = relationship(Ramdisk, backref=backref('instances', order_by=id)) # kernel = relationship(Kernel, backref=backref('instances', order_by=id)) -# project = relationship(Project, backref=backref('instances', order_by=id)) launch_index = Column(Integer) key_name = Column(String(255)) @@ -738,47 +736,6 @@ class FloatingIp(BASE, NovaBase): interface = Column(String(255)) -class AuthToken(BASE, NovaBase): - """Represents an authorization token for all API transactions. - - Fields are a string representing the actual token and a user id for - mapping to the actual user - - """ - __tablename__ = 'auth_tokens' - token_hash = Column(String(255), primary_key=True) - user_id = Column(String(255)) - server_management_url = Column(String(255)) - storage_url = Column(String(255)) - cdn_management_url = Column(String(255)) - - -class User(BASE, NovaBase): - """Represents a user.""" - __tablename__ = 'users' - id = Column(String(255), primary_key=True) - - name = Column(String(255)) - access_key = Column(String(255)) - secret_key = Column(String(255)) - - is_admin = Column(Boolean) - - -class Project(BASE, NovaBase): - """Represents a project.""" - __tablename__ = 'projects' - id = Column(String(255), primary_key=True) - name = Column(String(255)) - description = Column(String(255)) - - project_manager = Column(String(255), ForeignKey(User.id)) - - members = relationship(User, - secondary='user_project_association', - backref='projects') - - class DNSDomain(BASE, NovaBase): """Represents a DNS domain with availability zone or project info.""" __tablename__ = 'dns_domains' @@ -786,44 +743,6 @@ class DNSDomain(BASE, NovaBase): scope = Column(String(255)) availability_zone = Column(String(255)) project_id = Column(String(255)) - project = relationship(Project, - primaryjoin=project_id == Project.id, - foreign_keys=[Project.id], - uselist=False) - - -class UserProjectRoleAssociation(BASE, NovaBase): - __tablename__ = 'user_project_role_association' - user_id = Column(String(255), primary_key=True) - user = relationship(User, - primaryjoin=user_id == User.id, - foreign_keys=[User.id], - uselist=False) - - project_id = Column(String(255), primary_key=True) - project = relationship(Project, - primaryjoin=project_id == Project.id, - foreign_keys=[Project.id], - uselist=False) - - role = Column(String(255), primary_key=True) - ForeignKeyConstraint(['user_id', - 'project_id'], - ['user_project_association.user_id', - 'user_project_association.project_id']) - - -class UserRoleAssociation(BASE, NovaBase): - __tablename__ = 'user_role_association' - user_id = Column(String(255), ForeignKey('users.id'), primary_key=True) - user = relationship(User, backref='roles') - role = Column(String(255), primary_key=True) - - -class UserProjectAssociation(BASE, NovaBase): - __tablename__ = 'user_project_association' - user_id = Column(String(255), ForeignKey(User.id), primary_key=True) - project_id = Column(String(255), ForeignKey(Project.id), primary_key=True) class ConsolePool(BASE, NovaBase): |