diff options
-rw-r--r-- | nova/compute/api.py | 2 | ||||
-rw-r--r-- | nova/compute/manager.py | 1 | ||||
-rw-r--r-- | nova/compute/utils.py | 2 | ||||
-rw-r--r-- | nova/notifications.py | 126 | ||||
-rw-r--r-- | nova/tests/test_notifications.py | 78 |
5 files changed, 163 insertions, 46 deletions
diff --git a/nova/compute/api.py b/nova/compute/api.py index b0f350bd3..d97390e04 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -1169,7 +1169,7 @@ class API(base.Base): notifications.send_update_with_states(context, instance, old_vm_state, instance["vm_state"], old_task_state, instance["task_state"], - service="api") + service="api", verify_states=True) properties = { 'instance_uuid': instance_uuid, diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 0a896c478..9e6407bbf 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -642,6 +642,7 @@ class ComputeManager(manager.SchedulerDependentManager): update_info['access_ip_v6'] = ip['address'] if update_info: self.db.instance_update(context, instance.uuid, update_info) + notifications.send_update(context, instance, instance) def _check_instance_not_already_created(self, context, instance): """Ensure an instance with the same name is not already present.""" diff --git a/nova/compute/utils.py b/nova/compute/utils.py index 65a3b2d90..0370988ea 100644 --- a/nova/compute/utils.py +++ b/nova/compute/utils.py @@ -96,7 +96,7 @@ def notify_about_instance_usage(context, instance, event_suffix, if not extra_usage_info: extra_usage_info = {} - usage_info = notifications.usage_from_instance(context, instance, + usage_info = notifications.info_from_instance(context, instance, network_info, system_metadata, **extra_usage_info) notifier_api.notify(context, 'compute.%s' % host, diff --git a/nova/notifications.py b/nova/notifications.py index fdaf82f7a..d0374ac16 100644 --- a/nova/notifications.py +++ b/nova/notifications.py @@ -40,56 +40,111 @@ notify_state_opt = cfg.StrOpt('notify_on_state_change', default=None, '"vm_and_task_state" for notifications on VM and task state ' 'changes.') +notify_any_opt = cfg.BoolOpt('notify_on_any_change', default=False, + help='If set, send compute.instance.update notifications on instance ' + 'state changes. Valid values are False for no notifications, ' + 'True for notifications on any instance changes.') + FLAGS = flags.FLAGS FLAGS.register_opt(notify_state_opt) +FLAGS.register_opt(notify_any_opt) def send_update(context, old_instance, new_instance, service=None, host=None): - """Send compute.instance.update notification to report changes - in vm state and (optionally) task state + """Send compute.instance.update notification to report any changes occurred + in that instance """ - send_update_with_states(context, new_instance, old_instance["vm_state"], - new_instance["vm_state"], old_instance["task_state"], - new_instance["task_state"], service, host) + if not FLAGS.notify_on_any_change and not FLAGS.notify_on_state_change: + # skip all this if updates are disabled + return + + update_with_state_change = False + + old_vm_state = old_instance["vm_state"] + new_vm_state = new_instance["vm_state"] + old_task_state = old_instance["task_state"], + new_task_state = new_instance["task_state"] + + # we should check if we need to send a state change or a regular + # notification + if old_vm_state != new_vm_state: + # yes, the vm state is changing: + update_with_state_change = True + elif FLAGS.notify_on_state_change: + if (FLAGS.notify_on_state_change.lower() == "vm_and_task_state" and + old_task_state != new_task_state): + # yes, the task state is changing: + update_with_state_change = True + + if update_with_state_change: + # send a notification with state changes + # value of verify_states need not be True as the check for states is + # already done here + send_update_with_states(context, new_instance, old_vm_state, + new_vm_state, old_task_state, new_task_state, service, host) + + else: + try: + _send_instance_update_notification(context, new_instance, + service=service, host=host) + except Exception: + LOG.exception(_("Failed to send state update notification"), + instance=instance) def send_update_with_states(context, instance, old_vm_state, new_vm_state, - old_task_state, new_task_state, service=None, host=None): - """Send compute.instance.update notification to report changes - in vm state and (optionally) task state + old_task_state, new_task_state, service="compute", host=None, + verify_states=False): + """Send compute.instance.update notification to report changes if there + are any, in the instance """ if not FLAGS.notify_on_state_change: - # skip all this if state updates are disabled + # skip all this if updates are disabled return - fire_update = False - - if old_vm_state != new_vm_state: - # yes, the vm state is changing: - fire_update = True - elif (FLAGS.notify_on_state_change.lower() == "vm_and_task_state" and - old_task_state != new_task_state): - # yes, the task state is changing: - fire_update = True + fire_update = True + # send update notification by default + + if verify_states: + # check whether we need to send notification related to state changes + fire_update = False + # do not send notification if the confitions for vm and(or) task state + # are not satisfied + if old_vm_state != new_vm_state: + # yes, the vm state is changing: + fire_update = True + elif FLAGS.notify_on_state_change: + if (FLAGS.notify_on_state_change.lower() == "vm_and_task_state" and + old_task_state != new_task_state): + # yes, the task state is changing: + fire_update = True if fire_update: + # send either a state change or a regular notificaion try: - _send_instance_update_notification(context, instance, old_vm_state, - old_task_state, new_vm_state, new_task_state, service, - host) + _send_instance_update_notification(context, instance, + old_vm_state=old_vm_state, old_task_state=old_task_state, + new_vm_state=new_vm_state, new_task_state=new_task_state, + service=service, host=host) except Exception: LOG.exception(_("Failed to send state update notification"), instance=instance) -def _send_instance_update_notification(context, instance, old_vm_state, - old_task_state, new_vm_state, new_task_state, service=None, host=None): +def _send_instance_update_notification(context, instance, old_vm_state=None, + old_task_state=None, new_vm_state=None, new_task_state=None, + service="compute", host=None): """Send 'compute.instance.update' notification to inform observers about instance state changes""" - payload = usage_from_instance(context, instance, None, None) + payload = info_from_instance(context, instance, None, None) + + if not new_vm_state: + new_vm_state = instance["vm_state"] + if not new_task_state: + new_task_state = instance["task_state"] states_payload = { "old_state": old_vm_state, @@ -109,11 +164,6 @@ def _send_instance_update_notification(context, instance, old_vm_state, bw = bandwidth_usage(instance, audit_start) payload["bandwidth"] = bw - # if the service name (e.g. api/scheduler/compute) is not provided, default - # to "compute" - if not service: - service = "compute" - publisher_id = notifier_api.publisher_id(service, host) notifier_api.notify(context, publisher_id, 'compute.instance.update', @@ -194,9 +244,9 @@ def image_meta(system_metadata): return image_meta -def usage_from_instance(context, instance_ref, network_info, +def info_from_instance(context, instance_ref, network_info, system_metadata, **kw): - """Get usage information for an instance which is common to all + """Get detailed instance information for an instance which is common to all notifications. :param network_info: network_info provided if not None @@ -220,7 +270,7 @@ def usage_from_instance(context, instance_ref, network_info, except exception.NotFound: system_metadata = {} - usage_info = dict( + instance_info = dict( # Owner properties tenant_id=instance_ref['project_id'], user_id=instance_ref['user_id'], @@ -266,6 +316,10 @@ def usage_from_instance(context, instance_ref, network_info, # Status properties state=instance_ref['vm_state'], state_description=null_safe_str(instance_ref.get('task_state')), + + # accessIPs + access_ip_v4=instance_ref['access_ip_v4'], + access_ip_v6=instance_ref['access_ip_v6'], ) if network_info is not None: @@ -274,11 +328,11 @@ def usage_from_instance(context, instance_ref, network_info, for ip in vif.fixed_ips(): ip["label"] = vif["network"]["label"] fixed_ips.append(ip) - usage_info['fixed_ips'] = fixed_ips + instance_info['fixed_ips'] = fixed_ips # add image metadata image_meta_props = image_meta(system_metadata) - usage_info["image_meta"] = image_meta_props + instance_info["image_meta"] = image_meta_props - usage_info.update(kw) - return usage_info + instance_info.update(kw) + return instance_info diff --git a/nova/tests/test_notifications.py b/nova/tests/test_notifications.py index 59f179bd2..0626f16e8 100644 --- a/nova/tests/test_notifications.py +++ b/nova/tests/test_notifications.py @@ -75,6 +75,9 @@ class NotificationsTestCase(test.TestCase): inst['instance_type_id'] = type_id inst['root_gb'] = 0 inst['ephemeral_gb'] = 0 + inst['access_ip_v4'] = '1.2.3.4' + inst['access_ip_v6'] = 'feed:5eed' + inst['display_name'] = 'test_instance' if params: inst.update(params) return db.instance_create(self.context, inst) @@ -83,10 +86,20 @@ class NotificationsTestCase(test.TestCase): # test config disable of the notifcations self.flags(notify_on_state_change=None) + self.flags(notify_on_any_change=False) old = copy.copy(self.instance) self.instance["vm_state"] = vm_states.ACTIVE + 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) + notifications.send_update(self.context, old, self.instance) self.assertEquals(0, len(test_notifier.NOTIFICATIONS)) @@ -99,7 +112,15 @@ class NotificationsTestCase(test.TestCase): old = copy.copy(self.instance) self.instance["task_state"] = task_states.SPAWNING - notifications.send_update(self.context, old, self.instance) + 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.assertEquals(0, len(test_notifier.NOTIFICATIONS)) # ok now enable task state notifcations and re-try @@ -111,7 +132,15 @@ class NotificationsTestCase(test.TestCase): def test_send_no_notif(self): # test notification on send no initial vm state: - notifications.send_update(self.context, self.instance, self.instance) + old_vm_state = self.instance['vm_state'] + new_vm_state = self.instance['vm_state'] + old_task_state = self.instance['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, + service="compute", host=None, verify_states=True) + self.assertEquals(0, len(test_notifier.NOTIFICATIONS)) def test_send_on_vm_change(self): @@ -140,36 +169,49 @@ class NotificationsTestCase(test.TestCase): notifications.send_update_with_states(self.context, self.instance, vm_states.BUILDING, vm_states.BUILDING, task_states.SPAWNING, - task_states.SPAWNING) + task_states.SPAWNING, verify_states=True) self.assertEquals(0, len(test_notifier.NOTIFICATIONS)) def test_vm_update_with_states(self): notifications.send_update_with_states(self.context, self.instance, vm_states.BUILDING, vm_states.ACTIVE, task_states.SPAWNING, - task_states.SPAWNING) + task_states.SPAWNING, verify_states=True) 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"] + display_name = self.instance["display_name"] self.assertEquals(vm_states.BUILDING, payload["old_state"]) self.assertEquals(vm_states.ACTIVE, payload["state"]) self.assertEquals(task_states.SPAWNING, payload["old_task_state"]) self.assertEquals(task_states.SPAWNING, payload["new_task_state"]) + self.assertEquals(payload["access_ip_v4"], access_ip_v4) + self.assertEquals(payload["access_ip_v6"], access_ip_v6) + self.assertEquals(payload["display_name"], display_name) def test_task_update_with_states(self): + self.flags(notify_on_state_change="vm_and_task_state") notifications.send_update_with_states(self.context, self.instance, vm_states.BUILDING, vm_states.BUILDING, task_states.SPAWNING, - None) + None, verify_states=True) 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"] + display_name = self.instance["display_name"] self.assertEquals(vm_states.BUILDING, payload["old_state"]) self.assertEquals(vm_states.BUILDING, payload["state"]) self.assertEquals(task_states.SPAWNING, payload["old_task_state"]) self.assertEquals(None, payload["new_task_state"]) + self.assertEquals(payload["access_ip_v4"], access_ip_v4) + self.assertEquals(payload["access_ip_v6"], access_ip_v6) + self.assertEquals(payload["display_name"], display_name) def test_update_no_service_name(self): notifications.send_update_with_states(self.context, self.instance, @@ -202,7 +244,27 @@ class NotificationsTestCase(test.TestCase): self.assertEquals('compute.someotherhost', notif['publisher_id']) def test_payload_has_fixed_ip_labels(self): - usage = notifications.usage_from_instance(self.context, self.instance, + info = notifications.info_from_instance(self.context, self.instance, self.net_info, None) - self.assertTrue("fixed_ips" in usage) - self.assertEquals(usage["fixed_ips"][0]["label"], "test1") + self.assertTrue("fixed_ips" in info) + self.assertEquals(info["fixed_ips"][0]["label"], "test1") + + 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_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) |