From 3a9fcc337cd0eaa1412a1f5fc634ce03fa6c6f0d Mon Sep 17 00:00:00 2001 From: Cory Stone Date: Mon, 25 Mar 2013 15:09:37 -0500 Subject: [PATCH] Add the volume and snapshot gigabytes together. The quota sync functions were not working correctly for both snapshots and volumes regarding gigabytes. Each would report their own count as the total gigabytes. Now the gigabytes sync is separate from the volume and snapshot count syncs. Fixes bug #1160004 Change-Id: Ib1200813acb6055412b3f7f6b63fa9ba2b8fa3be (cherry picked from commit 1fde5a4fbc73a31453f0fe288a0b4a929d7f3026) --- cinder/db/sqlalchemy/api.py | 2 +- cinder/quota.py | 32 ++++++++++++++++-------- cinder/tests/test_quota.py | 50 +++++++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 11 deletions(-) diff --git a/cinder/db/sqlalchemy/api.py b/cinder/db/sqlalchemy/api.py index 67a4e1e7f..ecd0bc689 100644 --- a/cinder/db/sqlalchemy/api.py +++ b/cinder/db/sqlalchemy/api.py @@ -607,7 +607,7 @@ def reservation_get(context, uuid, session=None): def reservation_get_all_by_project(context, project_id): authorize_project_context(context, project_id) - rows = model_query(context, models.QuotaUsage, read_deleted="no").\ + rows = model_query(context, models.Reservation, read_deleted="no").\ filter_by(project_id=project_id).all() result = {'project_id': project_id} diff --git a/cinder/quota.py b/cinder/quota.py index 1a1edc92b..d006f1f81 100644 --- a/cinder/quota.py +++ b/cinder/quota.py @@ -730,17 +730,30 @@ class QuotaEngine(object): def _sync_volumes(context, project_id, session): - return dict(zip(('volumes', 'gigabytes'), - db.volume_data_get_for_project(context, - project_id, - session=session))) + (volumes, gigs) = db.volume_data_get_for_project(context, + project_id, + session=session) + return {'volumes': volumes} def _sync_snapshots(context, project_id, session): - return dict(zip(('snapshots', 'gigabytes'), - db.snapshot_data_get_for_project(context, - project_id, - session=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 FLAGS.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 = QuotaEngine() @@ -749,8 +762,7 @@ QUOTAS = QuotaEngine() resources = [ ReservableResource('volumes', _sync_volumes, 'quota_volumes'), ReservableResource('snapshots', _sync_snapshots, 'quota_snapshots'), - ReservableResource('gigabytes', _sync_volumes, 'quota_gigabytes'), - ReservableResource('gigabytes', _sync_snapshots, 'quota_gigabytes'), ] + ReservableResource('gigabytes', _sync_gigabytes, 'quota_gigabytes'), ] QUOTAS.register_resources(resources) diff --git a/cinder/tests/test_quota.py b/cinder/tests/test_quota.py index 20b956335..53370ff01 100644 --- a/cinder/tests/test_quota.py +++ b/cinder/tests/test_quota.py @@ -71,6 +71,15 @@ class QuotaIntegrationTestCase(test.TestCase): vol['status'] = 'available' return db.volume_create(self.context, vol) + def _create_snapshot(self, volume): + snapshot = {} + snapshot['user_id'] = self.user_id + snapshot['project_id'] = self.project_id + snapshot['volume_id'] = volume['id'] + snapshot['volume_size'] = volume['size'] + snapshot['status'] = 'available' + return db.snapshot_create(self.context, snapshot) + def test_too_many_volumes(self): volume_ids = [] for i in range(FLAGS.quota_volumes): @@ -92,6 +101,47 @@ class QuotaIntegrationTestCase(test.TestCase): for volume_id in volume_ids: db.volume_destroy(self.context, volume_id) + def test_too_many_combined_gigabytes(self): + vol_ref = self._create_volume(size=10) + snap_ref = self._create_snapshot(vol_ref) + self.assertRaises(exception.QuotaError, + volume.API().create_snapshot, + self.context, vol_ref, '', '') + usages = db.quota_usage_get_all_by_project(self.context, + self.project_id) + self.assertEqual(usages['gigabytes']['in_use'], 20) + db.snapshot_destroy(self.context, snap_ref['id']) + db.volume_destroy(self.context, vol_ref['id']) + + def test_no_snapshot_gb_quota_flag(self): + self.flags(quota_volumes=2, + quota_snapshots=2, + quota_gigabytes=20, + no_snapshot_gb_quota=True) + vol_ref = self._create_volume(size=10) + snap_ref = self._create_snapshot(vol_ref) + snap_ref2 = volume.API().create_snapshot(self.context, + vol_ref, '', '') + + # Make sure no reservation was created for snapshot gigabytes. + reservations = db.reservation_get_all_by_project(self.context, + self.project_id) + self.assertEqual(reservations.get('gigabytes'), None) + + # Make sure the snapshot volume_size isn't included in usage. + vol_type = db.volume_type_create(self.context, + dict(name=FLAGS.default_volume_type)) + vol_ref2 = volume.API().create(self.context, 10, '', '') + usages = db.quota_usage_get_all_by_project(self.context, + self.project_id) + self.assertEqual(usages['gigabytes']['in_use'], 20) + + db.snapshot_destroy(self.context, snap_ref['id']) + db.snapshot_destroy(self.context, snap_ref2['id']) + db.volume_destroy(self.context, vol_ref['id']) + db.volume_destroy(self.context, vol_ref2['id']) + db.volume_type_destroy(self.context, vol_type['id']) + class FakeContext(object): def __init__(self, project_id, quota_class): -- 2.45.2