From 370644276487d378ab67c29faac8d15a5083e192 Mon Sep 17 00:00:00 2001 From: Sergey Skripnick Date: Wed, 28 Aug 2013 09:52:53 +0300 Subject: [PATCH] Cleanup and more tests for db.api methods New tests for methods: backup_* quota_* reservation_* snapshot_* transfer_* volume_* Removed unnecessary methods: quota_usage_create volume_metadata_get_item snapshot_metadata_get_item volume_type_extra_specs_get_item exact_filter Improved coverage of existing tests. Improved ModelsObjectComparatorMixin. blueprint: db-api-tests Change-Id: Ia2a3845f27a7b4b08fd8a16c6010513d213e642f --- cinder/db/api.py | 7 - cinder/db/sqlalchemy/api.py | 61 +------- cinder/tests/db/test_transfers.py | 7 + cinder/tests/test_db_api.py | 145 +++++++++++++++++++- cinder/tests/test_volume.py | 11 +- cinder/tests/test_volume_glance_metadata.py | 27 +++- 6 files changed, 178 insertions(+), 80 deletions(-) diff --git a/cinder/db/api.py b/cinder/db/api.py index df81e5ac8..e8aef4021 100644 --- a/cinder/db/api.py +++ b/cinder/db/api.py @@ -672,13 +672,6 @@ def quota_class_destroy_all_by_name(context, class_name): ################### -def quota_usage_create(context, project_id, resource, in_use, reserved, - until_refresh): - """Create a quota usage for the given project and resource.""" - return IMPL.quota_usage_create(context, project_id, resource, - in_use, reserved, until_refresh) - - def quota_usage_get(context, project_id, resource): """Retrieve a quota usage or raise if it does not exist.""" return IMPL.quota_usage_get(context, project_id, resource) diff --git a/cinder/db/sqlalchemy/api.py b/cinder/db/sqlalchemy/api.py index 36f9e8643..405c6fed5 100644 --- a/cinder/db/sqlalchemy/api.py +++ b/cinder/db/sqlalchemy/api.py @@ -198,48 +198,6 @@ def model_query(context, *args, **kwargs): return query -def exact_filter(query, model, filters, legal_keys): - """Applies exact match filtering to a query. - - Returns the updated query. Modifies filters argument to remove - filters consumed. - - :param query: query to apply filters to - :param model: model object the query applies to, for IN-style - filtering - :param filters: dictionary of filters; values that are lists, - tuples, sets, or frozensets cause an 'IN' test to - be performed, while exact matching ('==' operator) - is used for other values - :param legal_keys: list of keys to apply exact filtering to - """ - - filter_dict = {} - - # Walk through all the keys - for key in legal_keys: - # Skip ones we're not filtering on - if key not in filters: - continue - - # OK, filtering on this key; what value do we search for? - value = filters.pop(key) - - if isinstance(value, (list, tuple, set, frozenset)): - # Looking for values in a list; apply to query directly - column_attr = getattr(model, key) - query = query.filter(column_attr.in_(value)) - else: - # OK, simple exact match; save for later - filter_dict[key] = value - - # Apply simple exact matches - if filter_dict: - query = query.filter_by(**filter_dict) - - return query - - def _sync_volumes(context, project_id, session, volume_type_id=None, volume_type_name=None): (volumes, gigs) = _volume_data_get_for_project( @@ -670,12 +628,6 @@ def _quota_usage_create(context, project_id, resource, in_use, reserved, return quota_usage_ref -@require_admin_context -def quota_usage_create(context, project_id, resource, in_use, reserved, - until_refresh): - return _quota_usage_create(context, project_id, resource, in_use, reserved, - until_refresh) - ################### @@ -1652,12 +1604,6 @@ def _snapshot_metadata_get_item(context, snapshot_id, key, session=None): return result -@require_context -@require_snapshot_exists -def snapshot_metadata_get_item(context, snapshot_id, key): - return _snapshot_metadata_get_item(context, snapshot_id, key) - - @require_context @require_snapshot_exists def snapshot_metadata_update(context, snapshot_id, metadata, delete): @@ -1974,11 +1920,6 @@ def _volume_type_extra_specs_get_item(context, volume_type_id, key, return result -@require_context -def volume_type_extra_specs_get_item(context, volume_type_id, key): - return _volume_type_extra_specs_get_item(context, volume_type_id, key) - - @require_context def volume_type_extra_specs_update_or_create(context, volume_type_id, specs): @@ -2174,8 +2115,8 @@ def qos_specs_associations_get(context, qos_specs_id): extend qos specs association to other entities, such as volumes, sometime in future. """ + # Raise QoSSpecsNotFound if no specs found _qos_specs_get_ref(context, qos_specs_id, None) - return volume_type_qos_associations_get(context, qos_specs_id) diff --git a/cinder/tests/db/test_transfers.py b/cinder/tests/db/test_transfers.py index d62a92338..750fd77ff 100644 --- a/cinder/tests/db/test_transfers.py +++ b/cinder/tests/db/test_transfers.py @@ -54,6 +54,13 @@ class TransfersTableTestCase(test.TestCase): volume_id = utils.create_volume(self.ctxt)['id'] self._create_transfer(volume_id) + def test_transfer_create_not_available(self): + volume_id = utils.create_volume(self.ctxt, size=1, + status='notavailable')['id'] + self.assertRaises(exception.InvalidVolume, + self._create_transfer, + volume_id) + def test_transfer_get(self): volume_id1 = utils.create_volume(self.ctxt)['id'] xfer_id1 = self._create_transfer(volume_id1) diff --git a/cinder/tests/test_db_api.py b/cinder/tests/test_db_api.py index 5ff958029..7e37edf23 100644 --- a/cinder/tests/test_db_api.py +++ b/cinder/tests/test_db_api.py @@ -68,15 +68,18 @@ class ModelsObjectComparatorMixin(object): obj1 = self._dict_from_object(obj1, ignored_keys) obj2 = self._dict_from_object(obj2, ignored_keys) - self.assertEqual(len(obj1), len(obj2)) + self.assertEqual( + len(obj1), len(obj2), + "Keys mismatch: %s" % str(set(obj1.keys()) ^ set(obj2.keys()))) for key, value in obj1.iteritems(): self.assertEqual(value, obj2[key]) def _assertEqualListsOfObjects(self, objs1, objs2, ignored_keys=None): - self.assertEqual(len(objs1), len(objs2)) - objs2 = dict([(o['id'], o) for o in objs2]) - for o1 in objs1: - self._assertEqualObjects(o1, objs2[o1['id']], ignored_keys) + obj_to_dict = lambda o: self._dict_from_object(o, ignored_keys) + sort_key = lambda d: [d[k] for k in sorted(d)] + conv_and_sort = lambda obj: sorted(map(obj_to_dict, obj), key=sort_key) + + self.assertEqual(conv_and_sort(objs1), conv_and_sort(objs2)) def _assertEqualListsOfPrimitivesAsSets(self, primitives1, primitives2): self.assertEqual(len(primitives1), len(primitives2)) @@ -392,6 +395,10 @@ class DBAPIVolumeTestCase(BaseTest): db.volume_get_all_by_instance_uuid( self.ctxt, instance_uuids[i])) + def test_volume_get_all_by_instance_uuid_empty(self): + self.assertEqual([], db.volume_get_all_by_instance_uuid(self.ctxt, + 'empty')) + def test_volume_get_all_by_project(self): volumes = [] for i in xrange(3): @@ -414,7 +421,8 @@ class DBAPIVolumeTestCase(BaseTest): def test_volume_update(self): volume = db.volume_create(self.ctxt, {'host': 'h1'}) - db.volume_update(self.ctxt, volume['id'], {'host': 'h2'}) + db.volume_update(self.ctxt, volume['id'], + {'host': 'h2', 'metadata': {'m1': 'v1'}}) volume = db.volume_get(self.ctxt, volume['id']) self.assertEqual('h2', volume['host']) @@ -448,8 +456,37 @@ class DBAPIVolumeTestCase(BaseTest): self.assertEqual(should_be, db.volume_metadata_get(self.ctxt, 1)) + def test_volume_metadata_delete(self): + metadata = {'a': 'b', 'c': 'd'} + db.volume_create(self.ctxt, {'id': 1, 'metadata': metadata}) + db.volume_metadata_delete(self.ctxt, 1, 'c') + metadata.pop('c') + self.assertEquals(metadata, db.volume_metadata_get(self.ctxt, 1)) + class DBAPISnapshotTestCase(BaseTest): + + """Tests for cinder.db.api.snapshot_*.""" + + def test_snapshot_data_get_for_project(self): + actual = db.snapshot_data_get_for_project(self.ctxt, 'project1') + self.assertEqual(actual, (0, 0)) + db.volume_create(self.ctxt, {'id': 1, + 'project_id': 'project1', + 'size': 42}) + snapshot = db.snapshot_create(self.ctxt, {'id': 1, 'volume_id': 1, + 'project_id': 'project1', + 'volume_size': 42}) + actual = db.snapshot_data_get_for_project(self.ctxt, 'project1') + self.assertEqual(actual, (1, 42)) + + def test_snapshot_get_all(self): + db.volume_create(self.ctxt, {'id': 1}) + snapshot = db.snapshot_create(self.ctxt, {'id': 1, 'volume_id': 1}) + self._assertEqualListsOfObjects([snapshot], + db.snapshot_get_all(self.ctxt), + ignored_keys=['metadata', 'volume']) + def test_snapshot_metadata_get(self): metadata = {'a': 'b', 'c': 'd'} db.volume_create(self.ctxt, {'id': 1}) @@ -494,6 +531,26 @@ class DBAPISnapshotTestCase(BaseTest): self.assertEqual(should_be, db.snapshot_metadata_get(self.ctxt, 1)) +class DBAPIVolumeTypeTestCase(BaseTest): + + """Tests for the db.api.volume_type_* methods.""" + + def setUp(self): + self.ctxt = context.get_admin_context() + super(DBAPIVolumeTypeTestCase, self).setUp() + + def test_volume_type_create_exists(self): + vt = db.volume_type_create(self.ctxt, {'name': 'n1'}) + self.assertRaises(exception.VolumeTypeExists, + db.volume_type_create, + self.ctxt, + {'name': 'n1'}) + self.assertRaises(exception.VolumeTypeExists, + db.volume_type_create, + self.ctxt, + {'name': 'n2', 'id': vt['id']}) + + class DBAPIEncryptionTestCase(BaseTest): """Tests for the db.api.volume_type_encryption_* methods.""" @@ -652,6 +709,16 @@ class DBAPIReservationTestCase(BaseTest): self.ctxt, 'project1')) + def test_reservation_get_all_by_project(self): + reservations = _quota_reserve(self.ctxt, 'project1') + r1 = db.reservation_get(self.ctxt, reservations[0]) + r2 = db.reservation_get(self.ctxt, reservations[1]) + expected = {'project_id': 'project1', + r1['resource']: {r1['uuid']: r1['delta']}, + r2['resource']: {r2['uuid']: r2['delta']}} + self.assertEqual(expected, db.reservation_get_all_by_project( + self.ctxt, 'project1')) + def test_reservation_expire(self): self.values['expire'] = datetime.datetime.utcnow() + \ datetime.timedelta(days=1) @@ -666,6 +733,60 @@ class DBAPIReservationTestCase(BaseTest): self.ctxt, 'project1')) + def test_reservation_destroy(self): + reservations = _quota_reserve(self.ctxt, 'project1') + r1 = db.reservation_get(self.ctxt, reservations[0]) + db.reservation_destroy(self.ctxt, reservations[1]) + expected = {'project_id': 'project1', + r1['resource']: {r1['uuid']: r1['delta']}} + self.assertEqual(expected, db.reservation_get_all_by_project( + self.ctxt, 'project1')) + + +class DBAPIQuotaClassTestCase(BaseTest): + + """Tests for db.api.quota_class_* methods.""" + + def setUp(self): + super(DBAPIQuotaClassTestCase, self).setUp() + self.sample_qc = db.quota_class_create(self.ctxt, 'test_qc', + 'test_resource', 42) + + def test_quota_class_get(self): + qc = db.quota_class_get(self.ctxt, 'test_qc', 'test_resource') + self._assertEqualObjects(self.sample_qc, qc) + + def test_quota_class_destroy(self): + db.quota_class_destroy(self.ctxt, 'test_qc', 'test_resource') + self.assertRaises(exception.QuotaClassNotFound, + db.quota_class_get, self.ctxt, + 'test_qc', 'test_resource') + + def test_quota_class_get_not_found(self): + self.assertRaises(exception.QuotaClassNotFound, + db.quota_class_get, self.ctxt, 'nonexistent', + 'nonexistent') + + def test_quota_class_get_all_by_name(self): + sample1 = db.quota_class_create(self.ctxt, 'test2', 'res1', 43) + sample2 = db.quota_class_create(self.ctxt, 'test2', 'res2', 44) + self.assertEqual({'class_name': 'test_qc', 'test_resource': 42}, + db.quota_class_get_all_by_name(self.ctxt, 'test_qc')) + self.assertEqual({'class_name': 'test2', 'res1': 43, 'res2': 44}, + db.quota_class_get_all_by_name(self.ctxt, 'test2')) + + def test_quota_class_update(self): + db.quota_class_update(self.ctxt, 'test_qc', 'test_resource', 43) + updated = db.quota_class_get(self.ctxt, 'test_qc', 'test_resource') + self.assertEqual(43, updated['hard_limit']) + + def test_quota_class_destroy_all_by_name(self): + sample1 = db.quota_class_create(self.ctxt, 'test2', 'res1', 43) + sample2 = db.quota_class_create(self.ctxt, 'test2', 'res2', 44) + db.quota_class_destroy_all_by_name(self.ctxt, 'test2') + self.assertEqual({'class_name': 'test2'}, + db.quota_class_get_all_by_name(self.ctxt, 'test2')) + class DBAPIQuotaTestCase(BaseTest): @@ -725,6 +846,13 @@ class DBAPIQuotaTestCase(BaseTest): self.assertIn(reservation.resource, res_names) res_names.remove(reservation.resource) + def test_quota_destroy(self): + db.quota_create(self.ctxt, 'project1', 'resource1', 41) + self.assertIsNone(db.quota_destroy(self.ctxt, 'project1', + 'resource1')) + self.assertRaises(exception.ProjectQuotaNotFound, db.quota_get, + self.ctxt, 'project1', 'resource1') + def test_quota_destroy_all_by_project(self): reservations = _quota_reserve(self.ctxt, 'project1') db.quota_destroy_all_by_project(self.ctxt, 'project1') @@ -855,6 +983,11 @@ class DBAPIBackupTestCase(BaseTest): self.created[1]['project_id']) self._assertEqualObjects(self.created[1], byproj[0]) + def test_backup_update_nonexistent(self): + self.assertRaises(exception.BackupNotFound, + db.backup_update, + self.ctxt, 'nonexistent', {}) + def test_backup_update(self): updated_values = self._get_values(one=True) update_id = self.created[1]['id'] diff --git a/cinder/tests/test_volume.py b/cinder/tests/test_volume.py index 295dcb18c..b747400e8 100644 --- a/cinder/tests/test_volume.py +++ b/cinder/tests/test_volume.py @@ -1851,6 +1851,7 @@ class GetActiveByWindowTestCase(BaseVolumeTestCase): { 'id': 1, 'host': 'devstack', + 'project_id': 'p1', 'created_at': datetime.datetime(1, 1, 1, 1, 1, 1), 'deleted': True, 'status': 'deleted', 'deleted_at': datetime.datetime(1, 2, 1, 1, 1, 1), @@ -1859,6 +1860,7 @@ class GetActiveByWindowTestCase(BaseVolumeTestCase): { 'id': 2, 'host': 'devstack', + 'project_id': 'p1', 'created_at': datetime.datetime(1, 1, 1, 1, 1, 1), 'deleted': True, 'status': 'deleted', 'deleted_at': datetime.datetime(1, 3, 10, 1, 1, 1), @@ -1866,6 +1868,7 @@ class GetActiveByWindowTestCase(BaseVolumeTestCase): { 'id': 3, 'host': 'devstack', + 'project_id': 'p1', 'created_at': datetime.datetime(1, 1, 1, 1, 1, 1), 'deleted': True, 'status': 'deleted', 'deleted_at': datetime.datetime(1, 5, 1, 1, 1, 1), @@ -1873,11 +1876,13 @@ class GetActiveByWindowTestCase(BaseVolumeTestCase): { 'id': 4, 'host': 'devstack', + 'project_id': 'p1', 'created_at': datetime.datetime(1, 3, 10, 1, 1, 1), }, { 'id': 5, 'host': 'devstack', + 'project_id': 'p1', 'created_at': datetime.datetime(1, 5, 1, 1, 1, 1), } ] @@ -1903,7 +1908,8 @@ class GetActiveByWindowTestCase(BaseVolumeTestCase): volumes = db.volume_get_active_by_window( self.context, datetime.datetime(1, 3, 1, 1, 1, 1), - datetime.datetime(1, 4, 1, 1, 1, 1)) + datetime.datetime(1, 4, 1, 1, 1, 1), + project_id='p1') self.assertEqual(len(volumes), 3) self.assertEqual(volumes[0].id, u'2') self.assertEqual(volumes[1].id, u'3') @@ -1932,7 +1938,8 @@ class GetActiveByWindowTestCase(BaseVolumeTestCase): snapshots = db.snapshot_get_active_by_window( self.context, datetime.datetime(1, 3, 1, 1, 1, 1), - datetime.datetime(1, 4, 1, 1, 1, 1)) + datetime.datetime(1, 4, 1, 1, 1, 1), + project_id='p1') self.assertEqual(len(snapshots), 3) self.assertEqual(snapshots[0].id, u'2') self.assertEqual(snapshots[0].volume.id, u'1') diff --git a/cinder/tests/test_volume_glance_metadata.py b/cinder/tests/test_volume_glance_metadata.py index f138340f9..0a8cf7c52 100644 --- a/cinder/tests/test_volume_glance_metadata.py +++ b/cinder/tests/test_volume_glance_metadata.py @@ -28,10 +28,7 @@ class VolumeGlanceMetadataTestCase(test.TestCase): def setUp(self): super(VolumeGlanceMetadataTestCase, self).setUp() - self.context = context.get_admin_context() - - def tearDown(self): - super(VolumeGlanceMetadataTestCase, self).tearDown() + self.ctxt = context.get_admin_context() def test_vol_glance_metadata_bad_vol_id(self): ctxt = context.get_admin_context() @@ -111,7 +108,7 @@ class VolumeGlanceMetadataTestCase(test.TestCase): for (key, value) in expected_meta.items(): self.assertEqual(meta[key], value) - def test_vol_glance_metadata_copy_to_volume(self): + def test_vol_glance_metadata_copy_from_volume_to_volume(self): ctxt = context.get_admin_context() db.volume_create(ctxt, {'id': 1}) db.volume_create(ctxt, {'id': 100, 'source_volid': 1}) @@ -125,3 +122,23 @@ class VolumeGlanceMetadataTestCase(test.TestCase): for meta in db.volume_glance_metadata_get(ctxt, 100): for (key, value) in expected_meta.items(): self.assertEqual(meta[key], value) + + def test_volume_glance_metadata_copy_to_volume(self): + vol1 = db.volume_create(self.ctxt, {}) + vol2 = db.volume_create(self.ctxt, {}) + db.volume_glance_metadata_create(self.ctxt, vol1['id'], 'm1', 'v1') + snapshot = db.snapshot_create(self.ctxt, {'volume_id': vol1['id']}) + db.volume_glance_metadata_copy_to_snapshot(self.ctxt, snapshot['id'], + vol1['id']) + db.volume_glance_metadata_copy_to_volume(self.ctxt, vol2['id'], + snapshot['id']) + metadata = db.volume_glance_metadata_get(self.ctxt, vol2['id']) + metadata = dict([(m['key'], m['value']) for m in metadata]) + self.assertEqual(metadata, {'m1': 'v1'}) + + def test_volume_snapshot_glance_metadata_get_nonexistent(self): + vol = db.volume_create(self.ctxt, {}) + snapshot = db.snapshot_create(self.ctxt, {'volume_id': vol['id']}) + self.assertRaises(exception.GlanceMetadataNotFound, + db.volume_snapshot_glance_metadata_get, + self.ctxt, snapshot['id']) -- 2.45.2