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:
parent
15f5171c25
commit
552667059a
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
Loading…
Reference in New Issue