Merge "Record instance actions and events"

This commit is contained in:
Jenkins
2013-02-05 01:26:44 +00:00
committed by Gerrit Code Review
4 changed files with 132 additions and 39 deletions

View File

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

View File

@@ -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'),
} }

View File

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

View File

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