Return metadata in volume summary
Cinder supports filter volumes with metadata, but in some case, users don't know what metadata the volumes contain or what metadata is valid to filter volumes. This patch updated volumes/summary API to return all valid distinct metadata to users. Then users could use these metadatas to filter volumes easily. This function is useful for dashboard, such as Horizon, as well. APIImpact Implements: blueprint metadata-for-volume-summary Change-Id: I33c77d9db88f70d8d3b8ea86c86c01220dcc537c
This commit is contained in:
parent
1f7adde05b
commit
bf40945dcc
@ -89,7 +89,7 @@ REST_API_VERSION_HISTORY = """
|
||||
``message``, ``attachment``, ``group`` and ``group-snapshot``
|
||||
list APIs.
|
||||
* 3.35 - Add ``volume-type`` filter to Get-Pools API.
|
||||
|
||||
* 3.36 - Add metadata to volumes/summary response body.
|
||||
"""
|
||||
|
||||
# The minimum and maximum versions of the API supported
|
||||
@ -97,7 +97,7 @@ REST_API_VERSION_HISTORY = """
|
||||
# minimum version of the API supported.
|
||||
# Explicitly using /v1 or /v2 endpoints will still work
|
||||
_MIN_API_VERSION = "3.0"
|
||||
_MAX_API_VERSION = "3.35"
|
||||
_MAX_API_VERSION = "3.36"
|
||||
_LEGACY_API_VERSION1 = "1.0"
|
||||
_LEGACY_API_VERSION2 = "2.0"
|
||||
|
||||
|
@ -317,3 +317,7 @@ user documentation.
|
||||
3.35
|
||||
----
|
||||
Add ``volume-type`` filter to Get-Pools API.
|
||||
|
||||
3.36
|
||||
----
|
||||
Add metadata to volumes/summary response body.
|
||||
|
@ -19,14 +19,22 @@ 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."""
|
||||
|
||||
def quick_summary(self, volume_count, volume_size):
|
||||
"""Number of volumes and size of volumes."""
|
||||
return {
|
||||
def quick_summary(self, volume_count, volume_size,
|
||||
all_distinct_metadata=None):
|
||||
"""View of volumes summary.
|
||||
|
||||
It includes number of volumes, size of volumes and all distinct
|
||||
metadata of volumes.
|
||||
"""
|
||||
summary = {
|
||||
'volume-summary': {
|
||||
'total_count': volume_count,
|
||||
'total_size': volume_size
|
||||
},
|
||||
}
|
||||
}
|
||||
if all_distinct_metadata is not None:
|
||||
summary['volume-summary']['metadata'] = all_distinct_metadata
|
||||
return summary
|
||||
|
||||
def detail(self, request, volume):
|
||||
"""Detailed view of a single volume."""
|
||||
|
@ -148,8 +148,17 @@ class VolumeController(volumes_v2.VolumeController):
|
||||
utils.remove_invalid_filter_options(context, filters,
|
||||
self._get_volume_filter_options())
|
||||
|
||||
volumes = self.volume_api.get_volume_summary(context, filters=filters)
|
||||
return view_builder_v3.quick_summary(volumes[0], int(volumes[1]))
|
||||
num_vols, sum_size, metadata = self.volume_api.get_volume_summary(
|
||||
context, filters=filters)
|
||||
|
||||
req_version = req.api_version_request
|
||||
if req_version.matches("3.36"):
|
||||
all_distinct_metadata = metadata
|
||||
else:
|
||||
all_distinct_metadata = None
|
||||
|
||||
return view_builder_v3.quick_summary(num_vols, int(sum_size),
|
||||
all_distinct_metadata)
|
||||
|
||||
@wsgi.response(http_client.ACCEPTED)
|
||||
def create(self, req, body):
|
||||
|
@ -2100,7 +2100,22 @@ def get_volume_summary(context, project_only):
|
||||
return []
|
||||
|
||||
result = query.first()
|
||||
return (result[0] or 0, result[1] or 0)
|
||||
|
||||
query_metadata = model_query(
|
||||
context, models.VolumeMetadata.key, models.VolumeMetadata.value,
|
||||
read_deleted="no")
|
||||
if project_only:
|
||||
query_metadata = query_metadata.join(
|
||||
models.Volume,
|
||||
models.Volume.id == models.VolumeMetadata.volume_id).filter_by(
|
||||
project_id=context.project_id)
|
||||
result_metadata = query_metadata.distinct().all()
|
||||
|
||||
result_metadata_list = collections.defaultdict(list)
|
||||
for key, value in result_metadata:
|
||||
result_metadata_list[key].append(value)
|
||||
|
||||
return (result[0] or 0, result[1] or 0, result_metadata_list)
|
||||
|
||||
|
||||
@require_admin_context
|
||||
|
@ -30,6 +30,7 @@ from cinder.tests.unit.api import fakes
|
||||
from cinder.tests.unit.api.v2 import fakes as v2_fakes
|
||||
from cinder.tests.unit.api.v2 import test_volumes as v2_test_volumes
|
||||
from cinder.tests.unit import fake_constants as fake
|
||||
from cinder.tests.unit import utils as test_utils
|
||||
from cinder import utils
|
||||
from cinder.volume import api as volume_api
|
||||
from cinder.volume import api as vol_get
|
||||
@ -156,8 +157,12 @@ class VolumeApiTest(test.TestCase):
|
||||
volumes = res_dict['volumes']
|
||||
self.assertEqual(2, len(volumes))
|
||||
|
||||
def _fake_volumes_summary_request(self, version='3.12'):
|
||||
req = fakes.HTTPRequest.blank('/v3/volumes/summary')
|
||||
def _fake_volumes_summary_request(self, version='3.12', all_tenant=False,
|
||||
is_admin=False):
|
||||
req_url = '/v3/volumes/summary'
|
||||
if all_tenant:
|
||||
req_url += '?all_tenants=True'
|
||||
req = fakes.HTTPRequest.blank(req_url, use_admin_context=is_admin)
|
||||
req.headers = {'OpenStack-API-Version': 'volume ' + version}
|
||||
req.api_version_request = api_version.APIVersionRequest(version)
|
||||
return req
|
||||
@ -186,6 +191,63 @@ class VolumeApiTest(test.TestCase):
|
||||
expected = {'volume-summary': {'total_size': 1.0, 'total_count': 1}}
|
||||
self.assertEqual(expected, res_dict)
|
||||
|
||||
@ddt.data(
|
||||
('3.35', {'volume-summary': {'total_size': 0.0,
|
||||
'total_count': 0}}),
|
||||
('3.36', {'volume-summary': {'total_size': 0.0,
|
||||
'total_count': 0,
|
||||
'metadata': {}}}))
|
||||
@ddt.unpack
|
||||
def test_volume_summary_empty(self, summary_api_version, expect_result):
|
||||
req = self._fake_volumes_summary_request(version=summary_api_version)
|
||||
res_dict = self.controller.summary(req)
|
||||
self.assertEqual(expect_result, res_dict)
|
||||
|
||||
@ddt.data(
|
||||
('3.35', {'volume-summary': {'total_size': 2,
|
||||
'total_count': 2}}),
|
||||
('3.36', {'volume-summary': {'total_size': 2,
|
||||
'total_count': 2,
|
||||
'metadata': {
|
||||
'name': ['test_name1', 'test_name2'],
|
||||
'age': ['test_age']}}}))
|
||||
@ddt.unpack
|
||||
def test_volume_summary_return_metadata(self, summary_api_version,
|
||||
expect_result):
|
||||
test_utils.create_volume(self.ctxt, metadata={'name': 'test_name1',
|
||||
'age': 'test_age'})
|
||||
test_utils.create_volume(self.ctxt, metadata={'name': 'test_name2',
|
||||
'age': 'test_age'})
|
||||
ctxt2 = context.RequestContext(fake.USER_ID, fake.PROJECT2_ID, True)
|
||||
test_utils.create_volume(ctxt2, metadata={'name': 'test_name3'})
|
||||
|
||||
req = self._fake_volumes_summary_request(version=summary_api_version)
|
||||
res_dict = self.controller.summary(req)
|
||||
self.assertEqual(expect_result, res_dict)
|
||||
|
||||
@ddt.data(
|
||||
('3.35', {'volume-summary': {'total_size': 2,
|
||||
'total_count': 2}}),
|
||||
('3.36', {'volume-summary': {'total_size': 2,
|
||||
'total_count': 2,
|
||||
'metadata': {
|
||||
'name': ['test_name1', 'test_name2'],
|
||||
'age': ['test_age']}}}))
|
||||
@ddt.unpack
|
||||
def test_volume_summary_return_metadata_all_tenant(
|
||||
self, summary_api_version, expect_result):
|
||||
test_utils.create_volume(self.ctxt, metadata={'name': 'test_name1',
|
||||
'age': 'test_age'})
|
||||
ctxt2 = context.RequestContext(fake.USER_ID, fake.PROJECT2_ID, True)
|
||||
test_utils.create_volume(ctxt2, metadata={'name': 'test_name2',
|
||||
'age': 'test_age'})
|
||||
|
||||
req = self._fake_volumes_summary_request(version=summary_api_version,
|
||||
all_tenant=True,
|
||||
is_admin=True)
|
||||
res_dict = self.controller.summary(req)
|
||||
self.assertEqual(expect_result, res_dict)
|
||||
|
||||
def _vol_in_request_body(self,
|
||||
size=v2_fakes.DEFAULT_VOL_SIZE,
|
||||
name=v2_fakes.DEFAULT_VOL_NAME,
|
||||
|
@ -69,6 +69,7 @@ def create_volume(ctxt,
|
||||
previous_status=None,
|
||||
testcase_instance=None,
|
||||
id=None,
|
||||
metadata=None,
|
||||
**kwargs):
|
||||
"""Create a volume object in the DB."""
|
||||
vol = {}
|
||||
@ -89,6 +90,8 @@ def create_volume(ctxt,
|
||||
vol['group_id'] = group_id
|
||||
if volume_type_id:
|
||||
vol['volume_type_id'] = volume_type_id
|
||||
if metadata:
|
||||
vol['metadata'] = metadata
|
||||
for key in kwargs:
|
||||
vol[key] = kwargs[key]
|
||||
vol['replication_status'] = replication_status
|
||||
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
features:
|
||||
- Added support for get all distinct volumes' metadata from
|
||||
volume-summary API.
|
Loading…
Reference in New Issue
Block a user