Merge "Add pagination support to Qos specs"
This commit is contained in:
commit
b576ccf4b2
@ -20,6 +20,7 @@ from oslo_utils import strutils
|
||||
import six
|
||||
import webob
|
||||
|
||||
from cinder.api import common
|
||||
from cinder.api import extensions
|
||||
from cinder.api.openstack import wsgi
|
||||
from cinder.api.views import qos_specs as view_qos_specs
|
||||
@ -115,7 +116,20 @@ class QoSSpecsController(wsgi.Controller):
|
||||
"""Returns the list of qos_specs."""
|
||||
context = req.environ['cinder.context']
|
||||
authorize(context)
|
||||
specs = qos_specs.get_all_specs(context)
|
||||
|
||||
params = req.params.copy()
|
||||
|
||||
marker, limit, offset = common.get_pagination_params(params)
|
||||
sort_keys, sort_dirs = common.get_sort_params(params)
|
||||
filters = params
|
||||
allowed_search_options = ('id', 'name', 'consumer')
|
||||
utils.remove_invalid_filter_options(context, filters,
|
||||
allowed_search_options)
|
||||
|
||||
specs = qos_specs.get_all_specs(context, filters=filters,
|
||||
marker=marker, limit=limit,
|
||||
offset=offset, sort_keys=sort_keys,
|
||||
sort_dirs=sort_dirs)
|
||||
return self._view_builder.summary_list(req, specs)
|
||||
|
||||
@wsgi.serializers(xml=QoSSpecsTemplate)
|
||||
|
@ -24,15 +24,15 @@ LOG = logging.getLogger(__name__)
|
||||
class ViewBuilder(common.ViewBuilder):
|
||||
"""Model QoS specs API responses as a python dictionary."""
|
||||
|
||||
_collection_name = "qos_specs"
|
||||
_collection_name = "qos-specs"
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize view builder."""
|
||||
super(ViewBuilder, self).__init__()
|
||||
|
||||
def summary_list(self, request, qos_specs):
|
||||
def summary_list(self, request, qos_specs, qos_count=None):
|
||||
"""Show a list of qos_specs without many details."""
|
||||
return self._list_view(self.detail, request, qos_specs)
|
||||
return self._list_view(self.detail, request, qos_specs, qos_count)
|
||||
|
||||
def summary(self, request, qos_spec):
|
||||
"""Generic, non-detailed view of a qos_specs."""
|
||||
@ -57,9 +57,14 @@ class ViewBuilder(common.ViewBuilder):
|
||||
'qos_associations': associates
|
||||
}
|
||||
|
||||
def _list_view(self, func, request, qos_specs):
|
||||
def _list_view(self, func, request, qos_specs, qos_count=None):
|
||||
"""Provide a view for a list of qos_specs."""
|
||||
specs_list = [func(request, specs)['qos_specs'] for specs in qos_specs]
|
||||
specs_links = self._get_collection_links(request, qos_specs,
|
||||
self._collection_name,
|
||||
qos_count)
|
||||
specs_dict = dict(qos_specs=specs_list)
|
||||
if specs_links:
|
||||
specs_dict['qos_specs_links'] = specs_links
|
||||
|
||||
return specs_dict
|
||||
|
@ -596,9 +596,12 @@ def qos_specs_get(context, qos_specs_id):
|
||||
return IMPL.qos_specs_get(context, qos_specs_id)
|
||||
|
||||
|
||||
def qos_specs_get_all(context, inactive=False, filters=None):
|
||||
def qos_specs_get_all(context, filters=None, marker=None, limit=None,
|
||||
offset=None, sort_keys=None, sort_dirs=None):
|
||||
"""Get all qos_specs."""
|
||||
return IMPL.qos_specs_get_all(context, inactive, filters)
|
||||
return IMPL.qos_specs_get_all(context, filters=filters, marker=marker,
|
||||
limit=limit, offset=offset,
|
||||
sort_keys=sort_keys, sort_dirs=sort_dirs)
|
||||
|
||||
|
||||
def qos_specs_get_by_name(context, name):
|
||||
|
@ -1592,13 +1592,13 @@ def _generate_paginate_query(context, session, marker, limit, sort_keys,
|
||||
if query is None:
|
||||
return None
|
||||
|
||||
marker_volume = None
|
||||
marker_object = None
|
||||
if marker is not None:
|
||||
marker_volume = get(context, marker, session)
|
||||
marker_object = get(context, marker, session)
|
||||
|
||||
return sqlalchemyutils.paginate_query(query, paginate_type, limit,
|
||||
sort_keys,
|
||||
marker=marker_volume,
|
||||
marker=marker_object,
|
||||
sort_dirs=sort_dirs,
|
||||
offset=offset)
|
||||
|
||||
@ -3013,7 +3013,8 @@ def qos_specs_get(context, qos_specs_id, inactive=False):
|
||||
|
||||
|
||||
@require_admin_context
|
||||
def qos_specs_get_all(context, inactive=False, filters=None):
|
||||
def qos_specs_get_all(context, filters=None, marker=None, limit=None,
|
||||
offset=None, sort_keys=None, sort_dirs=None):
|
||||
"""Returns a list of all qos_specs.
|
||||
|
||||
Results is like:
|
||||
@ -3039,15 +3040,48 @@ def qos_specs_get_all(context, inactive=False, filters=None):
|
||||
},
|
||||
]
|
||||
"""
|
||||
filters = filters or {}
|
||||
# TODO(zhiteng) Add filters for 'consumer'
|
||||
session = get_session()
|
||||
with session.begin():
|
||||
# Generate the query
|
||||
query = _generate_paginate_query(context, session, marker, limit,
|
||||
sort_keys, sort_dirs, filters,
|
||||
offset, models.QualityOfServiceSpecs)
|
||||
# No Qos specs would match, return empty list
|
||||
if query is None:
|
||||
return []
|
||||
rows = query.all()
|
||||
return _dict_with_qos_specs(rows)
|
||||
|
||||
read_deleted = "yes" if inactive else "no"
|
||||
|
||||
@require_admin_context
|
||||
def _qos_specs_get_query(context, session):
|
||||
rows = model_query(context, models.QualityOfServiceSpecs,
|
||||
read_deleted=read_deleted). \
|
||||
options(joinedload_all('specs')).all()
|
||||
session=session,
|
||||
read_deleted='no').\
|
||||
options(joinedload_all('specs')).filter_by(key='QoS_Specs_Name')
|
||||
return rows
|
||||
|
||||
return _dict_with_qos_specs(rows)
|
||||
|
||||
def _process_qos_specs_filters(query, filters):
|
||||
if filters:
|
||||
# Ensure that filters' keys exist on the model
|
||||
if not is_valid_model_filters(models.QualityOfServiceSpecs, filters):
|
||||
return
|
||||
query = query.filter_by(**filters)
|
||||
return query
|
||||
|
||||
|
||||
@require_admin_context
|
||||
def _qos_specs_get(context, qos_spec_id, session=None):
|
||||
result = model_query(context, models.QualityOfServiceSpecs,
|
||||
session=session,
|
||||
read_deleted='no').\
|
||||
filter_by(id=qos_spec_id).filter_by(key='QoS_Specs_Name').first()
|
||||
|
||||
if not result:
|
||||
raise exception.QoSSpecsNotFound(specs_id=qos_spec_id)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@require_admin_context
|
||||
@ -4060,7 +4094,9 @@ def driver_initiator_data_get(context, initiator, namespace):
|
||||
PAGINATION_HELPERS = {
|
||||
models.Volume: (_volume_get_query, _process_volume_filters, _volume_get),
|
||||
models.Snapshot: (_snaps_get_query, _process_snaps_filters, _snapshot_get),
|
||||
models.Backup: (_backups_get_query, _process_backups_filters, _backup_get)
|
||||
models.Backup: (_backups_get_query, _process_backups_filters, _backup_get),
|
||||
models.QualityOfServiceSpecs: (_qos_specs_get_query,
|
||||
_process_qos_specs_filters, _qos_specs_get)
|
||||
}
|
||||
|
||||
|
||||
|
@ -22,6 +22,8 @@ import webob
|
||||
|
||||
from cinder.api.contrib import qos_specs_manage
|
||||
from cinder.api import xmlutil
|
||||
from cinder import context
|
||||
from cinder import db
|
||||
from cinder import exception
|
||||
from cinder import test
|
||||
from cinder.tests.unit.api import fakes
|
||||
@ -48,7 +50,8 @@ def stub_qos_associates(id):
|
||||
'id': 'FakeVolTypeID'}]
|
||||
|
||||
|
||||
def return_qos_specs_get_all(context):
|
||||
def return_qos_specs_get_all(context, filters=None, marker=None, limit=None,
|
||||
offset=None, sort_keys=None, sort_dirs=None):
|
||||
return [
|
||||
stub_qos_specs(1),
|
||||
stub_qos_specs(2),
|
||||
@ -142,10 +145,30 @@ def return_disassociate_all(context, id):
|
||||
|
||||
|
||||
class QoSSpecManageApiTest(test.TestCase):
|
||||
|
||||
def _create_qos_specs(self, name, values=None):
|
||||
"""Create a transfer object."""
|
||||
if values:
|
||||
specs = dict(name=name, qos_specs=values)
|
||||
else:
|
||||
specs = {'name': name,
|
||||
'qos_specs': {
|
||||
'consumer': 'back-end',
|
||||
'key1': 'value1',
|
||||
'key2': 'value2'}}
|
||||
return db.qos_specs_create(self.ctxt, specs)['id']
|
||||
|
||||
def setUp(self):
|
||||
super(QoSSpecManageApiTest, self).setUp()
|
||||
self.flags(host='fake')
|
||||
self.controller = qos_specs_manage.QoSSpecsController()
|
||||
self.ctxt = context.RequestContext(user_id='user_id',
|
||||
project_id='project_id',
|
||||
is_admin=True)
|
||||
self.qos_id1 = self._create_qos_specs("Qos_test_1")
|
||||
self.qos_id2 = self._create_qos_specs("Qos_test_2")
|
||||
self.qos_id3 = self._create_qos_specs("Qos_test_3")
|
||||
self.qos_id4 = self._create_qos_specs("Qos_test_4")
|
||||
|
||||
@mock.patch('cinder.volume.qos_specs.get_all_specs',
|
||||
side_effect=return_qos_specs_get_all)
|
||||
@ -184,6 +207,78 @@ class QoSSpecManageApiTest(test.TestCase):
|
||||
expected_names = ['qos_specs_1', 'qos_specs_2', 'qos_specs_3']
|
||||
self.assertEqual(set(expected_names), names)
|
||||
|
||||
def test_index_with_limit(self):
|
||||
url = '/v2/fake/qos-specs?limit=2'
|
||||
req = fakes.HTTPRequest.blank(url, use_admin_context=True)
|
||||
res = self.controller.index(req)
|
||||
|
||||
self.assertEqual(2, len(res['qos_specs']))
|
||||
self.assertEqual(self.qos_id4, res['qos_specs'][0]['id'])
|
||||
self.assertEqual(self.qos_id3, res['qos_specs'][1]['id'])
|
||||
|
||||
expect_next_link = ('http://localhost/v2/fakeproject/qos-specs?limit'
|
||||
'=2&marker=%s') % res['qos_specs'][1]['id']
|
||||
self.assertEqual(expect_next_link, res['qos_specs_links'][0]['href'])
|
||||
|
||||
def test_index_with_offset(self):
|
||||
url = '/v2/fake/qos-specs?offset=1'
|
||||
req = fakes.HTTPRequest.blank(url, use_admin_context=True)
|
||||
res = self.controller.index(req)
|
||||
|
||||
self.assertEqual(3, len(res['qos_specs']))
|
||||
|
||||
def test_index_with_limit_and_offset(self):
|
||||
url = '/v2/fake/qos-specs?limit=2&offset=1'
|
||||
req = fakes.HTTPRequest.blank(url, use_admin_context=True)
|
||||
res = self.controller.index(req)
|
||||
|
||||
self.assertEqual(2, len(res['qos_specs']))
|
||||
self.assertEqual(self.qos_id3, res['qos_specs'][0]['id'])
|
||||
self.assertEqual(self.qos_id2, res['qos_specs'][1]['id'])
|
||||
|
||||
def test_index_with_marker(self):
|
||||
url = '/v2/fake/qos-specs?marker=%s' % self.qos_id4
|
||||
req = fakes.HTTPRequest.blank(url, use_admin_context=True)
|
||||
res = self.controller.index(req)
|
||||
|
||||
self.assertEqual(3, len(res['qos_specs']))
|
||||
|
||||
def test_index_with_filter(self):
|
||||
url = '/v2/fake/qos-specs?id=%s' % self.qos_id4
|
||||
req = fakes.HTTPRequest.blank(url, use_admin_context=True)
|
||||
res = self.controller.index(req)
|
||||
|
||||
self.assertEqual(1, len(res['qos_specs']))
|
||||
self.assertEqual(self.qos_id4, res['qos_specs'][0]['id'])
|
||||
|
||||
def test_index_with_sort_keys(self):
|
||||
url = '/v2/fake/qos-specs?sort=id'
|
||||
req = fakes.HTTPRequest.blank(url, use_admin_context=True)
|
||||
res = self.controller.index(req)
|
||||
self.assertEqual(4, len(res['qos_specs']))
|
||||
expect_result = [self.qos_id1, self.qos_id2,
|
||||
self.qos_id3, self.qos_id4]
|
||||
expect_result.sort(reverse=True)
|
||||
|
||||
self.assertEqual(expect_result[0], res['qos_specs'][0]['id'])
|
||||
self.assertEqual(expect_result[1], res['qos_specs'][1]['id'])
|
||||
self.assertEqual(expect_result[2], res['qos_specs'][2]['id'])
|
||||
self.assertEqual(expect_result[3], res['qos_specs'][3]['id'])
|
||||
|
||||
def test_index_with_sort_keys_and_sort_dirs(self):
|
||||
url = '/v2/fake/qos-specs?sort=id:asc'
|
||||
req = fakes.HTTPRequest.blank(url, use_admin_context=True)
|
||||
res = self.controller.index(req)
|
||||
self.assertEqual(4, len(res['qos_specs']))
|
||||
expect_result = [self.qos_id1, self.qos_id2,
|
||||
self.qos_id3, self.qos_id4]
|
||||
expect_result.sort()
|
||||
|
||||
self.assertEqual(expect_result[0], res['qos_specs'][0]['id'])
|
||||
self.assertEqual(expect_result[1], res['qos_specs'][1]['id'])
|
||||
self.assertEqual(expect_result[2], res['qos_specs'][2]['id'])
|
||||
self.assertEqual(expect_result[3], res['qos_specs'][3]['id'])
|
||||
|
||||
@mock.patch('cinder.volume.qos_specs.get_qos_specs',
|
||||
side_effect=return_qos_specs_get_qos_specs)
|
||||
@mock.patch('cinder.volume.qos_specs.delete',
|
||||
|
@ -229,42 +229,12 @@ def disassociate_all(context, specs_id):
|
||||
type_id=None)
|
||||
|
||||
|
||||
def get_all_specs(context, inactive=False, search_opts=None):
|
||||
"""Get all non-deleted qos specs.
|
||||
|
||||
Pass inactive=True as argument and deleted volume types would return
|
||||
as well.
|
||||
"""
|
||||
search_opts = search_opts or {}
|
||||
qos_specs = db.qos_specs_get_all(context, inactive)
|
||||
|
||||
if search_opts:
|
||||
LOG.debug("Searching by: %s", search_opts)
|
||||
|
||||
def _check_specs_match(qos_specs, searchdict):
|
||||
for k, v in searchdict.items():
|
||||
if ((k not in qos_specs['specs'].keys() or
|
||||
qos_specs['specs'][k] != v)):
|
||||
return False
|
||||
return True
|
||||
|
||||
# search_option to filter_name mapping.
|
||||
filter_mapping = {'qos_specs': _check_specs_match}
|
||||
|
||||
result = {}
|
||||
for name, args in qos_specs.items():
|
||||
# go over all filters in the list
|
||||
for opt, values in search_opts.items():
|
||||
try:
|
||||
filter_func = filter_mapping[opt]
|
||||
except KeyError:
|
||||
# no such filter - ignore it, go to next filter
|
||||
continue
|
||||
else:
|
||||
if filter_func(args, values):
|
||||
result[name] = args
|
||||
break
|
||||
qos_specs = result
|
||||
def get_all_specs(context, filters=None, marker=None, limit=None, offset=None,
|
||||
sort_keys=None, sort_dirs=None):
|
||||
"""Get all non-deleted qos specs."""
|
||||
qos_specs = db.qos_specs_get_all(context, filters=filters, marker=marker,
|
||||
limit=limit, offset=offset,
|
||||
sort_keys=sort_keys, sort_dirs=sort_dirs)
|
||||
return qos_specs
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user