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:
Stephen Finucane 2020-05-18 17:49:24 +01:00
parent 45a88f08b4
commit b72980960e
9 changed files with 113 additions and 439 deletions

View File

@ -58,7 +58,6 @@ from nova.db import base
from nova.db.sqlalchemy import api as db_api from nova.db.sqlalchemy import api as db_api
from nova import exception from nova import exception
from nova import exception_wrapper from nova import exception_wrapper
from nova import hooks
from nova.i18n import _ from nova.i18n import _
from nova.image import glance from nova.image import glance
from nova.network import constants from nova.network import constants
@ -1927,7 +1926,6 @@ class API(base.Base):
"is specified.") "is specified.")
raise exception.InvalidFixedIpAndMaxCountRequest(reason=msg) raise exception.InvalidFixedIpAndMaxCountRequest(reason=msg)
@hooks.add_hook("create_instance")
def create(self, context, instance_type, def create(self, context, instance_type,
image_href, kernel_id=None, ramdisk_id=None, image_href, kernel_id=None, ramdisk_id=None,
min_count=None, max_count=None, min_count=None, max_count=None,

View File

@ -73,7 +73,6 @@ import nova.conf
import nova.context import nova.context
from nova import exception from nova import exception
from nova import exception_wrapper from nova import exception_wrapper
from nova import hooks
from nova.i18n import _ from nova.i18n import _
from nova.image import glance from nova.image import glance
from nova import manager from nova import manager
@ -2159,7 +2158,6 @@ class ComputeManager(manager.Manager):
'Trusted image certificates provided on host that does not ' 'Trusted image certificates provided on host that does not '
'support certificate validation.') 'support certificate validation.')
@hooks.add_hook('build_instance')
@wrap_exception() @wrap_exception()
@reverts_task_state @reverts_task_state
@wrap_instance_event(prefix='compute') @wrap_instance_event(prefix='compute')
@ -2949,7 +2947,6 @@ class ComputeManager(manager.Manager):
if exc_info is not None and raise_exc: if exc_info is not None and raise_exc:
six.reraise(exc_info[0], exc_info[1], exc_info[2]) six.reraise(exc_info[0], exc_info[1], exc_info[2])
@hooks.add_hook("delete_instance")
def _delete_instance(self, context, instance, bdms): def _delete_instance(self, context, instance, bdms):
"""Delete an instance on this host. """Delete an instance on this host.

View File

@ -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()

View File

@ -37,7 +37,6 @@ import nova.conf
from nova import context as nova_context from nova import context as nova_context
from nova.db import base from nova.db import base
from nova import exception from nova import exception
from nova import hooks
from nova.i18n import _ from nova.i18n import _
from nova.network import constants from nova.network import constants
from nova.network import model as network_model from nova.network import model as network_model
@ -102,7 +101,6 @@ def get_binding_profile(port):
return port.get(constants.BINDING_PROFILE, {}) or {} 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): def update_instance_cache_with_nw_info(impl, context, instance, nw_info=None):
if instance.deleted: if instance.deleted:
LOG.debug('Instance is deleted, no further info cache update', LOG.debug('Instance is deleted, no further info cache update',

View File

@ -748,12 +748,6 @@ class NoDBTestCase(TestCase):
USES_DB = False 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): class MatchType(object):
"""Matches any instance of a specified type """Matches any instance of a specified type

View File

@ -13425,18 +13425,3 @@ class CheckRequestedImageTestCase(test.TestCase):
self.compute_api._validate_flavor_image, self.compute_api._validate_flavor_image,
self.context, image['id'], image, self.instance_type, self.context, image['id'], image, self.instance_type,
None) 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)

View File

