Move resource usage sync functions to db backend

Resource usage sync functions was declared in cinder/quota.py, and
using db.api public methods. This functions was moved to database
backend implementation, so now sync functions can use private
methods of database backend, and session attribute can be removed
from this public methods.

Blueprint: db-session-cleanup

Change-Id: If5386e3dc1e0d6e3127732aeb5b35bbd96bc93f0
This commit is contained in:
Sergey Skripnick 2013-07-10 11:55:24 +03:00
parent 568e75c511
commit 4d4627f9c2
5 changed files with 102 additions and 163 deletions

View File

@ -188,13 +188,9 @@ def volume_data_get_for_host(context, host):
host)
def volume_data_get_for_project(context, project_id, volume_type_id=None,
session=None):
def volume_data_get_for_project(context, project_id):
"""Get (volume_count, gigabytes) for project."""
return IMPL.volume_data_get_for_project(context,
project_id,
volume_type_id,
session)
return IMPL.volume_data_get_for_project(context, project_id)
def finish_volume_migration(context, src_vol_id, dest_vol_id):
@ -295,13 +291,11 @@ def snapshot_update(context, snapshot_id, values):
return IMPL.snapshot_update(context, snapshot_id, values)
def snapshot_data_get_for_project(context, project_id, volume_type_id=None,
session=None):
def snapshot_data_get_for_project(context, project_id, volume_type_id=None):
"""Get count and gigabytes used for snapshots for specified project."""
return IMPL.snapshot_data_get_for_project(context,
project_id,
volume_type_id,
session)
volume_type_id)
def snapshot_get_active_by_window(context, begin, end=None, project_id=None):

View File

