Skip to content

Commit c419be6

Browse files
stephenfinBalazs Gibizer
authored andcommitted
Remove 'ComputeManager._reschedule'
Resolve a TODO and remove this function, folding its contents back into its caller. This will make a future patch much simpler. Part of blueprint remove-cells-v1 Change-Id: I02b8f07cb0a325cbb28603610f752041ef6cd02c Signed-off-by: Stephen Finucane <[email protected]> Co-Authored-By: Balazs Gibizer <[email protected]>
1 parent d7bad34 commit c419be6

File tree

4 files changed

+130
-161
lines changed

4 files changed

+130
-161
lines changed

nova/compute/manager.py

Lines changed: 28 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1422,37 +1422,6 @@ def _log_original_error(self, exc_info, instance_uuid):
14221422
LOG.error('Error: %s', exc_info[1], instance_uuid=instance_uuid,
14231423
exc_info=exc_info)
14241424

1425-
# TODO(mriedem): This method is confusing and only ever used for resize
1426-
# reschedules; remove it and merge into _reschedule_resize_or_reraise.
1427-
def _reschedule(self, context, request_spec, filter_properties,
1428-
instance, reschedule_method, method_args, task_state,
1429-
exc_info=None, host_list=None):
1430-
"""Attempt to re-schedule a compute operation."""
1431-
1432-
instance_uuid = instance.uuid
1433-
retry = filter_properties.get('retry')
1434-
if not retry:
1435-
# no retry information, do not reschedule.
1436-
LOG.debug("Retry info not present, will not reschedule",
1437-
instance_uuid=instance_uuid)
1438-
return
1439-
1440-
LOG.debug("Re-scheduling %(method)s: attempt %(num)d",
1441-
{'method': reschedule_method.__name__,
1442-
'num': retry['num_attempts']}, instance_uuid=instance_uuid)
1443-
1444-
# reset the task state:
1445-
self._instance_update(context, instance, task_state=task_state)
1446-
1447-
if exc_info:
1448-
# stringify to avoid circular ref problem in json serialization:
1449-
retry['exc'] = traceback.format_exception_only(exc_info[0],
1450-
exc_info[1])
1451-
1452-
reschedule_method(context, *method_args, request_spec=request_spec,
1453-
host_list=host_list)
1454-
return True
1455-
14561425
@periodic_task.periodic_task
14571426
def _check_instance_build_time(self, context):
14581427
"""Ensure that instances are not stuck in build."""
@@ -4458,14 +4427,33 @@ def _reschedule_resize_or_reraise(self, context, instance, exc_info,
44584427
instance_uuid = instance.uuid
44594428

44604429
try:
4461-
reschedule_method = self.compute_task_api.resize_instance
4462-
scheduler_hint = dict(filter_properties=filter_properties)
4463-
method_args = (instance, None, scheduler_hint, instance_type)
4464-
task_state = task_states.RESIZE_PREP
4465-
4466-
rescheduled = self._reschedule(context, request_spec,
4467-
filter_properties, instance, reschedule_method,
4468-
method_args, task_state, exc_info, host_list=host_list)
4430+
retry = filter_properties.get('retry')
4431+
if retry:
4432+
LOG.debug('Rescheduling, attempt %d', retry['num_attempts'],
4433+
instance_uuid=instance_uuid)
4434+
4435+
# reset the task state
4436+
task_state = task_states.RESIZE_PREP
4437+
self._instance_update(context, instance, task_state=task_state)
4438+
4439+
if exc_info:
4440+
# stringify to avoid circular ref problem in json
4441+
# serialization
4442+
retry['exc'] = traceback.format_exception_only(
4443+
exc_info[0], exc_info[1])
4444+
4445+
scheduler_hint = {'filter_properties': filter_properties}
4446+
4447+
self.compute_task_api.resize_instance(
4448+
context, instance, None, scheduler_hint, instance_type,
4449+
request_spec=request_spec, host_list=host_list)
4450+
4451+
rescheduled = True
4452+
else:
4453+
# no retry information, do not reschedule.
4454+
LOG.debug('Retry info not present, will not reschedule',
4455+
instance_uuid=instance_uuid)
4456+
rescheduled = False
44694457
except Exception as error:
44704458
rescheduled = False
44714459
LOG.exception("Error trying to reschedule",
@@ -4481,6 +4469,7 @@ def _reschedule_resize_or_reraise(self, context, instance, exc_info,
44814469
phase=fields.NotificationPhase.ERROR,
44824470
exception=error,
44834471
tb=','.join(traceback.format_exception(*exc_info)))
4472+
44844473
if rescheduled:
44854474
self._log_original_error(exc_info, instance_uuid)
44864475
compute_utils.add_instance_fault_from_exc(context,

nova/tests/functional/notification_sample_tests/test_instance.py

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,11 +1056,9 @@ def _test_resize_and_revert_server(self, server):
10561056
'uuid': server['id']},
10571057
actual=fake_notifier.VERSIONED_NOTIFICATIONS[1])
10581058

1059-
@mock.patch('nova.compute.manager.ComputeManager._reschedule',
1060-
return_value=True)
10611059
@mock.patch('nova.compute.manager.ComputeManager._prep_resize')
10621060
def test_resize_server_error_but_reschedule_was_success(
1063-
self, mock_prep_resize, mock_reschedule):
1061+
self, mock_prep_resize):
10641062
"""Test it, when the prep_resize method raise an exception,
10651063
but the reschedule_resize_or_reraise was successful and
10661064
scheduled the resize. In this case we get a notification
@@ -1089,6 +1087,14 @@ def _build_resources(*args, **kwargs):
10891087
}
10901088
fake_notifier.reset()
10911089
mock_prep_resize.side_effect = _build_resources
1090+
# NOTE(gibi): the first resize_instance call (from the API) should be
1091+
# unaffected so that we can reach _prep_resize at all. But the
1092+
# subsequent resize_instance call (from _reschedule_resize_or_reraise)
1093+
# needs to be mocked as there is no alternative host to resize to.
1094+
patcher = mock.patch.object(self.compute.manager.compute_task_api,
1095+
'resize_instance')
1096+
self.addCleanup(patcher.stop)
1097+
patcher.start()
10921098
self.api.post_server_action(server['id'], post)
10931099
self._wait_for_notification('instance.resize.error')
10941100
self._pop_and_verify_dest_select_notification(server['id'],
@@ -1120,10 +1126,9 @@ def _build_resources(*args, **kwargs):
11201126
},
11211127
actual=fake_notifier.VERSIONED_NOTIFICATIONS[2])
11221128

1123-
@mock.patch('nova.compute.manager.ComputeManager._reschedule')
11241129
@mock.patch('nova.compute.manager.ComputeManager._prep_resize')
11251130
def test_resize_server_error_and_reschedule_was_failed(
1126-
self, mock_prep_resize, mock_reschedule):
1131+
self, mock_prep_resize):
11271132
"""Test it, when the prep_resize method raise an exception,
11281133
after trying again with the reschedule_resize_or_reraise method
11291134
call, but the rescheduled also was unsuccessful. In this
@@ -1156,9 +1161,17 @@ def _build_resources(*args, **kwargs):
11561161
}
11571162
fake_notifier.reset()
11581163
mock_prep_resize.side_effect = _build_resources
1159-
# This isn't realistic that _reschedule would raise FlavorDiskTooSmall,
1160-
# but it's needed for the notification sample to work.
1161-
mock_reschedule.side_effect = _build_resources
1164+
# NOTE(gibi): the first resize_instance call (from the API) should be
1165+
# unaffected so that we can reach _prep_resize at all. But the
1166+
# subsequent resize_instance call (from _reschedule_resize_or_reraise)
1167+
# needs to fail. It isn't realistic that resize_instance would raise
1168+
# FlavorDiskTooSmall, but it's needed for the notification sample
1169+
# to work.
1170+
patcher = mock.patch.object(self.compute.manager.compute_task_api,
1171+
'resize_instance',
1172+
side_effect=_build_resources)
1173+
self.addCleanup(patcher.stop)
1174+
patcher.start()
11621175
self.api.post_server_action(server['id'], post)
11631176
self._wait_for_state_change(self.api, server, expected_status='ERROR')
11641177
self._wait_for_notification('compute.exception')