@ -6025,15 +6025,6 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
mock_finish.assert_called_once_with(self.context, self.instance.uuid, mock_finish.assert_called_once_with(self.context, self.instance.uuid,
mock.ANY, exc_val=mock.ANY, exc_tb=mock.ANY, want_result=False) 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(objects.Instance, 'save')
@mock.patch.object(nova.compute.manager.ComputeManager, @mock.patch.object(nova.compute.manager.ComputeManager,
'_default_block_device_names') '_default_block_device_names')
@ -6349,12 +6340,23 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
@mock.patch.object(objects.InstanceActionEvent, 'event_start') @mock.patch.object(objects.InstanceActionEvent, 'event_start')
@mock.patch.object(objects.Instance, 'save') @mock.patch.object(objects.Instance, 'save')
@mock.patch.object(manager.ComputeManager, '_build_and_run_instance') @mock.patch.object(manager.ComputeManager, '_build_and_run_instance')
@mock.patch('nova.hooks._HOOKS') def _test_build_and_run_instance(self, mock_build, mock_save,
def _test_build_and_run_instance(self, mock_hooks, mock_build, mock_save,
mock_start, mock_finish): mock_start, mock_finish):
self._do_build_instance_update(mock_save) 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={}, self.image, request_spec={},
filter_properties=self.filter_properties, filter_properties=self.filter_properties,
injected_files=self.injected_files, injected_files=self.injected_files,
@ -6364,8 +6366,6 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
block_device_mapping=self.block_device_mapping, node=self.node, block_device_mapping=self.block_device_mapping, node=self.node,
limits=self.limits, host_list=fake_host_list) 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._instance_action_events(mock_start, mock_finish)
self._assert_build_instance_update(mock_save) self._assert_build_instance_update(mock_save)
mock_build.assert_called_once_with(self.context, self.instance, 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(manager.ComputeManager, '_set_instance_obj_error_state')
@mock.patch.object(conductor_api.ComputeTaskAPI, 'build_instances') @mock.patch.object(conductor_api.ComputeTaskAPI, 'build_instances')
@mock.patch.object(manager.ComputeManager, '_build_and_run_instance') @mock.patch.object(manager.ComputeManager, '_build_and_run_instance')
@mock.patch('nova.hooks._HOOKS') def test_build_abort_exception(self, mock_build_run,
def test_build_abort_exception(self, mock_hooks, mock_build_run,
mock_build, mock_set, mock_nil, mock_add, mock_build, mock_set, mock_nil, mock_add,
mock_clean_vol, mock_clean_net, mock_save, mock_clean_vol, mock_clean_net, mock_save,
mock_start, mock_finish): mock_start, mock_finish):
@ -6420,7 +6419,19 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
mock_build_run.side_effect = exception.BuildAbortException(reason='', mock_build_run.side_effect = exception.BuildAbortException(reason='',
instance_uuid=self.instance.uuid) 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={}, self.image, request_spec={},
filter_properties=self.filter_properties, filter_properties=self.filter_properties,
injected_files=self.injected_files, injected_files=self.injected_files,
@ -6432,8 +6443,6 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
self._instance_action_events(mock_start, mock_finish) self._instance_action_events(mock_start, mock_finish)
self._assert_build_instance_update(mock_save) 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, mock_build_run.assert_called_once_with(self.context, self.instance,
self.image, self.injected_files, self.admin_pass, self.image, self.injected_files, self.admin_pass,
self.requested_networks, self.security_groups, 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(manager.ComputeManager, '_set_instance_obj_error_state')
@mock.patch.object(conductor_api.ComputeTaskAPI, 'build_instances') @mock.patch.object(conductor_api.ComputeTaskAPI, 'build_instances')
@mock.patch.object(manager.ComputeManager, '_build_and_run_instance') @mock.patch.object(manager.ComputeManager, '_build_and_run_instance')
@mock.patch('nova.hooks._HOOKS') def test_rescheduled_exception(self, mock_build_run,
def test_rescheduled_exception(self, mock_hooks, mock_build_run,
mock_build, mock_set, mock_nil, mock_build, mock_set, mock_nil,
mock_save, mock_start, mock_finish): mock_save, mock_start, mock_finish):
self._do_build_instance_update(mock_save, reschedule_update=True) self._do_build_instance_update(mock_save, reschedule_update=True)
mock_build_run.side_effect = exception.RescheduledException(reason='', mock_build_run.side_effect = exception.RescheduledException(reason='',
instance_uuid=self.instance.uuid) instance_uuid=self.instance.uuid)
with mock.patch.object( orig_do_build_and_run = self.compute._do_build_and_run_instance
self.compute.network_api, 'get_instance_nw_info',
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.compute.build_and_run_instance(
self.context, self.instance, self.context, self.instance,
@ -6481,8 +6502,6 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
node=self.node, limits=self.limits, node=self.node, limits=self.limits,
host_list=fake_host_list) 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._instance_action_events(mock_start, mock_finish)
self._assert_build_instance_update(mock_save, reschedule_update=True) self._assert_build_instance_update(mock_save, reschedule_update=True)
mock_build_run.assert_called_once_with(self.context, self.instance, 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(manager.ComputeManager, '_set_instance_obj_error_state')
@mock.patch.object(compute_utils, 'add_instance_fault_from_exc') @mock.patch.object(compute_utils, 'add_instance_fault_from_exc')
@mock.patch.object(manager.ComputeManager, '_build_and_run_instance') @mock.patch.object(manager.ComputeManager, '_build_and_run_instance')
@mock.patch('nova.hooks._HOOKS') def test_rescheduled_exception_without_retry(self,
def test_rescheduled_exception_without_retry(self, mock_hooks,
mock_build_run, mock_add, mock_set, mock_clean_net, mock_clean_vol, mock_build_run, mock_add, mock_set, mock_clean_net, mock_clean_vol,
mock_nil, mock_save, mock_start, mock_finish): mock_nil, mock_save, mock_start, mock_finish):
self._do_build_instance_update(mock_save) self._do_build_instance_update(mock_save)
mock_build_run.side_effect = exception.RescheduledException(reason='', mock_build_run.side_effect = exception.RescheduledException(reason='',
instance_uuid=self.instance.uuid) 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={}, self.image, request_spec={},
filter_properties={}, filter_properties={},
injected_files=self.injected_files, injected_files=self.injected_files,
@ -6710,8 +6740,6 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
block_device_mapping=self.block_device_mapping, node=self.node, block_device_mapping=self.block_device_mapping, node=self.node,
limits=self.limits, host_list=fake_host_list) 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._instance_action_events(mock_start, mock_finish)
self._assert_build_instance_update(mock_save) self._assert_build_instance_update(mock_save)
mock_build_run.assert_called_once_with(self.context, self.instance, 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') '_nil_out_instance_obj_host_and_node')
@mock.patch.object(conductor_api.ComputeTaskAPI, 'build_instances') @mock.patch.object(conductor_api.ComputeTaskAPI, 'build_instances')
@mock.patch.object(manager.ComputeManager, '_build_and_run_instance') @mock.patch.object(manager.ComputeManager, '_build_and_run_instance')
@mock.patch('nova.hooks._HOOKS') def test_rescheduled_exception_do_not_deallocate_network(self,
def test_rescheduled_exception_do_not_deallocate_network(self, mock_hooks,
mock_build_run, mock_build, mock_nil, mock_build_run, mock_build, mock_nil,
mock_clean_net, mock_save, mock_start, mock_clean_net, mock_save, mock_start,
mock_finish): mock_finish):
@ -6748,7 +6775,19 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
mock_build_run.side_effect = exception.RescheduledException(reason='', mock_build_run.side_effect = exception.RescheduledException(reason='',
instance_uuid=self.instance.uuid) 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={}, self.image, request_spec={},
filter_properties=self.filter_properties, filter_properties=self.filter_properties,
injected_files=self.injected_files, injected_files=self.injected_files,
@ -6759,8 +6798,6 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
node=self.node, limits=self.limits, node=self.node, limits=self.limits,
host_list=fake_host_list) 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._instance_action_events(mock_start, mock_finish)
self._assert_build_instance_update(mock_save, reschedule_update=True) self._assert_build_instance_update(mock_save, reschedule_update=True)
mock_build_run.assert_called_once_with(self.context, self.instance, 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') '_nil_out_instance_obj_host_and_node')
@mock.patch.object(conductor_api.ComputeTaskAPI, 'build_instances') @mock.patch.object(conductor_api.ComputeTaskAPI, 'build_instances')
@mock.patch.object(manager.ComputeManager, '_build_and_run_instance') @mock.patch.object(manager.ComputeManager, '_build_and_run_instance')
@mock.patch('nova.hooks._HOOKS') def test_rescheduled_exception_deallocate_network(self,
def test_rescheduled_exception_deallocate_network(self, mock_hooks,
mock_build_run, mock_build, mock_nil, mock_clean, mock_build_run, mock_build, mock_nil, mock_clean,
mock_save, mock_start, mock_finish): mock_save, mock_start, mock_finish):
self._do_build_instance_update(mock_save, reschedule_update=True) self._do_build_instance_update(mock_save, reschedule_update=True)
mock_build_run.side_effect = exception.RescheduledException(reason='', mock_build_run.side_effect = exception.RescheduledException(reason='',
instance_uuid=self.instance.uuid) 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={}, self.image, request_spec={},
filter_properties=self.filter_properties, filter_properties=self.filter_properties,
injected_files=self.injected_files, injected_files=self.injected_files,
@ -6802,8 +6850,6 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
block_device_mapping=self.block_device_mapping, node=self.node, block_device_mapping=self.block_device_mapping, node=self.node,
limits=self.limits, host_list=fake_host_list) 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._instance_action_events(mock_start, mock_finish)
self._assert_build_instance_update(mock_save, reschedule_update=True) self._assert_build_instance_update(mock_save, reschedule_update=True)
mock_build_run.assert_called_once_with(self.context, self.instance, 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(manager.ComputeManager, '_set_instance_obj_error_state')
@mock.patch.object(conductor_api.ComputeTaskAPI, 'build_instances') @mock.patch.object(conductor_api.ComputeTaskAPI, 'build_instances')
@mock.patch.object(manager.ComputeManager, '_build_and_run_instance') @mock.patch.object(manager.ComputeManager, '_build_and_run_instance')
@mock.patch('nova.hooks._HOOKS') def _test_build_and_run_exceptions(self, exc, mock_build_run,
def _test_build_and_run_exceptions(self, exc, mock_hooks, mock_build_run,
mock_build, mock_set, mock_nil, mock_add, mock_clean_vol, mock_build, mock_set, mock_nil, mock_add, mock_clean_vol,
mock_clean_net, mock_save, mock_start, mock_finish, mock_clean_net, mock_save, mock_start, mock_finish,
set_error=False, cleanup_volumes=False, set_error=False, cleanup_volumes=False,
@ -6841,7 +6886,19 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
self._do_build_instance_update(mock_save) self._do_build_instance_update(mock_save)
mock_build_run.side_effect = exc 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={}, self.image, request_spec={},
filter_properties=self.filter_properties, filter_properties=self.filter_properties,
injected_files=self.injected_files, injected_files=self.injected_files,
@ -6851,8 +6908,6 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
block_device_mapping=self.block_device_mapping, node=self.node, block_device_mapping=self.block_device_mapping, node=self.node,
limits=self.limits, host_list=fake_host_list) 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._instance_action_events(mock_start, mock_finish)
self._assert_build_instance_update(mock_save) self._assert_build_instance_update(mock_save)
if cleanup_volumes: if cleanup_volumes:

View File

@ -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)

View 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