Support count info in List&Detail APIs

This patch adds support for display count info
in volume, backup and snapshot's list&detail APIs
since microversion 3.45, for instance:

1. /v3/{project_id}/volumes?with_count=True
2. /v3/{project_id}/volumes/detail?with_count=True

Depends-On: 1c8fe0ade4
Change-Id: I2e92b27c36357120fcf0ec5917c6484441c946a8
Implements: bp add-amount-info-in-list-api
This commit is contained in:
TommyLike 2017-09-19 11:39:16 +08:00
parent 859d3ac945
commit 23b7463984
26 changed files with 439 additions and 23 deletions

View File

@ -62,6 +62,7 @@ Request
- limit: limit
- offset: offset
- marker: marker
- with_count: with_count
Response Parameters
@ -88,6 +89,7 @@ Response Parameters
- data_timestamp: data_timestamp
- snapshot_id: snapshot_id_2
- os-backup-project-attr:project_id: os-backup-project-attr:project_id
- count: count
Response Example
----------------
@ -329,6 +331,7 @@ Request
- sort: sort
- limit: limit
- marker: marker
- with_count: with_count
Response Parameters
-------------------
@ -339,6 +342,7 @@ Response Parameters
- id: id_1
- links: links_1
- name: name_1
- count: count
Response Example
----------------

View File

@ -398,6 +398,13 @@ vol_type_id_query:
in: query
required: true
type: string
with_count:
description: |
Whether to show ``count`` in API response or not, default is ``False``.
in: query
required: false
type: boolean
min_version: 3.45
# variables in body
absolute:
@ -740,6 +747,13 @@ control_location:
in: body
required: false
type: string
count:
description: |
The total count of requested resource before pagination is applied.
in: body
required: false
type: integer
min_version: 3.45
create-from-src:
description: |
The create from source action.

View File

@ -54,5 +54,6 @@
"is_incremental": true,
"has_dependent_backups": false
}
]
],
"count": 10
}

View File

@ -56,5 +56,6 @@
"id": "4dbf0ec2-0b57-4669-9823-9f7c76f2b4f8",
"size": 1
}
]
],
"count": 10
}

View File

@ -15,5 +15,6 @@
"id": "b1323cda-8e4b-41c1-afc5-2fc791809c8c",
"description": "volume snapshot"
}
]
],
"count": 10
}

View File

@ -16,5 +16,6 @@
"id": "b1323cda-8e4b-41c1-afc5-2fc791809c8c",
"description": "volume snapshot"
}
]
],
"count": 10
}

View File

@ -98,5 +98,6 @@
"created_at": "2015-11-29T02:25:18.000000",
"volume_type": "lvmdriver-1"
}
]
],
"count": 10
}

View File

@ -28,5 +28,6 @@
],
"name": "vol-003"
}
]
],
"count": 10
}

View File

@ -59,6 +59,7 @@ Request
- limit: limit
- offset: offset
- marker: marker
- with_count: with_count
Response Parameters
@ -77,6 +78,7 @@ Response Parameters
- size: size
- id: id
- metadata: metadata
- count: count
Response Example
----------------
@ -164,6 +166,7 @@ Request
- limit: limit
- offset: offset
- marker: marker
- with_count: with_count
Response Parameters
@ -180,6 +183,7 @@ Response Parameters
- metadata: metadata
- id: id
- size: size
- count: count
Response Example
----------------

View File

@ -86,6 +86,7 @@ Request
- limit: limit
- offset: offset
- marker: marker
- with_count: with_count
Response Parameters
@ -121,6 +122,7 @@ Response Parameters
- os-volume-replication:driver_data: os-volume-replication:driver_data
- volumes: volumes
- volume_type: volume_type
- count: count
@ -260,6 +262,7 @@ Request
- limit: limit
- offset: offset
- marker: marker
- with_count: with_count
Response Parameters
@ -271,6 +274,7 @@ Response Parameters
- id: id_5
- links: links_3
- name: name_13
- count: count

View File