nova/tests/unit/compute/test_compute.py

Lines changed: 76 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import operator
2323
import sys
2424
import time
25-
import traceback
2625

2726
import ddt
2827

@@ -12789,62 +12788,6 @@ def fake_get_flavor_by_flavor_id(flavor_id, ctxt=None,
1278912788
self.compute_api.resize, self.context, instance, '4')
1279012789

1279112790

12792-
class ComputeReschedulingTestCase(BaseTestCase):
12793-
"""Tests re-scheduling logic for new build requests."""
12794-
12795-
def setUp(self):
12796-
super(ComputeReschedulingTestCase, self).setUp()
12797-
12798-
self.expected_task_state = task_states.SCHEDULING
12799-
12800-
def fake_update(*args, **kwargs):
12801-
self.updated_task_state = kwargs.get('task_state')
12802-
self.stub_out('nova.compute.manager.ComputeManager._instance_update',
12803-
fake_update)
12804-
12805-
def _reschedule(self, request_spec=None, filter_properties=None,
12806-
exc_info=None):
12807-
if not filter_properties:
12808-
filter_properties = {}
12809-
fake_taskapi = FakeComputeTaskAPI()
12810-
with mock.patch.object(self.compute, 'compute_task_api',
12811-
fake_taskapi):
12812-
instance = self._create_fake_instance_obj()
12813-
12814-
scheduler_method = self.compute.compute_task_api.resize_instance
12815-
method_args = (instance, None,
12816-
dict(filter_properties=filter_properties),
12817-
{}, None)
12818-
return self.compute._reschedule(self.context, request_spec,
12819-
filter_properties, instance, scheduler_method,
12820-
method_args, self.expected_task_state, exc_info=exc_info)
12821-
12822-
def test_reschedule_no_filter_properties(self):
12823-
# no filter_properties will disable re-scheduling.
12824-
self.assertFalse(self._reschedule())
12825-
12826-
def test_reschedule_no_retry_info(self):
12827-
# no retry info will also disable re-scheduling.
12828-
filter_properties = {}
12829-
self.assertFalse(self._reschedule(filter_properties=filter_properties))
12830-
12831-
def test_reschedule_success(self):
12832-
retry = dict(num_attempts=1)
12833-
filter_properties = dict(retry=retry)
12834-
request_spec = {'num_instances': 1}
12835-
try:
12836-
raise test.TestingException("just need an exception")
12837-
except test.TestingException:
12838-
exc_info = sys.exc_info()
12839-
exc_str = traceback.format_exception_only(exc_info[0],
12840-
exc_info[1])
12841-
12842-
self.assertTrue(self._reschedule(filter_properties=filter_properties,
12843-
request_spec=request_spec, exc_info=exc_info))
12844-
self.assertEqual(self.updated_task_state, self.expected_task_state)
12845-
self.assertEqual(exc_str, filter_properties['retry']['exc'])
12846-
12847-
1284812791
class InnerTestingException(Exception):
1284912792
pass
1285012793

