diff --git a/ceilometer/compute/virt/hyperv/inspector.py b/ceilometer/compute/virt/hyperv/inspector.py index f1819f2207..3d4fcb42dc 100644 --- a/ceilometer/compute/virt/hyperv/inspector.py +++ b/ceilometer/compute/virt/hyperv/inspector.py @@ -26,39 +26,45 @@ from ceilometer.compute.pollsters import util from ceilometer.compute.virt import inspector as virt_inspector -def convert_exceptions(function, exception_map): +def convert_exceptions(exception_map, yields=True): expected_exceptions = tuple(exception_map.keys()) - @functools.wraps(function) - def wrapper(*args, **kwargs): - try: - return function(*args, **kwargs) - except expected_exceptions as ex: - # exception might be a subclass of an expected exception. - for expected in expected_exceptions: - if isinstance(ex, expected): - raised_exception = exception_map[expected] - break + def _reraise_exception(exc): + # exception might be a subclass of an expected exception. + for expected in expected_exceptions: + if isinstance(exc, expected): + raised_exception = exception_map[expected] + break - exc_info = sys.exc_info() - # NOTE(claudiub): Python 3 raises the exception object given as - # the second argument in six.reraise. - # The original message will be maintained by passing the original - # exception. - exc = raised_exception(six.text_type(exc_info[1])) - six.reraise(raised_exception, exc, exc_info[2]) - return wrapper + exc_info = sys.exc_info() + # NOTE(claudiub): Python 3 raises the exception object given as + # the second argument in six.reraise. + # The original message will be maintained by passing the + # original exception. + exc = raised_exception(six.text_type(exc_info[1])) + six.reraise(raised_exception, exc, exc_info[2]) + def decorator(function): + if yields: + @functools.wraps(function) + def wrapper(*args, **kwargs): + try: + # NOTE(claudiub): We're consuming the function's yield in + # order to avoid yielding a generator. + for item in function(*args, **kwargs): + yield item + except expected_exceptions as ex: + _reraise_exception(ex) + else: + @functools.wraps(function) + def wrapper(*args, **kwargs): + try: + return function(*args, **kwargs) + except expected_exceptions as ex: + _reraise_exception(ex) -def decorate_all_methods(decorator, *args, **kwargs): - def decorate(cls): - for attr in cls.__dict__: - class_member = getattr(cls, attr) - if callable(class_member): - setattr(cls, attr, decorator(class_member, *args, **kwargs)) - return cls - - return decorate + return wrapper + return decorator exception_conversion_map = collections.OrderedDict([ @@ -69,12 +75,11 @@ exception_conversion_map = collections.OrderedDict([ (os_win_exc.OSWinException, virt_inspector.InspectorException), ]) -# NOTE(claudiub): the purpose of the decorator below is to prevent any +# NOTE(claudiub): the purpose of the decorators below is to prevent any # os_win exceptions (subclasses of OSWinException) to leak outside of the # HyperVInspector. -@decorate_all_methods(convert_exceptions, exception_conversion_map) class HyperVInspector(virt_inspector.Inspector): def __init__(self, conf): @@ -91,6 +96,7 @@ class HyperVInspector(virt_inspector.Inspector): return float(host_cpu_clock * host_cpu_count) + @convert_exceptions(exception_conversion_map, yields=False) def inspect_instance(self, instance, duration): instance_name = util.instance_name(instance) (cpu_clock_used, @@ -105,6 +111,7 @@ class HyperVInspector(virt_inspector.Inspector): cpu_time=cpu_time, memory_usage=memory_usage) + @convert_exceptions(exception_conversion_map) def inspect_vnics(self, instance, duration): instance_name = util.instance_name(instance) for vnic_metrics in self._utils.get_vnic_metrics(instance_name): @@ -122,6 +129,7 @@ class HyperVInspector(virt_inspector.Inspector): tx_drop=0, tx_errors=0) + @convert_exceptions(exception_conversion_map) def inspect_disks(self, instance, duration): instance_name = util.instance_name(instance) for disk_metrics in self._utils.get_disk_metrics(instance_name): @@ -134,6 +142,7 @@ class HyperVInspector(virt_inspector.Inspector): write_bytes=disk_metrics['write_mb'] * units.Mi, errors=0, wr_total_times=0, rd_total_times=0) + @convert_exceptions(exception_conversion_map) def inspect_disk_latency(self, instance, duration): instance_name = util.instance_name(instance) for disk_metrics in self._utils.get_disk_latency_metrics( @@ -142,6 +151,7 @@ class HyperVInspector(virt_inspector.Inspector): device=disk_metrics['instance_id'], disk_latency=disk_metrics['disk_latency'] / 1000) + @convert_exceptions(exception_conversion_map) def inspect_disk_iops(self, instance, duration): instance_name = util.instance_name(instance) for disk_metrics in self._utils.get_disk_iops_count(instance_name): diff --git a/ceilometer/tests/unit/compute/virt/hyperv/test_inspector.py b/ceilometer/tests/unit/compute/virt/hyperv/test_inspector.py index 701f0e0a68..a515aff0c7 100644 --- a/ceilometer/tests/unit/compute/virt/hyperv/test_inspector.py +++ b/ceilometer/tests/unit/compute/virt/hyperv/test_inspector.py @@ -58,6 +58,27 @@ class TestHyperVInspection(base.BaseTestCase): self._inspector.inspect_instance, mock.sentinel.instance, None) + def _yield_consumer(generator_method, *args, **kwargs): + list(generator_method(*args, **kwargs)) + + self._inspector._utils.get_vnic_metrics.side_effect = ( + os_win_exc.OSWinException) + self.assertRaises(virt_inspector.InspectorException, + _yield_consumer, self._inspector.inspect_vnics, + mock.sentinel.instance, None) + + self._inspector._utils.get_vnic_metrics.side_effect = ( + os_win_exc.HyperVException) + self.assertRaises(virt_inspector.InspectorException, + _yield_consumer, self._inspector.inspect_vnics, + mock.sentinel.instance, None) + + self._inspector._utils.get_vnic_metrics.side_effect = ( + os_win_exc.NotFound(resource='foofoo')) + self.assertRaises(virt_inspector.InstanceNotFoundException, + _yield_consumer, self._inspector.inspect_vnics, + mock.sentinel.instance, None) + def test_assert_original_traceback_maintained(self): def bar(self): foo = "foofoo"