Merge "Record instance actions and events"
This commit is contained in:
@@ -104,22 +104,26 @@ class SchedulerManager(manager.Manager):
|
|||||||
"""Tries to call schedule_run_instance on the driver.
|
"""Tries to call schedule_run_instance on the driver.
|
||||||
Sets instance vm_state to ERROR on exceptions
|
Sets instance vm_state to ERROR on exceptions
|
||||||
"""
|
"""
|
||||||
try:
|
instance_uuids = request_spec['instance_uuids']
|
||||||
return self.driver.schedule_run_instance(context,
|
with compute_utils.EventReporter(context, conductor_api.LocalAPI(),
|
||||||
request_spec, admin_password, injected_files,
|
'schedule', *instance_uuids):
|
||||||
requested_networks, is_first_time, filter_properties)
|
try:
|
||||||
except exception.NoValidHost as ex:
|
return self.driver.schedule_run_instance(context,
|
||||||
# don't re-raise
|
request_spec, admin_password, injected_files,
|
||||||
self._set_vm_state_and_notify('run_instance',
|
requested_networks, is_first_time, filter_properties)
|
||||||
{'vm_state': vm_states.ERROR,
|
|
||||||
'task_state': None},
|
except exception.NoValidHost as ex:
|
||||||
context, ex, request_spec)
|
# don't re-raise
|
||||||
except Exception as ex:
|
|
||||||
with excutils.save_and_reraise_exception():
|
|
||||||
self._set_vm_state_and_notify('run_instance',
|
self._set_vm_state_and_notify('run_instance',
|
||||||
{'vm_state': vm_states.ERROR,
|
{'vm_state': vm_states.ERROR,
|
||||||
'task_state': None},
|
'task_state': None},
|
||||||
context, ex, request_spec)
|
context, ex, request_spec)
|
||||||
|
except Exception as ex:
|
||||||
|
with excutils.save_and_reraise_exception():
|
||||||
|
self._set_vm_state_and_notify('run_instance',
|
||||||
|
{'vm_state': vm_states.ERROR,
|
||||||
|
'task_state': None},
|
||||||
|
context, ex, request_spec)
|
||||||
|
|
||||||
def prep_resize(self, context, image, request_spec, filter_properties,
|
def prep_resize(self, context, image, request_spec, filter_properties,
|
||||||
instance, instance_type, reservations):
|
instance, instance_type, reservations):
|
||||||
@@ -127,32 +131,35 @@ class SchedulerManager(manager.Manager):
|
|||||||
Sets instance vm_state to ACTIVE on NoHostFound
|
Sets instance vm_state to ACTIVE on NoHostFound
|
||||||
Sets vm_state to ERROR on other exceptions
|
Sets vm_state to ERROR on other exceptions
|
||||||
"""
|
"""
|
||||||
try:
|
instance_uuid = instance['uuid']
|
||||||
kwargs = {
|
with compute_utils.EventReporter(context, conductor_api.LocalAPI(),
|
||||||
'context': context,
|
'schedule', instance_uuid):
|
||||||
'image': image,
|
try:
|
||||||
'request_spec': request_spec,
|
kwargs = {
|
||||||
'filter_properties': filter_properties,
|
'context': context,
|
||||||
'instance': instance,
|
'image': image,
|
||||||
'instance_type': instance_type,
|
'request_spec': request_spec,
|
||||||
'reservations': reservations,
|
'filter_properties': filter_properties,
|
||||||
}
|
'instance': instance,
|
||||||
return self.driver.schedule_prep_resize(**kwargs)
|
'instance_type': instance_type,
|
||||||
except exception.NoValidHost as ex:
|
'reservations': reservations,
|
||||||
self._set_vm_state_and_notify('prep_resize',
|
}
|
||||||
{'vm_state': vm_states.ACTIVE,
|
return self.driver.schedule_prep_resize(**kwargs)
|
||||||
'task_state': None},
|
except exception.NoValidHost as ex:
|
||||||
context, ex, request_spec)
|
|
||||||
if reservations:
|
|
||||||
QUOTAS.rollback(context, reservations)
|
|
||||||
except Exception as ex:
|
|
||||||
with excutils.save_and_reraise_exception():
|
|
||||||
self._set_vm_state_and_notify('prep_resize',
|
self._set_vm_state_and_notify('prep_resize',
|
||||||
{'vm_state': vm_states.ERROR,
|
{'vm_state': vm_states.ACTIVE,
|
||||||
'task_state': None},
|
'task_state': None},
|
||||||
context, ex, request_spec)
|
context, ex, request_spec)
|
||||||
if reservations:
|
if reservations:
|
||||||
QUOTAS.rollback(context, reservations)
|
QUOTAS.rollback(context, reservations)
|
||||||
|
except Exception as ex:
|
||||||
|
with excutils.save_and_reraise_exception():
|
||||||
|
self._set_vm_state_and_notify('prep_resize',
|
||||||
|
{'vm_state': vm_states.ERROR,
|
||||||
|
'task_state': None},
|
||||||
|
context, ex, request_spec)
|
||||||
|
if reservations:
|
||||||
|
QUOTAS.rollback(context, reservations)
|
||||||
|
|
||||||
def _set_vm_state_and_notify(self, method, updates, context, ex,
|
def _set_vm_state_and_notify(self, method, updates, context, ex,
|
||||||
request_spec):
|
request_spec):
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ from nova.scheduler import driver
|
|||||||
from nova.scheduler import manager
|
from nova.scheduler import manager
|
||||||
from nova import servicegroup
|
from nova import servicegroup
|
||||||
from nova import test
|
from nova import test
|
||||||
|
from nova.tests import fake_instance_actions
|
||||||
from nova.tests import matchers
|
from nova.tests import matchers
|
||||||
from nova.tests.scheduler import fakes
|
from nova.tests.scheduler import fakes
|
||||||
|
|
||||||
@@ -57,6 +58,7 @@ class SchedulerManagerTestCase(test.TestCase):
|
|||||||
self.topic = 'fake_topic'
|
self.topic = 'fake_topic'
|
||||||
self.fake_args = (1, 2, 3)
|
self.fake_args = (1, 2, 3)
|
||||||
self.fake_kwargs = {'cat': 'meow', 'dog': 'woof'}
|
self.fake_kwargs = {'cat': 'meow', 'dog': 'woof'}
|
||||||
|
fake_instance_actions.stub_out_action_events(self.stubs)
|
||||||
|
|
||||||
def test_1_correct_init(self):
|
def test_1_correct_init(self):
|
||||||
# Correct scheduler driver
|
# Correct scheduler driver
|
||||||
@@ -179,8 +181,8 @@ class SchedulerManagerTestCase(test.TestCase):
|
|||||||
self.mox.StubOutWithMock(compute_utils, 'add_instance_fault_from_exc')
|
self.mox.StubOutWithMock(compute_utils, 'add_instance_fault_from_exc')
|
||||||
self.mox.StubOutWithMock(db, 'instance_update_and_get_original')
|
self.mox.StubOutWithMock(db, 'instance_update_and_get_original')
|
||||||
|
|
||||||
request_spec = {'instance_properties':
|
request_spec = {'instance_properties': inst,
|
||||||
{'uuid': fake_instance_uuid}}
|
'instance_uuids': [fake_instance_uuid]}
|
||||||
|
|
||||||
self.manager.driver.schedule_run_instance(self.context,
|
self.manager.driver.schedule_run_instance(self.context,
|
||||||
request_spec, None, None, None, None, {}).AndRaise(
|
request_spec, None, None, None, None, {}).AndRaise(
|
||||||
@@ -199,6 +201,7 @@ class SchedulerManagerTestCase(test.TestCase):
|
|||||||
|
|
||||||
def test_prep_resize_no_valid_host_back_in_active_state(self):
|
def test_prep_resize_no_valid_host_back_in_active_state(self):
|
||||||
fake_instance_uuid = 'fake-instance-id'
|
fake_instance_uuid = 'fake-instance-id'
|
||||||
|
fake_instance = {'uuid': fake_instance_uuid}
|
||||||
inst = {"vm_state": "", "task_state": ""}
|
inst = {"vm_state": "", "task_state": ""}
|
||||||
|
|
||||||
self._mox_schedule_method_helper('schedule_prep_resize')
|
self._mox_schedule_method_helper('schedule_prep_resize')
|
||||||
@@ -214,7 +217,7 @@ class SchedulerManagerTestCase(test.TestCase):
|
|||||||
'image': 'fake_image',
|
'image': 'fake_image',
|
||||||
'request_spec': request_spec,
|
'request_spec': request_spec,
|
||||||
'filter_properties': 'fake_props',
|
'filter_properties': 'fake_props',
|
||||||
'instance': 'fake_instance',
|
'instance': fake_instance,
|
||||||
'instance_type': 'fake_type',
|
'instance_type': 'fake_type',
|
||||||
'reservations': list('fake_res'),
|
'reservations': list('fake_res'),
|
||||||
}
|
}
|
||||||
@@ -233,6 +236,7 @@ class SchedulerManagerTestCase(test.TestCase):
|
|||||||
|
|
||||||
def test_prep_resize_exception_host_in_error_state_and_raise(self):
|
def test_prep_resize_exception_host_in_error_state_and_raise(self):
|
||||||
fake_instance_uuid = 'fake-instance-id'
|
fake_instance_uuid = 'fake-instance-id'
|
||||||
|
fake_instance = {'uuid': fake_instance_uuid}
|
||||||
|
|
||||||
self._mox_schedule_method_helper('schedule_prep_resize')
|
self._mox_schedule_method_helper('schedule_prep_resize')
|
||||||
|
|
||||||
@@ -246,7 +250,7 @@ class SchedulerManagerTestCase(test.TestCase):
|
|||||||
'image': 'fake_image',
|
'image': 'fake_image',
|
||||||
'request_spec': request_spec,
|
'request_spec': request_spec,
|
||||||
'filter_properties': 'fake_props',
|
'filter_properties': 'fake_props',
|
||||||
'instance': 'fake_instance',
|
'instance': fake_instance,
|
||||||
'instance_type': 'fake_type',
|
'instance_type': 'fake_type',
|
||||||
'reservations': list('fake_res'),
|
'reservations': list('fake_res'),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -750,6 +750,34 @@ class DbApiTestCase(test.TestCase):
|
|||||||
self.assertEqual(start_time, events[0]['start_time'])
|
self.assertEqual(start_time, events[0]['start_time'])
|
||||||
self.assertEqual(finish_time, events[0]['finish_time'])
|
self.assertEqual(finish_time, events[0]['finish_time'])
|
||||||
|
|
||||||
|
def test_instance_action_and_event_start_string_time(self):
|
||||||
|
"""Create an instance action and event with a string start_time."""
|
||||||
|
ctxt = context.get_admin_context()
|
||||||
|
uuid = str(stdlib_uuid.uuid4())
|
||||||
|
|
||||||
|
start_time = timeutils.utcnow()
|
||||||
|
start_time_str = timeutils.strtime(start_time)
|
||||||
|
action_values = {'action': 'run_instance',
|
||||||
|
'instance_uuid': uuid,
|
||||||
|
'request_id': ctxt.request_id,
|
||||||
|
'user_id': ctxt.user_id,
|
||||||
|
'project_id': ctxt.project_id,
|
||||||
|
'start_time': start_time_str}
|
||||||
|
action = db.action_start(ctxt, action_values)
|
||||||
|
|
||||||
|
event_values = {'event': 'schedule',
|
||||||
|
'instance_uuid': uuid,
|
||||||
|
'request_id': ctxt.request_id,
|
||||||
|
'start_time': start_time_str}
|
||||||
|
db.action_event_start(ctxt, event_values)
|
||||||
|
|
||||||
|
# Retrieve the event to ensure it was successfully added
|
||||||
|
events = db.action_events_get(ctxt, action['id'])
|
||||||
|
self.assertEqual(1, len(events))
|
||||||
|
self.assertEqual('schedule', events[0]['event'])
|
||||||
|
# db api still returns models with datetime, not str, values
|
||||||
|
self.assertEqual(start_time, events[0]['start_time'])
|
||||||
|
|
||||||
def test_instance_action_event_get_by_id(self):
|
def test_instance_action_event_get_by_id(self):
|
||||||
"""Get a specific instance action event."""
|
"""Get a specific instance action event."""
|
||||||
ctxt1 = context.get_admin_context()
|
ctxt1 = context.get_admin_context()
|
||||||
|
|||||||
@@ -1284,3 +1284,57 @@ def metadata_to_dict(metadata):
|
|||||||
if not item.get('deleted'):
|
if not item.get('deleted'):
|
||||||
result[item['key']] = item['value']
|
result[item['key']] = item['value']
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def get_wrapped_function(function):
|
||||||
|
"""Get the method at the bottom of a stack of decorators."""
|
||||||
|
if not hasattr(function, 'func_closure') or not function.func_closure:
|
||||||
|
return function
|
||||||
|
|
||||||
|
def _get_wrapped_function(function):
|
||||||
|
if not hasattr(function, 'func_closure') or not function.func_closure:
|
||||||
|
return None
|
||||||
|
|
||||||
|
for closure in function.func_closure:
|
||||||
|
func = closure.cell_contents
|
||||||
|
|
||||||
|
deeper_func = _get_wrapped_function(func)
|
||||||
|
if deeper_func:
|
||||||
|
return deeper_func
|
||||||
|
elif hasattr(closure.cell_contents, '__call__'):
|
||||||
|
return closure.cell_contents
|
||||||
|
|
||||||
|
return _get_wrapped_function(function)
|
||||||
|
|
||||||
|
|
||||||
|
def getcallargs(function, *args, **kwargs):
|
||||||
|
"""This is a simplified inspect.getcallargs (2.7+).
|
||||||
|
|
||||||
|
It should be replaced when python >= 2.7 is standard.
|
||||||
|
"""
|
||||||
|
keyed_args = {}
|
||||||
|
argnames, varargs, keywords, defaults = inspect.getargspec(function)
|
||||||
|
|
||||||
|
keyed_args.update(kwargs)
|
||||||
|
|
||||||
|
#NOTE(alaski) the implicit 'self' or 'cls' argument shows up in
|
||||||
|
# argnames but not in args or kwargs. Uses 'in' rather than '==' because
|
||||||
|
# some tests use 'self2'.
|
||||||
|
if 'self' in argnames[0] or 'cls' == argnames[0]:
|
||||||
|
# The function may not actually be a method or have im_self.
|
||||||
|
# Typically seen when it's stubbed with mox.
|
||||||
|
if inspect.ismethod(function) and hasattr(function, 'im_self'):
|
||||||
|
keyed_args[argnames[0]] = function.im_self
|
||||||
|
else:
|
||||||
|
keyed_args[argnames[0]] = None
|
||||||
|
|
||||||
|
remaining_argnames = filter(lambda x: x not in keyed_args, argnames)
|
||||||
|
keyed_args.update(dict(zip(remaining_argnames, args)))
|
||||||
|
|
||||||
|
if defaults:
|
||||||
|
num_defaults = len(defaults)
|
||||||
|
for argname, value in zip(argnames[-num_defaults:], defaults):
|
||||||
|
if argname not in keyed_args:
|
||||||
|
keyed_args[argname] = value
|
||||||
|
|
||||||
|
return keyed_args
|
||||||
|
|||||||
Reference in New Issue
Block a user