Create db functions for quotas

This patch adds 2 functions:
 * count_artifact_number that counts how many artifacts tenant has
 * calculate_uploaded_data that calculates how much data tenant uploaded

Partially Implements: blueprint glare-quotas

Change-Id: I52615442ba9d63cca60766d9236e65eaff2e0872
This commit is contained in:
Mike Fedosin 2017-07-27 23:58:29 +03:00
parent 15f5171c25
commit 552667059a
3 changed files with 175 additions and 0 deletions

View File

@ -118,6 +118,32 @@ class ArtifactAPI(object):
marker=marker, limit=limit, sort=sort,
latest=latest)
@retry(retry_on_exception=_retry_on_connection_error, wait_fixed=1000,
stop_max_attempt_number=20)
def count_artifact_number(self, context, type_name=None):
"""Count the number of artifacts for the tenant.
:param context: user context
:param type_name: name of specific artifact type to count artifacts.
If None count artifacts of all types.
:return: number of artifacts for given tenant
"""
session = api.get_session()
return api.count_artifact_number(context, session, type_name)
@retry(retry_on_exception=_retry_on_connection_error, wait_fixed=1000,
stop_max_attempt_number=20)
def calculate_uploaded_data(self, context, type_name=None):
"""Calculate the amount of uploaded data for tenant.
:param context: user context
:param type_name: name of specific artifact type to calculate data.
If None calculate data of artifacts of all types.
:return: amount of uploaded data for given user
"""
session = api.get_session()
return api.calculate_uploaded_data(context, session, type_name)
class ArtifactLockApi(locking.LockApiBase):
@retry(retry_on_exception=_retry_on_connection_error, wait_fixed=1000,

View File

@ -590,6 +590,26 @@ def _do_blobs(artifact, new_blobs):
return blobs_to_update
def count_artifact_number(context, session, type_name=None):
"""Return a number of artifacts for tenant."""
query = session.query(func.count(models.Artifact.id)).filter(
models.Artifact.owner == context.tenant)
if type_name is not None:
query = query.filter(models.Artifact.type_name == type_name)
return query.order_by(None).scalar() or 0
def calculate_uploaded_data(context, session, type_name=None):
"""Return the amount of uploaded data for tenant."""
query = session.query(
func.sum(models.ArtifactBlob.size)).join(
models.Artifact, aliased=True).filter(
models.Artifact.owner == context.tenant)
if type_name is not None:
query = query.filter(models.Artifact.type_name == type_name)
return query.order_by(None).scalar() or 0
@retry(retry_on_exception=_retry_on_deadlock, wait_fixed=500,
stop_max_attempt_number=50)
@utils.no_4byte_params

View File

@ -0,0 +1,129 @@
# Copyright 2017 - Nokia Networks
#
# 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 six import BytesIO
from glare.db.sqlalchemy import api
from glare.tests.unit import base
class TestQuotaFunctions(base.BaseTestArtifactAPI):
"""Test quota db functions."""
def test_count_artifact_number(self):
session = api.get_session()
# initially there are no artifacts
self.assertEqual(0, api.count_artifact_number(
self.req.context, session))
# create 5 images, 3 heat templates, 2 murano packages and 7 samples
amount = {
'images': 5,
'heat_templates': 3,
'murano_packages': 2,
'sample_artifact': 7
}
for type_name in amount:
for num in range(amount[type_name]):
self.controller.create(
self.req, type_name, {'name': type_name + str(num)})
# create 1 artifact of each type from different user
req = self.get_fake_request(self.users['user2'])
for type_name in amount:
self.controller.create(req, type_name, {'name': type_name})
# count numbers for each type
for type_name in amount:
num = api.count_artifact_number(
self.req.context, session, type_name)
self.assertEqual(amount[type_name], num)
# count the whole amount of artifacts
self.assertEqual(17, api.count_artifact_number(
self.req.context, session))
def test_calculate_uploaded_data(self):
session = api.get_session()
# initially there is no data
self.assertEqual(0, api.calculate_uploaded_data(
self.req.context, session))
# create a sample artifact
art1 = self.controller.create(
self.req, 'sample_artifact', {'name': 'art1'})
# upload 10 bytes to 'blob'
self.controller.upload_blob(
self.req, 'sample_artifact', art1['id'], 'blob',
BytesIO(b'a' * 10), 'application/octet-stream')
self.assertEqual(10, api.calculate_uploaded_data(
self.req.context, session))
# upload 3 blobs to dict_of_blobs with 25, 35 and 45 bytes respectively
self.controller.upload_blob(
self.req, 'sample_artifact', art1['id'], 'dict_of_blobs/blob1',
BytesIO(b'a' * 25), 'application/octet-stream')
self.controller.upload_blob(
self.req, 'sample_artifact', art1['id'], 'dict_of_blobs/blob2',
BytesIO(b'a' * 35), 'application/octet-stream')
self.controller.upload_blob(
self.req, 'sample_artifact', art1['id'], 'dict_of_blobs/blob3',
BytesIO(b'a' * 45), 'application/octet-stream')
self.assertEqual(115, api.calculate_uploaded_data(
self.req.context, session))
# create another sample artifact and upload 100 bytes there
art2 = self.controller.create(
self.req, 'sample_artifact', {'name': 'art2'})
self.controller.upload_blob(
self.req, 'sample_artifact', art2['id'], 'blob',
BytesIO(b'a' * 100), 'application/octet-stream')
self.assertEqual(215, api.calculate_uploaded_data(
self.req.context, session))
# create image and upload 150 bytes there
img1 = self.controller.create(
self.req, 'images', {'name': 'img1'})
self.controller.upload_blob(
self.req, 'images', img1['id'], 'image',
BytesIO(b'a' * 150), 'application/octet-stream')
# the whole amount of uploaded data is 365 bytes
self.assertEqual(365, api.calculate_uploaded_data(
self.req.context, session))
# 215 bytes for sample_artifact
self.assertEqual(215, api.calculate_uploaded_data(
self.req.context, session, 'sample_artifact'))
# 150 bytes for images
self.assertEqual(150, api.calculate_uploaded_data(
self.req.context, session, 'images'))
# create an artifact from another user and check that it's not included
# for the original user
req = self.get_fake_request(self.users['user2'])
another_art = self.controller.create(
req, 'sample_artifact', {'name': 'another'})
# upload 1000 bytes to 'blob'
self.controller.upload_blob(
req, 'sample_artifact', another_art['id'], 'blob',
BytesIO(b'a' * 1000), 'application/octet-stream')
# original user still has 365 bytes
self.assertEqual(365, api.calculate_uploaded_data(
self.req.context, session))
# user2 has 1000
self.assertEqual(
1000, api.calculate_uploaded_data(req.context, session))