@@ -12859,6 +12802,7 @@ def setUp(self):
1285912802
self.instance_uuid = self.instance['uuid']
1286012803
self.instance_type = objects.Flavor.get_by_name(
1286112804
context.get_admin_context(), 'm1.tiny')
12805+
self.request_spec = objects.RequestSpec()
1286212806

1286312807
@mock.patch('nova.compute.manager.ComputeManager._prep_resize',
1286412808
side_effect=test.TestingException)
@@ -12873,93 +12817,113 @@ def test_reschedule_resize_or_reraise_called(self, mock_res, mock_prep):
1287312817
self.compute.prep_resize(self.context, image=None,
1287412818
instance=inst_obj,
1287512819
instance_type=self.instance_type,
12876-
request_spec=mock.sentinel.reqspec,
12820+
request_spec=self.request_spec,
1287712821
filter_properties={}, migration=mock.Mock(),
1287812822
node=None,
1287912823
clean_shutdown=True, host_list=None)
1288012824

1288112825
mock_res.assert_called_once_with(mock.ANY, inst_obj, mock.ANY,
1288212826
self.instance_type,
12883-
mock.sentinel.reqspec, {}, None)
12827+
self.request_spec, {}, None)
1288412828

12885-
@mock.patch.object(compute_manager.ComputeManager, "_reschedule")
12886-
@mock.patch('nova.compute.utils.notify_about_instance_action')
12887-
def test_reschedule_fails_with_exception(self, mock_notify, mock_res):
12888-
"""Original exception should be raised if the _reschedule method
12889-
raises another exception
12829+
def test_reschedule_resize_or_reraise_no_filter_properties(self):
12830+
"""Test behavior when ``filter_properties`` is None.
12831+
12832+
This should disable rescheduling and the original exception should be
12833+
raised.
1289012834
"""
12891-
instance = self._create_fake_instance_obj()
12892-
scheduler_hint = dict(filter_properties={})
12893-
method_args = (instance, None, scheduler_hint, self.instance_type)
12894-
mock_res.side_effect = InnerTestingException("Inner")
12835+
filter_properties = None
1289512836

1289612837
try:
1289712838
raise test.TestingException("Original")
1289812839
except Exception:
1289912840
exc_info = sys.exc_info()
12900-
reqspec = objects.RequestSpec()
12841+
# because we're not retrying, we should re-raise the exception
1290112842
self.assertRaises(test.TestingException,
12902-
self.compute._reschedule_resize_or_reraise, self.context,
12903-
instance, exc_info, self.instance_type, reqspec,
12904-
{}, None)
12905-
12906-
mock_res.assert_called_once_with(
12907-
self.context, reqspec, {}, instance,
12908-
self.compute.compute_task_api.resize_instance, method_args,
12909-
task_states.RESIZE_PREP, exc_info, host_list=None)
12910-
mock_notify.assert_called_once_with(
12911-
self.context, instance, 'fake-mini', action='resize',
12912-
phase='error', exception=mock_res.side_effect, tb=mock.ANY)
12843+
self.compute._reschedule_resize_or_reraise, self.context,
12844+
self.instance, exc_info, self.instance_type,
12845+
self.request_spec, filter_properties, None)
12846+
12847+
def test_reschedule_resize_or_reraise_no_retry_info(self):
12848+
"""Test behavior when ``filter_properties`` doesn't contain 'retry'.
1291312849

