def _notify(): # NOTE(danms): We have to be super careful here not to trigger # any lazy-loads that will unmigrate or unbackport something. So, # make a copy of the instance for notifications first. new_ref = self.obj_clone() notifications.send_update(context, old_ref, new_ref)
def test_task_notif(self): # test config disable of just the task state notifications self.flags(notify_on_state_change="vm_state") # we should not get a notification on task stgate chagne now old = copy.copy(self.instance) self.instance.task_state = task_states.SPAWNING old_vm_state = old['vm_state'] new_vm_state = self.instance.vm_state old_task_state = old['task_state'] new_task_state = self.instance.task_state notifications.send_update_with_states(self.context, self.instance, old_vm_state, new_vm_state, old_task_state, new_task_state, verify_states=True) self.assertEqual(0, len(fake_notifier.NOTIFICATIONS)) # ok now enable task state notifications and re-try self.flags(notify_on_state_change="vm_and_task_state") notifications.send_update(self.context, old, self.instance) self.assertEqual(1, len(fake_notifier.NOTIFICATIONS))
def save(self, context, expected_task_state=None): """Save updates to this instance Column-wise updates will be made based on the result of self.what_changed(). If expected_task_state is provided, it will be checked against the in-database copy of the instance before updates are made. :param context: Security context :param expected_task_state: Optional tuple of valid task states for the instance to be in. """ updates = {} changes = self.obj_what_changed() for field in changes: updates[field] = self[field] if expected_task_state is not None: updates['expected_task_state'] = expected_task_state old_ref, inst_ref = db.instance_update_and_get_original( context, self.uuid, updates) expected_attrs = [] for attr in ('metadata', 'system_metadata'): if hasattr(self, base.get_attrname(attr)): expected_attrs.append(attr) Instance._from_db_object(self, inst_ref, expected_attrs) if 'vm_state' in changes or 'task_state' in changes: notifications.send_update(context, old_ref, inst_ref) self.obj_reset_changes()
def handle_schedule_error(context, ex, instance_uuid, request_spec): """On run_instance failure, update instance state and send notifications. """ if isinstance(ex, exception.NoValidHost): LOG.warning(_LW("NoValidHost exception with message: \'%s\'"), ex.format_message().strip(), instance_uuid=instance_uuid) else: LOG.exception(_("Exception during scheduler.run_instance")) state = vm_states.ERROR.upper() LOG.warning(_LW('Setting instance to %s state.'), state, instance_uuid=instance_uuid) (old_ref, new_ref) = db.instance_update_and_get_original(context, instance_uuid, { 'vm_state': vm_states.ERROR, 'task_state': None }) notifications.send_update(context, old_ref, new_ref, service="scheduler") compute_utils.add_instance_fault_from_exc(context, new_ref, ex, sys.exc_info()) properties = request_spec.get('instance_properties', {}) payload = dict(request_spec=request_spec, instance_properties=properties, instance_id=instance_uuid, state=vm_states.ERROR, method='run_instance', reason=ex) rpc.get_notifier('scheduler').error(context, 'scheduler.run_instance', payload)
def handle_schedule_error(context, ex, instance_uuid, request_spec): if not isinstance(ex, exception.NoValidHost): LOG.exception(_("Exception during scheduler.run_instance")) state = vm_states.ERROR.upper() LOG.warning(_('Setting instance to %s state.'), state, instance_uuid=instance_uuid) # update instance state and notify on the transition (old_ref, new_ref) = db.instance_update_and_get_original(context, instance_uuid, {'vm_state': vm_states.ERROR, 'task_state': None}) notifications.send_update(context, old_ref, new_ref, service="scheduler") compute_utils.add_instance_fault_from_exc(context, conductor_api.LocalAPI(), new_ref, ex, sys.exc_info()) properties = request_spec.get('instance_properties', {}) payload = dict(request_spec=request_spec, instance_properties=properties, instance_id=instance_uuid, state=vm_states.ERROR, method='run_instance', reason=ex) notifier.get_notifier('scheduler').error(context, 'scheduler.run_instance', payload)
def test_save_rename_sends_notification(self): # Tests that simply changing the 'display_name' on the instance # will send a notification. self.flags(enable=False, group='cells') old_ref = dict(self.fake_instance, display_name='hello') fake_uuid = old_ref['uuid'] expected_updates = dict(display_name='goodbye') new_ref = dict(old_ref, **expected_updates) self.mox.StubOutWithMock(db, 'instance_get_by_uuid') self.mox.StubOutWithMock(db, 'instance_update_and_get_original') self.mox.StubOutWithMock(notifications, 'send_update') db.instance_get_by_uuid(self.context, fake_uuid, columns_to_join=['info_cache', 'security_groups'] ).AndReturn(old_ref) db.instance_update_and_get_original( self.context, fake_uuid, expected_updates, update_cells=False, columns_to_join=['info_cache', 'security_groups'] ).AndReturn((old_ref, new_ref)) notifications.send_update(self.context, mox.IgnoreArg(), mox.IgnoreArg()) self.mox.ReplayAll() inst = instance.Instance.get_by_uuid(self.context, old_ref['uuid']) self.assertEqual('hello', inst.display_name) inst.display_name = 'goodbye' inst.save() self.assertEqual('goodbye', inst.display_name) self.assertEqual(set([]), inst.obj_what_changed())
def save(self, context, expected_task_state=None): """Save updates to this instance Column-wise updates will be made based on the result of self.what_changed(). If expected_task_state is provided, it will be checked against the in-database copy of the instance before updates are made. :param context: Security context :param expected_task_state: Optional tuple of valid task states for the instance to be in. """ updates = {} changes = self.obj_what_changed() for field in self.fields: if (hasattr(self, base.get_attrname(field)) and isinstance(self[field], base.NovaObject)): self[field].save(context) elif field in changes: updates[field] = self[field] if expected_task_state is not None: updates['expected_task_state'] = expected_task_state if updates: old_ref, inst_ref = db.instance_update_and_get_original(context, self.uuid, updates) expected_attrs = [] for attr in INSTANCE_OPTIONAL_FIELDS: if hasattr(self, base.get_attrname(attr)): expected_attrs.append(attr) Instance._from_db_object(self, inst_ref, expected_attrs) if 'vm_state' in changes or 'task_state' in changes: notifications.send_update(context, old_ref, inst_ref) self.obj_reset_changes()
def handle_schedule_error(context, ex, instance_uuid, request_spec): if not isinstance(ex, exception.NoValidHost): LOG.exception(_("Exception during scheduler.run_instance")) compute_utils.add_instance_fault_from_exc(context, instance_uuid, ex, sys.exc_info()) state = vm_states.ERROR.upper() LOG.warning(_('Setting instance to %(state)s state.'), locals(), instance_uuid=instance_uuid) # update instance state and notify on the transition (old_ref, new_ref) = db.instance_update_and_get_original(context, instance_uuid, {'vm_state': vm_states.ERROR, 'task_state': None}) notifications.send_update(context, old_ref, new_ref, service="scheduler") properties = request_spec.get('instance_properties', {}) payload = dict(request_spec=request_spec, instance_properties=properties, instance_id=instance_uuid, state=vm_states.ERROR, method='run_instance', reason=ex) notifier.notify(context, notifier.publisher_id("scheduler"), 'scheduler.run_instance', notifier.ERROR, payload)
def set_vm_state_and_notify(context, service, method, updates, ex, request_spec, db): """changes VM state and notifies.""" LOG.warning(_("Failed to %(service)s_%(method)s: %(ex)s"), {"service": service, "method": method, "ex": ex}) vm_state = updates["vm_state"] properties = request_spec.get("instance_properties", {}) # NOTE(vish): We shouldn't get here unless we have a catastrophic # failure, so just set all instances to error. if uuid # is not set, instance_uuids will be set to [None], this # is solely to preserve existing behavior and can # be removed along with the 'if instance_uuid:' if we can # verify that uuid is always set. uuids = [properties.get("uuid")] notifier = rpc.get_notifier(service) for instance_uuid in request_spec.get("instance_uuids") or uuids: if instance_uuid: state = vm_state.upper() LOG.warning(_("Setting instance to %s state."), state, instance_uuid=instance_uuid) # update instance state and notify on the transition (old_ref, new_ref) = db.instance_update_and_get_original(context, instance_uuid, updates) notifications.send_update(context, old_ref, new_ref, service=service) compute_utils.add_instance_fault_from_exc(context, new_ref, ex, sys.exc_info()) payload = dict( request_spec=request_spec, instance_properties=properties, instance_id=instance_uuid, state=vm_state, method=method, reason=ex, ) event_type = "%s.%s" % (service, method) notifier.error(context, event_type, payload)
def test_save_rename_sends_notification(self): # Tests that simply changing the 'display_name' on the instance # will send a notification. self.flags(enable=False, group='cells') old_ref = dict(self.fake_instance, display_name='hello') fake_uuid = old_ref['uuid'] expected_updates = dict(display_name='goodbye') new_ref = dict(old_ref, **expected_updates) self.mox.StubOutWithMock(db, 'instance_get_by_uuid') self.mox.StubOutWithMock(db, 'instance_update_and_get_original') self.mox.StubOutWithMock(notifications, 'send_update') db.instance_get_by_uuid(self.context, fake_uuid, columns_to_join=['info_cache', 'security_groups'], use_slave=False ).AndReturn(old_ref) db.instance_update_and_get_original( self.context, fake_uuid, expected_updates, update_cells=False, columns_to_join=['info_cache', 'security_groups', 'system_metadata'] ).AndReturn((old_ref, new_ref)) notifications.send_update(self.context, mox.IgnoreArg(), mox.IgnoreArg()) self.mox.ReplayAll() inst = instance.Instance.get_by_uuid(self.context, old_ref['uuid'], use_slave=False) self.assertEqual('hello', inst.display_name) inst.display_name = 'goodbye' inst.save() self.assertEqual('goodbye', inst.display_name) self.assertEqual(set([]), inst.obj_what_changed())
def handle_schedule_error(context, ex, instance_uuid, request_spec): """On run_instance failure, update instance state and send notifications. """ if not isinstance(ex, exception.NoValidHost): LOG.exception(_("Exception during scheduler.run_instance")) state = vm_states.ERROR.upper() LOG.warning(_("Setting instance to %s state."), state, instance_uuid=instance_uuid) (old_ref, new_ref) = db.instance_update_and_get_original( context, instance_uuid, {"vm_state": vm_states.ERROR, "task_state": None} ) notifications.send_update(context, old_ref, new_ref, service="scheduler") compute_utils.add_instance_fault_from_exc(context, conductor_api.LocalAPI(), new_ref, ex, sys.exc_info()) properties = request_spec.get("instance_properties", {}) payload = dict( request_spec=request_spec, instance_properties=properties, instance_id=instance_uuid, state=vm_states.ERROR, method="run_instance", reason=ex, ) notifier.get_notifier("scheduler").error(context, "scheduler.run_instance", payload)
def handle_schedule_error(context, ex, instance_uuid, request_spec): """On run_instance failure, update instance state and send notifications. """ if isinstance(ex, exception.NoValidHost): LOG.warning(_LW("NoValidHost exception with message: \'%s\'"), ex.format_message().strip(), instance_uuid=instance_uuid) else: LOG.exception(_LE("Exception during scheduler.run_instance")) state = vm_states.ERROR.upper() LOG.warning(_LW('Setting instance to %s state.'), state, instance_uuid=instance_uuid) (old_ref, new_ref) = db.instance_update_and_get_original(context, instance_uuid, {'vm_state': vm_states.ERROR, 'task_state': None}) notifications.send_update(context, old_ref, new_ref, service="scheduler") compute_utils.add_instance_fault_from_exc(context, new_ref, ex, sys.exc_info()) properties = request_spec.get('instance_properties', {}) payload = dict(request_spec=request_spec, instance_properties=properties, instance_id=instance_uuid, state=vm_states.ERROR, method='run_instance', reason=ex) rpc.get_notifier('scheduler').error(context, 'scheduler.run_instance', payload)
def instance_update(self, context, instance_uuid, updates, service): for key, value in updates.iteritems(): if key not in allowed_updates: LOG.error( _LE("Instance update attempted for " "'%(key)s' on %(instance_uuid)s"), { 'key': key, 'instance_uuid': instance_uuid }) raise KeyError("unexpected update keyword '%s'" % key) if key in datetime_fields and isinstance(value, six.string_types): updates[key] = timeutils.parse_strtime(value) # NOTE(danms): the send_update() call below is going to want to know # about the flavor, so we need to join the appropriate things here, # and objectify the result. old_ref, instance_ref = self.db.instance_update_and_get_original( context, instance_uuid, updates, columns_to_join=['system_metadata']) inst_obj = objects.Instance._from_db_object( context, objects.Instance(), instance_ref, expected_attrs=['system_metadata']) notifications.send_update(context, old_ref, inst_obj, service) return jsonutils.to_primitive(instance_ref)
def test_save_rename_sends_notification(self): # Tests that simply changing the 'display_name' on the instance # will send a notification. self.flags(enable=False, group="cells") old_ref = dict(self.fake_instance, display_name="hello") fake_uuid = old_ref["uuid"] expected_updates = dict(display_name="goodbye") new_ref = dict(old_ref, **expected_updates) self.mox.StubOutWithMock(db, "instance_get_by_uuid") self.mox.StubOutWithMock(db, "instance_update_and_get_original") self.mox.StubOutWithMock(notifications, "send_update") db.instance_get_by_uuid( self.context, fake_uuid, columns_to_join=["info_cache", "security_groups"], use_slave=False ).AndReturn(old_ref) db.instance_update_and_get_original( self.context, fake_uuid, expected_updates, update_cells=False, columns_to_join=["info_cache", "security_groups"], ).AndReturn((old_ref, new_ref)) notifications.send_update(self.context, mox.IgnoreArg(), mox.IgnoreArg()) self.mox.ReplayAll() inst = instance.Instance.get_by_uuid(self.context, old_ref["uuid"], use_slave=False) self.assertEqual("hello", inst.display_name) inst.display_name = "goodbye" inst.save() self.assertEqual("goodbye", inst.display_name) self.assertEqual(set([]), inst.obj_what_changed())
def test_send_on_vm_change(self): # pretend we just transitioned to ACTIVE: params = {"vm_state": vm_states.ACTIVE} (old_ref, new_ref) = db.instance_update_and_get_original(self.context, self.instance["uuid"], params) notifications.send_update(self.context, old_ref, new_ref) self.assertEqual(1, len(fake_notifier.NOTIFICATIONS))
def test_send_name_update(self): notifications.send_update(self.context, self.instance, self.instance) self.assertEquals(1, len(test_notifier.NOTIFICATIONS)) notif = test_notifier.NOTIFICATIONS[0] payload = notif["payload"] display_name = self.instance["display_name"] self.assertEquals(payload["display_name"], display_name)
def test_fail_sending_update_instance_not_found(self, mock_log_exception): # Tests that InstanceNotFound is handled as an expected exception and # not logged as an error. notfound = exception.InstanceNotFound(instance_id=self.instance.uuid) with mock.patch.object(notifications, "_send_instance_update_notification", side_effect=notfound): notifications.send_update(self.context, self.instance, self.instance) self.assertEqual(0, len(fake_notifier.NOTIFICATIONS)) self.assertEqual(0, mock_log_exception.call_count)
def test_fail_sending_update(self): def fail_sending(context, instance, **kwargs): raise Exception('failed to notify') self.stubs.Set(notifications, '_send_instance_update_notification', fail_sending) notifications.send_update(self.context, self.instance, self.instance) self.assertEqual(0, len(fake_notifier.NOTIFICATIONS))
def test_send_on_vm_change(self): old = obj_base.obj_to_primitive(self.instance) old['vm_state'] = None # pretend we just transitioned to ACTIVE: self.instance.vm_state = vm_states.ACTIVE notifications.send_update(self.context, old, self.instance) self.assertEqual(1, len(fake_notifier.NOTIFICATIONS))
def test_send_on_task_change(self): # pretend we just transitioned to task SPAWNING: params = {"task_state": task_states.SPAWNING} (old_ref, new_ref) = db.instance_update_and_get_original(self.context, self.instance["uuid"], params) notifications.send_update(self.context, old_ref, new_ref) self.assertEquals(1, len(test_notifier.NOTIFICATIONS))
def test_send_no_state_change(self): called = [False] def sending_no_state_change(context, instance, **kwargs): called[0] = True self.stubs.Set(notifications, '_send_instance_update_notification', sending_no_state_change) notifications.send_update(self.context, self.instance, self.instance) self.assertTrue(called[0])
def test_send_on_task_change(self): old = obj_base.obj_to_primitive(self.instance) old['task_state'] = None # pretend we just transitioned to task SPAWNING: self.instance.task_state = task_states.SPAWNING notifications.send_update(self.context, old, self.instance) self.assertEqual(1, len(fake_notifier.NOTIFICATIONS))
def test_send_on_vm_change(self): # pretend we just transitioned to ACTIVE: params = {"vm_state": vm_states.ACTIVE} (old_ref, new_ref) = db.instance_update_and_get_original( self.context, self.instance['uuid'], params) notifications.send_update(self.context, old_ref, new_ref) self.assertEquals(1, len(test_notifier.NOTIFICATIONS))
def test_send_on_task_change(self): # pretend we just transitioned to task SPAWNING: params = {"task_state": task_states.SPAWNING} (old_ref, new_ref) = db.instance_update_and_get_original( self.context, self.instance['uuid'], params) notifications.send_update(self.context, old_ref, new_ref) self.assertEqual(1, len(fake_notifier.NOTIFICATIONS))
def test_send_access_ip_update(self): notifications.send_update(self.context, self.instance, self.instance) self.assertEqual(1, len(fake_notifier.NOTIFICATIONS)) notif = fake_notifier.NOTIFICATIONS[0] payload = notif.payload access_ip_v4 = str(self.instance.access_ip_v4) access_ip_v6 = str(self.instance.access_ip_v6) self.assertEqual(payload["access_ip_v4"], access_ip_v4) self.assertEqual(payload["access_ip_v6"], access_ip_v6)
def instance_update(self, context, instance_uuid, updates): for key in updates: if key not in allowed_updates: LOG.error(_("Instance update attempted for " "'%(key)s' on %(instance_uuid)s") % locals()) raise KeyError("unexpected update keyword '%s'" % key) old_ref, instance_ref = self.db.instance_update_and_get_original( context, instance_uuid, updates) notifications.send_update(context, old_ref, instance_ref) return jsonutils.to_primitive(instance_ref)
def _set_instance_host(self, context, instance_uuid): """Tag the instance as belonging to this host. This should be done while the COMPUTE_RESOURCES_SEMPAHORE is being held so the resource claim will not be lost if the audit process starts. """ values = {'host': self.host, 'launched_on': self.host} (old_ref, instance_ref) = db.instance_update_and_get_original( context, instance_uuid, values) notifications.send_update(context, old_ref, instance_ref) return instance_ref
def test_send_access_ip_update(self): notifications.send_update(self.context, self.instance, self.instance) self.assertEquals(1, len(test_notifier.NOTIFICATIONS)) notif = test_notifier.NOTIFICATIONS[0] payload = notif["payload"] access_ip_v4 = self.instance["access_ip_v4"] access_ip_v6 = self.instance["access_ip_v6"] self.assertEquals(payload["access_ip_v4"], access_ip_v4) self.assertEquals(payload["access_ip_v6"], access_ip_v6)
def test_notif_disabled(self): # test config disable of the notifcations self.flags(notify_on_state_change=None) old = copy.copy(self.instance) self.instance["vm_state"] = vm_states.ACTIVE notifications.send_update(self.context, old, self.instance) self.assertEquals(0, len(test_notifier.NOTIFICATIONS))
def _set_instance_host(self, context, instance_uuid): """Tag the instance as belonging to this host. This should be done while the COMPUTE_RESOURCES_SEMPAHORE is being held so the resource claim will not be lost if the audit process starts. """ values = {'host': self.host, 'launched_on': self.host} (old_ref, instance_ref) = db.instance_update_and_get_original(context, instance_uuid, values) notifications.send_update(context, old_ref, instance_ref) return instance_ref
def instance_update(self, context, instance_uuid, updates, service=None): for key, value in updates.iteritems(): if key not in allowed_updates: LOG.error(_("Instance update attempted for " "'%(key)s' on %(instance_uuid)s") % locals()) raise KeyError("unexpected update keyword '%s'" % key) if key in datetime_fields and isinstance(value, basestring): updates[key] = timeutils.parse_strtime(value) old_ref, instance_ref = self.db.instance_update_and_get_original(context, instance_uuid, updates) notifications.send_update(context, old_ref, instance_ref, service) return jsonutils.to_primitive(instance_ref)
def test_fail_sending_update_instance_not_found(self, mock_log_exception): # Tests that InstanceNotFound is handled as an expected exception and # not logged as an error. notfound = exception.InstanceNotFound(instance_id=self.instance.uuid) with mock.patch.object(notifications, '_send_instance_update_notification', side_effect=notfound): notifications.send_update( self.context, self.instance, self.instance) self.assertEqual(0, len(fake_notifier.NOTIFICATIONS)) self.assertEqual(0, mock_log_exception.call_count)
def test_send_on_vm_change(self): old = obj_base.obj_to_primitive(self.instance) old['vm_state'] = None # pretend we just transitioned to ACTIVE: self.instance.vm_state = vm_states.ACTIVE notifications.send_update(self.context, old, self.instance) self.assertEqual(1, len(fake_notifier.NOTIFICATIONS)) # service name should default to 'compute' notif = fake_notifier.NOTIFICATIONS[0] self.assertEqual('compute.testhost', notif.publisher_id)
def test_send_name_update(self): param = {"display_name": "new_display_name"} new_name_inst = self._wrapped_create(params=param) notifications.send_update(self.context, self.instance, new_name_inst) self.assertEquals(1, len(test_notifier.NOTIFICATIONS)) notif = test_notifier.NOTIFICATIONS[0] payload = notif["payload"] old_display_name = self.instance["display_name"] new_display_name = new_name_inst["display_name"] self.assertEquals(payload["old_display_name"], old_display_name) self.assertEquals(payload["display_name"], new_display_name)
def test_send_name_update(self): param = {"display_name": "new_display_name"} new_name_inst = self._wrapped_create(params=param) notifications.send_update(self.context, self.instance, new_name_inst) self.assertEqual(1, len(fake_notifier.NOTIFICATIONS)) notif = fake_notifier.NOTIFICATIONS[0] payload = notif.payload old_display_name = self.instance.display_name new_display_name = new_name_inst.display_name self.assertEqual(payload["old_display_name"], old_display_name) self.assertEqual(payload["display_name"], new_display_name)
def _test_set_vm_state_and_notify(self, request_spec, expected_uuids): updates = dict(vm_state='fake-vm-state') service = 'fake-service' method = 'fake-method' exc_info = 'exc_info' self.mox.StubOutWithMock(compute_utils, 'add_instance_fault_from_exc') self.mox.StubOutWithMock(notifications, 'send_update') self.mox.StubOutWithMock(db, 'instance_update_and_get_original') self.mox.StubOutWithMock(rpc, 'get_notifier') notifier = self.mox.CreateMockAnything() rpc.get_notifier(service).AndReturn(notifier) old_ref = 'old_ref' new_ref = 'new_ref' inst_obj = 'inst_obj' for _uuid in expected_uuids: db.instance_update_and_get_original( self.context, _uuid, updates, columns_to_join=['system_metadata']).AndReturn( (old_ref, new_ref)) notifications.send_update(self.context, old_ref, inst_obj, service=service) compute_utils.add_instance_fault_from_exc( self.context, new_ref, exc_info, mox.IsA(tuple)) payload = dict(request_spec=request_spec, instance_properties=request_spec.get( 'instance_properties', {}), instance_id=_uuid, state='fake-vm-state', method=method, reason=exc_info) event_type = '%s.%s' % (service, method) notifier.error(self.context, event_type, payload) self.mox.ReplayAll() with mock.patch.object(objects.Instance, '_from_db_object', return_value=inst_obj): scheduler_utils.set_vm_state_and_notify(self.context, service, method, updates, exc_info, request_spec, db)
def _set_vm_state_and_notify(self, method, updates, context, ex, request_spec): """changes VM state and notifies.""" # FIXME(comstud): Re-factor this somehow. Not sure this belongs in the # scheduler manager like this. We should make this easier. # run_instance only sends a request_spec, and an instance may or may # not have been created in the API (or scheduler) already. If it was # created, there's a 'uuid' set in the instance_properties of the # request_spec. # (littleidea): I refactored this a bit, and I agree # it should be easier :) # The refactoring could go further but trying to minimize changes # for essex timeframe LOG.warning(_("Failed to schedule_%(method)s: %(ex)s") % locals()) vm_state = updates['vm_state'] properties = request_spec.get('instance_properties', {}) # NOTE(vish): We shouldn't get here unless we have a catastrophic # failure, so just set all instances to error. if uuid # is not set, instance_uuids will be set to [None], this # is solely to preserve existing behavior and can # be removed along with the 'if instance_uuid:' if we can # verify that uuid is always set. uuids = [properties.get('uuid')] for instance_uuid in request_spec.get('instance_uuids') or uuids: if instance_uuid: state = vm_state.upper() LOG.warning(_('Setting instance to %(state)s state.'), locals(), instance_uuid=instance_uuid) # update instance state and notify on the transition (old_ref, new_ref) = self.db.instance_update_and_get_original( context, instance_uuid, updates) notifications.send_update(context, old_ref, new_ref, service="scheduler") compute_utils.add_instance_fault_from_exc( context, conductor_api.LocalAPI(), new_ref, ex, sys.exc_info()) payload = dict(request_spec=request_spec, instance_properties=properties, instance_id=instance_uuid, state=vm_state, method=method, reason=ex) notifier.notify(context, notifier.publisher_id("scheduler"), 'scheduler.' + method, notifier.ERROR, payload)
def test_set_vm_state_and_notify(self): expected_uuid = 'fake-uuid' request_spec = dict(instance_properties=dict(uuid='other-uuid')) updates = dict(vm_state='fake-vm-state') service = 'fake-service' method = 'fake-method' exc_info = 'exc_info' self.mox.StubOutWithMock(compute_utils, 'add_instance_fault_from_exc') self.mox.StubOutWithMock(notifications, 'send_update') self.mox.StubOutWithMock(db, 'instance_update_and_get_original') self.mox.StubOutWithMock(rpc, 'get_notifier') notifier = self.mox.CreateMockAnything() rpc.get_notifier(service).AndReturn(notifier) old_ref = 'old_ref' new_ref = 'new_ref' inst_obj = 'inst_obj' db.instance_update_and_get_original( self.context, expected_uuid, updates, columns_to_join=['system_metadata']).AndReturn((old_ref, new_ref)) notifications.send_update(self.context, old_ref, inst_obj, service=service) compute_utils.add_instance_fault_from_exc( self.context, new_ref, exc_info, mox.IsA(tuple)) payload = dict(request_spec=request_spec, instance_properties=request_spec.get( 'instance_properties', {}), instance_id=expected_uuid, state='fake-vm-state', method=method, reason=exc_info) event_type = '%s.%s' % (service, method) notifier.error(self.context, event_type, payload) self.mox.ReplayAll() with mock.patch.object(objects.Instance, '_from_db_object', return_value=inst_obj): scheduler_utils.set_vm_state_and_notify(self.context, expected_uuid, service, method, updates, exc_info, request_spec, db)
def instance_update(self, context, instance_uuid, updates): for key, value in updates.iteritems(): if key not in allowed_updates: LOG.error(_("Instance update attempted for " "'%(key)s' on %(instance_uuid)s") % locals()) raise KeyError("unexpected update keyword '%s'" % key) if key in datetime_fields and isinstance(value, basestring): updates[key] = timeutils.parse_strtime(value) old_ref, instance_ref = self.db.instance_update_and_get_original( context, instance_uuid, updates) notifications.send_update(context, old_ref, instance_ref) return jsonutils.to_primitive(instance_ref)
def schedule_live_migration(self, context, instance_id, dest, block_migration=False, disk_over_commit=False): """Live migration scheduling method. :param context: :param instance_id: :param dest: destination host :param block_migration: if true, block_migration. :param disk_over_commit: if True, consider real(not virtual) disk size. :return: The host where instance is running currently. Then scheduler send request that host. """ # Whether instance exists and is running. instance_ref = db.instance_get(context, instance_id) # Checking instance. self._live_migration_src_check(context, instance_ref) # Checking destination host. self._live_migration_dest_check(context, instance_ref, dest, block_migration, disk_over_commit) # Common checking. self._live_migration_common_check(context, instance_ref, dest, block_migration, disk_over_commit) # Changing instance_state. values = {"vm_state": vm_states.MIGRATING} # update instance state and notify (old_ref, new_instance_ref) = db.instance_update_and_get_original( context, instance_id, values) notifications.send_update(context, old_ref, new_instance_ref, service="scheduler") src = instance_ref['host'] cast_to_compute_host(context, src, 'live_migration', update_db=False, instance_id=instance_id, dest=dest, block_migration=block_migration)
def schedule_live_migration(self, context, dest, block_migration=False, disk_over_commit=False, instance=None, instance_id=None): """Live migration scheduling method. :param context: :param instance_id: (deprecated) :param instance: instance dict :param dest: destination host :param block_migration: if true, block_migration. :param disk_over_commit: if True, consider real(not virtual) disk size. :return: The host where instance is running currently. Then scheduler send request that host. """ # Check we can do live migration if not instance: instance = db.instance_get(context, instance_id) self._live_migration_src_check(context, instance) self._live_migration_dest_check(context, instance, dest) self._live_migration_common_check(context, instance, dest) migrate_data = self.compute_rpcapi.check_can_live_migrate_destination( context, instance, dest, block_migration, disk_over_commit) # Change instance_state values = {"task_state": task_states.MIGRATING} # update instance state and notify (old_ref, new_instance_ref) = db.instance_update_and_get_original( context, instance['uuid'], values) notifications.send_update(context, old_ref, new_instance_ref, service="scheduler") # Perform migration src = instance['host'] self.compute_rpcapi.live_migration(context, host=src, instance=new_instance_ref, dest=dest, block_migration=block_migration, migrate_data=migrate_data)
def _set_vm_state_and_notify(self, method, updates, context, ex, request_spec): """changes VM state and notifies.""" # FIXME(comstud): Re-factor this somehow. Not sure this belongs in the # scheduler manager like this. We should make this easier. # run_instance only sends a request_spec, and an instance may or may # not have been created in the API (or scheduler) already. If it was # created, there's a 'uuid' set in the instance_properties of the # request_spec. # (littleidea): I refactored this a bit, and I agree # it should be easier :) # The refactoring could go further but trying to minimize changes # for essex timeframe LOG.warning(_("Failed to schedule_%(method)s: %(ex)s"), {'method': method, 'ex': ex}) vm_state = updates['vm_state'] properties = request_spec.get('instance_properties', {}) # NOTE(vish): We shouldn't get here unless we have a catastrophic # failure, so just set all instances to error. if uuid # is not set, instance_uuids will be set to [None], this # is solely to preserve existing behavior and can # be removed along with the 'if instance_uuid:' if we can # verify that uuid is always set. uuids = [properties.get('uuid')] for instance_uuid in request_spec.get('instance_uuids') or uuids: if instance_uuid: state = vm_state.upper() LOG.warning(_('Setting instance to %s state.'), state, instance_uuid=instance_uuid) # update instance state and notify on the transition (old_ref, new_ref) = self.db.instance_update_and_get_original( context, instance_uuid, updates) notifications.send_update(context, old_ref, new_ref, service="scheduler") compute_utils.add_instance_fault_from_exc(context, conductor_api.LocalAPI(), new_ref, ex, sys.exc_info()) payload = dict(request_spec=request_spec, instance_properties=properties, instance_id=instance_uuid, state=vm_state, method=method, reason=ex) notifier.notify(context, notifier.publisher_id("scheduler"), 'scheduler.' + method, notifier.ERROR, payload)
def _set_vm_state_and_notify(self, method, updates, context, ex, request_spec): """changes VM state and notifies""" # FIXME(comstud): Re-factor this somehow. Not sure this belongs in the # scheduler manager like this. We should make this easier. # run_instance only sends a request_spec, and an instance may or may # not have been created in the API (or scheduler) already. If it was # created, there's a 'uuid' set in the instance_properties of the # request_spec. # (littleidea): I refactored this a bit, and I agree # it should be easier :) # The refactoring could go further but trying to minimize changes # for essex timeframe LOG.warning(_("Failed to schedule_%(method)s: %(ex)s") % locals()) vm_state = updates['vm_state'] properties = request_spec.get('instance_properties', {}) # FIXME(comstud): We really need to move error handling closer # to where the errors occur so we can deal with errors on # individual instances when scheduling multiple. if 'instance_uuids' in request_spec: instance_uuid = request_spec['instance_uuids'][0] else: instance_uuid = properties.get('uuid', {}) if instance_uuid: state = vm_state.upper() LOG.warning(_('Setting instance to %(state)s state.'), locals(), instance_uuid=instance_uuid) # update instance state and notify on the transition (old_ref, new_ref) = db.instance_update_and_get_original( context, instance_uuid, updates) notifications.send_update(context, old_ref, new_ref, service="scheduler") payload = dict(request_spec=request_spec, instance_properties=properties, instance_id=instance_uuid, state=vm_state, method=method, reason=ex) notifier.notify(context, notifier.publisher_id("scheduler"), 'scheduler.' + method, notifier.ERROR, payload)
def _test_set_vm_state_and_notify(self, request_spec, expected_uuids): updates = dict(vm_state='fake-vm-state') service = 'fake-service' method = 'fake-method' exc_info = 'exc_info' self.mox.StubOutWithMock(compute_utils, 'add_instance_fault_from_exc') self.mox.StubOutWithMock(notifications, 'send_update') self.mox.StubOutWithMock(db, 'instance_update_and_get_original') self.mox.StubOutWithMock(notify, 'get_notifier') notifier = self.mox.CreateMockAnything() notify.get_notifier('conductor', CONF.host).AndReturn(notifier) notify.get_notifier(service).AndReturn(notifier) old_ref = 'old_ref' new_ref = 'new_ref' for uuid in expected_uuids: db.instance_update_and_get_original( self.context, uuid, updates).AndReturn((old_ref, new_ref)) notifications.send_update(self.context, old_ref, new_ref, service=service) compute_utils.add_instance_fault_from_exc( self.context, mox.IsA(conductor_api.LocalAPI), new_ref, exc_info, mox.IsA(tuple)) payload = dict(request_spec=request_spec, instance_properties=request_spec.get( 'instance_properties', {}), instance_id=uuid, state='fake-vm-state', method=method, reason=exc_info) event_type = '%s.%s' % (service, method) notifier.error(self.context, event_type, payload) self.mox.ReplayAll() scheduler_utils.set_vm_state_and_notify(self.context, service, method, updates, exc_info, request_spec, db)
def delete_metadata_key(self, key): """Optimized metadata delete method. This provides a more efficient way to delete a single metadata key, instead of just calling instance.save(). This should be called with the key still present in self.metadata, which it will update after completion. """ db.instance_metadata_delete(self._context, self.uuid, key) md_was_changed = 'metadata' in self.obj_what_changed() del self.metadata[key] self._orig_metadata.pop(key, None) notifications.send_update(self._context, self, self) if not md_was_changed: self.obj_reset_changes(['metadata'])