a0e07a77d0
Add new feature called 'hooks', that allows to: - Perform actions before some share driver methods calls. - Perform actions after some share driver methods calls with results of driver call and preceding hook call. - Call additional 'periodic' hook each 'N' ticks. - Possibility to update results of driver's action by post-running hook. Features of hooks: - Errors can be suppressed. - Any of hooks can be disabled. - Any amount of hook instances can be run. Known limitations: - Hooks are not asynchronous. It means, if we run hooks, and especially, more than one instance, then all of them will be executed in one thread. Implements bp mount-automation-framework Change-Id: I7f496ac49e828f361c18ff89c5a308d698f2a4aa
322 lines
13 KiB
Python
322 lines
13 KiB
Python
# Copyright 2015 Mirantis Inc.
|
|
# 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.
|
|
|
|
import ddt
|
|
import mock
|
|
|
|
from manila import context
|
|
from manila.share import hook
|
|
from manila import test
|
|
|
|
|
|
class FakeHookImplementation(hook.HookBase):
|
|
def _execute_pre_hook(self, context, func_name, *args, **kwargs):
|
|
"""Fake implementation of a pre hook action."""
|
|
|
|
def _execute_post_hook(self, context, func_name, pre_hook_data,
|
|
driver_action_results, *args, **kwargs):
|
|
"""Fake implementation of a post hook action."""
|
|
|
|
def _execute_periodic_hook(self, context, periodic_hook_data,
|
|
*args, **kwargs):
|
|
"""Fake implementation of a periodic hook action."""
|
|
|
|
|
|
@ddt.ddt
|
|
class HookBaseTestCase(test.TestCase):
|
|
|
|
def setUp(self):
|
|
super(HookBaseTestCase, self).setUp()
|
|
self.context = context.get_admin_context()
|
|
self.default_config = {
|
|
"enable_pre_hooks": "fake_enable_pre_hooks",
|
|
"enable_post_hooks": "fake_enable_post_hooks",
|
|
"enable_periodic_hooks": "fake_enable_periodic_hooks",
|
|
"suppress_pre_hooks_errors": "fake_suppress_pre_hook_errors",
|
|
"suppress_post_hooks_errors": "fake_suppress_post_hook_errors",
|
|
}
|
|
for k, v in self.default_config.items():
|
|
hook.CONF.set_default(k, v)
|
|
|
|
def _fake_safe_get(self, key):
|
|
return self.default_config.get(key) + "_safe_get"
|
|
|
|
def _get_hook_instance(self, set_configuration=True, host="fake_host"):
|
|
if set_configuration:
|
|
configuration = mock.Mock()
|
|
configuration.safe_get.side_effect = self._fake_safe_get
|
|
else:
|
|
configuration = None
|
|
instance = FakeHookImplementation(
|
|
configuration=configuration, host=host)
|
|
return instance
|
|
|
|
def test_instantiate_hook_fail(self):
|
|
self.assertRaises(TypeError, hook.HookBase)
|
|
|
|
@ddt.data(True, False)
|
|
def test_instantiate_hook_successfully_and_set_configuration(
|
|
self, set_configuration):
|
|
instance = self._get_hook_instance(set_configuration)
|
|
|
|
self.assertTrue(hasattr(instance, 'host'))
|
|
self.assertEqual("fake_host", instance.host)
|
|
self.assertTrue(hasattr(instance, 'configuration'))
|
|
if not set_configuration:
|
|
self.assertIsNone(instance.configuration)
|
|
for attr_name in ("pre_hooks_enabled",
|
|
"post_hooks_enabled",
|
|
"periodic_hooks_enabled",
|
|
"suppress_pre_hooks_errors",
|
|
"suppress_post_hooks_errors"):
|
|
self.assertTrue(hasattr(instance, attr_name))
|
|
if set_configuration:
|
|
instance.configuration.append_config_values.assert_has_calls([
|
|
mock.call(hook.hook_options)])
|
|
conf_func = self._fake_safe_get
|
|
else:
|
|
conf_func = self.default_config.get
|
|
self.assertEqual(
|
|
conf_func("enable_pre_hooks"), instance.pre_hooks_enabled)
|
|
self.assertEqual(
|
|
conf_func("enable_post_hooks"), instance.post_hooks_enabled)
|
|
self.assertEqual(
|
|
conf_func("enable_periodic_hooks"),
|
|
instance.periodic_hooks_enabled)
|
|
self.assertEqual(
|
|
conf_func("suppress_pre_hooks_errors"),
|
|
instance.suppress_pre_hooks_errors)
|
|
self.assertEqual(
|
|
conf_func("suppress_post_hooks_errors"),
|
|
instance.suppress_post_hooks_errors)
|
|
|
|
def test_execute_pre_hook_disabled(self):
|
|
instance = self._get_hook_instance()
|
|
instance.pre_hooks_enabled = False
|
|
self.mock_object(
|
|
instance, "_execute_pre_hook",
|
|
mock.Mock(side_effect=Exception("I should not be raised.")))
|
|
|
|
result = instance.execute_pre_hook(
|
|
self.context, "fake_func_name", "some_arg", some_kwarg="foo")
|
|
|
|
self.assertIsNone(result)
|
|
|
|
@ddt.data(True, False)
|
|
def test_execute_pre_hook_success(self, provide_context):
|
|
instance = self._get_hook_instance()
|
|
instance.pre_hooks_enabled = True
|
|
instance.suppress_pre_hooks_errors = True
|
|
expected = "fake_expected_result"
|
|
some_arg = "some_arg"
|
|
func_name = "fake_func_name"
|
|
self.mock_object(hook.LOG, 'error')
|
|
self.mock_object(
|
|
instance, "_execute_pre_hook", mock.Mock(return_value=expected))
|
|
mock_ctxt = self.mock_object(context, 'get_admin_context')
|
|
ctxt = self.context if provide_context else mock_ctxt
|
|
|
|
result = instance.execute_pre_hook(
|
|
ctxt, func_name, some_arg, some_kwarg="foo")
|
|
|
|
self.assertEqual(expected, result)
|
|
instance._execute_pre_hook.assert_called_once_with(
|
|
some_arg,
|
|
context=self.context if provide_context else mock_ctxt,
|
|
func_name=func_name,
|
|
some_kwarg="foo")
|
|
self.assertFalse(hook.LOG.error.called)
|
|
|
|
def test_execute_pre_hook_exception_with_suppression(self):
|
|
instance = self._get_hook_instance()
|
|
instance.pre_hooks_enabled = True
|
|
instance.suppress_pre_hooks_errors = True
|
|
some_arg = "some_arg"
|
|
func_name = "fake_func_name"
|
|
FakeException = type("FakeException", (Exception, ), {})
|
|
self.mock_object(hook.LOG, 'warning')
|
|
self.mock_object(
|
|
instance, "_execute_pre_hook", mock.Mock(side_effect=(
|
|
FakeException("Some exception that should be suppressed."))))
|
|
|
|
result = instance.execute_pre_hook(
|
|
self.context, func_name, some_arg, some_kwarg="foo")
|
|
|
|
self.assertIsInstance(result, FakeException)
|
|
instance._execute_pre_hook.assert_called_once_with(
|
|
some_arg,
|
|
context=self.context,
|
|
func_name=func_name,
|
|
some_kwarg="foo")
|
|
self.assertTrue(hook.LOG.warning.called)
|
|
|
|
def test_execute_pre_hook_exception_without_suppression(self):
|
|
instance = self._get_hook_instance()
|
|
instance.pre_hooks_enabled = True
|
|
instance.suppress_pre_hooks_errors = False
|
|
some_arg = "some_arg"
|
|
func_name = "fake_func_name"
|
|
FakeException = type("FakeException", (Exception, ), {})
|
|
self.mock_object(hook.LOG, 'warning')
|
|
self.mock_object(
|
|
instance, "_execute_pre_hook", mock.Mock(side_effect=(
|
|
FakeException(
|
|
"Some exception that should NOT be suppressed."))))
|
|
|
|
self.assertRaises(
|
|
FakeException,
|
|
instance.execute_pre_hook,
|
|
self.context, func_name, some_arg, some_kwarg="foo")
|
|
|
|
instance._execute_pre_hook.assert_called_once_with(
|
|
some_arg,
|
|
context=self.context,
|
|
func_name=func_name,
|
|
some_kwarg="foo")
|
|
self.assertFalse(hook.LOG.warning.called)
|
|
|
|
def test_execute_post_hook_disabled(self):
|
|
instance = self._get_hook_instance()
|
|
instance.post_hooks_enabled = False
|
|
self.mock_object(
|
|
instance, "_execute_post_hook",
|
|
mock.Mock(side_effect=Exception("I should not be raised.")))
|
|
|
|
result = instance.execute_post_hook(
|
|
self.context, "fake_func_name", "some_pre_hook_data",
|
|
"some_driver_action_results", "some_arg", some_kwarg="foo")
|
|
|
|
self.assertIsNone(result)
|
|
|
|
@ddt.data(True, False)
|
|
def test_execute_post_hook_success(self, provide_context):
|
|
instance = self._get_hook_instance()
|
|
instance.post_hooks_enabled = True
|
|
instance.suppress_post_hooks_errors = True
|
|
expected = "fake_expected_result"
|
|
some_arg = "some_arg"
|
|
func_name = "fake_func_name"
|
|
pre_hook_data = "some_pre_hook_data"
|
|
driver_action_results = "some_driver_action_results"
|
|
self.mock_object(hook.LOG, 'warning')
|
|
self.mock_object(
|
|
instance, "_execute_post_hook", mock.Mock(return_value=expected))
|
|
mock_ctxt = self.mock_object(context, 'get_admin_context')
|
|
ctxt = self.context if provide_context else mock_ctxt
|
|
|
|
result = instance.execute_post_hook(
|
|
ctxt, func_name, pre_hook_data, driver_action_results,
|
|
some_arg, some_kwarg="foo")
|
|
|
|
self.assertEqual(expected, result)
|
|
instance._execute_post_hook.assert_called_once_with(
|
|
some_arg,
|
|
context=self.context if provide_context else mock_ctxt,
|
|
func_name=func_name,
|
|
pre_hook_data=pre_hook_data,
|
|
driver_action_results=driver_action_results,
|
|
some_kwarg="foo")
|
|
self.assertFalse(hook.LOG.warning.called)
|
|
|
|
def test_execute_post_hook_exception_with_suppression(self):
|
|
instance = self._get_hook_instance()
|
|
instance.post_hooks_enabled = True
|
|
instance.suppress_post_hooks_errors = True
|
|
some_arg = "some_arg"
|
|
func_name = "fake_func_name"
|
|
pre_hook_data = "some_pre_hook_data"
|
|
driver_action_results = "some_driver_action_results"
|
|
FakeException = type("FakeException", (Exception, ), {})
|
|
self.mock_object(hook.LOG, 'warning')
|
|
self.mock_object(
|
|
instance, "_execute_post_hook", mock.Mock(side_effect=(
|
|
FakeException("Some exception that should be suppressed."))))
|
|
|
|
result = instance.execute_post_hook(
|
|
self.context, func_name, pre_hook_data, driver_action_results,
|
|
some_arg, some_kwarg="foo")
|
|
|
|
self.assertIsInstance(result, FakeException)
|
|
instance._execute_post_hook.assert_called_once_with(
|
|
some_arg,
|
|
context=self.context,
|
|
func_name=func_name,
|
|
pre_hook_data=pre_hook_data,
|
|
driver_action_results=driver_action_results,
|
|
some_kwarg="foo")
|
|
self.assertTrue(hook.LOG.warning.called)
|
|
|
|
def test_execute_post_hook_exception_without_suppression(self):
|
|
instance = self._get_hook_instance()
|
|
instance.post_hooks_enabled = True
|
|
instance.suppress_post_hooks_errors = False
|
|
some_arg = "some_arg"
|
|
func_name = "fake_func_name"
|
|
pre_hook_data = "some_pre_hook_data"
|
|
driver_action_results = "some_driver_action_results"
|
|
FakeException = type("FakeException", (Exception, ), {})
|
|
self.mock_object(hook.LOG, 'error')
|
|
self.mock_object(
|
|
instance, "_execute_post_hook", mock.Mock(side_effect=(
|
|
FakeException(
|
|
"Some exception that should NOT be suppressed."))))
|
|
|
|
self.assertRaises(
|
|
FakeException,
|
|
instance.execute_post_hook,
|
|
self.context, func_name, pre_hook_data, driver_action_results,
|
|
some_arg, some_kwarg="foo")
|
|
|
|
instance._execute_post_hook.assert_called_once_with(
|
|
some_arg,
|
|
context=self.context,
|
|
func_name=func_name,
|
|
pre_hook_data=pre_hook_data,
|
|
driver_action_results=driver_action_results,
|
|
some_kwarg="foo")
|
|
self.assertFalse(hook.LOG.error.called)
|
|
|
|
def test_execute_periodic_hook_disabled(self):
|
|
instance = self._get_hook_instance()
|
|
instance.periodic_hooks_enabled = False
|
|
self.mock_object(instance, "_execute_periodic_hook")
|
|
|
|
instance.execute_periodic_hook(
|
|
self.context, "fake_periodic_hook_data",
|
|
"some_arg", some_kwarg="foo")
|
|
|
|
self.assertFalse(instance._execute_periodic_hook.called)
|
|
|
|
@ddt.data(True, False)
|
|
def test_execute_periodic_hook_enabled(self, provide_context):
|
|
instance = self._get_hook_instance()
|
|
instance.periodic_hooks_enabled = True
|
|
expected = "some_expected_result"
|
|
self.mock_object(
|
|
instance,
|
|
"_execute_periodic_hook",
|
|
mock.Mock(return_value=expected))
|
|
mock_ctxt = self.mock_object(context, 'get_admin_context')
|
|
ctxt = self.context if provide_context else mock_ctxt
|
|
|
|
result = instance.execute_periodic_hook(
|
|
ctxt, "fake_periodic_hook_data",
|
|
"some_arg", some_kwarg="foo")
|
|
|
|
instance._execute_periodic_hook.assert_called_once_with(
|
|
ctxt, "fake_periodic_hook_data",
|
|
"some_arg", some_kwarg="foo")
|
|
self.assertEqual(expected, result)
|