@ -241,6 +241,47 @@ def exact_filter(query, model, filters, legal_keys):
return query
def _sync_volumes(context, project_id, session, volume_type_id=None,
volume_type_name=None):
(volumes, gigs) = _volume_data_get_for_project(
context, project_id, volume_type_id=volume_type_id, session=session)
key = 'volumes'
if volume_type_name:
key += '_' + volume_type_name
return {key: volumes}
def _sync_snapshots(context, project_id, session, volume_type_id=None,
volume_type_name=None):
(snapshots, gigs) = _snapshot_data_get_for_project(
context, project_id, volume_type_id=volume_type_id, session=session)
key = 'snapshots'
if volume_type_name:
key += '_' + volume_type_name
return {key: snapshots}
def _sync_gigabytes(context, project_id, session, volume_type_id=None,
volume_type_name=None):
(_junk, vol_gigs) = _volume_data_get_for_project(
context, project_id, volume_type_id=volume_type_id, session=session)
key = 'gigabytes'
if volume_type_name:
key += '_' + volume_type_name
if CONF.no_snapshot_gb_quota:
return {key: vol_gigs}
(_junk, snap_gigs) = _snapshot_data_get_for_project(
context, project_id, volume_type_id=volume_type_id, session=session)
return {key: vol_gigs + snap_gigs}
QUOTA_SYNC_FUNCTIONS = {
'_sync_volumes': _sync_volumes,
'_sync_snapshots': _sync_snapshots,
'_sync_gigabytes': _sync_gigabytes,
}
###################
@ -763,9 +804,15 @@ def quota_reserve(context, resources, quotas, deltas, expire,
# OK, refresh the usage
if refresh:
# Grab the sync routine
sync = resources[resource].sync
updates = sync(elevated, project_id, session)
sync = QUOTA_SYNC_FUNCTIONS[resources[resource].sync]
volume_type_id = getattr(resources[resource],
'volume_type_id', None)
volume_type_name = getattr(resources[resource],
'volume_type_name', None)
updates = sync(elevated, project_id,
volume_type_id=volume_type_id,
volume_type_name=volume_type_name,
session=session)
for res, in_use in updates.items():
# Make sure we have a destination for the usage!
if res not in usages:
@ -1042,10 +1089,8 @@ def _volume_data_get_for_project(context, project_id, volume_type_id=None,
@require_admin_context
def volume_data_get_for_project(context, project_id, volume_type_id=None,
session=None):
return _volume_data_get_for_project(context, project_id, volume_type_id,
session)
def volume_data_get_for_project(context, project_id, volume_type_id=None):
return _volume_data_get_for_project(context, project_id, volume_type_id)
@require_admin_context
@ -1416,10 +1461,8 @@ def _snapshot_data_get_for_project(context, project_id, volume_type_id=None,
@require_context
def snapshot_data_get_for_project(context, project_id, volume_type_id=None,
session=None):
return _snapshot_data_get_for_project(context, project_id, volume_type_id,
session)
def snapshot_data_get_for_project(context, project_id, volume_type_id=None):
return _snapshot_data_get_for_project(context, project_id, volume_type_id)
@require_context

View File

@ -89,6 +89,7 @@ class DbQuotaDriver(object):
def get_defaults(self, context, resources):
"""Given a list of resources, retrieve the default quotas.
Use the class quotas named `_DEFAULT_QUOTA_NAME` as default quotas,
if it exists.
@ -487,8 +488,7 @@ class ReservableResource(BaseResource):
"""Describe a reservable resource."""
def __init__(self, name, sync, flag=None):
"""
Initializes a ReservableResource.
"""Initializes a ReservableResource.
Reservable resources are those resources which directly
correspond to objects in the database, i.e., volumes, gigabytes,
@ -507,8 +507,8 @@ class ReservableResource(BaseResource):
ReservableResource.
:param name: The name of the resource, i.e., "volumes".
:param sync: A callable which returns a dictionary to
resynchronize the in_use count for one or more
:param sync: A dbapi methods name which returns a dictionary
to resynchronize the in_use count for one or more
resources, as described above.
:param flag: The name of the flag or configuration option
which specifies the default value of the quota
@ -532,8 +532,7 @@ class CountableResource(AbsoluteResource):
"""
def __init__(self, name, count, flag=None):
"""
Initializes a CountableResource.
"""Initializes a CountableResource.
Countable resources are those resources which directly
correspond to objects in the database, i.e., volumes, gigabytes,
@ -576,51 +575,10 @@ class VolumeTypeResource(ReservableResource):
:param volume_type: The volume type for this resource.
"""
try:
method = getattr(self, '_sync_%s' % part_name)
except AttributeError:
raise ValueError('Invalid resource: %s' % part_name)
self.volume_type_name = volume_type['name']
self.volume_type_id = volume_type['id']
name = "%s_%s" % (part_name, self.volume_type_name)
super(VolumeTypeResource, self).__init__(name, method)
def _sync_snapshots(self, context, project_id, session):
"""Sync snapshots for this specific volume type."""
(snapshots, gigs) = db.snapshot_data_get_for_project(
context,
project_id,
volume_type_id=self.volume_type_id,
session=session)
return {'snapshots_%s' % self.volume_type_name: snapshots}
def _sync_volumes(self, context, project_id, session):
"""Sync volumes for this specific volume type."""
(volumes, gigs) = db.volume_data_get_for_project(
context,
project_id,
volume_type_id=self.volume_type_id,
session=session)
return {'volumes_%s' % self.volume_type_name: volumes}
def _sync_gigabytes(self, context, project_id, session):
"""Sync gigabytes for this specific volume type."""
key = 'gigabytes_%s' % self.volume_type_name
(_junk, vol_gigs) = db.volume_data_get_for_project(
context,
project_id,
volume_type_id=self.volume_type_id,
session=session)
if CONF.no_snapshot_gb_quota:
return {key: vol_gigs}
(_junk, snap_gigs) = db.snapshot_data_get_for_project(
context,
project_id,
volume_type_id=self.volume_type_id,
session=session)
return {key: vol_gigs + snap_gigs}
super(VolumeTypeResource, self).__init__(name, "_sync_%s" % part_name)
class QuotaEngine(object):
@ -908,9 +866,9 @@ class VolumeTypeQuotaEngine(QuotaEngine):
result = {}
# Global quotas.
argses = [('volumes', _sync_volumes, 'quota_volumes'),
('snapshots', _sync_snapshots, 'quota_snapshots'),
('gigabytes', _sync_gigabytes, 'quota_gigabytes'), ]
argses = [('volumes', '_sync_volumes', 'quota_volumes'),
('snapshots', '_sync_snapshots', 'quota_snapshots'),
('gigabytes', '_sync_gigabytes', 'quota_gigabytes'), ]
for args in argses:
resource = ReservableResource(*args)
result[resource.name] = resource
@ -932,32 +890,4 @@ class VolumeTypeQuotaEngine(QuotaEngine):
def register_resources(self, resources):
raise NotImplementedError(_("Cannot register resources"))
def _sync_volumes(context, project_id, session):
(volumes, gigs) = db.volume_data_get_for_project(context,
project_id,
session=session)
return {'volumes': volumes}
def _sync_snapshots(context, project_id, session):
(snapshots, gigs) = db.snapshot_data_get_for_project(context,
project_id,
session=session)
return {'snapshots': snapshots}
def _sync_gigabytes(context, project_id, session):
(_junk, vol_gigs) = db.volume_data_get_for_project(context,
project_id,
session=session)
if CONF.no_snapshot_gb_quota:
return {'gigabytes': vol_gigs}
(_junk, snap_gigs) = db.snapshot_data_get_for_project(context,
project_id,
session=session)
return {'gigabytes': vol_gigs + snap_gigs}
QUOTAS = VolumeTypeQuotaEngine()

View File

@ -44,13 +44,12 @@ def _quota_reserve(context, project_id):
quotas = {}
resources = {}
deltas = {}
for i in range(3):
resource = 'res%d' % i
quotas[resource] = db.quota_create(context, project_id, resource, i)
resources[resource] = ReservableResource(
resource,
get_sync(resource, i), 'quota_res_%d' % i)
deltas[resource] = i
for i, resource in enumerate(('volumes', 'gigabytes')):
quotas[resource] = db.quota_create(context, project_id,
resource, i + 1)
resources[resource] = ReservableResource(resource,
'_sync_%s' % resource)
deltas[resource] = i + 1
return db.quota_reserve(
context, resources, quotas, deltas,
datetime.datetime.utcnow(), datetime.datetime.utcnow(),
@ -534,9 +533,9 @@ class DBAPIReservationTestCase(BaseTest):
def test_reservation_commit(self):
reservations = _quota_reserve(self.ctxt, 'project1')
expected = {'project_id': 'project1',
'res0': {'reserved': 0, 'in_use': 0},
'res1': {'reserved': 1, 'in_use': 1},
'res2': {'reserved': 2, 'in_use': 2}}
'volumes': {'reserved': 1, 'in_use': 0},
'gigabytes': {'reserved': 2, 'in_use': 0},
}
self.assertEqual(expected,
db.quota_usage_get_all_by_project(
self.ctxt, 'project1'))
@ -547,9 +546,9 @@ class DBAPIReservationTestCase(BaseTest):
self.ctxt,
reservations[0])
expected = {'project_id': 'project1',
'res0': {'reserved': 0, 'in_use': 0},
'res1': {'reserved': 0, 'in_use': 2},
'res2': {'reserved': 0, 'in_use': 4}}
'volumes': {'reserved': 0, 'in_use': 1},
'gigabytes': {'reserved': 0, 'in_use': 2},
}
self.assertEqual(expected,
db.quota_usage_get_all_by_project(
self.ctxt,
@ -558,9 +557,9 @@ class DBAPIReservationTestCase(BaseTest):
def test_reservation_rollback(self):
reservations = _quota_reserve(self.ctxt, 'project1')
expected = {'project_id': 'project1',
'res0': {'reserved': 0, 'in_use': 0},
'res1': {'reserved': 1, 'in_use': 1},
'res2': {'reserved': 2, 'in_use': 2}}
'volumes': {'reserved': 1, 'in_use': 0},
'gigabytes': {'reserved': 2, 'in_use': 0},
}
self.assertEqual(expected,
db.quota_usage_get_all_by_project(
self.ctxt,
@ -572,9 +571,9 @@ class DBAPIReservationTestCase(BaseTest):
self.ctxt,
reservations[0])
expected = {'project_id': 'project1',
'res0': {'reserved': 0, 'in_use': 0},
'res1': {'reserved': 0, 'in_use': 1},
'res2': {'reserved': 0, 'in_use': 2}}
'volumes': {'reserved': 0, 'in_use': 0},
'gigabytes': {'reserved': 0, 'in_use': 0},
}
self.assertEqual(expected,
db.quota_usage_get_all_by_project(
self.ctxt,
@ -587,9 +586,8 @@ class DBAPIReservationTestCase(BaseTest):
db.reservation_expire(self.ctxt)
expected = {'project_id': 'project1',
'res0': {'reserved': 0, 'in_use': 0},
'res1': {'reserved': 0, 'in_use': 1},
'res2': {'reserved': 0, 'in_use': 2}}
'gigabytes': {'reserved': 0, 'in_use': 0},
'volumes': {'reserved': 0, 'in_use': 0}}
self.assertEqual(expected,
db.quota_usage_get_all_by_project(
self.ctxt,
@ -647,8 +645,8 @@ class DBAPIQuotaTestCase(BaseTest):
def test_quota_reserve(self):
reservations = _quota_reserve(self.ctxt, 'project1')
self.assertEqual(len(reservations), 3)
res_names = ['res0', 'res1', 'res2']
self.assertEqual(len(reservations), 2)
res_names = ['gigabytes', 'volumes']
for uuid in reservations:
reservation = db.reservation_get(self.ctxt, uuid)
self.assertTrue(reservation.resource in res_names)
@ -677,18 +675,17 @@ class DBAPIQuotaTestCase(BaseTest):
def test_quota_usage_get(self):
reservations = _quota_reserve(self.ctxt, 'p1')
quota_usage = db.quota_usage_get(self.ctxt, 'p1', 'res0')
expected = {'resource': 'res0', 'project_id': 'p1',
'in_use': 0, 'reserved': 0, 'total': 0}
quota_usage = db.quota_usage_get(self.ctxt, 'p1', 'gigabytes')
expected = {'resource': 'gigabytes', 'project_id': 'p1',
'in_use': 0, 'reserved': 2, 'total': 2}
for key, value in expected.iteritems():
self.assertEqual(value, quota_usage[key])
self.assertEqual(value, quota_usage[key], key)
def test_quota_usage_get_all_by_project(self):
reservations = _quota_reserve(self.ctxt, 'p1')
expected = {'project_id': 'p1',
'res0': {'in_use': 0, 'reserved': 0},
'res1': {'in_use': 1, 'reserved': 1},
'res2': {'in_use': 2, 'reserved': 2}}
'volumes': {'in_use': 0, 'reserved': 1},
'gigabytes': {'in_use': 0, 'reserved': 2}}
self.assertEqual(expected, db.quota_usage_get_all_by_project(
self.ctxt, 'p1'))

View File

@ -415,35 +415,6 @@ class QuotaEngineTestCase(test.TestCase):
test_resource2=resources[1],
test_resource3=resources[2], ))
def test_sync_predeclared(self):
quota_obj = quota.QuotaEngine()
def spam(*args, **kwargs):
pass
resource = quota.ReservableResource('test_resource', spam)
quota_obj.register_resource(resource)
self.assertEqual(resource.sync, spam)
def test_sync_multi(self):
quota_obj = quota.QuotaEngine()
def spam(*args, **kwargs):
pass
resources = [
quota.ReservableResource('test_resource1', spam),
quota.ReservableResource('test_resource2', spam),
quota.ReservableResource('test_resource3', spam),
quota.ReservableResource('test_resource4', spam), ]
quota_obj.register_resources(resources[:2])
self.assertEqual(resources[0].sync, spam)
self.assertEqual(resources[1].sync, spam)
self.assertEqual(resources[2].sync, spam)
self.assertEqual(resources[3].sync, spam)
def test_get_by_project(self):
context = FakeContext('test_project', 'test_class')
driver = FakeDriver(
@ -1099,7 +1070,8 @@ class QuotaReserveSqlAlchemyTestCase(test.TestCase):
self.sync_called = set()
def make_sync(res_name):
def sync(context, project_id, session):
def fake_sync(context, project_id, volume_type_id=None,
volume_type_name=None, session=None):
self.sync_called.add(res_name)
if res_name in self.usages:
if self.usages[res_name].in_use < 0:
@ -1107,13 +1079,16 @@ class QuotaReserveSqlAlchemyTestCase(test.TestCase):
else:
return {res_name: self.usages[res_name].in_use - 1}
return {res_name: 0}
return sync
return fake_sync
self.resources = {}
QUOTA_SYNC_FUNCTIONS = {}
for res_name in ('volumes', 'gigabytes'):
res = quota.ReservableResource(res_name, make_sync(res_name))
res = quota.ReservableResource(res_name, '_sync_%s' % res_name)
QUOTA_SYNC_FUNCTIONS['_sync_%s' % res_name] = make_sync(res_name)
self.resources[res_name] = res
self.stubs.Set(sqa_api, 'QUOTA_SYNC_FUNCTIONS', QUOTA_SYNC_FUNCTIONS)
self.expire = timeutils.utcnow() + datetime.timedelta(seconds=3600)
self.usages = {}