From 2c79edd9270e6385cbe30af5c8efacd73aefbf8f Mon Sep 17 00:00:00 2001 From: Kien Nguyen Date: Mon, 28 May 2018 22:39:42 +0700 Subject: [PATCH] Add quota_usage_get_by_project This method will be used in the follow-up patch to add api controller for the quotas. Change-Id: Ia3686b4bd3f7815c457587f908084cc0300c3296 --- zun/common/exception.py | 4 ++ zun/db/api.py | 6 +++ zun/db/etcd/api.py | 17 +++++++ zun/db/etcd/models.py | 30 ++++++++++++- .../versions/012a730926e8_add_quota_usage.py | 44 +++++++++++++++++++ zun/db/sqlalchemy/api.py | 12 +++++ zun/db/sqlalchemy/models.py | 19 ++++++++ 7 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 zun/db/sqlalchemy/alembic/versions/012a730926e8_add_quota_usage.py diff --git a/zun/common/exception.py b/zun/common/exception.py index d7547e2f2..7f44a01bf 100644 --- a/zun/common/exception.py +++ b/zun/common/exception.py @@ -687,6 +687,10 @@ class QuotaNotFound(NotFound): message = _("Quota could not be found.") +class QuotaUsageNotFound(NotFound): + message = _("Quota usage could not be found") + + class ProjectQuotaNotFound(QuotaNotFound): message = _("Quota for project %(project_id)s could not be found.") diff --git a/zun/db/api.py b/zun/db/api.py index e852021a2..72e1994c8 100644 --- a/zun/db/api.py +++ b/zun/db/api.py @@ -974,6 +974,12 @@ def quota_class_update(context, class_name, resource, limit): resource, limit) +def quota_usage_get_all_by_project(context, project_id): + """Retrieve all usage associated with a given resource.""" + return _get_dbdriver_instance().quota_usage_get_all_by_project(context, + project_id) + + @profiler.trace("db") def create_network(context, values): """Create a new network. diff --git a/zun/db/etcd/api.py b/zun/db/etcd/api.py index c16d34ff3..b6c913ee5 100644 --- a/zun/db/etcd/api.py +++ b/zun/db/etcd/api.py @@ -93,6 +93,8 @@ def translate_etcd_result(etcd_result, model_type): ret = models.Quota(data) elif model_type == 'quota_class': ret = models.QuotaClass(data) + elif model_type == 'quota_usage': + ret = models.QuotaUsage(data) else: raise exception.InvalidParameterValue( _('The model_type value: %s is invalid.'), model_type) @@ -1314,3 +1316,18 @@ class EtcdAPI(object): LOG.error('Error occurred while updating quota class: %s', six.text_type(e)) raise + + def quota_usage_get_all_by_project(self, context, project_id): + try: + res = getattr( + self.client.read('/quota_usages/{}' . format(project_id)), + 'children', None) + if res.value is not None: + return translate_etcd_result(res, 'quota_usage') + else: + raise exception.QuotaUsageNotFound() + except etcd.EtcdKeyNotFound: + raise exception.QuotaUsageNotFound() + except Exception as e: + LOG.error('Error occurred while retrieving quota usage: %s', + six.text_type(e)) diff --git a/zun/db/etcd/models.py b/zun/db/etcd/models.py index fedb5aac7..7dc659365 100644 --- a/zun/db/etcd/models.py +++ b/zun/db/etcd/models.py @@ -414,7 +414,7 @@ class QuotaClass(Base): class_name=quota_class_data.get('class_name'), resource=quota_class_data.get('resource')) - for f in Quota.fields(): + for f in QuotaClass.fields(): setattr(self, f, None) self.id = 1 @@ -431,3 +431,31 @@ class QuotaClass(Base): @classmethod def fields(cls): return cls._fields + + +class QuotaUsage(Base): + + """Represents the current usage for a given resource.""" + + _path = '/quota_usages' + + _fields = ['id', 'project_id', 'resource', 'in_use', 'reserved'] + + def __init__(self, quota_usage_data): + self.path = QuotaUsage.path( + project_id=quota_usage_data['project_id'], + resource=quota_usage_data['resource']) + + for f in QuotaUsage.fields(): + setattr(self, f, None) + + self.id = 1 + self.update(quota_usage_data) + + @classmethod + def path(cls, project_id, resource): + return '{}/{}/{}' . format(cls._path, project_id, resource) + + @classmethod + def fields(cls): + return cls._fields diff --git a/zun/db/sqlalchemy/alembic/versions/012a730926e8_add_quota_usage.py b/zun/db/sqlalchemy/alembic/versions/012a730926e8_add_quota_usage.py new file mode 100644 index 000000000..f69c17c8f --- /dev/null +++ b/zun/db/sqlalchemy/alembic/versions/012a730926e8_add_quota_usage.py @@ -0,0 +1,44 @@ +# 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. + + +"""Add quota usage + +Revision ID: 012a730926e8 +Revises: 3298c6a5c3d9 +Create Date: 2018-05-30 21:23:17.659203 + +""" + +# revision identifiers, used by Alembic. +revision = '012a730926e8' +down_revision = '3298c6a5c3d9' +branch_labels = None +depends_on = None + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + op.create_table( + 'quota_usages', + sa.Column('created_at', sa.DateTime()), + sa.Column('updated_at', sa.DateTime()), + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('project_id', sa.String(length=255), index=True), + sa.Column('resource', sa.String(length=255), nullable=False), + sa.Column('in_use', sa.Integer, nullable=False), + sa.Column('reserved', sa.Integer, nullable=False), + sa.Column('until_refresh', sa.Integer), + mysql_engine='InnoDB' + ) diff --git a/zun/db/sqlalchemy/api.py b/zun/db/sqlalchemy/api.py index 31c73556e..f299d7d94 100644 --- a/zun/db/sqlalchemy/api.py +++ b/zun/db/sqlalchemy/api.py @@ -1158,6 +1158,18 @@ class Connection(object): if not result: raise exception.QuotaClassNotFound(class_name=class_name) + def quota_usage_get_all_by_project(self, context, project_id): + rows = model_query(context, models.QuotaUsage).\ + filter_by(project_id=project_id).\ + all() + + result = {'project_id': project_id} + for row in rows: + result[row.resource] = dict(in_use=row.in_use, + reserved=row.reserved) + + return result + def create_network(self, context, values): # ensure defaults are present for new containers if not values.get('uuid'): diff --git a/zun/db/sqlalchemy/models.py b/zun/db/sqlalchemy/models.py index d469373e3..002f358ff 100644 --- a/zun/db/sqlalchemy/models.py +++ b/zun/db/sqlalchemy/models.py @@ -501,6 +501,25 @@ class QuotaClass(Base): hard_limit = Column(Integer) +class QuotaUsage(Base): + """Respents the current usage for a given resource.""" + + __tablename__ = 'quota_usages' + id = Column(Integer, primary_key=True) + + project_id = Column(String(255), index=True) + resource = Column(String(255), index=True) + + in_use = Column(Integer, nullable=False) + reserved = Column(Integer, nullable=False) + + @property + def total(self): + return self.in_use + self.reserved + + until_refresh = Column(Integer, nullable=True) + + class Network(Base): """Represents a network. """