12914-
@mock.patch.object(compute_manager.ComputeManager, "_reschedule")
12915-
def test_reschedule_false(self, mock_res):
12916-
"""Original exception should be raised if the resize is not
12917-
rescheduled.
12850+
This should disable rescheduling and the original exception should be
12851+
raised.
1291812852
"""
12919-
instance = self._create_fake_instance_obj()
12920-
scheduler_hint = dict(filter_properties={})
12921-
method_args = (instance, None, scheduler_hint, self.instance_type)
12922-
mock_res.return_value = False
12853+
filter_properties = {}
1292312854

1292412855
try:
1292512856
raise test.TestingException("Original")
1292612857
except Exception:
1292712858
exc_info = sys.exc_info()
12928-
reqspec = objects.RequestSpec()
12859+
# because we're not retrying, we should re-raise the exception
1292912860
self.assertRaises(test.TestingException,
12930-
self.compute._reschedule_resize_or_reraise, self.context,
12931-
instance, exc_info, self.instance_type, reqspec,
12932-
{}, None)
12861+
self.compute._reschedule_resize_or_reraise, self.context,
12862+
self.instance, exc_info, self.instance_type,
12863+
self.request_spec, filter_properties, None)
12864+
12865+
@mock.patch.object(compute_manager.ComputeManager, '_instance_update')
12866+
@mock.patch('nova.conductor.api.ComputeTaskAPI.resize_instance',
12867+
side_effect=InnerTestingException('inner'))
12868+
@mock.patch('nova.compute.utils.notify_about_instance_action')
12869+
@mock.patch.object(compute_manager.ComputeManager, "_log_original_error")
12870+
def test_reschedule_fails_with_exception(self, mock_log, mock_notify,
12871+
mock_resize, mock_update):
12872+
"""Original exception should be raised if the conductor call
12873+
raises another exception.
12874+
"""
12875+
filter_properties = {'retry': {'num_attempts': 0}}
1293312876

