Remove hooks
This extension point was deprecated in 13.0.0 (Mitaka) as it was unmaintainable. Now, over four years later, it's finally time to remove them. A combination of notifications, versioned or otherwise, and dynamic vendordata should be used instead. Change-Id: Idb9a0c06d8abdb158bcee5be12c35dcb67257e60 Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
This commit is contained in:
parent
45a88f08b4
commit
b72980960e
@ -58,7 +58,6 @@ from nova.db import base
|
||||
from nova.db.sqlalchemy import api as db_api
|
||||
from nova import exception
|
||||
from nova import exception_wrapper
|
||||
from nova import hooks
|
||||
from nova.i18n import _
|
||||
from nova.image import glance
|
||||
from nova.network import constants
|
||||
@ -1927,7 +1926,6 @@ class API(base.Base):
|
||||
"is specified.")
|
||||
raise exception.InvalidFixedIpAndMaxCountRequest(reason=msg)
|
||||
|
||||
@hooks.add_hook("create_instance")
|
||||
def create(self, context, instance_type,
|
||||
image_href, kernel_id=None, ramdisk_id=None,
|
||||
min_count=None, max_count=None,
|
||||
|
@ -73,7 +73,6 @@ import nova.conf
|
||||
import nova.context
|
||||
from nova import exception
|
||||
from nova import exception_wrapper
|
||||
from nova import hooks
|
||||
from nova.i18n import _
|
||||
from nova.image import glance
|
||||
from nova import manager
|
||||
@ -2159,7 +2158,6 @@ class ComputeManager(manager.Manager):
|
||||
'Trusted image certificates provided on host that does not '
|
||||
'support certificate validation.')
|
||||
|
||||
@hooks.add_hook('build_instance')
|
||||
@wrap_exception()
|
||||
@reverts_task_state
|
||||
@wrap_instance_event(prefix='compute')
|
||||
@ -2949,7 +2947,6 @@ class ComputeManager(manager.Manager):
|
||||
if exc_info is not None and raise_exc:
|
||||
six.reraise(exc_info[0], exc_info[1], exc_info[2])
|
||||
|
||||
@hooks.add_hook("delete_instance")
|
||||
def _delete_instance(self, context, instance, bdms):
|
||||
"""Delete an instance on this host.
|
||||
|
||||
|
167
nova/hooks.py
167
nova/hooks.py
@ -1,167 +0,0 @@
|
||||
# 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.
|
||||
|
||||
"""Decorator and config option definitions for adding custom code (hooks)
|
||||
around callables.
|
||||
|
||||
NOTE: as of Nova 13.0 hooks are DEPRECATED and will be removed in the
|
||||
near future. You should not build any new code using this facility.
|
||||
|
||||
Any method may have the 'add_hook' decorator applied, which yields the
|
||||
ability to invoke Hook objects before or after the method. (i.e. pre and
|
||||
post)
|
||||
|
||||
Hook objects are loaded by HookLoaders. Each named hook may invoke multiple
|
||||
Hooks.
|
||||
|
||||
Example Hook object::
|
||||
|
||||
| class MyHook(object):
|
||||
| def pre(self, *args, **kwargs):
|
||||
| # do stuff before wrapped callable runs
|
||||
|
|
||||
| def post(self, rv, *args, **kwargs):
|
||||
| # do stuff after wrapped callable runs
|
||||
|
||||
Example Hook object with function parameters::
|
||||
|
||||
| class MyHookWithFunction(object):
|
||||
| def pre(self, f, *args, **kwargs):
|
||||
| # do stuff with wrapped function info
|
||||
| def post(self, f, *args, **kwargs):
|
||||
| # do stuff with wrapped function info
|
||||
|
||||
"""
|
||||
|
||||
import functools
|
||||
|
||||
from oslo_log import log as logging
|
||||
import stevedore
|
||||
|
||||
from nova.i18n import _
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
NS = 'nova.hooks'
|
||||
|
||||
_HOOKS = {} # hook name => hook manager
|
||||
|
||||
|
||||
class FatalHookException(Exception):
|
||||
"""Exception which should be raised by hooks to indicate that normal
|
||||
execution of the hooked function should be terminated. Raised exception
|
||||
will be logged and reraised.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class HookManager(stevedore.hook.HookManager):
|
||||
def __init__(self, name):
|
||||
"""Invoke_on_load creates an instance of the Hook class
|
||||
|
||||
:param name: The name of the hooks to load.
|
||||
:type name: str
|
||||
"""
|
||||
super(HookManager, self).__init__(NS, name, invoke_on_load=True)
|
||||
|
||||
def _run(self, name, method_type, args, kwargs, func=None):
|
||||
if method_type not in ('pre', 'post'):
|
||||
msg = _("Wrong type of hook method. "
|
||||
"Only 'pre' and 'post' type allowed")
|
||||
raise ValueError(msg)
|
||||
|
||||
# TODO(stephenfin): Kill this
|
||||
for e in self.extensions:
|
||||
obj = e.obj
|
||||
hook_method = getattr(obj, method_type, None)
|
||||
if hook_method:
|
||||
LOG.warning("Hooks are deprecated as of Nova 13.0 and "
|
||||
"will be removed in a future release")
|
||||
LOG.debug("Running %(name)s %(type)s-hook: %(obj)s",
|
||||
{'name': name, 'type': method_type, 'obj': obj})
|
||||
try:
|
||||
if func:
|
||||
hook_method(func, *args, **kwargs)
|
||||
else:
|
||||
hook_method(*args, **kwargs)
|
||||
except FatalHookException:
|
||||
msg = (
|
||||
"Fatal Exception running %(name)s %(type)s-hook: "
|
||||
"%(obj)s"
|
||||
)
|
||||
LOG.exception(msg, {'name': name, 'type': method_type,
|
||||
'obj': obj})
|
||||
raise
|
||||
except Exception:
|
||||
msg = "Exception running %(name)s %(type)s-hook: %(obj)s"
|
||||
LOG.exception(msg, {'name': name, 'type': method_type,
|
||||
'obj': obj})
|
||||
|
||||
def run_pre(self, name, args, kwargs, f=None):
|
||||
"""Execute optional pre methods of loaded hooks.
|
||||
|
||||
:param name: The name of the loaded hooks.
|
||||
:param args: Positional arguments which would be transmitted into
|
||||
all pre methods of loaded hooks.
|
||||
:param kwargs: Keyword args which would be transmitted into all pre
|
||||
methods of loaded hooks.
|
||||
:param f: Target function.
|
||||
"""
|
||||
self._run(name=name, method_type='pre', args=args, kwargs=kwargs,
|
||||
func=f)
|
||||
|
||||
def run_post(self, name, rv, args, kwargs, f=None):
|
||||
"""Execute optional post methods of loaded hooks.
|
||||
|
||||
:param name: The name of the loaded hooks.
|
||||
:param rv: Return values of target method call.
|
||||
:param args: Positional arguments which would be transmitted into
|
||||
all post methods of loaded hooks.
|
||||
:param kwargs: Keyword args which would be transmitted into all post
|
||||
methods of loaded hooks.
|
||||
:param f: Target function.
|
||||
"""
|
||||
self._run(name=name, method_type='post', args=(rv,) + args,
|
||||
kwargs=kwargs, func=f)
|
||||
|
||||
|
||||
def add_hook(name, pass_function=False):
|
||||
"""Execute optional pre and post methods around the decorated
|
||||
function. This is useful for customization around callables.
|
||||
"""
|
||||
|
||||
def outer(f):
|
||||
f.__hook_name__ = name
|
||||
|
||||
@functools.wraps(f)
|
||||
def inner(*args, **kwargs):
|
||||
manager = _HOOKS.setdefault(name, HookManager(name))
|
||||
|
||||
function = None
|
||||
if pass_function:
|
||||
function = f
|
||||
|
||||
manager.run_pre(name, args, kwargs, f=function)
|
||||
rv = f(*args, **kwargs)
|
||||
manager.run_post(name, rv, args, kwargs, f=function)
|
||||
|
||||
return rv
|
||||
|
||||
return inner
|
||||
return outer
|
||||
|
||||
|
||||
def reset():
|
||||
"""Clear loaded hooks."""
|
||||
_HOOKS.clear()
|
@ -37,7 +37,6 @@ import nova.conf
|
||||
from nova import context as nova_context
|
||||
from nova.db import base
|
||||
from nova import exception
|
||||
from nova import hooks
|
||||
from nova.i18n import _
|
||||
from nova.network import constants
|
||||
from nova.network import model as network_model
|
||||
@ -102,7 +101,6 @@ def get_binding_profile(port):
|
||||
return port.get(constants.BINDING_PROFILE, {}) or {}
|
||||
|
||||
|
||||
@hooks.add_hook('instance_network_info')
|
||||
def update_instance_cache_with_nw_info(impl, context, instance, nw_info=None):
|
||||
if instance.deleted:
|
||||
LOG.debug('Instance is deleted, no further info cache update',
|
||||
|
@ -748,12 +748,6 @@ class NoDBTestCase(TestCase):
|
||||
USES_DB = False
|
||||
|
||||
|
||||
class BaseHookTestCase(NoDBTestCase):
|
||||
def assert_has_hook(self, expected_name, func):
|
||||
self.assertTrue(hasattr(func, '__hook_name__'))
|
||||
self.assertEqual(expected_name, func.__hook_name__)
|
||||
|
||||
|
||||
class MatchType(object):
|
||||
"""Matches any instance of a specified type
|
||||
|
||||
|
@ -13425,18 +13425,3 @@ class CheckRequestedImageTestCase(test.TestCase):
|
||||
self.compute_api._validate_flavor_image,
|
||||
self.context, image['id'], image, self.instance_type,
|
||||
None)
|
||||
|
||||
|
||||
class ComputeHooksTestCase(test.BaseHookTestCase):
|
||||
def test_delete_instance_has_hook(self):
|
||||
delete_func = compute_manager.ComputeManager._delete_instance
|
||||
self.assert_has_hook('delete_instance', delete_func)
|
||||
|
||||
def test_create_instance_has_hook(self):
|
||||
create_func = compute.API.create
|
||||
self.assert_has_hook('create_instance', create_func)
|
||||
|
||||
def test_build_instance_has_hook(self):
|
||||
build_instance_func = (compute_manager.ComputeManager.
|
||||
_do_build_and_run_instance)
|
||||
self.assert_has_hook('build_instance', build_instance_func)
|
||||
|
@ -6025,15 +6025,6 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
|
||||
mock_finish.assert_called_once_with(self.context, self.instance.uuid,
|
||||
mock.ANY, exc_val=mock.ANY, exc_tb=mock.ANY, want_result=False)
|
||||
|
||||
@staticmethod
|
||||
def _assert_build_instance_hook_called(mock_hooks, result):
|
||||
# NOTE(coreywright): we want to test the return value of
|
||||
# _do_build_and_run_instance, but it doesn't bubble all the way up, so
|
||||
# mock the hooking, which allows us to test that too, though a little
|
||||
# too intimately
|
||||
mock_hooks.setdefault().run_post.assert_called_once_with(
|
||||
'build_instance', result, mock.ANY, mock.ANY, f=None)
|
||||
|
||||
@mock.patch.object(objects.Instance, 'save')
|
||||
@mock.patch.object(nova.compute.manager.ComputeManager,
|
||||
'_default_block_device_names')
|
||||
@ -6349,12 +6340,23 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
|
||||
@mock.patch.object(objects.InstanceActionEvent, 'event_start')
|
||||
@mock.patch.object(objects.Instance, 'save')
|
||||
@mock.patch.object(manager.ComputeManager, '_build_and_run_instance')
|
||||
@mock.patch('nova.hooks._HOOKS')
|
||||
def _test_build_and_run_instance(self, mock_hooks, mock_build, mock_save,
|
||||
def _test_build_and_run_instance(self, mock_build, mock_save,
|
||||
mock_start, mock_finish):
|
||||
self._do_build_instance_update(mock_save)
|
||||
|
||||
self.compute.build_and_run_instance(self.context, self.instance,
|
||||
orig_do_build_and_run = self.compute._do_build_and_run_instance
|
||||
|
||||
def _wrapped_do_build_and_run_instance(*args, **kwargs):
|
||||
ret = orig_do_build_and_run(*args, **kwargs)
|
||||
self.assertEqual(build_results.ACTIVE, ret)
|
||||
return ret
|
||||
|
||||
with mock.patch.object(
|
||||
self.compute, '_do_build_and_run_instance',
|
||||
side_effect=_wrapped_do_build_and_run_instance,
|
||||
):
|
||||
self.compute.build_and_run_instance(
|
||||
self.context, self.instance,
|
||||
self.image, request_spec={},
|
||||
filter_properties=self.filter_properties,
|
||||
injected_files=self.injected_files,
|
||||
@ -6364,8 +6366,6 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
|
||||
block_device_mapping=self.block_device_mapping, node=self.node,
|
||||
limits=self.limits, host_list=fake_host_list)
|
||||
|
||||
self._assert_build_instance_hook_called(mock_hooks,
|
||||
build_results.ACTIVE)
|
||||
self._instance_action_events(mock_start, mock_finish)
|
||||
self._assert_build_instance_update(mock_save)
|
||||
mock_build.assert_called_once_with(self.context, self.instance,
|
||||
@ -6411,8 +6411,7 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
|
||||
@mock.patch.object(manager.ComputeManager, '_set_instance_obj_error_state')
|
||||
@mock.patch.object(conductor_api.ComputeTaskAPI, 'build_instances')
|
||||
@mock.patch.object(manager.ComputeManager, '_build_and_run_instance')
|
||||
@mock.patch('nova.hooks._HOOKS')
|
||||
def test_build_abort_exception(self, mock_hooks, mock_build_run,
|
||||
def test_build_abort_exception(self, mock_build_run,
|
||||
mock_build, mock_set, mock_nil, mock_add,
|
||||
mock_clean_vol, mock_clean_net, mock_save,
|
||||
mock_start, mock_finish):
|
||||
@ -6420,7 +6419,19 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
|
||||
mock_build_run.side_effect = exception.BuildAbortException(reason='',
|
||||
instance_uuid=self.instance.uuid)
|
||||
|
||||
self.compute.build_and_run_instance(self.context, self.instance,
|
||||
orig_do_build_and_run = self.compute._do_build_and_run_instance
|
||||
|
||||
def _wrapped_do_build_and_run_instance(*args, **kwargs):
|
||||
ret = orig_do_build_and_run(*args, **kwargs)
|
||||
self.assertEqual(build_results.FAILED, ret)
|
||||
return ret
|
||||
|
||||
with mock.patch.object(
|
||||
self.compute, '_do_build_and_run_instance',
|
||||
side_effect=_wrapped_do_build_and_run_instance,
|
||||
):
|
||||
self.compute.build_and_run_instance(
|
||||
self.context, self.instance,
|
||||
self.image, request_spec={},
|
||||
filter_properties=self.filter_properties,
|
||||
injected_files=self.injected_files,
|
||||
@ -6432,8 +6443,6 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
|
||||
|
||||
self._instance_action_events(mock_start, mock_finish)
|
||||
self._assert_build_instance_update(mock_save)
|
||||
self._assert_build_instance_hook_called(mock_hooks,
|
||||
build_results.FAILED)
|
||||
mock_build_run.assert_called_once_with(self.context, self.instance,
|
||||
self.image, self.injected_files, self.admin_pass,
|
||||
self.requested_networks, self.security_groups,
|
||||
@ -6458,16 +6467,28 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
|
||||
@mock.patch.object(manager.ComputeManager, '_set_instance_obj_error_state')
|
||||
@mock.patch.object(conductor_api.ComputeTaskAPI, 'build_instances')
|
||||
@mock.patch.object(manager.ComputeManager, '_build_and_run_instance')
|
||||
@mock.patch('nova.hooks._HOOKS')
|
||||
def test_rescheduled_exception(self, mock_hooks, mock_build_run,
|
||||
def test_rescheduled_exception(self, mock_build_run,
|
||||
mock_build, mock_set, mock_nil,
|
||||
mock_save, mock_start, mock_finish):
|
||||
self._do_build_instance_update(mock_save, reschedule_update=True)
|
||||
mock_build_run.side_effect = exception.RescheduledException(reason='',
|
||||
instance_uuid=self.instance.uuid)
|
||||
|
||||
with mock.patch.object(
|
||||
self.compute.network_api, 'get_instance_nw_info',
|
||||
orig_do_build_and_run = self.compute._do_build_and_run_instance
|
||||
|
||||
def _wrapped_do_build_and_run_instance(*args, **kwargs):
|
||||
ret = orig_do_build_and_run(*args, **kwargs)
|
||||
self.assertEqual(build_results.RESCHEDULED, ret)
|
||||
return ret
|
||||
|
||||
with test.nested(
|
||||
mock.patch.object(
|
||||
self.compute, '_do_build_and_run_instance',
|
||||
side_effect=_wrapped_do_build_and_run_instance,
|
||||
),
|
||||
mock.patch.object(
|
||||
self.compute.network_api, 'get_instance_nw_info',
|
||||
),
|
||||
):
|
||||
self.compute.build_and_run_instance(
|
||||
self.context, self.instance,
|
||||
@ -6481,8 +6502,6 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
|
||||
node=self.node, limits=self.limits,
|
||||
host_list=fake_host_list)
|
||||
|
||||
self._assert_build_instance_hook_called(mock_hooks,
|
||||
build_results.RESCHEDULED)
|
||||
self._instance_action_events(mock_start, mock_finish)
|
||||
self._assert_build_instance_update(mock_save, reschedule_update=True)
|
||||
mock_build_run.assert_called_once_with(self.context, self.instance,
|
||||
@ -6692,15 +6711,26 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
|
||||
@mock.patch.object(manager.ComputeManager, '_set_instance_obj_error_state')
|
||||
@mock.patch.object(compute_utils, 'add_instance_fault_from_exc')
|
||||
@mock.patch.object(manager.ComputeManager, '_build_and_run_instance')
|
||||
@mock.patch('nova.hooks._HOOKS')
|
||||
def test_rescheduled_exception_without_retry(self, mock_hooks,
|
||||
def test_rescheduled_exception_without_retry(self,
|
||||
mock_build_run, mock_add, mock_set, mock_clean_net, mock_clean_vol,
|
||||
mock_nil, mock_save, mock_start, mock_finish):
|
||||
self._do_build_instance_update(mock_save)
|
||||
mock_build_run.side_effect = exception.RescheduledException(reason='',
|
||||
instance_uuid=self.instance.uuid)
|
||||
|
||||
self.compute.build_and_run_instance(self.context, self.instance,
|
||||
orig_do_build_and_run = self.compute._do_build_and_run_instance
|
||||
|
||||
def _wrapped_do_build_and_run_instance(*args, **kwargs):
|
||||
ret = orig_do_build_and_run(*args, **kwargs)
|
||||
self.assertEqual(build_results.FAILED, ret)
|
||||
return ret
|
||||
|
||||
with mock.patch.object(
|
||||
self.compute, '_do_build_and_run_instance',
|
||||
side_effect=_wrapped_do_build_and_run_instance,
|
||||
):
|
||||
self.compute.build_and_run_instance(
|
||||
self.context, self.instance,
|
||||
self.image, request_spec={},
|
||||
filter_properties={},
|
||||
injected_files=self.injected_files,
|
||||
@ -6710,8 +6740,6 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
|
||||
block_device_mapping=self.block_device_mapping, node=self.node,
|
||||
limits=self.limits, host_list=fake_host_list)
|
||||
|
||||
self._assert_build_instance_hook_called(mock_hooks,
|
||||
build_results.FAILED)
|
||||
self._instance_action_events(mock_start, mock_finish)
|
||||
self._assert_build_instance_update(mock_save)
|
||||
mock_build_run.assert_called_once_with(self.context, self.instance,
|
||||
@ -6739,8 +6767,7 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
|
||||
'_nil_out_instance_obj_host_and_node')
|
||||
@mock.patch.object(conductor_api.ComputeTaskAPI, 'build_instances')
|
||||
@mock.patch.object(manager.ComputeManager, '_build_and_run_instance')
|
||||
@mock.patch('nova.hooks._HOOKS')
|
||||
def test_rescheduled_exception_do_not_deallocate_network(self, mock_hooks,
|
||||
def test_rescheduled_exception_do_not_deallocate_network(self,
|
||||
mock_build_run, mock_build, mock_nil,
|
||||
mock_clean_net, mock_save, mock_start,
|
||||
mock_finish):
|
||||
@ -6748,7 +6775,19 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
|
||||
mock_build_run.side_effect = exception.RescheduledException(reason='',
|
||||
instance_uuid=self.instance.uuid)
|
||||
|
||||
self.compute.build_and_run_instance(self.context, self.instance,
|
||||
orig_do_build_and_run = self.compute._do_build_and_run_instance
|
||||
|
||||
def _wrapped_do_build_and_run_instance(*args, **kwargs):
|
||||
ret = orig_do_build_and_run(*args, **kwargs)
|
||||
self.assertEqual(build_results.RESCHEDULED, ret)
|
||||
return ret
|
||||
|
||||
with mock.patch.object(
|
||||
self.compute, '_do_build_and_run_instance',
|
||||
side_effect=_wrapped_do_build_and_run_instance,
|
||||
):
|
||||
self.compute.build_and_run_instance(
|
||||
self.context, self.instance,
|
||||
self.image, request_spec={},
|
||||
filter_properties=self.filter_properties,
|
||||
injected_files=self.injected_files,
|
||||
@ -6759,8 +6798,6 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
|
||||
node=self.node, limits=self.limits,
|
||||
host_list=fake_host_list)
|
||||
|
||||
self._assert_build_instance_hook_called(mock_hooks,
|
||||
build_results.RESCHEDULED)
|
||||
self._instance_action_events(mock_start, mock_finish)
|
||||
self._assert_build_instance_update(mock_save, reschedule_update=True)
|
||||
mock_build_run.assert_called_once_with(self.context, self.instance,
|
||||
@ -6784,15 +6821,26 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
|
||||
'_nil_out_instance_obj_host_and_node')
|
||||
@mock.patch.object(conductor_api.ComputeTaskAPI, 'build_instances')
|
||||
@mock.patch.object(manager.ComputeManager, '_build_and_run_instance')
|
||||
@mock.patch('nova.hooks._HOOKS')
|
||||
def test_rescheduled_exception_deallocate_network(self, mock_hooks,
|
||||
def test_rescheduled_exception_deallocate_network(self,
|
||||
mock_build_run, mock_build, mock_nil, mock_clean,
|
||||
mock_save, mock_start, mock_finish):
|
||||
self._do_build_instance_update(mock_save, reschedule_update=True)
|
||||
mock_build_run.side_effect = exception.RescheduledException(reason='',
|
||||
instance_uuid=self.instance.uuid)
|
||||
|
||||
self.compute.build_and_run_instance(self.context, self.instance,
|
||||
orig_do_build_and_run = self.compute._do_build_and_run_instance
|
||||
|
||||
def _wrapped_do_build_and_run_instance(*args, **kwargs):
|
||||
ret = orig_do_build_and_run(*args, **kwargs)
|
||||
self.assertEqual(build_results.RESCHEDULED, ret)
|
||||
return ret
|
||||
|
||||
with mock.patch.object(
|
||||
self.compute, '_do_build_and_run_instance',
|
||||
side_effect=_wrapped_do_build_and_run_instance,
|
||||
):
|
||||
self.compute.build_and_run_instance(
|
||||
self.context, self.instance,
|
||||
self.image, request_spec={},
|
||||
filter_properties=self.filter_properties,
|
||||
injected_files=self.injected_files,
|
||||
@ -6802,8 +6850,6 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
|
||||
block_device_mapping=self.block_device_mapping, node=self.node,
|
||||
limits=self.limits, host_list=fake_host_list)
|
||||
|
||||
self._assert_build_instance_hook_called(mock_hooks,
|
||||
build_results.RESCHEDULED)
|
||||
self._instance_action_events(mock_start, mock_finish)
|
||||
self._assert_build_instance_update(mock_save, reschedule_update=True)
|
||||
mock_build_run.assert_called_once_with(self.context, self.instance,
|
||||
@ -6832,8 +6878,7 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
|
||||
@mock.patch.object(manager.ComputeManager, '_set_instance_obj_error_state')
|
||||
@mock.patch.object(conductor_api.ComputeTaskAPI, 'build_instances')
|
||||
@mock.patch.object(manager.ComputeManager, '_build_and_run_instance')
|
||||
@mock.patch('nova.hooks._HOOKS')
|
||||
def _test_build_and_run_exceptions(self, exc, mock_hooks, mock_build_run,
|
||||
def _test_build_and_run_exceptions(self, exc, mock_build_run,
|
||||
mock_build, mock_set, mock_nil, mock_add, mock_clean_vol,
|
||||
mock_clean_net, mock_save, mock_start, mock_finish,
|
||||
set_error=False, cleanup_volumes=False,
|
||||
@ -6841,7 +6886,19 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
|
||||
self._do_build_instance_update(mock_save)
|
||||
mock_build_run.side_effect = exc
|
||||
|
||||
self.compute.build_and_run_instance(self.context, self.instance,
|
||||
orig_do_build_and_run = self.compute._do_build_and_run_instance
|
||||
|
||||
def _wrapped_do_build_and_run_instance(*args, **kwargs):
|
||||
ret = orig_do_build_and_run(*args, **kwargs)
|
||||
self.assertEqual(build_results.FAILED, ret)
|
||||
return ret
|
||||
|
||||
with mock.patch.object(
|
||||
self.compute, '_do_build_and_run_instance',
|
||||
side_effect=_wrapped_do_build_and_run_instance,
|
||||
):
|
||||
self.compute.build_and_run_instance(
|
||||
self.context, self.instance,
|
||||
self.image, request_spec={},
|
||||
filter_properties=self.filter_properties,
|
||||
injected_files=self.injected_files,
|
||||
@ -6851,8 +6908,6 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
|
||||
block_device_mapping=self.block_device_mapping, node=self.node,
|
||||
limits=self.limits, host_list=fake_host_list)
|
||||
|
||||
self._assert_build_instance_hook_called(mock_hooks,
|
||||
build_results.FAILED)
|
||||
self._instance_action_events(mock_start, mock_finish)
|
||||
self._assert_build_instance_update(mock_save)
|
||||
if cleanup_volumes:
|
||||
|
@ -1,199 +0,0 @@
|
||||
# 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):
|
||||
PLUGINS = []
|
||||
|
||||
def setUp(self):
|
||||
super(MockedHookTestCase, self).setUp()
|
||||
|
||||
hooks.reset()
|
||||
|
||||
hook_manager = hooks.HookManager.make_test_instance(self.PLUGINS)
|
||||
self.stub_out('nova.hooks.HookManager', lambda x: hook_manager)
|
||||
|
||||
|
||||
class HookTestCase(MockedHookTestCase):
|
||||
PLUGINS = [
|
||||
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()
|
||||
|
||||
@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):
|
||||
PLUGINS = [
|
||||
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):
|
||||
PLUGINS = [
|
||||
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.stub_out('nova.tests.unit.test_hooks.'
|
||||
'SampleHookExceptionPre.exception',
|
||||
hooks.FatalHookException())
|
||||
|
||||
self.assertRaises(hooks.FatalHookException,
|
||||
self._hooked, 1)
|
||||
|
||||
|
||||
class HookFailPostTestCase(MockedHookTestCase):
|
||||
PLUGINS = [
|
||||
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.stub_out('nova.tests.unit.test_hooks.'
|
||||
'SampleHookExceptionPost.exception',
|
||||
hooks.FatalHookException())
|
||||
|
||||
self.assertRaises(hooks.FatalHookException,
|
||||
self._hooked, 1)
|
13
releasenotes/notes/remove-hooks-96d08645404d327c.yaml
Normal file
13
releasenotes/notes/remove-hooks-96d08645404d327c.yaml
Normal file
@ -0,0 +1,13 @@
|
||||
---
|
||||
upgrade:
|
||||
- |
|
||||
Support for hooks has been removed. In previous versions of nova, these
|
||||
provided a mechanism to extend nova with custom code through a plugin
|
||||
mechanism. However, they were deprecated in 13.0.0 (Mitaka) as
|
||||
unmaintainable long-term. `Versioned notifications`__ and `vendordata`__
|
||||
should be used instead.
|
||||
For more information, refer to `this thread`__.
|
||||
|
||||
__ https://docs.openstack.org/nova/latest/reference/notifications.html
|
||||
__ https://docs.openstack.org/nova/latest/admin/vendordata.html
|
||||
__ http://lists.openstack.org/pipermail/openstack-dev/2016-February/087782.html
|
Loading…
Reference in New Issue
Block a user