@ -30,6 +30,7 @@ from cinder import backup as backupAPI
from cinder import exception
from cinder.i18n import _
from cinder import utils
from cinder import volume as volumeAPI
LOG = logging.getLogger(__name__)
@ -41,6 +42,7 @@ class BackupsController(wsgi.Controller):
def __init__(self):
self.backup_api = backupAPI.API()
self.volume_api = volumeAPI.API()
super(BackupsController, self).__init__()
def show(self, req, id):
@ -100,6 +102,10 @@ class BackupsController(wsgi.Controller):
marker, limit, offset = common.get_pagination_params(filters)
sort_keys, sort_dirs = common.get_sort_params(filters)
show_count = False
if req_version.matches(mv.SUPPORT_COUNT_INFO):
show_count = utils.get_bool_param('with_count', filters)
filters.pop('with_count')
self._convert_sort_name(req_version, sort_keys)
self._process_backup_filtering(context=context, filters=filters,
req_version=req_version)
@ -107,7 +113,7 @@ class BackupsController(wsgi.Controller):
if 'name' in filters:
filters['display_name'] = filters.pop('name')
backups = self.backup_api.get_all(context, search_opts=filters,
backups = self.backup_api.get_all(context, search_opts=filters.copy(),
marker=marker,
limit=limit,
offset=offset,
@ -115,12 +121,18 @@ class BackupsController(wsgi.Controller):
sort_dirs=sort_dirs,
)
total_count = None
if show_count:
total_count = self.volume_api.calculate_resource_count(
context, 'backup', filters)
req.cache_db_backups(backups.objects)
if is_detail:
backups = self._view_builder.detail_list(req, backups.objects)
backups = self._view_builder.detail_list(req, backups.objects,
total_count)
else:
backups = self._view_builder.summary_list(req, backups.objects)
backups = self._view_builder.summary_list(req, backups.objects,
total_count)
return backups
# TODO(frankm): Add some checks here including

View File

@ -127,6 +127,8 @@ BACKUP_METADATA = '3.43'
NEW_ATTACH_COMPLETION = '3.44'
SUPPORT_COUNT_INFO = '3.45'
def get_mv_header(version):
"""Gets a formatted HTTP microversion header.

View File

@ -107,6 +107,8 @@ REST_API_VERSION_HISTORY = """
state is intentionally NOT allowed.
* 3.43 - Support backup CRUD with metadata.
* 3.44 - Add attachment-complete.
* 3.45 - Add ``count`` field to volume, backup and snapshot list and
detail APIs.
"""
# The minimum and maximum versions of the API supported
@ -114,7 +116,7 @@ REST_API_VERSION_HISTORY = """
# minimum version of the API supported.
# Explicitly using /v2 endpoints will still work
_MIN_API_VERSION = "3.0"
_MAX_API_VERSION = "3.44"
_MAX_API_VERSION = "3.45"
_LEGACY_API_VERSION2 = "2.0"
UPDATED = "2017-09-19T20:18:14Z"

View File

@ -373,3 +373,7 @@ user documentation.
Support attachment completion. See the
`API reference <https://developer.openstack.org/api-ref/block-storage/v3/index.html#complete-attachment>`__
for details.
3.45
----
Add ``count`` field to volume, backup and snapshot list and detail APIs.

View File

@ -78,6 +78,12 @@ class SnapshotsController(snapshots_v2.SnapshotsController):
sort_keys, sort_dirs = common.get_sort_params(search_opts)
marker, limit, offset = common.get_pagination_params(search_opts)
req_version = req.api_version_request
show_count = False
if req_version.matches(mv.SUPPORT_COUNT_INFO):
show_count = utils.get_bool_param('with_count', search_opts)
search_opts.pop('with_count')
# process filters
self._process_snapshot_filtering(context=context,
filters=search_opts,
@ -93,20 +99,27 @@ class SnapshotsController(snapshots_v2.SnapshotsController):
if 'name' in search_opts:
search_opts['display_name'] = search_opts.pop('name')
snapshots = self.volume_api.get_all_snapshots(context,
search_opts=search_opts,
marker=marker,
limit=limit,
sort_keys=sort_keys,
sort_dirs=sort_dirs,
offset=offset)
snapshots = self.volume_api.get_all_snapshots(
context,
search_opts=search_opts.copy(),
marker=marker,
limit=limit,
sort_keys=sort_keys,
sort_dirs=sort_dirs,
offset=offset)
total_count = None
if show_count:
total_count = self.volume_api.calculate_resource_count(
context, 'snapshot', search_opts)
req.cache_db_snapshots(snapshots.objects)
if is_detail:
snapshots = self._view_builder.detail_list(req, snapshots.objects)
snapshots = self._view_builder.detail_list(req, snapshots.objects,
total_count)
else:
snapshots = self._view_builder.summary_list(req, snapshots.objects)
snapshots = self._view_builder.summary_list(req, snapshots.objects,
total_count)
return snapshots

View File

@ -20,6 +20,8 @@ from cinder.api.v2.views import volumes as views_v2
class ViewBuilder(views_v2.ViewBuilder):
"""Model a volumes API V3 response as a python dictionary."""
_collection_name = "volumes"
def quick_summary(self, volume_count, volume_size,
all_distinct_metadata=None):
"""View of volumes summary.
@ -53,3 +55,32 @@ class ViewBuilder(views_v2.ViewBuilder):
volume_ref['volume']['provider_id'] = volume.get('provider_id')
return volume_ref
def _list_view(self, func, request, volumes, volume_count,
coll_name=_collection_name):
"""Provide a view for a list of volumes.
:param func: Function used to format the volume data
:param request: API request
:param volumes: List of volumes in dictionary format
:param volume_count: Length of the original list of volumes
:param coll_name: Name of collection, used to generate the next link
for a pagination query
:returns: Volume data in dictionary format
"""
volumes_list = [func(request, volume)['volume'] for volume in volumes]
volumes_links = self._get_collection_links(request,
volumes,
coll_name,
volume_count)
volumes_dict = {"volumes": volumes_list}
if volumes_links:
volumes_dict['volumes_links'] = volumes_links
req_version = request.api_version_request
if req_version.matches(
mv.SUPPORT_COUNT_INFO, None) and volume_count is not None:
volumes_dict['count'] = volume_count
return volumes_dict

View File

@ -97,6 +97,11 @@ class VolumeController(volumes_v2.VolumeController):
sort_keys, sort_dirs = common.get_sort_params(params)
filters = params
show_count = False
if req_version.matches(mv.SUPPORT_COUNT_INFO):
show_count = utils.get_bool_param('with_count', filters)
filters.pop('with_count')
self._process_volume_filtering(context=context, filters=filters,
req_version=req_version)
@ -114,9 +119,13 @@ class VolumeController(volumes_v2.VolumeController):
volumes = self.volume_api.get_all(context, marker, limit,
sort_keys=sort_keys,
sort_dirs=sort_dirs,
filters=filters,
filters=filters.copy(),
viewable_admin_meta=True,
offset=offset)
total_count = None
if show_count:
total_count = self.volume_api.calculate_resource_count(
context, 'volume', filters)
for volume in volumes:
utils.add_visible_admin_metadata(volume)
@ -124,9 +133,11 @@ class VolumeController(volumes_v2.VolumeController):
req.cache_db_volumes(volumes.objects)
if is_detail:
volumes = self._view_builder.detail_list(req, volumes)
volumes = self._view_builder.detail_list(
req, volumes, total_count)
else:
volumes = self._view_builder.summary_list(req, volumes)
volumes = self._view_builder.summary_list(
req, volumes, total_count)
return volumes
@wsgi.Controller.api_version(mv.VOLUME_SUMMARY)

View File

@ -92,6 +92,9 @@ class ViewBuilder(common.ViewBuilder):
if backups_links:
backups_dict['backups_links'] = backups_links
if backup_count is not None:
backups_dict['count'] = backup_count
return backups_dict
def export_summary(self, request, export):

View File

@ -75,4 +75,7 @@ class ViewBuilder(common.ViewBuilder):
if snapshots_links:
snapshots_dict[self._collection_name + '_links'] = snapshots_links
if snapshot_count is not None:
snapshots_dict['count'] = snapshot_count
return snapshots_dict

View File

@ -281,6 +281,10 @@ def volume_get_all(context, marker=None, limit=None, sort_keys=None,
offset=offset)
def calculate_resource_count(context, resource_type, filters):
return IMPL.calculate_resource_count(context, resource_type, filters)
def volume_get_all_by_host(context, host, filters=None):
"""Get all volumes belonging to a host."""
return IMPL.volume_get_all_by_host(context, host, filters=filters)

View File

@ -2364,6 +2364,23 @@ def _generate_paginate_query(context, session, marker, limit, sort_keys,
offset=offset)
def calculate_resource_count(context, resource_type, filters):
"""Calculate total count with filters applied"""
session = get_session()
if resource_type not in CALCULATE_COUNT_HELPERS.keys():
raise exception.InvalidInput(
reason=_("Model %s doesn't support "
"counting resource.") % resource_type)
get_query, process_filters = CALCULATE_COUNT_HELPERS[resource_type]
query = get_query(context, session=session)
if filters:
query = process_filters(query, filters)
if query is None:
return 0
return query.with_entities(func.count()).scalar()
@apply_like_filters(model=models.Volume)
def _process_volume_filters(query, filters):
"""Common filter processing for Volume queries.
@ -6589,6 +6606,13 @@ PAGINATION_HELPERS = {
}
CALCULATE_COUNT_HELPERS = {
'volume': (_volume_get_query, _process_volume_filters),
'snapshot': (_snaps_get_query, _process_snaps_filters),
'backup': (_backups_get_query, _process_backups_filters),
}
###############################

View File

@ -17,6 +17,7 @@
import ddt
import mock
from oslo_utils import strutils
import webob
from cinder.api import microversions as mv
@ -88,6 +89,90 @@ class BackupsControllerAPITestCase(test.TestCase):
self.controller.update,
req, fake.BACKUP_ID, body)
def _create_multiple_backups_with_different_project(self):
test_utils.create_backup(
context.RequestContext(fake.USER_ID, fake.PROJECT_ID, True))
test_utils.create_backup(
context.RequestContext(fake.USER_ID, fake.PROJECT_ID, True))
test_utils.create_backup(
context.RequestContext(fake.USER_ID, fake.PROJECT2_ID, True))
@ddt.data('backups', 'backups/detail')
def test_list_backup_with_count_param_version_not_matched(self, action):
self._create_multiple_backups_with_different_project()
is_detail = True if 'detail' in action else False
req = fakes.HTTPRequest.blank("/v3/%s?with_count=True" % action)
req.headers = mv.get_mv_header(
mv.get_prior_version(mv.SUPPORT_COUNT_INFO))
req.api_version_request = mv.get_api_version(
mv.get_prior_version(mv.SUPPORT_COUNT_INFO))
ctxt = context.RequestContext(fake.USER_ID, fake.PROJECT_ID, True)
req.environ['cinder.context'] = ctxt
res_dict = self.controller._get_backups(req, is_detail=is_detail)
self.assertNotIn('count', res_dict)
@ddt.data({'method': 'backups',
'display_param': 'True'},
{'method': 'backups',
'display_param': 'False'},
{'method': 'backups',
'display_param': '1'},
{'method': 'backups/detail',
'display_param': 'True'},
{'method': 'backups/detail',
'display_param': 'False'},
{'method': 'backups/detail',
'display_param': '1'}
)
@ddt.unpack
def test_list_backups_with_count_param(self, method, display_param):
self._create_multiple_backups_with_different_project()
is_detail = True if 'detail' in method else False
show_count = strutils.bool_from_string(display_param, strict=True)
# Request with 'with_count' and 'limit'
req = fakes.HTTPRequest.blank(
"/v3/%s?with_count=%s&limit=1" % (method, display_param))
req.headers = mv.get_mv_header(mv.SUPPORT_COUNT_INFO)
req.api_version_request = mv.get_api_version(mv.SUPPORT_COUNT_INFO)
ctxt = context.RequestContext(fake.USER_ID, fake.PROJECT_ID, False)
req.environ['cinder.context'] = ctxt
res_dict = self.controller._get_backups(req, is_detail=is_detail)
self.assertEqual(1, len(res_dict['backups']))
if show_count:
self.assertEqual(2, res_dict['count'])
else:
self.assertNotIn('count', res_dict)
# Request with 'with_count'
req = fakes.HTTPRequest.blank(
"/v3/%s?with_count=%s" % (method, display_param))
req.headers = mv.get_mv_header(mv.SUPPORT_COUNT_INFO)
req.api_version_request = mv.get_api_version(mv.SUPPORT_COUNT_INFO)
ctxt = context.RequestContext(fake.USER_ID, fake.PROJECT_ID, False)
req.environ['cinder.context'] = ctxt
res_dict = self.controller._get_backups(req, is_detail=is_detail)
self.assertEqual(2, len(res_dict['backups']))
if show_count:
self.assertEqual(2, res_dict['count'])
else:
self.assertNotIn('count', res_dict)
# Request with admin context and 'all_tenants'
req = fakes.HTTPRequest.blank(
"/v3/%s?with_count=%s&all_tenants=1" % (method, display_param))
req.headers = mv.get_mv_header(mv.SUPPORT_COUNT_INFO)
req.api_version_request = mv.get_api_version(mv.SUPPORT_COUNT_INFO)
ctxt = context.RequestContext(fake.USER_ID, fake.PROJECT_ID, True)
req.environ['cinder.context'] = ctxt
res_dict = self.controller._get_backups(req, is_detail=is_detail)
self.assertEqual(3, len(res_dict['backups']))
if show_count:
self.assertEqual(3, res_dict['count'])
else:
self.assertNotIn('count', res_dict)
@ddt.data(mv.get_prior_version(mv.RESOURCE_FILTER),
mv.RESOURCE_FILTER,
mv.LIKE_FILTER)

View File

@ -14,8 +14,8 @@
# under the License.
import ddt
import mock
from oslo_utils import strutils
from cinder.api import microversions as mv
from cinder.api.v3 import snapshots
@ -151,6 +151,97 @@ class SnapshotApiTest(test.TestCase):
self.assertEqual(1, len(res_dict['snapshots']))
self.assertEqual(snapshot1.id, res_dict['snapshots'][0]['id'])
def _create_multiple_snapshots_with_different_project(self):
volume1 = test_utils.create_volume(self.ctx,
project=fake.PROJECT_ID)
volume2 = test_utils.create_volume(self.ctx,
project=fake.PROJECT2_ID)
test_utils.create_snapshot(
context.RequestContext(fake.USER_ID, fake.PROJECT_ID, True),
volume1.id)
test_utils.create_snapshot(
context.RequestContext(fake.USER_ID, fake.PROJECT_ID, True),
volume1.id)
test_utils.create_snapshot(
context.RequestContext(fake.USER_ID, fake.PROJECT2_ID, True),
volume2.id)
@ddt.data('snapshots', 'snapshots/detail')
def test_list_snapshot_with_count_param_version_not_matched(self, action):
self._create_multiple_snapshots_with_different_project()
is_detail = True if 'detail' in action else False
req = fakes.HTTPRequest.blank("/v3/%s?with_count=True" % action)
req.headers = mv.get_mv_header(
mv.get_prior_version(mv.SUPPORT_COUNT_INFO))
req.api_version_request = mv.get_api_version(
mv.get_prior_version(mv.SUPPORT_COUNT_INFO))
ctxt = context.RequestContext(fake.USER_ID, fake.PROJECT_ID, True)
req.environ['cinder.context'] = ctxt
res_dict = self.controller._items(req, is_detail=is_detail)
self.assertNotIn('count', res_dict)
@ddt.data({'method': 'snapshots',
'display_param': 'True'},
{'method': 'snapshots',
'display_param': 'False'},
{'method': 'snapshots',
'display_param': '1'},
{'method': 'snapshots/detail',
'display_param': 'True'},
{'method': 'snapshots/detail',
'display_param': 'False'},
{'method': 'snapshots/detail',
'display_param': '1'}
)
@ddt.unpack
def test_list_snapshot_with_count_param(self, method, display_param):
self._create_multiple_snapshots_with_different_project()
is_detail = True if 'detail' in method else False
show_count = strutils.bool_from_string(display_param, strict=True)
# Request with 'with_count' and 'limit'
req = fakes.HTTPRequest.blank(
"/v3/%s?with_count=%s&limit=1" % (method, display_param))
req.headers = mv.get_mv_header(mv.SUPPORT_COUNT_INFO)
req.api_version_request = mv.get_api_version(mv.SUPPORT_COUNT_INFO)
ctxt = context.RequestContext(fake.USER_ID, fake.PROJECT_ID, False)
req.environ['cinder.context'] = ctxt
res_dict = self.controller._items(req, is_detail=is_detail)
self.assertEqual(1, len(res_dict['snapshots']))
if show_count:
self.assertEqual(2, res_dict['count'])
else:
self.assertNotIn('count', res_dict)
# Request with 'with_count'
req = fakes.HTTPRequest.blank(
"/v3/%s?with_count=%s" % (method, display_param))
req.headers = mv.get_mv_header(mv.SUPPORT_COUNT_INFO)
req.api_version_request = mv.get_api_version(mv.SUPPORT_COUNT_INFO)
ctxt = context.RequestContext(fake.USER_ID, fake.PROJECT_ID, False)
req.environ['cinder.context'] = ctxt
res_dict = self.controller._items(req, is_detail=is_detail)
self.assertEqual(2, len(res_dict['snapshots']))
if show_count:
self.assertEqual(2, res_dict['count'])
else:
self.assertNotIn('count', res_dict)
# Request with admin context and 'all_tenants'
req = fakes.HTTPRequest.blank(
"/v3/%s?with_count=%s&all_tenants=1" % (method, display_param))
req.headers = mv.get_mv_header(mv.SUPPORT_COUNT_INFO)
req.api_version_request = mv.get_api_version(mv.SUPPORT_COUNT_INFO)
ctxt = context.RequestContext(fake.USER_ID, fake.PROJECT_ID, True)
req.environ['cinder.context'] = ctxt
res_dict = self.controller._items(req, is_detail=is_detail)
self.assertEqual(3, len(res_dict['snapshots']))
if show_count:
self.assertEqual(3, res_dict['count'])
else:
self.assertNotIn('count', res_dict)
def test_snapshot_list_with_sort_name(self):
self._create_snapshot(name='test1')
self._create_snapshot(name='test2')

View File

@ -16,6 +16,7 @@ import ddt
import iso8601
import mock
from oslo_utils import strutils
import webob
from cinder.api import extensions
@ -113,6 +114,16 @@ class VolumeApiTest(test.TestCase):
fake.GROUP2_ID})
return [vol1, vol2]
def _create_multiple_volumes_with_different_project(self):
# Create volumes in project 1
db.volume_create(self.ctxt, {'display_name': 'test1',
'project_id': fake.PROJECT_ID})
db.volume_create(self.ctxt, {'display_name': 'test2',
'project_id': fake.PROJECT_ID})
# Create volume in project 2
db.volume_create(self.ctxt, {'display_name': 'test3',
'project_id': fake.PROJECT2_ID})
def test_volume_index_filter_by_glance_metadata(self):
vols = self._create_volume_with_glance_metadata()
req = fakes.HTTPRequest.blank("/v3/volumes?glance_metadata="
@ -149,6 +160,82 @@ class VolumeApiTest(test.TestCase):
self.assertEqual(1, len(volumes))
self.assertEqual(vols[0].id, volumes[0]['id'])
@ddt.data('volumes', 'volumes/detail')
def test_list_volume_with_count_param_version_not_matched(self, action):
self._create_multiple_volumes_with_different_project()
is_detail = True if 'detail' in action else False
req = fakes.HTTPRequest.blank("/v3/%s?with_count=True" % action)
req.headers = mv.get_mv_header(
mv.get_prior_version(mv.SUPPORT_COUNT_INFO))
req.api_version_request = mv.get_api_version(
mv.get_prior_version(mv.SUPPORT_COUNT_INFO))
ctxt = context.RequestContext(fake.USER_ID, fake.PROJECT_ID, True)
req.environ['cinder.context'] = ctxt
res_dict = self.controller._get_volumes(req, is_detail=is_detail)
self.assertNotIn('count', res_dict)
@ddt.data({'method': 'volumes',
'display_param': 'True'},
{'method': 'volumes',
'display_param': 'False'},
{'method': 'volumes',
'display_param': '1'},
{'method': 'volumes/detail',
'display_param': 'True'},
{'method': 'volumes/detail',
'display_param': 'False'},
{'method': 'volumes/detail',
'display_param': '1'}
)
@ddt.unpack
def test_list_volume_with_count_param(self, method, display_param):
self._create_multiple_volumes_with_different_project()
is_detail = True if 'detail' in method else False
show_count = strutils.bool_from_string(display_param, strict=True)
# Request with 'with_count' and 'limit'
req = fakes.HTTPRequest.blank(
"/v3/%s?with_count=%s&limit=1" % (method, display_param))
req.headers = mv.get_mv_header(mv.SUPPORT_COUNT_INFO)
req.api_version_request = mv.get_api_version(mv.SUPPORT_COUNT_INFO)
ctxt = context.RequestContext(fake.USER_ID, fake.PROJECT_ID, False)
req.environ['cinder.context'] = ctxt
res_dict = self.controller._get_volumes(req, is_detail=is_detail)
self.assertEqual(1, len(res_dict['volumes']))
if show_count:
self.assertEqual(2, res_dict['count'])
else:
self.assertNotIn('count', res_dict)
# Request with 'with_count'
req = fakes.HTTPRequest.blank(
"/v3/%s?with_count=%s" % (method, display_param))
req.headers = mv.get_mv_header(mv.SUPPORT_COUNT_INFO)
req.api_version_request = mv.get_api_version(mv.SUPPORT_COUNT_INFO)
ctxt = context.RequestContext(fake.USER_ID, fake.PROJECT_ID, False)
req.environ['cinder.context'] = ctxt
res_dict = self.controller._get_volumes(req, is_detail=is_detail)
self.assertEqual(2, len(res_dict['volumes']))
if show_count:
self.assertEqual(2, res_dict['count'])
else:
self.assertNotIn('count', res_dict)
# Request with admin context and 'all_tenants'
req = fakes.HTTPRequest.blank(
"/v3/%s?with_count=%s&all_tenants=1" % (method, display_param))
req.headers = mv.get_mv_header(mv.SUPPORT_COUNT_INFO)
req.api_version_request = mv.get_api_version(mv.SUPPORT_COUNT_INFO)
ctxt = context.RequestContext(fake.USER_ID, fake.PROJECT_ID, True)
req.environ['cinder.context'] = ctxt
res_dict = self.controller._get_volumes(req, is_detail=is_detail)
self.assertEqual(3, len(res_dict['volumes']))
if show_count:
self.assertEqual(3, res_dict['count'])
else:
self.assertNotIn('count', res_dict)
def test_volume_index_filter_by_group_id_in_unsupport_version(self):
self._create_volume_with_group()
req = fakes.HTTPRequest.blank(("/v3/volumes?group_id=%s") %

View File

@ -535,6 +535,15 @@ class API(base.Base):
LOG.info("Volume info retrieved successfully.", resource=volume)
return volume
def calculate_resource_count(self, context, resource_type, filters):
filters = filters if filters else {}
allTenants = utils.get_bool_param('all_tenants', filters)
if context.is_admin and allTenants:
del filters['all_tenants']
else:
filters['project_id'] = context.project_id
return db.calculate_resource_count(context, resource_type, filters)
def get_all(self, context, marker=None, limit=None, sort_keys=None,
sort_dirs=None, filters=None, viewable_admin_meta=False,
offset=None):

View File

@ -0,0 +1,3 @@
---
features:
- Added count info in volume, snapshot and backup's list APIs since 3.45.