
This patch adds exception handling when calling pre and post functions of hooks. Exceptions will be caught and logged, but the hook chain and function call will be allowed to proceed. Partially implements: bp instance-network-info-hook Change-Id: I45e76cd156c244ff57f77db103a9a579f14ad4f1
206 lines
6.2 KiB
Python
206 lines
6.2 KiB
Python
# Copyright (c) 2012 OpenStack Foundation
|
|
# All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
"""Tests for hook customization."""
|
|
|
|
import stevedore
|
|
|
|
from nova import hooks
|
|
from nova import test
|
|
|
|
|
|
class SampleHookA(object):
|
|
name = "a"
|
|
|
|
def _add_called(self, op, kwargs):
|
|
called = kwargs.get('called', None)
|
|
if called is not None:
|
|
called.append(op + self.name)
|
|
|
|
def pre(self, *args, **kwargs):
|
|
self._add_called("pre", kwargs)
|
|
|
|
|
|
class SampleHookB(SampleHookA):
|
|
name = "b"
|
|
|
|
def post(self, rv, *args, **kwargs):
|
|
self._add_called("post", kwargs)
|
|
|
|
|
|
class SampleHookC(SampleHookA):
|
|
name = "c"
|
|
|
|
def pre(self, f, *args, **kwargs):
|
|
self._add_called("pre" + f.__name__, kwargs)
|
|
|
|
def post(self, f, rv, *args, **kwargs):
|
|
self._add_called("post" + f.__name__, kwargs)
|
|
|
|
|
|
class SampleHookExceptionPre(SampleHookA):
|
|
name = "epre"
|
|
exception = Exception()
|
|
|
|
def pre(self, f, *args, **kwargs):
|
|
raise self.exception
|
|
|
|
|
|
class SampleHookExceptionPost(SampleHookA):
|
|
name = "epost"
|
|
exception = Exception()
|
|
|
|
def post(self, f, rv, *args, **kwargs):
|
|
raise self.exception
|
|
|
|
|
|
class MockEntryPoint(object):
|
|
|
|
def __init__(self, cls):
|
|
self.cls = cls
|
|
|
|
def load(self):
|
|
return self.cls
|
|
|
|
|
|
class MockedHookTestCase(test.BaseHookTestCase):
|
|
def _mock_load_plugins(self, iload, *iargs, **ikwargs):
|
|
return []
|
|
|
|
def setUp(self):
|
|
super(MockedHookTestCase, self).setUp()
|
|
|
|
hooks.reset()
|
|
|
|
self.stubs.Set(stevedore.extension.ExtensionManager, '_load_plugins',
|
|
self._mock_load_plugins)
|
|
|
|
|
|
class HookTestCase(MockedHookTestCase):
|
|
def _mock_load_plugins(self, iload, *iargs, **ikwargs):
|
|
return [
|
|
stevedore.extension.Extension('test_hook',
|
|
MockEntryPoint(SampleHookA), SampleHookA, SampleHookA()),
|
|
stevedore.extension.Extension('test_hook',
|
|
MockEntryPoint(SampleHookB), SampleHookB, SampleHookB()),
|
|
]
|
|
|
|
def setUp(self):
|
|
super(HookTestCase, self).setUp()
|
|
|
|
hooks.reset()
|
|
|
|
self.stubs.Set(stevedore.extension.ExtensionManager, '_load_plugins',
|
|
self._mock_load_plugins)
|
|
|
|
@hooks.add_hook('test_hook')
|
|
def _hooked(self, a, b=1, c=2, called=None):
|
|
return 42
|
|
|
|
def test_basic(self):
|
|
self.assertEqual(42, self._hooked(1))
|
|
|
|
mgr = hooks._HOOKS['test_hook']
|
|
self.assert_has_hook('test_hook', self._hooked)
|
|
self.assertEqual(2, len(mgr.extensions))
|
|
self.assertEqual(SampleHookA, mgr.extensions[0].plugin)
|
|
self.assertEqual(SampleHookB, mgr.extensions[1].plugin)
|
|
|
|
def test_order_of_execution(self):
|
|
called_order = []
|
|
self._hooked(42, called=called_order)
|
|
self.assertEqual(['prea', 'preb', 'postb'], called_order)
|
|
|
|
|
|
class HookTestCaseWithFunction(MockedHookTestCase):
|
|
def _mock_load_plugins(self, iload, *iargs, **ikwargs):
|
|
return [
|
|
stevedore.extension.Extension('function_hook',
|
|
MockEntryPoint(SampleHookC), SampleHookC, SampleHookC()),
|
|
]
|
|
|
|
@hooks.add_hook('function_hook', pass_function=True)
|
|
def _hooked(self, a, b=1, c=2, called=None):
|
|
return 42
|
|
|
|
def test_basic(self):
|
|
self.assertEqual(42, self._hooked(1))
|
|
mgr = hooks._HOOKS['function_hook']
|
|
|
|
self.assert_has_hook('function_hook', self._hooked)
|
|
self.assertEqual(1, len(mgr.extensions))
|
|
self.assertEqual(SampleHookC, mgr.extensions[0].plugin)
|
|
|
|
def test_order_of_execution(self):
|
|
called_order = []
|
|
self._hooked(42, called=called_order)
|
|
self.assertEqual(['pre_hookedc', 'post_hookedc'], called_order)
|
|
|
|
|
|
class HookFailPreTestCase(MockedHookTestCase):
|
|
def _mock_load_plugins(self, iload, *iargs, **ikwargs):
|
|
return [
|
|
stevedore.extension.Extension('fail_pre',
|
|
MockEntryPoint(SampleHookExceptionPre),
|
|
SampleHookExceptionPre, SampleHookExceptionPre()),
|
|
]
|
|
|
|
@hooks.add_hook('fail_pre', pass_function=True)
|
|
def _hooked(self, a, b=1, c=2, called=None):
|
|
return 42
|
|
|
|
def test_hook_fail_should_still_return(self):
|
|
self.assertEqual(42, self._hooked(1))
|
|
|
|
mgr = hooks._HOOKS['fail_pre']
|
|
self.assert_has_hook('fail_pre', self._hooked)
|
|
self.assertEqual(1, len(mgr.extensions))
|
|
self.assertEqual(SampleHookExceptionPre, mgr.extensions[0].plugin)
|
|
|
|
def test_hook_fail_should_raise_fatal(self):
|
|
self.stubs.Set(SampleHookExceptionPre, 'exception',
|
|
hooks.FatalHookException())
|
|
|
|
self.assertRaises(hooks.FatalHookException,
|
|
self._hooked, 1)
|
|
|
|
|
|
class HookFailPostTestCase(MockedHookTestCase):
|
|
def _mock_load_plugins(self, iload, *iargs, **ikwargs):
|
|
return [
|
|
stevedore.extension.Extension('fail_post',
|
|
MockEntryPoint(SampleHookExceptionPost),
|
|
SampleHookExceptionPost, SampleHookExceptionPost()),
|
|
]
|
|
|
|
@hooks.add_hook('fail_post', pass_function=True)
|
|
def _hooked(self, a, b=1, c=2, called=None):
|
|
return 42
|
|
|
|
def test_hook_fail_should_still_return(self):
|
|
self.assertEqual(42, self._hooked(1))
|
|
|
|
mgr = hooks._HOOKS['fail_post']
|
|
self.assert_has_hook('fail_post', self._hooked)
|
|
self.assertEqual(1, len(mgr.extensions))
|
|
self.assertEqual(SampleHookExceptionPost, mgr.extensions[0].plugin)
|
|
|
|
def test_hook_fail_should_raise_fatal(self):
|
|
self.stubs.Set(SampleHookExceptionPost, 'exception',
|
|
hooks.FatalHookException())
|
|
|
|
self.assertRaises(hooks.FatalHookException,
|
|
self._hooked, 1)
|