summaryrefslogtreecommitdiffstats
path: root/nova/db
diff options
context:
space:
mode:
authorunicell <unicell@gmail.com>2012-08-13 20:19:54 +0800
committerunicell <unicell@gmail.com>2012-08-27 23:45:05 +0800
commit34c012c709cc5ae577330c7d67ba060293158210 (patch)
treee04765cbb3e09e9047c51aa9e03d4b1db15ac80c /nova/db
parent5e012d8d45935b68a5ce5d50ed043d4bb8066cf8 (diff)
downloadnova-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.py15
-rw-r--r--nova/db/sqlalchemy/api.py78
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/132_add_instance_type_projects.py67
-rw-r--r--nova/db/sqlalchemy/models.py16
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'