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.
|
||||
Sets instance vm_state to ERROR on exceptions
|
||||
"""
|
||||
try:
|
||||
return self.driver.schedule_run_instance(context,
|
||||
request_spec, admin_password, injected_files,
|
||||
requested_networks, is_first_time, filter_properties)
|
||||
except exception.NoValidHost as ex:
|
||||
# don't re-raise
|
||||
self._set_vm_state_and_notify('run_instance',
|
||||
{'vm_state': vm_states.ERROR,
|
||||
'task_state': None},
|
||||
context, ex, request_spec)
|
||||
except Exception as ex:
|
||||
with excutils.save_and_reraise_exception():
|
||||
instance_uuids = request_spec['instance_uuids']
|
||||
with compute_utils.EventReporter(context, conductor_api.LocalAPI(),
|
||||
'schedule', *instance_uuids):
|
||||
try:
|
||||
return self.driver.schedule_run_instance(context,
|
||||
request_spec, admin_password, injected_files,
|
||||
requested_networks, is_first_time, filter_properties)
|
||||
|
||||
except exception.NoValidHost as ex:
|
||||
# don't re-raise
|
||||
self._set_vm_state_and_notify('run_instance',
|
||||
{'vm_state': vm_states.ERROR,
|
||||
{'vm_state': vm_states.ERROR,
|
||||
'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,
|
||||
instance, instance_type, reservations):
|
||||
@@ -127,32 +131,35 @@ class SchedulerManager(manager.Manager):
|
||||
Sets instance vm_state to ACTIVE on NoHostFound
|
||||
Sets vm_state to ERROR on other exceptions
|
||||
"""
|
||||
try:
|
||||
kwargs = {
|
||||
'context': context,
|
||||
'image': image,
|
||||
'request_spec': request_spec,
|
||||
'filter_properties': filter_properties,
|
||||
'instance': instance,
|
||||
'instance_type': instance_type,
|
||||
'reservations': reservations,
|
||||
}
|
||||
return self.driver.schedule_prep_resize(**kwargs)
|
||||
except exception.NoValidHost as ex:
|
||||
self._set_vm_state_and_notify('prep_resize',
|
||||
{'vm_state': vm_states.ACTIVE,
|
||||
'task_state': None},
|
||||
context, ex, request_spec)
|
||||
if reservations:
|
||||
QUOTAS.rollback(context, reservations)
|
||||
except Exception as ex:
|
||||
with excutils.save_and_reraise_exception():
|
||||
instance_uuid = instance['uuid']
|
||||
with compute_utils.EventReporter(context, conductor_api.LocalAPI(),
|
||||
'schedule', instance_uuid):
|
||||
try:
|
||||
kwargs = {
|
||||
'context': context,
|
||||
'image': image,
|
||||
'request_spec': request_spec,
|
||||
'filter_properties': filter_properties,
|
||||
'instance': instance,
|
||||
'instance_type': instance_type,
|
||||
'reservations': reservations,
|
||||
}
|
||||
return self.driver.schedule_prep_resize(**kwargs)
|
||||
except exception.NoValidHost as ex:
|
||||
self._set_vm_state_and_notify('prep_resize',
|
||||
{'vm_state': vm_states.ERROR,
|
||||
{'vm_state': vm_states.ACTIVE,
|
||||
'task_state': None},
|
||||
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',
|
||||
{'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,
|
||||
request_spec):
|
||||
|
||||
@@ -37,6 +37,7 @@ from nova.scheduler import driver
|
||||
from nova.scheduler import manager
|
||||
from nova import servicegroup
|
||||
from nova import test
|
||||
from nova.tests import fake_instance_actions
|
||||
from nova.tests import matchers
|
||||
from nova.tests.scheduler import fakes
|
||||
|
||||
@@ -57,6 +58,7 @@ class SchedulerManagerTestCase(test.TestCase):
|
||||
self.topic = 'fake_topic'
|
||||
self.fake_args = (1, 2, 3)
|
||||
self.fake_kwargs = {'cat': 'meow', 'dog': 'woof'}
|
||||
fake_instance_actions.stub_out_action_events(self.stubs)
|
||||
|
||||
def test_1_correct_init(self):
|
||||
# Correct scheduler driver
|
||||
@@ -179,8 +181,8 @@ class SchedulerManagerTestCase(test.TestCase):
|
||||
self.mox.StubOutWithMock(compute_utils, 'add_instance_fault_from_exc')
|
||||
self.mox.StubOutWithMock(db, 'instance_update_and_get_original')
|
||||
|
||||
request_spec = {'instance_properties':
|
||||
{'uuid': fake_instance_uuid}}
|
||||
request_spec = {'instance_properties': inst,
|
||||
'instance_uuids': [fake_instance_uuid]}
|
||||
|
||||
self.manager.driver.schedule_run_instance(self.context,
|
||||
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):
|
||||
fake_instance_uuid = 'fake-instance-id'
|
||||
fake_instance = {'uuid': fake_instance_uuid}
|
||||
inst = {"vm_state": "", "task_state": ""}
|
||||
|
||||
self._mox_schedule_method_helper('schedule_prep_resize')
|
||||
@@ -214,7 +217,7 @@ class SchedulerManagerTestCase(test.TestCase):
|
||||
'image': 'fake_image',
|
||||
'request_spec': request_spec,
|
||||
'filter_properties': 'fake_props',
|
||||
'instance': 'fake_instance',
|
||||
'instance': fake_instance,
|
||||
'instance_type': 'fake_type',
|
||||
'reservations': list('fake_res'),
|
||||
}
|
||||
@@ -233,6 +236,7 @@ class SchedulerManagerTestCase(test.TestCase):
|
||||
|
||||
def test_prep_resize_exception_host_in_error_state_and_raise(self):
|
||||
fake_instance_uuid = 'fake-instance-id'
|
||||
fake_instance = {'uuid': fake_instance_uuid}
|
||||
|
||||
self._mox_schedule_method_helper('schedule_prep_resize')
|
||||
|
||||
@@ -246,7 +250,7 @@ class SchedulerManagerTestCase(test.TestCase):
|
||||
'image': 'fake_image',
|
||||
'request_spec': request_spec,
|
||||
'filter_properties': 'fake_props',
|
||||
'instance': 'fake_instance',
|
||||
'instance': fake_instance,
|
||||
'instance_type': 'fake_type',
|
||||
'reservations': list('fake_res'),
|
||||
}
|
||||
|
||||
@@ -750,6 +750,34 @@ class DbApiTestCase(test.TestCase):
|
||||
self.assertEqual(start_time, events[0]['start_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):
|
||||
"""Get a specific instance action event."""
|
||||
ctxt1 = context.get_admin_context()
|
||||
|
||||
@@ -1284,3 +1284,57 @@ def metadata_to_dict(metadata):
|
||||
if not item.get('deleted'):
|
||||
result[item['key']] = item['value']
|
||||
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