diff --git a/cinder/db/sqlalchemy/api.py b/cinder/db/sqlalchemy/api.py index d721afa80dd..082c8274c1b 100644 --- a/cinder/db/sqlalchemy/api.py +++ b/cinder/db/sqlalchemy/api.py @@ -1593,10 +1593,20 @@ def _volume_data_get_for_project(context, project_id, volume_type_id=None, # When calling the method for quotas we don't count volumes that are the # destination of a migration since they were not accounted for quotas or # reservations in the first place. + # Also skip temporary volumes that have 'temporary' admin_metadata key set + # to True. if skip_internal: + admin_model = models.VolumeAdminMetadata query = query.filter( - or_(model.migration_status.is_(None), - ~model.migration_status.startswith('target:'))) + and_(or_(model.migration_status.is_(None), + ~model.migration_status.startswith('target:')), + ~sql.exists().where(and_(model.id == admin_model.volume_id, + ~admin_model.deleted, + admin_model.key == 'temporary', + admin_model.value == 'True') + ) + ) + ) if host: query = query.filter(_filter_host(model.host, host)) diff --git a/cinder/tests/unit/test_db_api.py b/cinder/tests/unit/test_db_api.py index 77cd15581ac..7223bafe4e7 100644 --- a/cinder/tests/unit/test_db_api.py +++ b/cinder/tests/unit/test_db_api.py @@ -554,6 +554,28 @@ class DBAPIVolumeTestCase(BaseTest): self.ctxt, 'project', skip_internal=skip_internal) self.assertEqual((count, gigabytes), result) + @ddt.data((True, THREE_HUNDREDS, THREE), + (False, THREE_HUNDREDS + ONE_HUNDREDS, THREE + 1)) + @ddt.unpack + def test__volume_data_get_for_project_temporary(self, skip_internal, + gigabytes, count): + for i in range(3): + db.volume_create(self.ctxt, + {'project_id': 'project', + 'size': ONE_HUNDREDS, + 'host': 'h-%d' % i, + 'volume_type_id': fake.VOLUME_TYPE_ID}) + # This is a temporary volume + db.volume_create(self.ctxt, {'project_id': 'project', + 'size': ONE_HUNDREDS, + 'host': 'h-%d' % i, + 'volume_type_id': fake.VOLUME_TYPE_ID, + 'admin_metadata': {'temporary': 'True'}}) + + result = sqlalchemy_api._volume_data_get_for_project( + self.ctxt, 'project', skip_internal=skip_internal) + self.assertEqual((count, gigabytes), result) + def test_volume_data_get_for_project_with_host(self): db.volume_create(self.ctxt, {'project_id': fake.PROJECT_ID, diff --git a/releasenotes/notes/quota-sync-temporary-b4103ebc2c484c89.yaml b/releasenotes/notes/quota-sync-temporary-b4103ebc2c484c89.yaml new file mode 100644 index 00000000000..13193d88419 --- /dev/null +++ b/releasenotes/notes/quota-sync-temporary-b4103ebc2c484c89.yaml @@ -0,0 +1,8 @@ +--- +fixes: + - | + `Bug #1919161 `_: Fix + automatic quota refresh to correctly account for temporary volumes. During + some cinder operations, such as create a backup from a snapshot, temporary + volumes are created and are not counted towards quota usage, but the sync + mechanism was counting them, thus incorrectly updating volume usage.