Merge "Add logging when filtering returns nothing"
This commit is contained in:
commit
b81f4f2f94
@ -17,6 +17,7 @@
|
|||||||
Filter support
|
Filter support
|
||||||
"""
|
"""
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
import six
|
||||||
|
|
||||||
from cinder.i18n import _LI
|
from cinder.i18n import _LI
|
||||||
from cinder.scheduler import base_handler
|
from cinder.scheduler import base_handler
|
||||||
@ -63,6 +64,28 @@ class BaseFilterHandler(base_handler.BaseHandler):
|
|||||||
This class should be subclassed where one needs to use filters.
|
This class should be subclassed where one needs to use filters.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def _log_filtration(self, full_filter_results,
|
||||||
|
part_filter_results, filter_properties):
|
||||||
|
# Log the filtration history
|
||||||
|
rspec = filter_properties.get("request_spec", {})
|
||||||
|
msg_dict = {"vol_id": rspec.get("volume_id", ""),
|
||||||
|
"str_results": six.text_type(full_filter_results),
|
||||||
|
}
|
||||||
|
full_msg = ("Filtering removed all hosts for the request with "
|
||||||
|
"volume ID "
|
||||||
|
"'%(vol_id)s'. Filter results: %(str_results)s"
|
||||||
|
) % msg_dict
|
||||||
|
msg_dict["str_results"] = ', '.join(
|
||||||
|
_LI("%(cls_name)s: (start: %(start)s, end: %(end)s)") % {
|
||||||
|
"cls_name": value[0], "start": value[1], "end": value[2]}
|
||||||
|
for value in part_filter_results)
|
||||||
|
part_msg = _LI("Filtering removed all hosts for the request with "
|
||||||
|
"volume ID "
|
||||||
|
"'%(vol_id)s'. Filter results: %(str_results)s"
|
||||||
|
) % msg_dict
|
||||||
|
LOG.debug(full_msg)
|
||||||
|
LOG.info(part_msg)
|
||||||
|
|
||||||
def get_filtered_objects(self, filter_classes, objs,
|
def get_filtered_objects(self, filter_classes, objs,
|
||||||
filter_properties, index=0):
|
filter_properties, index=0):
|
||||||
"""Get objects after filter
|
"""Get objects after filter
|
||||||
@ -77,21 +100,37 @@ class BaseFilterHandler(base_handler.BaseHandler):
|
|||||||
"""
|
"""
|
||||||
list_objs = list(objs)
|
list_objs = list(objs)
|
||||||
LOG.debug("Starting with %d host(s)", len(list_objs))
|
LOG.debug("Starting with %d host(s)", len(list_objs))
|
||||||
|
# The 'part_filter_results' list just tracks the number of hosts
|
||||||
|
# before and after the filter, unless the filter returns zero
|
||||||
|
# hosts, in which it records the host/nodename for the last batch
|
||||||
|
# that was removed. Since the full_filter_results can be very large,
|
||||||
|
# it is only recorded if the LOG level is set to debug.
|
||||||
|
part_filter_results = []
|
||||||
|
full_filter_results = []
|
||||||
for filter_cls in filter_classes:
|
for filter_cls in filter_classes:
|
||||||
cls_name = filter_cls.__name__
|
cls_name = filter_cls.__name__
|
||||||
|
start_count = len(list_objs)
|
||||||
filter_class = filter_cls()
|
filter_class = filter_cls()
|
||||||
|
|
||||||
if filter_class.run_filter_for_index(index):
|
if filter_class.run_filter_for_index(index):
|
||||||
objs = filter_class.filter_all(list_objs, filter_properties)
|
objs = filter_class.filter_all(list_objs, filter_properties)
|
||||||
if objs is None:
|
if objs is None:
|
||||||
LOG.debug("Filter %(cls_name)s says to stop filtering",
|
LOG.info(_LI("Filter %s returned 0 hosts"), cls_name)
|
||||||
{'cls_name': cls_name})
|
full_filter_results.append((cls_name, None))
|
||||||
return
|
list_objs = None
|
||||||
list_objs = list(objs)
|
|
||||||
msg = (_LI("Filter %(cls_name)s returned %(obj_len)d host(s)")
|
|
||||||
% {'cls_name': cls_name, 'obj_len': len(list_objs)})
|
|
||||||
if not list_objs:
|
|
||||||
LOG.info(msg)
|
|
||||||
break
|
break
|
||||||
LOG.debug(msg)
|
|
||||||
|
list_objs = list(objs)
|
||||||
|
end_count = len(list_objs)
|
||||||
|
part_filter_results.append((cls_name, start_count, end_count))
|
||||||
|
remaining = [getattr(obj, "host", obj)
|
||||||
|
for obj in list_objs]
|
||||||
|
full_filter_results.append((cls_name, remaining))
|
||||||
|
|
||||||
|
LOG.debug("Filter %(cls_name)s returned "
|
||||||
|
"%(obj_len)d host(s)",
|
||||||
|
{'cls_name': cls_name, 'obj_len': len(list_objs)})
|
||||||
|
if not list_objs:
|
||||||
|
self._log_filtration(full_filter_results,
|
||||||
|
part_filter_results, filter_properties)
|
||||||
return list_objs
|
return list_objs
|
||||||
|
@ -17,7 +17,9 @@ import mock
|
|||||||
from oslotest import moxstubout
|
from oslotest import moxstubout
|
||||||
|
|
||||||
from cinder.scheduler import base_filter
|
from cinder.scheduler import base_filter
|
||||||
|
from cinder.scheduler import host_manager
|
||||||
from cinder import test
|
from cinder import test
|
||||||
|
from cinder.tests.unit import fake_constants as fake
|
||||||
|
|
||||||
|
|
||||||
class TestBaseFilter(test.TestCase):
|
class TestBaseFilter(test.TestCase):
|
||||||
@ -95,6 +97,18 @@ class FakeFilter5(BaseFakeFilter):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class FilterA(base_filter.BaseFilter):
|
||||||
|
def filter_all(self, list_objs, filter_properties):
|
||||||
|
# return all but the first object
|
||||||
|
return list_objs[1:]
|
||||||
|
|
||||||
|
|
||||||
|
class FilterB(base_filter.BaseFilter):
|
||||||
|
def filter_all(self, list_objs, filter_properties):
|
||||||
|
# return an empty list
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
class FakeExtensionManager(list):
|
class FakeExtensionManager(list):
|
||||||
|
|
||||||
def __init__(self, namespace):
|
def __init__(self, namespace):
|
||||||
@ -166,3 +180,32 @@ class TestBaseFilterHandler(test.TestCase):
|
|||||||
result = self._get_filtered_objects(filter_classes, index=2)
|
result = self._get_filtered_objects(filter_classes, index=2)
|
||||||
self.assertEqual(filter_objs_expected, result)
|
self.assertEqual(filter_objs_expected, result)
|
||||||
self.assertEqual(1, fake5_filter_all.call_count)
|
self.assertEqual(1, fake5_filter_all.call_count)
|
||||||
|
|
||||||
|
def test_get_filtered_objects_info_and_debug_log_none_returned(self):
|
||||||
|
|
||||||
|
all_filters = [FilterA, FilterA, FilterB]
|
||||||
|
fake_hosts = [host_manager.HostState('fake_host%s' % x)
|
||||||
|
for x in range(1, 4)]
|
||||||
|
|
||||||
|
filt_props = {"request_spec": {'volume_id': fake.VOLUME_ID,
|
||||||
|
'volume_properties': {'project_id': fake.PROJECT_ID,
|
||||||
|
'size': 2048,
|
||||||
|
'host': 'host4'}}}
|
||||||
|
with mock.patch.object(base_filter, 'LOG') as mock_log:
|
||||||
|
result = self.handler.get_filtered_objects(
|
||||||
|
all_filters, fake_hosts, filt_props)
|
||||||
|
self.assertFalse(result)
|
||||||
|
msg = "with volume ID '%s'" % fake.VOLUME_ID
|
||||||
|
# FilterA should leave Host1 and Host2; FilterB should leave None.
|
||||||
|
exp_output = ("FilterA: (start: 3, end: 2), "
|
||||||
|
"FilterA: (start: 2, end: 1)")
|
||||||
|
cargs = mock_log.info.call_args[0][0]
|
||||||
|
self.assertIn(msg, cargs)
|
||||||
|
self.assertIn(exp_output, cargs)
|
||||||
|
|
||||||
|
exp_output = ("[('FilterA', ['fake_host2', 'fake_host3']), "
|
||||||
|
"('FilterA', ['fake_host3']), "
|
||||||
|
+ "('FilterB', None)]")
|
||||||
|
cargs = mock_log.debug.call_args[0][0]
|
||||||
|
self.assertIn(msg, cargs)
|
||||||
|
self.assertIn(exp_output, cargs)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user