Add findall server side filtering

Findall method in cinderclient/base.py isn't very efficient: it loads
the entire list then filters on the Python side. When calling "cinder
show 'volName'" on a tenant holding a lot of volumes (> 1000) the run
time is too long.

On my env show command with 10000 volumes takes ~2 min, after apply
this patch few seconds.

Closes-Bug: #1449444
Change-Id: I23070d94d5bb100b2dd8677f67d7c8b1e7d34e52
This commit is contained in:
Anton Arefiev 2015-05-06 14:54:15 +03:00
parent f098b02564
commit 0b8b9f2de7
3 changed files with 24 additions and 9 deletions

View File

@ -210,8 +210,8 @@ class ManagerWithFind(six.with_metaclass(abc.ABCMeta, Manager)):
"""
Find a single item with attributes matching ``**kwargs``.
This isn't very efficient: it loads the entire list then filters on
the Python side.
This isn't very efficient for search options which require the
Python side filtering(e.g. 'human_id')
"""
matches = self.findall(**kwargs)
num_matches = len(matches)
@ -227,16 +227,29 @@ class ManagerWithFind(six.with_metaclass(abc.ABCMeta, Manager)):
"""
Find all items with attributes matching ``**kwargs``.
This isn't very efficient: it loads the entire list then filters on
the Python side.
This isn't very efficient for search options which require the
Python side filtering(e.g. 'human_id')
"""
found = []
searches = list(kwargs.items())
# Want to search for all tenants here so that when attempting to delete
# that a user like admin doesn't get a failure when trying to delete
# another tenant's volume by name.
for obj in self.list(search_opts={'all_tenants': 1}):
search_opts = {'all_tenants': 1}
# Pass 'name' or 'display_name' search_opts to server filtering to
# increase search performance.
if 'name' in kwargs:
search_opts['name'] = kwargs['name']
elif 'display_name' in kwargs:
search_opts['display_name'] = kwargs['display_name']
found = []
searches = kwargs.items()
# Not all resources attributes support filters on server side
# (e.g. 'human_id' doesn't), so when doing findall some client
# side filtering is still needed.
for obj in self.list(search_opts=search_opts):
try:
if all(getattr(obj, attr) == value
for (attr, value) in searches):

View File

@ -234,7 +234,8 @@ class ShellTest(utils.TestCase):
def test_delete_by_name(self):
self.run_command('delete sample-volume')
self.assert_called_anytime('GET', '/volumes/detail?all_tenants=1')
self.assert_called_anytime('GET', '/volumes/detail?all_tenants=1&'
'display_name=sample-volume')
self.assert_called('DELETE', '/volumes/1234')
def test_delete_multiple(self):

View File

@ -344,7 +344,8 @@ class ShellTest(utils.TestCase):
def test_delete_by_name(self):
self.run_command('delete sample-volume')
self.assert_called_anytime('GET', '/volumes/detail?all_tenants=1')
self.assert_called_anytime('GET', '/volumes/detail?all_tenants=1&'
'name=sample-volume')
self.assert_called('DELETE', '/volumes/1234')
def test_delete_multiple(self):