summaryrefslogtreecommitdiffstats
path: root/nova/db
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2012-03-21 16:29:34 +0000
committerGerrit Code Review <review@openstack.org>2012-03-21 16:29:34 +0000
commitf25cb41f5ea351bbbc0abbc1b040cfba8ef25186 (patch)
tree7b28bcca90964650372c18e9034f19aa69f14d74 /nova/db
parent41c57e267752d7cf51a5cbd6bfab7332d218382b (diff)
parent6a38b650c001ec8e6da435856c37a28737401aaf (diff)
downloadnova-f25cb41f5ea351bbbc0abbc1b040cfba8ef25186.tar.gz
nova-f25cb41f5ea351bbbc0abbc1b040cfba8ef25186.tar.xz
nova-f25cb41f5ea351bbbc0abbc1b040cfba8ef25186.zip
Merge "Implement quota classes."
Diffstat (limited to 'nova/db')
-rw-r--r--nova/db/api.py33
-rw-r--r--nova/db/sqlalchemy/api.py83
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/083_quota_class.py61
-rw-r--r--nova/db/sqlalchemy/models.py25
4 files changed, 199 insertions, 3 deletions
diff --git a/nova/db/api.py b/nova/db/api.py
index 0b06f87fa..02eaa14a3 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -927,6 +927,39 @@ def quota_destroy_all_by_project(context, project_id):
###################
+def quota_class_create(context, class_name, resource, limit):
+ """Create a quota class for the given name and resource."""
+ return IMPL.quota_class_create(context, class_name, resource, limit)
+
+
+def quota_class_get(context, class_name, resource):
+ """Retrieve a quota class or raise if it does not exist."""
+ return IMPL.quota_class_get(context, class_name, resource)
+
+
+def quota_class_get_all_by_name(context, class_name):
+ """Retrieve all quotas associated with a given quota class."""
+ return IMPL.quota_class_get_all_by_name(context, class_name)
+
+
+def quota_class_update(context, class_name, resource, limit):
+ """Update a quota class or raise if it does not exist."""
+ return IMPL.quota_class_update(context, class_name, resource, limit)
+
+
+def quota_class_destroy(context, class_name, resource):
+ """Destroy the quota class or raise if it does not exist."""
+ return IMPL.quota_class_destroy(context, class_name, resource)
+
+
+def quota_class_destroy_all_by_name(context, class_name):
+ """Destroy all quotas associated with a given quota class."""
+ return IMPL.quota_class_destroy_all_by_name(context, class_name)
+
+
+###################
+
+
def volume_allocate_iscsi_target(context, volume_id, host):
"""Atomically allocate a free iscsi_target from the pool."""
return IMPL.volume_allocate_iscsi_target(context, volume_id, host)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index c6af4b85f..d990b970a 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -89,6 +89,15 @@ def authorize_user_context(context, user_id):
raise exception.NotAuthorized()
+def authorize_quota_class_context(context, class_name):
+ """Ensures a request has permission to access the given quota class."""
+ if is_user_context(context):
+ if not context.quota_class:
+ raise exception.NotAuthorized()
+ elif context.quota_class != class_name:
+ raise exception.NotAuthorized()
+
+
def require_admin_context(f):
"""Decorator to require admin request context.
@@ -2291,6 +2300,80 @@ def quota_destroy_all_by_project(context, project_id):
###################
+@require_context
+def quota_class_get(context, class_name, resource, session=None):
+ result = model_query(context, models.QuotaClass, session=session,
+ read_deleted="no").\
+ filter_by(class_name=class_name).\
+ filter_by(resource=resource).\
+ first()
+
+ if not result:
+ raise exception.QuotaClassNotFound(class_name=class_name)
+
+ return result
+
+
+@require_context
+def quota_class_get_all_by_name(context, class_name):
+ authorize_quota_class_context(context, class_name)
+
+ rows = model_query(context, models.QuotaClass, read_deleted="no").\
+ filter_by(class_name=class_name).\
+ all()
+
+ result = {'class_name': class_name}
+ for row in rows:
+ result[row.resource] = row.hard_limit
+
+ return result
+
+
+@require_admin_context
+def quota_class_create(context, class_name, resource, limit):
+ quota_class_ref = models.QuotaClass()
+ quota_class_ref.class_name = class_name
+ quota_class_ref.resource = resource
+ quota_class_ref.hard_limit = limit
+ quota_class_ref.save()
+ return quota_class_ref
+
+
+@require_admin_context
+def quota_class_update(context, class_name, resource, limit):
+ session = get_session()
+ with session.begin():
+ quota_class_ref = quota_class_get(context, class_name, resource,
+ session=session)
+ quota_class_ref.hard_limit = limit
+ quota_class_ref.save(session=session)
+
+
+@require_admin_context
+def quota_class_destroy(context, class_name, resource):
+ session = get_session()
+ with session.begin():
+ quota_class_ref = quota_class_get(context, class_name, resource,
+ session=session)
+ quota_class_ref.delete(session=session)
+
+
+@require_admin_context
+def quota_class_destroy_all_by_name(context, class_name):
+ session = get_session()
+ with session.begin():
+ quota_classes = model_query(context, models.QuotaClass,
+ session=session, read_deleted="no").\
+ filter_by(class_name=class_name).\
+ all()
+
+ for quota_class_ref in quota_classes:
+ quota_class_ref.delete(session=session)
+
+
+###################
+
+
@require_admin_context
def volume_allocate_iscsi_target(context, volume_id, host):
session = get_session()
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/083_quota_class.py b/nova/db/sqlalchemy/migrate_repo/versions/083_quota_class.py
new file mode 100644
index 000000000..37d9695d7
--- /dev/null
+++ b/nova/db/sqlalchemy/migrate_repo/versions/083_quota_class.py
@@ -0,0 +1,61 @@
+# 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
+from sqlalchemy import MetaData, Integer, String, Table
+
+from nova import log as logging
+
+LOG = logging.getLogger(__name__)
+
+
+def upgrade(migrate_engine):
+ meta = MetaData()
+ meta.bind = migrate_engine
+
+ # New table
+ quota_classes = Table('quota_classes', meta,
+ Column('created_at', DateTime(timezone=False)),
+ Column('updated_at', DateTime(timezone=False)),
+ Column('deleted_at', DateTime(timezone=False)),
+ Column('deleted', Boolean(create_constraint=True, name=None)),
+ Column('id', Integer(), primary_key=True),
+ Column('class_name',
+ String(length=255, convert_unicode=True,
+ assert_unicode=None, unicode_error=None,
+ _warn_on_bytestring=False), index=True),
+ Column('resource',
+ String(length=255, convert_unicode=True,
+ assert_unicode=None, unicode_error=None,
+ _warn_on_bytestring=False)),
+ Column('hard_limit', Integer(), nullable=True),
+ )
+
+ try:
+ quota_classes.create()
+ except Exception:
+ LOG.error(_("Table |%s| not created!"), repr(quota_classes))
+ raise
+
+
+def downgrade(migrate_engine):
+ meta = MetaData()
+ meta.bind = migrate_engine
+
+ quota_classes = Table('quota_classes', meta, autoload=True)
+ try:
+ quota_classes.drop()
+ except Exception:
+ LOG.error(_("quota_classes table not dropped"))
+ raise
diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py
index 3865cd5d0..634f04dbc 100644
--- a/nova/db/sqlalchemy/models.py
+++ b/nova/db/sqlalchemy/models.py
@@ -419,9 +419,11 @@ class VolumeTypeExtraSpecs(BASE, NovaBase):
class Quota(BASE, NovaBase):
"""Represents a single quota override for a project.
- If there is no row for a given project id and resource, then
- the default for the deployment is used. If the row is present
- but the hard limit is Null, then the resource is unlimited.
+ If there is no row for a given project id and resource, then the
+ default for the quota class is used. If there is no row for a
+ given quota class and resource, then the default for the
+ deployment is used. If the row is present but the hard limit is
+ Null, then the resource is unlimited.
"""
__tablename__ = 'quotas'
@@ -433,6 +435,23 @@ class Quota(BASE, NovaBase):
hard_limit = Column(Integer, nullable=True)
+class QuotaClass(BASE, NovaBase):
+ """Represents a single quota override for a quota class.
+
+ If there is no row for a given quota class and resource, then the
+ default for the deployment is used. If the row is present but the
+ hard limit is Null, then the resource is unlimited.
+ """
+
+ __tablename__ = 'quota_classes'
+ id = Column(Integer, primary_key=True)
+
+ class_name = Column(String(255), index=True)
+
+ resource = Column(String(255))
+ hard_limit = Column(Integer, nullable=True)
+
+
class Snapshot(BASE, NovaBase):
"""Represents a block storage device that can be attached to a vm."""
__tablename__ = 'snapshots'