12934-
mock_res.assert_called_once_with(
12935-
self.context, reqspec, {}, instance,
12936-
self.compute.compute_task_api.resize_instance, method_args,
12937-
task_states.RESIZE_PREP, exc_info, host_list=None)
12877+
try:
12878+
raise test.TestingException('Original')
12879+
except Exception:
12880+
exc_info = sys.exc_info()
12881+
self.assertRaises(test.TestingException,
12882+
self.compute._reschedule_resize_or_reraise, self.context,
12883+
self.instance, exc_info, self.instance_type,
12884+
self.request_spec, filter_properties, None)
12885+
12886+
mock_update.assert_called_once_with(
12887+
self.context, mock.ANY, task_state=task_states.RESIZE_PREP)
12888+
mock_resize.assert_called_once_with(
12889+
self.context, mock.ANY, None,
12890+
{'filter_properties': filter_properties}, self.instance_type,
12891+
request_spec=self.request_spec, host_list=None)
12892+
mock_notify.assert_called_once_with(
12893+
self.context, self.instance, 'fake-mini', action='resize',
12894+
phase='error', exception=mock_resize.side_effect, tb=mock.ANY)
12895+
# If not rescheduled, the original resize exception should not be
12896+
# logged.
12897+
mock_log.assert_not_called()
1293812898

12939-
@mock.patch.object(compute_manager.ComputeManager, "_reschedule")
12899+
@mock.patch.object(compute_manager.ComputeManager, '_instance_update')
12900+
@mock.patch('nova.conductor.api.ComputeTaskAPI.resize_instance')
12901+
@mock.patch('nova.compute.utils.notify_about_instance_action')
1294012902
@mock.patch.object(compute_manager.ComputeManager, "_log_original_error")
12941-
def test_reschedule_true(self, mock_log, mock_res):
12942-
# If rescheduled, the original resize exception should be logged.
12943-
instance = self._create_fake_instance_obj()
12944-
scheduler_hint = dict(filter_properties={})
12945-
method_args = (instance, None, scheduler_hint, self.instance_type)
12903+
def test_reschedule_passes(self, mock_log, mock_notify, mock_resize,
12904+
mock_update):
12905+
filter_properties = {'retry': {'num_attempts': 0}}
1294612906

1294712907
try:
1294812908
raise test.TestingException("Original")
1294912909
except Exception:
1295012910
exc_info = sys.exc_info()
12951-
mock_res.return_value = True
1295212911

12953-
reqspec = objects.RequestSpec()
1295412912
self.compute._reschedule_resize_or_reraise(
12955-
self.context, instance, exc_info,
12956-
self.instance_type, reqspec, {}, None)
12957-
12958-
mock_res.assert_called_once_with(self.context, reqspec, {},
12959-
instance, self.compute.compute_task_api.resize_instance,
12960-
method_args, task_states.RESIZE_PREP, exc_info,
12961-
host_list=None)
12962-
mock_log.assert_called_once_with(exc_info, instance.uuid)
12913+
self.context, self.instance, exc_info, self.instance_type,
12914+
self.request_spec, filter_properties, None)
12915+
12916+
mock_update.assert_called_once_with(
12917+
self.context, mock.ANY, task_state=task_states.RESIZE_PREP)
12918+
mock_resize.assert_called_once_with(
12919+
self.context, mock.ANY, None,
12920+
{'filter_properties': filter_properties}, self.instance_type,
12921+
request_spec=self.request_spec, host_list=None)
12922+
mock_notify.assert_called_once_with(
12923+
self.context, self.instance, 'fake-mini', action='resize',
12924+
phase='error', exception=exc_info[1], tb=mock.ANY)
12925+
# If rescheduled, the original resize exception should be logged.
12926+
mock_log.assert_called_once_with(exc_info, self.instance.uuid)
1296312927

1296412928

1296512929
class ComputeInactiveImageTestCase(BaseTestCase):

0 commit comments

Comments
 (0)