From ec44fc89997624c692635d5fae8fba4ca5d9aa02 Mon Sep 17 00:00:00 2001 From: Gorka Eguileor Date: Fri, 9 Apr 2021 16:15:46 +0200 Subject: [PATCH] Expose volume & snapshot use_quota field This patch adds a new REST API microversion to expose the new use_quota DB field present in volumes and snapshots. The field will be reported when listing and showing resources and can be used when filtering as well. The field is exposed in the REST API as `consumes_quota` to prevent confusion for users and admins, since exposing it as `use_quota` may give them the wrong impression that they can set it up for their own purposes. For users we say what is happening with the quota for that resource -it consumes or doesn't consume quota-, whereas internally we express instruction to the core code -whether to use quota or not-, hence the difference in the naming. APIImpact DocImpact Implements: blueprint temp-resources Change-Id: I655a47fc75ddc11caf1defe984d9a66a9ad5a2e7 --- api-ref/source/v3/parameters.yaml | 21 +++++++ api-ref/source/v3/resource-filters.inc | 2 + .../snapshots/snapshot-create-response.json | 2 +- .../snapshots/snapshot-show-response.json | 2 +- .../snapshots/snapshot-update-response.json | 2 +- .../snapshots-list-detailed-response.json | 2 +- .../v3.65/snapshot-create-response.json | 18 ++++++ .../v3.65/snapshot-show-response.json | 20 +++++++ .../v3.65/snapshot-update-response.json | 18 ++++++ .../snapshots-list-detailed-response.json | 22 +++++++ .../versions/version-show-response.json | 4 +- .../samples/versions/versions-response.json | 4 +- .../volumes/v3.65/volume-create-response.json | 41 +++++++++++++ .../volumes/v3.65/volume-show-response.json | 45 ++++++++++++++ .../volumes/v3.65/volume-update-response.json | 43 +++++++++++++ .../v3.65/volumes-list-detailed-response.json | 47 +++++++++++++++ .../volumes/volume-create-response.json | 2 +- .../samples/volumes/volume-show-response.json | 2 +- .../volumes/volume-update-response.json | 2 +- api-ref/source/v3/volumes-v3-snapshots.inc | 22 ++++--- api-ref/source/v3/volumes-v3-volumes.inc | 22 ++++--- cinder/api/common.py | 3 +- cinder/api/microversions.py | 2 + cinder/api/openstack/api_version_request.py | 7 ++- cinder/api/v3/snapshots.py | 20 +++++-- cinder/api/v3/views/snapshots.py | 8 ++- cinder/api/v3/views/volumes.py | 3 + cinder/api/v3/volumes.py | 26 +++++--- .../v3.65/snapshot-create-response.json.tpl | 18 ++++++ .../v3.65/snapshot-show-response.json.tpl | 20 +++++++ .../v3.65/snapshot-update-response.json.tpl | 18 ++++++ .../snapshots-list-detailed-response.json.tpl | 22 +++++++ .../v3.65/volume-create-response.json.tpl | 41 +++++++++++++ .../v3.65/volume-show-response.json.tpl | 45 ++++++++++++++ .../v3.65/volume-update-response.json.tpl | 43 +++++++++++++ .../volumes-list-detailed-response.json.tpl | 47 +++++++++++++++ .../api_sample_tests/test_snapshots.py | 3 +- .../api_sample_tests/test_volumes.py | 3 +- cinder/tests/unit/api/test_common.py | 5 +- cinder/tests/unit/api/v3/test_snapshots.py | 37 ++++++++++-- cinder/tests/unit/api/v3/test_volumes.py | 60 +++++++++++++++++++ etc/cinder/resource_filters.json | 5 +- .../notes/mv-use_quota-b8e010f8f68a1eaa.yaml | 14 +++++ 43 files changed, 733 insertions(+), 60 deletions(-) create mode 100644 api-ref/source/v3/samples/snapshots/v3.65/snapshot-create-response.json create mode 100644 api-ref/source/v3/samples/snapshots/v3.65/snapshot-show-response.json create mode 100644 api-ref/source/v3/samples/snapshots/v3.65/snapshot-update-response.json create mode 100644 api-ref/source/v3/samples/snapshots/v3.65/snapshots-list-detailed-response.json create mode 100644 api-ref/source/v3/samples/volumes/v3.65/volume-create-response.json create mode 100644 api-ref/source/v3/samples/volumes/v3.65/volume-show-response.json create mode 100644 api-ref/source/v3/samples/volumes/v3.65/volume-update-response.json create mode 100644 api-ref/source/v3/samples/volumes/v3.65/volumes-list-detailed-response.json create mode 100644 cinder/tests/functional/api_sample_tests/samples/snapshots/v3.65/snapshot-create-response.json.tpl create mode 100644 cinder/tests/functional/api_sample_tests/samples/snapshots/v3.65/snapshot-show-response.json.tpl create mode 100644 cinder/tests/functional/api_sample_tests/samples/snapshots/v3.65/snapshot-update-response.json.tpl create mode 100644 cinder/tests/functional/api_sample_tests/samples/snapshots/v3.65/snapshots-list-detailed-response.json.tpl create mode 100644 cinder/tests/functional/api_sample_tests/samples/volumes/v3.65/volume-create-response.json.tpl create mode 100644 cinder/tests/functional/api_sample_tests/samples/volumes/v3.65/volume-show-response.json.tpl create mode 100644 cinder/tests/functional/api_sample_tests/samples/volumes/v3.65/volume-update-response.json.tpl create mode 100644 cinder/tests/functional/api_sample_tests/samples/volumes/v3.65/volumes-list-detailed-response.json.tpl create mode 100644 releasenotes/notes/mv-use_quota-b8e010f8f68a1eaa.yaml diff --git a/api-ref/source/v3/parameters.yaml b/api-ref/source/v3/parameters.yaml index 3551f330122..cf806829d3e 100644 --- a/api-ref/source/v3/parameters.yaml +++ b/api-ref/source/v3/parameters.yaml @@ -214,6 +214,18 @@ detail: in: query required: false type: boolean +filter_consumes_quota: + description: | + Filters results by ``consumes_quota`` field. Resources that don't use + quotas are usually temporary internal resources created to perform an + operation. Default is to not filter by it. Filtering by this option may + not be always possible in a cloud, see + :ref:`List Resource Filters ` to determine whether this + filter is available in your cloud. + in: query + required: false + type: boolean + min_version: 3.65 filter_created_at: description: | Filters reuslts by a time that resources are created at with time @@ -841,6 +853,15 @@ consumer: in: body required: false type: string +consumes_quota: + description: | + Whether this resource consumes quota or not. Resources that not counted + for quota usage are usually temporary internal resources created to perform + an operation. + in: body + required: false + type: boolean + min_version: 3.65 container: description: | The container name or null. diff --git a/api-ref/source/v3/resource-filters.inc b/api-ref/source/v3/resource-filters.inc index b5064e965d2..37f662c75b7 100644 --- a/api-ref/source/v3/resource-filters.inc +++ b/api-ref/source/v3/resource-filters.inc @@ -1,5 +1,7 @@ .. -*- rst -*- +.. _resource-filters: + Resource Filters (resource_filters) =================================== diff --git a/api-ref/source/v3/samples/snapshots/snapshot-create-response.json b/api-ref/source/v3/samples/snapshots/snapshot-create-response.json index 8819dd94a89..e19e407c8bd 100644 --- a/api-ref/source/v3/samples/snapshots/snapshot-create-response.json +++ b/api-ref/source/v3/samples/snapshots/snapshot-create-response.json @@ -12,4 +12,4 @@ "updated_at": null, "volume_id": "d291b81c-6e40-4525-8231-90aa1588121e" } -} \ No newline at end of file +} diff --git a/api-ref/source/v3/samples/snapshots/snapshot-show-response.json b/api-ref/source/v3/samples/snapshots/snapshot-show-response.json index da7e68da1f3..b4979f36869 100644 --- a/api-ref/source/v3/samples/snapshots/snapshot-show-response.json +++ b/api-ref/source/v3/samples/snapshots/snapshot-show-response.json @@ -14,4 +14,4 @@ "updated_at": null, "volume_id": "b72c48f1-64b7-4cd8-9745-b12e0be82d37" } -} \ No newline at end of file +} diff --git a/api-ref/source/v3/samples/snapshots/snapshot-update-response.json b/api-ref/source/v3/samples/snapshots/snapshot-update-response.json index 5d9c9bb4d76..d69a8aedcce 100644 --- a/api-ref/source/v3/samples/snapshots/snapshot-update-response.json +++ b/api-ref/source/v3/samples/snapshots/snapshot-update-response.json @@ -12,4 +12,4 @@ "updated_at": null, "volume_id": "070c942d-9909-42e9-a467-7a781f150c58" } -} \ No newline at end of file +} diff --git a/api-ref/source/v3/samples/snapshots/snapshots-list-detailed-response.json b/api-ref/source/v3/samples/snapshots/snapshots-list-detailed-response.json index 9c452f38783..a73af209418 100644 --- a/api-ref/source/v3/samples/snapshots/snapshots-list-detailed-response.json +++ b/api-ref/source/v3/samples/snapshots/snapshots-list-detailed-response.json @@ -16,4 +16,4 @@ "volume_id": "7acd675e-4e06-4653-af9f-2ecd546342d6" } ] -} \ No newline at end of file +} diff --git a/api-ref/source/v3/samples/snapshots/v3.65/snapshot-create-response.json b/api-ref/source/v3/samples/snapshots/v3.65/snapshot-create-response.json new file mode 100644 index 00000000000..b38d66242fa --- /dev/null +++ b/api-ref/source/v3/samples/snapshots/v3.65/snapshot-create-response.json @@ -0,0 +1,18 @@ +{ + "snapshot": { + "created_at": "2019-03-11T16:24:34.469003", + "description": "Daily backup", + "id": "b36476e5-d18b-47f9-ac69-4818cb43ee21", + "metadata": { + "key": "v3" + }, + "name": "snap-001", + "size": 10, + "status": "creating", + "updated_at": null, + "volume_id": "d291b81c-6e40-4525-8231-90aa1588121e", + "group_snapshot_id": null, + "user_id": "c853ca26-e8ea-4797-8a52-ee124a013d0e", + "consumes_quota": true + } +} diff --git a/api-ref/source/v3/samples/snapshots/v3.65/snapshot-show-response.json b/api-ref/source/v3/samples/snapshots/v3.65/snapshot-show-response.json new file mode 100644 index 00000000000..69be72e98e8 --- /dev/null +++ b/api-ref/source/v3/samples/snapshots/v3.65/snapshot-show-response.json @@ -0,0 +1,20 @@ +{ + "snapshot": { + "created_at": "2019-03-12T04:42:00.809352", + "description": "Daily backup", + "id": "4a584cae-e4ce-429b-9154-d4c9eb8fda4c", + "metadata": { + "key": "v3" + }, + "name": "snap-001", + "os-extended-snapshot-attributes:progress": "0%", + "os-extended-snapshot-attributes:project_id": "89afd400-b646-4bbc-b12b-c0a4d63e5bd3", + "size": 10, + "status": "creating", + "updated_at": null, + "volume_id": "b72c48f1-64b7-4cd8-9745-b12e0be82d37", + "group_snapshot_id": null, + "user_id": "c853ca26-e8ea-4797-8a52-ee124a013d0e", + "consumes_quota": true + } +} diff --git a/api-ref/source/v3/samples/snapshots/v3.65/snapshot-update-response.json b/api-ref/source/v3/samples/snapshots/v3.65/snapshot-update-response.json new file mode 100644 index 00000000000..c097889d2e1 --- /dev/null +++ b/api-ref/source/v3/samples/snapshots/v3.65/snapshot-update-response.json @@ -0,0 +1,18 @@ +{ + "snapshot": { + "created_at": "2019-03-12T04:53:53.426591", + "description": "This is yet, another snapshot.", + "id": "43666194-8e72-451a-b7bb-54fef763b2b8", + "metadata": { + "key": "v3" + }, + "name": "snap-002", + "size": 10, + "status": "creating", + "updated_at": null, + "volume_id": "070c942d-9909-42e9-a467-7a781f150c58", + "group_snapshot_id": null, + "user_id": "c853ca26-e8ea-4797-8a52-ee124a013d0e", + "consumes_quota": true + } +} diff --git a/api-ref/source/v3/samples/snapshots/v3.65/snapshots-list-detailed-response.json b/api-ref/source/v3/samples/snapshots/v3.65/snapshots-list-detailed-response.json new file mode 100644 index 00000000000..7c9e8d4760f --- /dev/null +++ b/api-ref/source/v3/samples/snapshots/v3.65/snapshots-list-detailed-response.json @@ -0,0 +1,22 @@ +{ + "snapshots": [ + { + "created_at": "2019-03-11T16:24:36.464445", + "description": "Daily backup", + "id": "d0083dc5-8795-4c1a-bc9c-74f70006c205", + "metadata": { + "key": "v3" + }, + "name": "snap-001", + "os-extended-snapshot-attributes:progress": "0%", + "os-extended-snapshot-attributes:project_id": "89afd400-b646-4bbc-b12b-c0a4d63e5bd3", + "size": 10, + "status": "creating", + "updated_at": null, + "volume_id": "7acd675e-4e06-4653-af9f-2ecd546342d6", + "group_snapshot_id": null, + "user_id": "c853ca26-e8ea-4797-8a52-ee124a013d0e", + "consumes_quota": true + } + ] +} diff --git a/api-ref/source/v3/samples/versions/version-show-response.json b/api-ref/source/v3/samples/versions/version-show-response.json index 5e34efae7c3..1d79bf38f19 100644 --- a/api-ref/source/v3/samples/versions/version-show-response.json +++ b/api-ref/source/v3/samples/versions/version-show-response.json @@ -21,8 +21,8 @@ ], "min_version": "3.0", "status": "CURRENT", - "updated": "2021-05-30T00:00:00Z", - "version": "3.64" + "updated": "2021-08-25T00:00:00Z", + "version": "3.65" } ] } diff --git a/api-ref/source/v3/samples/versions/versions-response.json b/api-ref/source/v3/samples/versions/versions-response.json index 529ca5d2140..dd7b843d105 100644 --- a/api-ref/source/v3/samples/versions/versions-response.json +++ b/api-ref/source/v3/samples/versions/versions-response.json @@ -21,8 +21,8 @@ ], "min_version": "3.0", "status": "CURRENT", - "updated": "2021-05-30T00:00:00Z", - "version": "3.64" + "updated": "2021-08-25T00:00:00Z", + "version": "3.65" } ] } diff --git a/api-ref/source/v3/samples/volumes/v3.65/volume-create-response.json b/api-ref/source/v3/samples/volumes/v3.65/volume-create-response.json new file mode 100644 index 00000000000..4dc4775ff5c --- /dev/null +++ b/api-ref/source/v3/samples/volumes/v3.65/volume-create-response.json @@ -0,0 +1,41 @@ +{ + "volume": { + "attachments": [], + "availability_zone": "nova", + "bootable": "false", + "consistencygroup_id": null, + "created_at": "2018-11-28T06:21:12.715987", + "description": null, + "encrypted": false, + "id": "2b955850-f177-45f7-9f49-ecb2c256d161", + "links": [ + { + "href": "http://127.0.0.1:33951/v3/89afd400-b646-4bbc-b12b-c0a4d63e5bd3/volumes/2b955850-f177-45f7-9f49-ecb2c256d161", + "rel": "self" + }, + { + "href": "http://127.0.0.1:33951/89afd400-b646-4bbc-b12b-c0a4d63e5bd3/volumes/2b955850-f177-45f7-9f49-ecb2c256d161", + "rel": "bookmark" + } + ], + "metadata": {}, + "migration_status": null, + "multiattach": false, + "name": null, + "replication_status": null, + "size": 10, + "snapshot_id": null, + "source_volid": null, + "status": "creating", + "updated_at": null, + "user_id": "c853ca26-e8ea-4797-8a52-ee124a013d0e", + "volume_type": "__DEFAULT__", + "group_id": null, + "provider_id": null, + "service_uuid": null, + "shared_targets": true, + "cluster_name": null, + "volume_type_id": "5fed9d7c-401d-46e2-8e80-f30c70cb7e1d", + "consumes_quota": true + } +} diff --git a/api-ref/source/v3/samples/volumes/v3.65/volume-show-response.json b/api-ref/source/v3/samples/volumes/v3.65/volume-show-response.json new file mode 100644 index 00000000000..690bb228b68 --- /dev/null +++ b/api-ref/source/v3/samples/volumes/v3.65/volume-show-response.json @@ -0,0 +1,45 @@ +{ + "volume": { + "attachments": [], + "availability_zone": "nova", + "bootable": "false", + "consistencygroup_id": null, + "created_at": "2018-11-29T06:50:07.770785", + "description": null, + "encrypted": false, + "id": "f7223234-1afc-4d19-bfa3-d19deb6235ef", + "links": [ + { + "href": "http://127.0.0.1:45839/v3/89afd400-b646-4bbc-b12b-c0a4d63e5bd3/volumes/f7223234-1afc-4d19-bfa3-d19deb6235ef", + "rel": "self" + }, + { + "href": "http://127.0.0.1:45839/89afd400-b646-4bbc-b12b-c0a4d63e5bd3/volumes/f7223234-1afc-4d19-bfa3-d19deb6235ef", + "rel": "bookmark" + } + ], + "metadata": {}, + "migration_status": null, + "multiattach": false, + "name": null, + "os-vol-host-attr:host": null, + "os-vol-mig-status-attr:migstat": null, + "os-vol-mig-status-attr:name_id": null, + "os-vol-tenant-attr:tenant_id": "89afd400-b646-4bbc-b12b-c0a4d63e5bd3", + "replication_status": null, + "size": 10, + "snapshot_id": null, + "source_volid": null, + "status": "creating", + "updated_at": null, + "user_id": "c853ca26-e8ea-4797-8a52-ee124a013d0e", + "volume_type": "__DEFAULT__", + "provider_id": null, + "group_id": null, + "service_uuid": null, + "shared_targets": true, + "cluster_name": null, + "volume_type_id": "5fed9d7c-401d-46e2-8e80-f30c70cb7e1d", + "consumes_quota": true + } +} diff --git a/api-ref/source/v3/samples/volumes/v3.65/volume-update-response.json b/api-ref/source/v3/samples/volumes/v3.65/volume-update-response.json new file mode 100644 index 00000000000..3d8f2092642 --- /dev/null +++ b/api-ref/source/v3/samples/volumes/v3.65/volume-update-response.json @@ -0,0 +1,43 @@ +{ + "volume": { + "attachments": [], + "availability_zone": "nova", + "bootable": "false", + "consistencygroup_id": null, + "created_at": "2018-11-29T06:59:23.679903", + "description": "This is yet, another volume.", + "encrypted": false, + "id": "8b2459d1-0059-4e14-a89f-dfa73a452af6", + "links": [ + { + "href": "http://127.0.0.1:41467/v3/89afd400-b646-4bbc-b12b-c0a4d63e5bd3/volumes/8b2459d1-0059-4e14-a89f-dfa73a452af6", + "rel": "self" + }, + { + "href": "http://127.0.0.1:41467/89afd400-b646-4bbc-b12b-c0a4d63e5bd3/volumes/8b2459d1-0059-4e14-a89f-dfa73a452af6", + "rel": "bookmark" + } + ], + "metadata": { + "name": "metadata0" + }, + "migration_status": null, + "multiattach": false, + "name": "vol-003", + "replication_status": null, + "size": 10, + "snapshot_id": null, + "source_volid": null, + "status": "creating", + "updated_at": null, + "user_id": "c853ca26-e8ea-4797-8a52-ee124a013d0e", + "volume_type": "__DEFAULT__", + "group_id": null, + "provider_id": null, + "service_uuid": null, + "shared_targets": true, + "cluster_name": null, + "volume_type_id": "5fed9d7c-401d-46e2-8e80-f30c70cb7e1d", + "consumes_quota": true + } +} diff --git a/api-ref/source/v3/samples/volumes/v3.65/volumes-list-detailed-response.json b/api-ref/source/v3/samples/volumes/v3.65/volumes-list-detailed-response.json new file mode 100644 index 00000000000..ceaa39c79e2 --- /dev/null +++ b/api-ref/source/v3/samples/volumes/v3.65/volumes-list-detailed-response.json @@ -0,0 +1,47 @@ +{ + "volumes": [ + { + "attachments": [], + "availability_zone": "nova", + "bootable": "false", + "consistencygroup_id": null, + "created_at": "2018-11-28T06:25:15.288987", + "description": null, + "encrypted": false, + "id": "cb49b381-9012-40cb-b8ee-80c19a4801b5", + "links": [ + { + "href": "http://127.0.0.1:43543/v3/89afd400-b646-4bbc-b12b-c0a4d63e5bd3/volumes/cb49b381-9012-40cb-b8ee-80c19a4801b5", + "rel": "self" + }, + { + "href": "http://127.0.0.1:43543/89afd400-b646-4bbc-b12b-c0a4d63e5bd3/volumes/cb49b381-9012-40cb-b8ee-80c19a4801b5", + "rel": "bookmark" + } + ], + "metadata": {}, + "migration_status": null, + "multiattach": false, + "name": null, + "os-vol-host-attr:host": null, + "os-vol-mig-status-attr:migstat": null, + "os-vol-mig-status-attr:name_id": null, + "os-vol-tenant-attr:tenant_id": "89afd400-b646-4bbc-b12b-c0a4d63e5bd3", + "replication_status": null, + "size": 10, + "snapshot_id": null, + "source_volid": null, + "status": "creating", + "updated_at": null, + "user_id": "c853ca26-e8ea-4797-8a52-ee124a013d0e", + "volume_type": "__DEFAULT__", + "volume_type_id": "5fed9d7c-401d-46e2-8e80-f30c70cb7e1d", + "provider_id": null, + "group_id": null, + "service_uuid": null, + "shared_targets": true, + "cluster_name": null, + "consumes_quota": true + } + ] +} diff --git a/api-ref/source/v3/samples/volumes/volume-create-response.json b/api-ref/source/v3/samples/volumes/volume-create-response.json index 30feb2ac669..4bd8a6e0fac 100644 --- a/api-ref/source/v3/samples/volumes/volume-create-response.json +++ b/api-ref/source/v3/samples/volumes/volume-create-response.json @@ -31,4 +31,4 @@ "user_id": "c853ca26-e8ea-4797-8a52-ee124a013d0e", "volume_type": "__DEFAULT__" } -} \ No newline at end of file +} diff --git a/api-ref/source/v3/samples/volumes/volume-show-response.json b/api-ref/source/v3/samples/volumes/volume-show-response.json index 186411e9012..faf9be288d5 100644 --- a/api-ref/source/v3/samples/volumes/volume-show-response.json +++ b/api-ref/source/v3/samples/volumes/volume-show-response.json @@ -35,4 +35,4 @@ "user_id": "c853ca26-e8ea-4797-8a52-ee124a013d0e", "volume_type": "__DEFAULT__" } -} \ No newline at end of file +} diff --git a/api-ref/source/v3/samples/volumes/volume-update-response.json b/api-ref/source/v3/samples/volumes/volume-update-response.json index 4a587f679d9..a41d41fba23 100644 --- a/api-ref/source/v3/samples/volumes/volume-update-response.json +++ b/api-ref/source/v3/samples/volumes/volume-update-response.json @@ -33,4 +33,4 @@ "user_id": "c853ca26-e8ea-4797-8a52-ee124a013d0e", "volume_type": "__DEFAULT__" } -} \ No newline at end of file +} diff --git a/api-ref/source/v3/volumes-v3-snapshots.inc b/api-ref/source/v3/volumes-v3-snapshots.inc index c7a63793f1e..f3d6608577f 100644 --- a/api-ref/source/v3/volumes-v3-snapshots.inc +++ b/api-ref/source/v3/volumes-v3-snapshots.inc @@ -67,6 +67,7 @@ Request - offset: offset - marker: marker - with_count: with_count + - consumes_quota: filter_consumes_quota Response Parameters @@ -89,11 +90,12 @@ Response Parameters - updated_at: updated_at - snapshots_links: links_snap - group_snapshot_id: group_snapshot_id_3_14 + - consumes_quota: consumes_quota -Response Example (v3.41) +Response Example (v3.65) ------------------------ -.. literalinclude:: ./samples/snapshots/v3.41/snapshots-list-detailed-response.json +.. literalinclude:: ./samples/snapshots/v3.65/snapshots-list-detailed-response.json :language: javascript @@ -149,11 +151,12 @@ Response Parameters - size: size - updated_at: updated_at - group_snapshot_id: group_snapshot_id_3_14 + - consumes_quota: consumes_quota -Response Example (v3.41) +Response Example (v3.65) ------------------------ -.. literalinclude:: ./samples/snapshots/v3.41/snapshot-create-response.json +.. literalinclude:: ./samples/snapshots/v3.65/snapshot-create-response.json :language: javascript @@ -189,6 +192,7 @@ Request - limit: limit - offset: offset - marker: marker + - consumes_quota: filter_consumes_quota - with_count: with_count @@ -388,11 +392,12 @@ Response Parameters - metadata: metadata - updated_at: updated_at - group_snapshot_id: group_snapshot_id_3_14 + - consumes_quota: consumes_quota -Response Example (v3.41) +Response Example (v3.65) ------------------------ -.. literalinclude:: ./samples/snapshots/v3.41/snapshot-show-response.json +.. literalinclude:: ./samples/snapshots/v3.65/snapshot-show-response.json :language: javascript @@ -446,11 +451,12 @@ Response Parameters - user_id: user_id_min - metadata: metadata - group_snapshot_id: group_snapshot_id_3_14 + - consumes_quota: consumes_quota -Response Example (v3.41) +Response Example (v3.65) ------------------------ -.. literalinclude:: ./samples/snapshots/v3.41/snapshot-update-response.json +.. literalinclude:: ./samples/snapshots/v3.65/snapshot-update-response.json :language: javascript diff --git a/api-ref/source/v3/volumes-v3-volumes.inc b/api-ref/source/v3/volumes-v3-volumes.inc index f40b3860633..2d7d8f4d315 100644 --- a/api-ref/source/v3/volumes-v3-volumes.inc +++ b/api-ref/source/v3/volumes-v3-volumes.inc @@ -98,6 +98,7 @@ Request - with_count: with_count - created_at: filter_created_at - updated_at: filter_updated_at + - consumes_quota: filter_consumes_quota Response Parameters @@ -140,13 +141,14 @@ Response Parameters - service_uuid: service_uuid - shared_targets: shared_targets - cluster_name: cluster_name + - consumes_quota: consumes_quota - count: count -Response Example (v3.63) +Response Example (v3.65) ------------------------ -.. literalinclude:: ./samples/volumes/v3.63/volumes-list-detailed-response.json +.. literalinclude:: ./samples/volumes/v3.65/volumes-list-detailed-response.json :language: javascript @@ -258,11 +260,12 @@ Response Parameters - service_uuid: service_uuid - shared_targets: shared_targets - cluster_name: cluster_name + - consumes_quota: consumes_quota -Response Example (v3.63) +Response Example (v3.65) ------------------------ -.. literalinclude:: ./samples/volumes/v3.63/volume-create-response.json +.. literalinclude:: ./samples/volumes/v3.65/volume-create-response.json :language: javascript @@ -301,6 +304,7 @@ Request - marker: marker - with_count: with_count - created_at: filter_created_at + - consumes_quota: filter_consumes_quota - updated_at: filter_updated_at @@ -394,12 +398,13 @@ Response Parameters - cluster_name: cluster_name - provider_id: provider_id - group_id: group_id_optional + - consumes_quota: consumes_quota -Response Example (v3.63) +Response Example (v3.65) ------------------------ -.. literalinclude:: ./samples/volumes/v3.63/volume-show-response.json +.. literalinclude:: ./samples/volumes/v3.65/volume-show-response.json :language: javascript @@ -473,12 +478,13 @@ Response Parameters - service_uuid: service_uuid - shared_targets: shared_targets - cluster_name: cluster_name + - consumes_quota: consumes_quota -Response Example (v3.63) +Response Example (v3.65) ------------------------ -.. literalinclude:: ./samples/volumes/v3.63/volume-update-response.json +.. literalinclude:: ./samples/volumes/v3.65/volume-update-response.json :language: javascript diff --git a/cinder/api/common.py b/cinder/api/common.py index 4320527862c..7479237eeab 100644 --- a/cinder/api/common.py +++ b/cinder/api/common.py @@ -49,7 +49,8 @@ LOG = logging.getLogger(__name__) _FILTERS_COLLECTION = None ATTRIBUTE_CONVERTERS = {'name~': 'display_name~', - 'description~': 'display_description~'} + 'description~': 'display_description~', + 'consumes_quota': 'use_quota'} METADATA_TYPES = enum.Enum('METADATA_TYPES', 'user image') diff --git a/cinder/api/microversions.py b/cinder/api/microversions.py index e72c76c07a0..909dcd875fb 100644 --- a/cinder/api/microversions.py +++ b/cinder/api/microversions.py @@ -167,6 +167,8 @@ VOLUME_TYPE_ID_IN_VOLUME_DETAIL = '3.63' ENCRYPTION_KEY_ID_IN_DETAILS = '3.64' +USE_QUOTA = '3.65' + def get_mv_header(version): """Gets a formatted HTTP microversion header. diff --git a/cinder/api/openstack/api_version_request.py b/cinder/api/openstack/api_version_request.py index b6597346de1..7f0769e9b19 100644 --- a/cinder/api/openstack/api_version_request.py +++ b/cinder/api/openstack/api_version_request.py @@ -148,14 +148,17 @@ REST_API_VERSION_HISTORY = """ ("GET /v3/{project_id}/volumes/detail") and volume-show ("GET /v3/{project_id}/volumes/{volume_id}") calls. * 3.64 - Include 'encryption_key_id' in volume and backup details + * 3.65 - Include 'consumes_quota' in volume and snapshot details + - Accept 'consumes_quota' filter in volume and snapshot list + operation. """ # The minimum and maximum versions of the API supported # The default api version request is defined to be the # minimum version of the API supported. _MIN_API_VERSION = "3.0" -_MAX_API_VERSION = "3.64" -UPDATED = "2021-05-30T00:00:00Z" +_MAX_API_VERSION = "3.65" +UPDATED = "2021-08-25T00:00:00Z" # NOTE(cyeoh): min and max versions declared as functions so we can diff --git a/cinder/api/v3/snapshots.py b/cinder/api/v3/snapshots.py index 9debfcebcdb..d5c1a47ba9e 100644 --- a/cinder/api/v3/snapshots.py +++ b/cinder/api/v3/snapshots.py @@ -53,16 +53,24 @@ class SnapshotsController(snapshots_v2.SnapshotsController): LOG.debug('Could not evaluate value %s, assuming string', search_opts['metadata']) + if 'use_quota' in search_opts: + search_opts['use_quota'] = utils.get_bool_param('use_quota', + search_opts) + + MV_ADDED_FILTERS = ( + (mv.get_prior_version(mv.SNAPSHOT_LIST_METADATA_FILTER), 'metadata'), + # REST API receives consumes_quota, but process_general_filtering + # transforms it into use_quota + (mv.get_prior_version(mv.USE_QUOTA), 'use_quota'), + ) + @common.process_general_filtering('snapshot') def _process_snapshot_filtering(self, context=None, filters=None, req_version=None): """Formats allowed filters""" - - # if the max version is less than SNAPSHOT_LIST_METADATA_FILTER - # metadata based filtering is not supported - if req_version.matches( - None, mv.get_prior_version(mv.SNAPSHOT_LIST_METADATA_FILTER)): - filters.pop('metadata', None) + for version, field in self.MV_ADDED_FILTERS: + if req_version.matches(None, version): + filters.pop(field, None) # Filter out invalid options allowed_search_options = self._get_snapshot_filter_options() diff --git a/cinder/api/v3/views/snapshots.py b/cinder/api/v3/views/snapshots.py index 857df059ca9..cd09f9cd5c9 100644 --- a/cinder/api/v3/views/snapshots.py +++ b/cinder/api/v3/views/snapshots.py @@ -27,9 +27,11 @@ class ViewBuilder(views_v2.ViewBuilder): req_version = request.api_version_request # Add group_snapshot_id if min version is greater than or equal # to GROUP_SNAPSHOTS. + snap = snapshot_ref['snapshot'] if req_version.matches(mv.GROUP_SNAPSHOTS, None): - snapshot_ref['snapshot']['group_snapshot_id'] = ( - snapshot.get('group_snapshot_id')) + snap['group_snapshot_id'] = snapshot.get('group_snapshot_id') if req_version.matches(mv.SNAPSHOT_LIST_USER_ID, None): - snapshot_ref['snapshot']['user_id'] = snapshot.get('user_id') + snap['user_id'] = snapshot.get('user_id') + if req_version.matches(mv.USE_QUOTA): + snap['consumes_quota'] = snapshot.get('use_quota') return snapshot_ref diff --git a/cinder/api/v3/views/volumes.py b/cinder/api/v3/views/volumes.py index ed171c7bde1..ded603a4fc2 100644 --- a/cinder/api/v3/views/volumes.py +++ b/cinder/api/v3/views/volumes.py @@ -77,6 +77,9 @@ class ViewBuilder(views_v2.ViewBuilder): encryption_key_id != cinder_constants.FIXED_KEY_ID): volume_ref['volume']['encryption_key_id'] = encryption_key_id + if req_version.matches(mv.USE_QUOTA): + volume_ref['volume']['consumes_quota'] = volume.get('use_quota') + return volume_ref def _list_view(self, func, request, volumes, volume_count, diff --git a/cinder/api/v3/volumes.py b/cinder/api/v3/volumes.py index e5c5f18ef16..0d2841c4f9e 100644 --- a/cinder/api/v3/volumes.py +++ b/cinder/api/v3/volumes.py @@ -80,18 +80,23 @@ class VolumeController(volumes_v2.VolumeController): return webob.Response(status_int=HTTPStatus.ACCEPTED) + MV_ADDED_FILTERS = ( + (mv.get_prior_version(mv.VOLUME_LIST_GLANCE_METADATA), + 'glance_metadata'), + (mv.get_prior_version(mv.VOLUME_LIST_GROUP), 'group_id'), + (mv.get_prior_version(mv.VOLUME_TIME_COMPARISON_FILTER), 'created_at'), + (mv.get_prior_version(mv.VOLUME_TIME_COMPARISON_FILTER), 'updated_at'), + # REST API receives consumes_quota, but process_general_filtering + # transforms it into use_quota + (mv.get_prior_version(mv.USE_QUOTA), 'use_quota'), + ) + @common.process_general_filtering('volume') def _process_volume_filtering(self, context=None, filters=None, req_version=None): - if req_version.matches(None, mv.MESSAGES): - filters.pop('glance_metadata', None) - - if req_version.matches(None, mv.BACKUP_UPDATE): - filters.pop('group_id', None) - - if req_version.matches(None, mv.SUPPORT_TRANSFER_PAGINATION): - filters.pop('created_at', None) - filters.pop('updated_at', None) + for version, field in self.MV_ADDED_FILTERS: + if req_version.matches(None, version): + filters.pop(field, None) api_utils.remove_invalid_filter_options( context, filters, @@ -155,6 +160,9 @@ class VolumeController(volumes_v2.VolumeController): if 'name' in filters: filters['display_name'] = filters.pop('name') + if 'use_quota' in filters: + filters['use_quota'] = utils.get_bool_param('use_quota', filters) + self._handle_time_comparison_filters(filters) strict = req.api_version_request.matches( diff --git a/cinder/tests/functional/api_sample_tests/samples/snapshots/v3.65/snapshot-create-response.json.tpl b/cinder/tests/functional/api_sample_tests/samples/snapshots/v3.65/snapshot-create-response.json.tpl new file mode 100644 index 00000000000..e2f6f55de0f --- /dev/null +++ b/cinder/tests/functional/api_sample_tests/samples/snapshots/v3.65/snapshot-create-response.json.tpl @@ -0,0 +1,18 @@ +{ + "snapshot": { + "created_at": "%(strtime)s", + "description": "Daily backup", + "id": "%(uuid)s", + "metadata": { + "key": "v3" + }, + "name": "snap-001", + "size": 10, + "status": "creating", + "updated_at": null, + "volume_id": "%(uuid)s", + "group_snapshot_id": null, + "user_id": "%(uuid)s", + "consumes_quota": true + } +} diff --git a/cinder/tests/functional/api_sample_tests/samples/snapshots/v3.65/snapshot-show-response.json.tpl b/cinder/tests/functional/api_sample_tests/samples/snapshots/v3.65/snapshot-show-response.json.tpl new file mode 100644 index 00000000000..2656017afc8 --- /dev/null +++ b/cinder/tests/functional/api_sample_tests/samples/snapshots/v3.65/snapshot-show-response.json.tpl @@ -0,0 +1,20 @@ +{ + "snapshot": { + "created_at": "%(strtime)s", + "description": "Daily backup", + "id": "%(uuid)s", + "metadata": { + "key": "v3" + }, + "name": "snap-001", + "os-extended-snapshot-attributes:progress": "0%", + "os-extended-snapshot-attributes:project_id": "%(uuid)s", + "size": 10, + "status": "creating", + "updated_at": null, + "volume_id": "%(uuid)s", + "group_snapshot_id": null, + "user_id": "%(uuid)s", + "consumes_quota": true + } +} diff --git a/cinder/tests/functional/api_sample_tests/samples/snapshots/v3.65/snapshot-update-response.json.tpl b/cinder/tests/functional/api_sample_tests/samples/snapshots/v3.65/snapshot-update-response.json.tpl new file mode 100644 index 00000000000..fb5285c6e62 --- /dev/null +++ b/cinder/tests/functional/api_sample_tests/samples/snapshots/v3.65/snapshot-update-response.json.tpl @@ -0,0 +1,18 @@ +{ + "snapshot": { + "created_at": "%(strtime)s", + "description": "This is yet, another snapshot.", + "id": "%(uuid)s", + "metadata": { + "key": "v3" + }, + "name": "snap-002", + "size": 10, + "status": "creating", + "updated_at": null, + "volume_id": "%(uuid)s", + "group_snapshot_id": null, + "user_id": "%(uuid)s", + "consumes_quota": true + } +} diff --git a/cinder/tests/functional/api_sample_tests/samples/snapshots/v3.65/snapshots-list-detailed-response.json.tpl b/cinder/tests/functional/api_sample_tests/samples/snapshots/v3.65/snapshots-list-detailed-response.json.tpl new file mode 100644 index 00000000000..a521bc85c9e --- /dev/null +++ b/cinder/tests/functional/api_sample_tests/samples/snapshots/v3.65/snapshots-list-detailed-response.json.tpl @@ -0,0 +1,22 @@ +{ + "snapshots": [ + { + "created_at": "%(strtime)s", + "description": "Daily backup", + "id": "%(uuid)s", + "metadata": { + "key": "v3" + }, + "name": "snap-001", + "os-extended-snapshot-attributes:progress": "0%", + "os-extended-snapshot-attributes:project_id": "%(uuid)s", + "size": 10, + "status": "creating", + "updated_at": null, + "volume_id": "%(uuid)s", + "group_snapshot_id": null, + "user_id": "%(uuid)s", + "consumes_quota": true + } + ] +} diff --git a/cinder/tests/functional/api_sample_tests/samples/volumes/v3.65/volume-create-response.json.tpl b/cinder/tests/functional/api_sample_tests/samples/volumes/v3.65/volume-create-response.json.tpl new file mode 100644 index 00000000000..ecfa16b1f6f --- /dev/null +++ b/cinder/tests/functional/api_sample_tests/samples/volumes/v3.65/volume-create-response.json.tpl @@ -0,0 +1,41 @@ +{ + "volume": { + "attachments": [], + "availability_zone": "nova", + "bootable": "false", + "consistencygroup_id": null, + "created_at": "%(strtime)s", + "description": null, + "encrypted": false, + "id": "%(uuid)s", + "links": [ + { + "href": "%(host)s/v3/89afd400-b646-4bbc-b12b-c0a4d63e5bd3/volumes/%(uuid)s", + "rel": "self" + }, + { + "href": "%(host)s/89afd400-b646-4bbc-b12b-c0a4d63e5bd3/volumes/%(uuid)s", + "rel": "bookmark" + } + ], + "metadata": {}, + "migration_status": null, + "multiattach": false, + "name": null, + "replication_status": null, + "size": 10, + "snapshot_id": null, + "source_volid": null, + "status": "creating", + "updated_at": null, + "user_id": "%(uuid)s", + "volume_type": "__DEFAULT__", + "group_id": null, + "provider_id": null, + "service_uuid": null, + "shared_targets": true, + "cluster_name": null, + "volume_type_id": "%(uuid)s", + "consumes_quota": true + } +} diff --git a/cinder/tests/functional/api_sample_tests/samples/volumes/v3.65/volume-show-response.json.tpl b/cinder/tests/functional/api_sample_tests/samples/volumes/v3.65/volume-show-response.json.tpl new file mode 100644 index 00000000000..4fc2ab447db --- /dev/null +++ b/cinder/tests/functional/api_sample_tests/samples/volumes/v3.65/volume-show-response.json.tpl @@ -0,0 +1,45 @@ +{ + "volume": { + "attachments": [], + "availability_zone": "nova", + "bootable": "false", + "consistencygroup_id": null, + "created_at": "%(strtime)s", + "description": null, + "encrypted": false, + "id": "%(uuid)s", + "links": [ + { + "href": "%(host)s/v3/89afd400-b646-4bbc-b12b-c0a4d63e5bd3/volumes/%(uuid)s", + "rel": "self" + }, + { + "href": "%(host)s/89afd400-b646-4bbc-b12b-c0a4d63e5bd3/volumes/%(uuid)s", + "rel": "bookmark" + } + ], + "metadata": {}, + "migration_status": null, + "multiattach": false, + "name": null, + "os-vol-host-attr:host": null, + "os-vol-mig-status-attr:migstat": null, + "os-vol-mig-status-attr:name_id": null, + "os-vol-tenant-attr:tenant_id": "%(uuid)s", + "replication_status": null, + "size": 10, + "snapshot_id": null, + "source_volid": null, + "status": "creating", + "updated_at": null, + "user_id": "%(uuid)s", + "volume_type": "__DEFAULT__", + "provider_id": null, + "group_id": null, + "service_uuid": null, + "shared_targets": true, + "cluster_name": null, + "volume_type_id": "%(uuid)s", + "consumes_quota": true + } +} diff --git a/cinder/tests/functional/api_sample_tests/samples/volumes/v3.65/volume-update-response.json.tpl b/cinder/tests/functional/api_sample_tests/samples/volumes/v3.65/volume-update-response.json.tpl new file mode 100644 index 00000000000..bc272350471 --- /dev/null +++ b/cinder/tests/functional/api_sample_tests/samples/volumes/v3.65/volume-update-response.json.tpl @@ -0,0 +1,43 @@ +{ + "volume": { + "attachments": [], + "availability_zone": "nova", + "bootable": "false", + "consistencygroup_id": null, + "created_at": "%(strtime)s", + "description": "This is yet, another volume.", + "encrypted": false, + "id": "%(uuid)s", + "links": [ + { + "href": "%(host)s/v3/89afd400-b646-4bbc-b12b-c0a4d63e5bd3/volumes/%(uuid)s", + "rel": "self" + }, + { + "href": "%(host)s/89afd400-b646-4bbc-b12b-c0a4d63e5bd3/volumes/%(uuid)s", + "rel": "bookmark" + } + ], + "metadata": { + "name": "metadata0" + }, + "migration_status": null, + "multiattach": false, + "name": "vol-003", + "replication_status": null, + "size": 10, + "snapshot_id": null, + "source_volid": null, + "status": "creating", + "updated_at": null, + "user_id": "%(uuid)s", + "volume_type": "__DEFAULT__", + "group_id": null, + "provider_id": null, + "service_uuid": null, + "shared_targets": true, + "cluster_name": null, + "volume_type_id": "%(uuid)s", + "consumes_quota": true + } +} diff --git a/cinder/tests/functional/api_sample_tests/samples/volumes/v3.65/volumes-list-detailed-response.json.tpl b/cinder/tests/functional/api_sample_tests/samples/volumes/v3.65/volumes-list-detailed-response.json.tpl new file mode 100644 index 00000000000..3aff1e88411 --- /dev/null +++ b/cinder/tests/functional/api_sample_tests/samples/volumes/v3.65/volumes-list-detailed-response.json.tpl @@ -0,0 +1,47 @@ +{ + "volumes": [ + { + "attachments": [], + "availability_zone": "nova", + "bootable": "false", + "consistencygroup_id": null, + "created_at": "%(strtime)s", + "description": null, + "encrypted": false, + "id": "%(uuid)s", + "links": [ + { + "href": "%(host)s/v3/89afd400-b646-4bbc-b12b-c0a4d63e5bd3/volumes/%(uuid)s", + "rel": "self" + }, + { + "href": "%(host)s/89afd400-b646-4bbc-b12b-c0a4d63e5bd3/volumes/%(uuid)s", + "rel": "bookmark" + } + ], + "metadata": {}, + "migration_status": null, + "multiattach": false, + "name": null, + "os-vol-host-attr:host": null, + "os-vol-mig-status-attr:migstat": null, + "os-vol-mig-status-attr:name_id": null, + "os-vol-tenant-attr:tenant_id": "%(uuid)s", + "replication_status": null, + "size": 10, + "snapshot_id": null, + "source_volid": null, + "status": "creating", + "updated_at": null, + "user_id": "%(uuid)s", + "volume_type": "%(name)s", + "volume_type_id": "%(uuid)s", + "service_uuid": null, + "provider_id": null, + "group_id": null, + "shared_targets": true, + "cluster_name": null, + "consumes_quota": true + } + ] +} diff --git a/cinder/tests/functional/api_sample_tests/test_snapshots.py b/cinder/tests/functional/api_sample_tests/test_snapshots.py index abec1251a99..14200ff012b 100644 --- a/cinder/tests/functional/api_sample_tests/test_snapshots.py +++ b/cinder/tests/functional/api_sample_tests/test_snapshots.py @@ -39,7 +39,8 @@ class SnapshotBaseTest(test_base.VolumesSampleBase): @test_base.VolumesSampleBase.use_versions( mv.BASE_VERSION, # 3.0 mv.GROUP_SNAPSHOTS, # 3.14 - mv.SNAPSHOT_LIST_USER_ID) # 3.41 + mv.SNAPSHOT_LIST_USER_ID, # 3.41 + mv.USE_QUOTA) # 3.65 class SnapshotDetailTests(SnapshotBaseTest): """Test snapshot details returned for operations with different MVs. diff --git a/cinder/tests/functional/api_sample_tests/test_volumes.py b/cinder/tests/functional/api_sample_tests/test_volumes.py index a80d3ef1cbc..24ef1d5da9a 100644 --- a/cinder/tests/functional/api_sample_tests/test_volumes.py +++ b/cinder/tests/functional/api_sample_tests/test_volumes.py @@ -22,7 +22,8 @@ from cinder.tests.functional import api_samples_test_base as test_base mv.VOLUME_DETAIL_PROVIDER_ID, # 3.21 mv.VOLUME_SHARED_TARGETS_AND_SERVICE_FIELDS, # 3.48 mv.VOLUME_CLUSTER_NAME, # 3.61 - mv.VOLUME_TYPE_ID_IN_VOLUME_DETAIL) # 3.63 + mv.VOLUME_TYPE_ID_IN_VOLUME_DETAIL, # 3.63 + mv.USE_QUOTA) # 3.65 class VolumeDetailTests(test_base.VolumesSampleBase): """Test volume details returned for operations with different MVs. diff --git a/cinder/tests/unit/api/test_common.py b/cinder/tests/unit/api/test_common.py index ecd38934096..e18bb24ccf6 100644 --- a/cinder/tests/unit/api/test_common.py +++ b/cinder/tests/unit/api/test_common.py @@ -487,12 +487,13 @@ class GeneralFiltersTest(test.TestCase): 'expected': ["name", "status", "metadata", "bootable", "migration_status", "availability_zone", "group_id", - "size", "created_at", "updated_at"]}, + "size", "created_at", "updated_at", + "consumes_quota"]}, {'resource': 'backup', 'expected': ["name", "status", "volume_id"]}, {'resource': 'snapshot', 'expected': ["name", "status", "volume_id", "metadata", - "availability_zone"]}, + "availability_zone", "consumes_quota"]}, {'resource': 'group_snapshot', 'expected': ["name", "status", "group_id"]}, {'resource': 'attachment', diff --git a/cinder/tests/unit/api/v3/test_snapshots.py b/cinder/tests/unit/api/v3/test_snapshots.py index d984c8c5319..473ae5b89be 100644 --- a/cinder/tests/unit/api/v3/test_snapshots.py +++ b/cinder/tests/unit/api/v3/test_snapshots.py @@ -115,6 +115,28 @@ class SnapshotApiTest(test.TestCase): self.assertNotIn('group_snapshot_id', resp_dict['snapshot']) self.assertNotIn('user_id', resp_dict['snapshot']) + @ddt.data( + (True, True, mv.USE_QUOTA), + (True, False, mv.USE_QUOTA), + (False, True, mv.get_prior_version(mv.USE_QUOTA)), + (False, False, mv.get_prior_version(mv.USE_QUOTA)), + ) + @ddt.unpack + def test_snapshot_show_with_use_quota(self, present, value, microversion): + volume = test_utils.create_volume(self.ctx, host='test_host1', + cluster_name='cluster1', + availability_zone='nova1') + snapshot = test_utils.create_snapshot(self.ctx, volume.id, + use_quota=value) + + url = '/v3/snapshots?%s' % snapshot.id + req = fakes.HTTPRequest.blank(url, version=microversion) + res_dict = self.controller.show(req, snapshot.id)['snapshot'] + if present: + self.assertIs(value, res_dict['consumes_quota']) + else: + self.assertNotIn('consumes_quota', res_dict) + def test_snapshot_show_invalid_id(self): snapshot_id = INVALID_UUID req = fakes.HTTPRequest.blank('/v3/snapshots/%s' % snapshot_id) @@ -133,25 +155,28 @@ class SnapshotApiTest(test.TestCase): body = {"snapshot": snap} self.controller.create(req, body=body) - @ddt.data(('host', 'test_host1', True), ('cluster_name', 'cluster1', True), - ('availability_zone', 'nova1', False)) + @ddt.data(('host', 'test_host1', True, mv.RESOURCE_FILTER), + ('cluster_name', 'cluster1', True, mv.RESOURCE_FILTER), + ('availability_zone', 'nova1', False, mv.RESOURCE_FILTER), + ('consumes_quota', 'true', False, mv.USE_QUOTA)) @ddt.unpack def test_snapshot_list_with_filter(self, filter_name, filter_value, - is_admin_user): + is_admin_user, microversion): volume1 = test_utils.create_volume(self.ctx, host='test_host1', cluster_name='cluster1', availability_zone='nova1') volume2 = test_utils.create_volume(self.ctx, host='test_host2', cluster_name='cluster2', availability_zone='nova2') - snapshot1 = test_utils.create_snapshot(self.ctx, volume1.id) - test_utils.create_snapshot(self.ctx, volume2.id) + snapshot1 = test_utils.create_snapshot(self.ctx, volume1.id, + use_quota=True) + test_utils.create_snapshot(self.ctx, volume2.id, use_quota=False) url = '/v3/snapshots?%s=%s' % (filter_name, filter_value) # Generic filtering is introduced since '3,31' and we add # 'availability_zone' support by using generic filtering. req = fakes.HTTPRequest.blank(url, use_admin_context=is_admin_user, - version=mv.RESOURCE_FILTER) + version=microversion) res_dict = self.controller.detail(req) self.assertEqual(1, len(res_dict['snapshots'])) diff --git a/cinder/tests/unit/api/v3/test_volumes.py b/cinder/tests/unit/api/v3/test_volumes.py index c2f787021ac..18e313527a0 100644 --- a/cinder/tests/unit/api/v3/test_volumes.py +++ b/cinder/tests/unit/api/v3/test_volumes.py @@ -343,6 +343,45 @@ class VolumeApiTest(test.TestCase): volumes = res_dict['volumes'] self.assertEqual(2, len(volumes)) + @ddt.data(('true', 0), ('false', 1)) + @ddt.unpack + def test_volume_list_with_quota_filter(self, use_quota, expected_index): + volumes = (test_utils.create_volume(self.ctxt, host='test_host1', + cluster_name='cluster1', + volume_type_id=None, + use_quota=True, + availability_zone='nova1'), + test_utils.create_volume(self.ctxt, host='test_host2', + cluster_name='cluster2', + volume_type_id=None, + use_quota=False, + availability_zone='nova2')) + req = fakes.HTTPRequest.blank( + '/v3/volumes?consumes_quota=%s' % use_quota, version=mv.USE_QUOTA) + res_dict = self.controller.detail(req) + self.assertEqual(1, len(res_dict['volumes'])) + self.assertEqual(volumes[expected_index].id, + res_dict['volumes'][0]['id']) + + def test_volume_list_without_quota_filter(self): + num_vols = 4 + vol_ids = set() + # Half of the volumes will use quota, the other half won't + for i in range(num_vols): + vol = test_utils.create_volume(self.ctxt, + use_quota=bool(i % 2), + host='test_host', + cluster_name='cluster', + volume_type_id=None, + availability_zone='nova1') + vol_ids.add(vol.id) + req = fakes.HTTPRequest.blank('/v3/volumes', version=mv.USE_QUOTA) + res_dict = self.controller.detail(req) + + res_vol_ids = {v['id'] for v in res_dict['volumes']} + self.assertEqual(num_vols, len(res_vol_ids)) + self.assertEqual(vol_ids, res_vol_ids) + def _fake_volumes_summary_request(self, version=mv.VOLUME_SUMMARY, all_tenant=False, @@ -900,6 +939,27 @@ class VolumeApiTest(test.TestCase): else: self.assertNotIn('encryption_key_id', volume_details) + @ddt.data( + (True, True, mv.USE_QUOTA), + (True, False, mv.USE_QUOTA), + (False, True, mv.get_prior_version(mv.USE_QUOTA)), + (False, False, mv.get_prior_version(mv.USE_QUOTA)), + ) + @ddt.unpack + def test_volume_show_with_use_quota(self, present, value, microversion): + volume = test_utils.create_volume(self.ctxt, + volume_type_id=None, + use_quota=value) + + req = fakes.HTTPRequest.blank('/v3/volumes/%s' % volume.id, + version=microversion) + volume_details = self.controller.show(req, volume.id)['volume'] + + if present: + self.assertIs(value, volume_details['consumes_quota']) + else: + self.assertNotIn('consumes_quota', volume_details) + def _fake_create_volume(self, size=1): vol = { 'display_name': 'fake_volume1', diff --git a/etc/cinder/resource_filters.json b/etc/cinder/resource_filters.json index 967361e692d..e9335ab14e5 100644 --- a/etc/cinder/resource_filters.json +++ b/etc/cinder/resource_filters.json @@ -1,10 +1,11 @@ { "volume": ["name", "status", "metadata", "bootable", "migration_status", "availability_zone", - "group_id", "size", "created_at", "updated_at"], + "group_id", "size", "created_at", "updated_at", + "consumes_quota"], "backup": ["name", "status", "volume_id"], "snapshot": ["name", "status", "volume_id", "metadata", - "availability_zone"], + "availability_zone", "consumes_quota"], "group": ["name"], "group_snapshot": ["name", "status", "group_id"], "attachment": ["volume_id", "status", "instance_id", "attach_status"], diff --git a/releasenotes/notes/mv-use_quota-b8e010f8f68a1eaa.yaml b/releasenotes/notes/mv-use_quota-b8e010f8f68a1eaa.yaml new file mode 100644 index 00000000000..e5c494e816a --- /dev/null +++ b/releasenotes/notes/mv-use_quota-b8e010f8f68a1eaa.yaml @@ -0,0 +1,14 @@ +--- +features: + - | + Starting with API microversion 3.65, a ``consumes_quota`` field + is included in the response body of volumes and snapshots to indicate + whether the volume is using quota or not. + + Additionally, ``consumes_quota`` can be used as a listing filter for + volumes and snapshots. Its availability is controlled by its inclusion in + ``etc/cinder/resource_filters.json``, where it is included by default. The + default listing behavior is not to use this filter. + + Only temporary resources created internally by cinder will have the value + set to ``false``.