diff options
| author | unicell <unicell@gmail.com> | 2012-08-13 20:19:54 +0800 |
|---|---|---|
| committer | unicell <unicell@gmail.com> | 2012-08-27 23:45:05 +0800 |
| commit | 34c012c709cc5ae577330c7d67ba060293158210 (patch) | |
| tree | e04765cbb3e09e9047c51aa9e03d4b1db15ac80c /nova/db | |
| parent | 5e012d8d45935b68a5ce5d50ed043d4bb8066cf8 (diff) | |
| download | nova-34c012c709cc5ae577330c7d67ba060293158210.tar.gz nova-34c012c709cc5ae577330c7d67ba060293158210.tar.xz nova-34c012c709cc5ae577330c7d67ba060293158210.zip | |
Implement project specific flavors API
blueprint project-specific-flavors
This change implements API extension to manage project specific flavor
types, so that non-public flavor type can only see by projects with
access rights.
Change-Id: Ie2d2c605065b0c76897f843a4548a0c984a05f1a
Diffstat (limited to 'nova/db')
| -rw-r--r-- | nova/db/api.py | 15 | ||||
| -rw-r--r-- | nova/db/sqlalchemy/api.py | 78 | ||||
| -rw-r--r-- | nova/db/sqlalchemy/migrate_repo/versions/132_add_instance_type_projects.py | 67 | ||||
| -rw-r--r-- | nova/db/sqlalchemy/models.py | 16 |
4 files changed, 176 insertions, 0 deletions
diff --git a/nova/db/api.py b/nova/db/api.py index d4be854b3..f718f0047 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -1445,6 +1445,21 @@ def instance_type_destroy(context, name): return IMPL.instance_type_destroy(context, name) +def instance_type_access_get_by_flavor_id(context, flavor_id): + """Get flavor access by flavor id.""" + return IMPL.instance_type_access_get_by_flavor_id(context, flavor_id) + + +def instance_type_access_add(context, flavor_id, project_id): + """Add flavor access for project.""" + return IMPL.instance_type_access_add(context, flavor_id, project_id) + + +def instance_type_access_remove(context, flavor_id, project_id): + """Remove flavor access for project.""" + return IMPL.instance_type_access_remove(context, flavor_id, project_id) + + #################### diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index da1376c30..3f6c504ab 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -3872,6 +3872,19 @@ def instance_type_get_all(context, inactive=False, filters=None): query = query.filter( models.InstanceTypes.disabled == filters['disabled']) + if 'is_public' in filters and filters['is_public'] is not None: + the_filter = [models.InstanceTypes.is_public == filters['is_public']] + if filters['is_public'] and context.project_id is not None: + the_filter.extend([ + models.InstanceTypes.projects.any( + project_id=context.project_id, deleted=False) + ]) + if len(the_filter) > 1: + query = query.filter(or_(*the_filter)) + else: + query = query.filter(the_filter[0]) + del filters['is_public'] + inst_types = query.order_by("name").all() return [_dict_with_extra_specs(i) for i in inst_types] @@ -3936,6 +3949,71 @@ def instance_type_destroy(context, name): 'updated_at': literal_column('updated_at')}) +@require_context +def _instance_type_access_query(context, session=None): + return model_query(context, models.InstanceTypeProjects, session=session, + read_deleted="yes") + + +@require_admin_context +def instance_type_access_get_by_flavor_id(context, flavor_id): + """Get flavor access list by flavor id""" + instance_type_ref = _instance_type_get_query(context).\ + filter_by(flavorid=flavor_id).\ + first() + + return [r for r in instance_type_ref.projects] + + +@require_admin_context +def instance_type_access_add(context, flavor_id, project_id): + """Add given tenant to the flavor access list""" + session = get_session() + with session.begin(): + instance_type_ref = instance_type_get_by_flavor_id(context, flavor_id, + session=session) + instance_type_id = instance_type_ref['id'] + access_ref = _instance_type_access_query(context, session=session).\ + filter_by(instance_type_id=instance_type_id).\ + filter_by(project_id=project_id).first() + + if not access_ref: + access_ref = models.InstanceTypeProjects() + access_ref.instance_type_id = instance_type_id + access_ref.project_id = project_id + access_ref.save(session=session) + elif access_ref.deleted: + access_ref.update({'deleted': False, + 'deleted_at': None}) + access_ref.save(session=session) + else: + raise exception.FlavorAccessExists(flavor_id=flavor_id, + project_id=project_id) + + return access_ref + + +@require_admin_context +def instance_type_access_remove(context, flavor_id, project_id): + """Remove given tenant from the flavor access list""" + session = get_session() + with session.begin(): + instance_type_ref = instance_type_get_by_flavor_id(context, flavor_id, + session=session) + instance_type_id = instance_type_ref['id'] + access_ref = _instance_type_access_query(context, session=session).\ + filter_by(instance_type_id=instance_type_id).\ + filter_by(project_id=project_id).first() + + if access_ref: + access_ref.update({'deleted': True, + 'deleted_at': timeutils.utcnow(), + 'updated_at': literal_column('updated_at')}) + else: + raise exception.FlavorAccessNotFound(flavor_id=flavor_id, + project_id=project_id) + + ######################## # User-provided metadata diff --git a/nova/db/sqlalchemy/migrate_repo/versions/132_add_instance_type_projects.py b/nova/db/sqlalchemy/migrate_repo/versions/132_add_instance_type_projects.py new file mode 100644 index 000000000..312ebbfc1 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/132_add_instance_type_projects.py @@ -0,0 +1,67 @@ +# 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 sqlalchemy import Boolean, Column, DateTime, String, ForeignKey, Integer +from sqlalchemy import 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 + + instance_types = Table('instance_types', meta, autoload=True) + is_public = Column('is_public', Boolean) + + instance_types.create_column(is_public) + instance_types.update().values(is_public=True).execute() + + # New table. + instance_type_projects = Table('instance_type_projects', meta, + Column('created_at', DateTime(timezone=False)), + Column('updated_at', DateTime(timezone=False)), + Column('deleted_at', DateTime(timezone=False)), + Column('deleted', Boolean(), default=False), + Column('id', Integer, primary_key=True, nullable=False), + Column('instance_type_id', + Integer, + ForeignKey('instance_types.id'), + nullable=False), + Column('project_id', String(length=255)), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + try: + instance_type_projects.create() + except Exception: + LOG.error(_("Table |%s| not created!"), repr(instance_type_projects)) + raise + + +def downgrade(migrate_engine): + meta = MetaData() + meta.bind = migrate_engine + + instance_types = Table('instance_types', meta, autoload=True) + is_public = Column('is_public', Boolean) + + instance_types.drop_column(is_public) + + instance_type_projects = Table( + 'instance_type_projects', meta, autoload=True) + instance_type_projects.drop() diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index a37b1c215..65d5eb5e0 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -336,6 +336,7 @@ class InstanceTypes(BASE, NovaBase): rxtx_factor = Column(Float, nullable=False, default=1) vcpu_weight = Column(Integer, nullable=True) disabled = Column(Boolean, default=False) + is_public = Column(Boolean, default=True) instances = relationship(Instance, backref=backref('instance_type', uselist=False), @@ -821,6 +822,21 @@ class InstanceSystemMetadata(BASE, NovaBase): primaryjoin=primary_join) +class InstanceTypeProjects(BASE, NovaBase): + """Represent projects associated instance_types""" + __tablename__ = "instance_type_projects" + id = Column(Integer, primary_key=True) + instance_type_id = Column(Integer, ForeignKey('instance_types.id'), + nullable=False) + project_id = Column(String(255)) + + instance_type = relationship(InstanceTypes, backref="projects", + foreign_keys=instance_type_id, + primaryjoin='and_(' + 'InstanceTypeProjects.instance_type_id == InstanceTypes.id,' + 'InstanceTypeProjects.deleted == False)') + + class InstanceTypeExtraSpecs(BASE, NovaBase): """Represents additional specs as key/value pairs for an instance_type""" __tablename__ = 'instance_type_extra_specs' |
