From 87e13112b128385a915589b43b8d376839215f8c Mon Sep 17 00:00:00 2001 From: Balazs Gibizer Date: Mon, 6 Jun 2016 15:03:47 +0200 Subject: [PATCH] Transform instance.update notification Change-Id: I4e1ca357322bdbcb95e4803001f4a7dc1bd37f04 Implements: bp versioned-notification-transformation-newton --- doc/notification_samples/instance-update.json | 65 ++++++++ nova/notifications/base.py | 59 ++++++++ nova/notifications/objects/instance.py | 86 +++++++++++ .../notification_sample_base.py | 7 +- .../test_instance.py | 143 ++++++++++++++++++ .../objects/test_notification.py | 5 + nova/tests/unit/test_notifications.py | 141 +++++++++++++---- nova/virt/fake.py | 5 + 8 files changed, 483 insertions(+), 28 deletions(-) create mode 100644 doc/notification_samples/instance-update.json diff --git a/doc/notification_samples/instance-update.json b/doc/notification_samples/instance-update.json new file mode 100644 index 000000000000..63e91918643c --- /dev/null +++ b/doc/notification_samples/instance-update.json @@ -0,0 +1,65 @@ +{ + "event_type": "instance.update", + "payload": { + "nova_object.data": { + "architecture": "x86_64", + "audit_period": { + "nova_object.data": { + "audit_period_beginning": "2012-10-01T00:00:00Z", + "audit_period_ending": "2012-10-29T13:42:11Z"}, + "nova_object.name": "AuditPeriodPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + }, + "availability_zone": null, + "bandwidth": [], + "created_at": "2012-10-29T13:42:11Z", + "deleted_at": null, + "display_name": "some-server", + "host": "compute", + "host_name": "some-server", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "kernel_id": "", + "launched_at": null, + "metadata": {}, + "node": "fake-mini", + "old_display_name": null, + "os_type": null, + "progress": 0, + "ramdisk_id": "", + "reservation_id": "r-sd3ygfjj", + "state": "building", + "task_state": "scheduling", + "power_state": "pending", + "ip_addresses": [], + "state_update": { + "nova_object.data": { + "new_task_state": null, + "old_state": "building", + "old_task_state": null, + "state": "building"}, + "nova_object.name": "InstanceStateUpdatePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0"}, + "tenant_id": "6f70656e737461636b20342065766572", + "terminated_at": null, + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512 + }, + "nova_object.version": "1.0", + "nova_object.namespace": "nova" + }, + "user_id": "fake", + "uuid": "c03c0bf9-f46e-4e4f-93f1-817568567ee2"}, + "nova_object.name": "InstanceUpdatePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0"}, + "priority": "INFO", + "publisher_id": "nova-compute:fake-mini" +} diff --git a/nova/notifications/base.py b/nova/notifications/base.py index f3d13727f4cf..34800a958843 100644 --- a/nova/notifications/base.py +++ b/nova/notifications/base.py @@ -33,11 +33,15 @@ from nova.i18n import _LE from nova.image import glance from nova import network from nova.network import model as network_model +from nova.notifications.objects import base as notification_base +from nova.notifications.objects import instance as instance_notification from nova import objects from nova.objects import base as obj_base +from nova.objects import fields from nova import rpc from nova import utils + LOG = log.getLogger(__name__) CONF = nova.conf.CONF @@ -244,6 +248,61 @@ def _send_instance_update_notification(context, instance, old_vm_state=None, rpc.get_notifier(service, host).info(context, 'compute.instance.update', payload) + _send_versioned_instance_update(context, instance, payload, host, service) + + +def _map_service_to_binary(service): + if service == 'api': + binary = 'nova-api' + elif service == 'compute': + binary = 'nova-compute' + else: + binary = service + return binary + + +def _send_versioned_instance_update(context, instance, payload, host, service): + + state_update = instance_notification.InstanceStateUpdatePayload( + old_state=payload.get('old_state'), + state=payload.get('state'), + old_task_state=payload.get('old_task_state'), + new_task_state=payload.get('new_task_state')) + + audit_period = instance_notification.AuditPeriodPayload( + audit_period_beginning=payload.get('audit_period_beginning'), + audit_period_ending=payload.get('audit_period_ending')) + + bandwidth = [instance_notification.BandwidthPayload( + network_name=label, + in_bytes=bw['bw_in'], + out_bytes=bw['bw_out']) + for label, bw in payload['bandwidth'].items()] + + network_info = instance.info_cache.network_info + flavor = instance_notification.FlavorPayload(instance=instance) + + versioned_payload = instance_notification.InstanceUpdatePayload( + instance=instance, + state_update=state_update, + audit_period=audit_period, + bandwidth=bandwidth, + ip_addresses=instance_notification.IpPayload.from_network_info( + network_info), + flavor=flavor, + old_display_name=payload.get('old_display_name')) + + notification = instance_notification.InstanceUpdateNotification( + priority=fields.NotificationPriority.INFO, + event_type=notification_base.EventType( + object='instance', + action=fields.NotificationAction.UPDATE), + publisher=notification_base.NotificationPublisher( + host=host or CONF.host, + binary=_map_service_to_binary(service)), + payload=versioned_payload) + notification.emit(context) + def audit_period_bounds(current_period=False): """Get the start and end of the relevant audit usage period diff --git a/nova/notifications/objects/instance.py b/nova/notifications/objects/instance.py index 573787e87de8..19632dfbdd02 100644 --- a/nova/notifications/objects/instance.py +++ b/nova/notifications/objects/instance.py @@ -105,6 +105,29 @@ class InstanceActionPayload(InstancePayload): flavor=flavor) +@nova_base.NovaObjectRegistry.register_notification +class InstanceUpdatePayload(InstancePayload): + # Version 1.0: Initial version + VERSION = '1.0' + fields = { + 'state_update': fields.ObjectField('InstanceStateUpdatePayload'), + 'audit_period': fields.ObjectField('AuditPeriodPayload'), + 'bandwidth': fields.ListOfObjectsField('BandwidthPayload'), + 'old_display_name': fields.StringField(nullable=True) + } + + def __init__(self, instance, flavor, ip_addresses, state_update, + audit_period, bandwidth, old_display_name): + super(InstanceUpdatePayload, self).__init__( + instance=instance, + flavor=flavor, + ip_addresses=ip_addresses, + state_update=state_update, + audit_period=audit_period, + bandwidth=bandwidth, + old_display_name=old_display_name) + + @nova_base.NovaObjectRegistry.register_notification class IpPayload(base.NotificationPayloadBase): # Version 1.0: Initial version @@ -119,6 +142,25 @@ class IpPayload(base.NotificationPayloadBase): 'device_name': fields.StringField(nullable=True) } + @classmethod + def from_network_info(cls, network_info): + """Returns a list of IpPayload object based on the passed + network_info. + """ + ips = [] + if network_info is not None: + for vif in network_info: + for ip in vif.fixed_ips(): + ips.append(cls( + label=vif["network"]["label"], + mac=vif["address"], + meta=vif["meta"], + port_uuid=vif["id"], + version=ip["version"], + address=ip["address"], + device_name=vif["devname"])) + return ips + @nova_base.NovaObjectRegistry.register_notification class FlavorPayload(base.NotificationPayloadBase): @@ -146,6 +188,39 @@ class FlavorPayload(base.NotificationPayloadBase): self.populate_schema(instance=instance, flavor=instance.flavor) +@nova_base.NovaObjectRegistry.register_notification +class BandwidthPayload(base.NotificationPayloadBase): + # Version 1.0: Initial version + VERSION = '1.0' + fields = { + 'network_name': fields.StringField(), + 'in_bytes': fields.IntegerField(), + 'out_bytes': fields.IntegerField(), + } + + +@nova_base.NovaObjectRegistry.register_notification +class AuditPeriodPayload(base.NotificationPayloadBase): + # Version 1.0: Initial version + VERSION = '1.0' + fields = { + 'audit_period_beginning': fields.DateTimeField(), + 'audit_period_ending': fields.DateTimeField(), + } + + +@nova_base.NovaObjectRegistry.register_notification +class InstanceStateUpdatePayload(base.NotificationPayloadBase): + # Version 1.0: Initial version + VERSION = '1.0' + fields = { + 'old_state': fields.StringField(nullable=True), + 'state': fields.StringField(nullable=True), + 'old_task_state': fields.StringField(nullable=True), + 'new_task_state': fields.StringField(nullable=True), + } + + @base.notification_sample('instance-delete-start.json') @base.notification_sample('instance-delete-end.json') # @base.notification_sample('instance-pause-start.json') @@ -182,3 +257,14 @@ class InstanceActionNotification(base.NotificationBase): fields = { 'payload': fields.ObjectField('InstanceActionPayload') } + + +@base.notification_sample('instance-update.json') +@nova_base.NovaObjectRegistry.register_notification +class InstanceUpdateNotification(base.NotificationBase): + # Version 1.0: Initial version + VERSION = '1.0' + + fields = { + 'payload': fields.ObjectField('InstanceUpdatePayload') + } diff --git a/nova/tests/functional/notification_sample_tests/notification_sample_base.py b/nova/tests/functional/notification_sample_tests/notification_sample_base.py index 8106404dddbf..5f4d0b13e842 100644 --- a/nova/tests/functional/notification_sample_tests/notification_sample_base.py +++ b/nova/tests/functional/notification_sample_tests/notification_sample_base.py @@ -77,7 +77,7 @@ class NotificationSampleTestBase(test.TestCase, self.start_service('conductor', manager=CONF.conductor.manager) self.start_service('scheduler') self.start_service('network') - self.start_service('compute') + self.compute = self.start_service('compute') def _get_notification_sample(self, sample): sample_dir = os.path.dirname(os.path.abspath(__file__)) @@ -177,3 +177,8 @@ class NotificationSampleTestBase(test.TestCase, self.fail('Server failed to delete.') except api_client.OpenStackApiNotFoundException: return + + def _get_notifications(self, event_type): + return [notification for notification + in fake_notifier.VERSIONED_NOTIFICATIONS + if notification['event_type'] == event_type] diff --git a/nova/tests/functional/notification_sample_tests/test_instance.py b/nova/tests/functional/notification_sample_tests/test_instance.py index 4d19bdba5a0a..17b4f3c9535b 100644 --- a/nova/tests/functional/notification_sample_tests/test_instance.py +++ b/nova/tests/functional/notification_sample_tests/test_instance.py @@ -9,6 +9,7 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. +from nova import context from nova.tests import fixtures from nova.tests.functional.notification_sample_tests \ import notification_sample_base @@ -47,3 +48,145 @@ class TestInstanceNotificationSample( notification_sample_base.NotificationSampleTestBase.ANY, 'uuid': server['id']}, actual=fake_notifier.VERSIONED_NOTIFICATIONS[1]) + + def _verify_instance_update_steps(self, steps, notifications, + initial=None): + replacements = {} + if initial: + replacements = initial + for i, step in enumerate(steps): + replacements.update(step) + self._verify_notification( + 'instance-update', + replacements=replacements, + actual=notifications[i]) + return replacements + + def test_create_delete_server_with_instance_update(self): + self.flags(notify_on_state_change='vm_and_task_state') + + server = self._boot_a_server( + extra_params={'networks': [{'port': self.neutron.port_1['id']}]}) + + instance_updates = self._get_notifications('instance.update') + + # The first notification comes from the nova-api the rest is from the + # nova-compute. To keep the test simpler assert this fact and then + # modify the publisher_id of the first notification to match the + # template + self.assertEqual('nova-api:fake-mini', + instance_updates[0]['publisher_id']) + instance_updates[0]['publisher_id'] = 'nova-compute:fake-mini' + + self.assertEqual(7, len(instance_updates)) + create_steps = [ + # nothing -> scheduling + {'reservation_id': + notification_sample_base.NotificationSampleTestBase.ANY, + 'uuid': server['id'], + 'host': None, + 'node': None, + 'state_update.new_task_state': 'scheduling', + 'state_update.old_task_state': 'scheduling', + 'state_update.state': 'building', + 'state_update.old_state': 'building', + 'state': 'building'}, + + # scheduling -> building + { + 'state_update.new_task_state': None, + 'state_update.old_task_state': 'scheduling', + 'task_state': None}, + + # scheduled + {'host': 'compute', + 'node': 'fake-mini', + 'state_update.old_task_state': None}, + + # building -> networking + {'state_update.new_task_state': 'networking', + 'state_update.old_task_state': 'networking', + 'task_state': 'networking'}, + + # networking -> block_device_mapping + {'state_update.new_task_state': 'block_device_mapping', + 'state_update.old_task_state': 'networking', + 'task_state': 'block_device_mapping', + }, + + # block_device_mapping -> spawning + {'state_update.new_task_state': 'spawning', + 'state_update.old_task_state': 'block_device_mapping', + 'task_state': 'spawning', + 'ip_addresses': [{ + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "mac": "fa:16:3e:4c:2c:30", + "address": "192.168.1.3", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "meta": {}, + "version": 4, + "label": "private-network", + "device_name": "tapce531f90-19" + }}] + }, + + # spawning -> active + {'state_update.new_task_state': None, + 'state_update.old_task_state': 'spawning', + 'state_update.state': 'active', + 'launched_at': '2012-10-29T13:42:11Z', + 'state': 'active', + 'task_state': None, + 'power_state': 'running'}, + ] + + replacements = self._verify_instance_update_steps( + create_steps, instance_updates) + + fake_notifier.reset() + + # Let's generate some bandwidth usage data. + # Just call the periodic task directly for simplicity + self.compute.manager._poll_bandwidth_usage(context.get_admin_context()) + + self.api.delete_server(server['id']) + self._wait_until_deleted(server) + + instance_updates = self._get_notifications('instance.update') + self.assertEqual(2, len(instance_updates)) + + delete_steps = [ + # active -> deleting + {'state_update.new_task_state': 'deleting', + 'state_update.old_task_state': 'deleting', + 'state_update.old_state': 'active', + 'state': 'active', + 'task_state': 'deleting', + 'bandwidth': [ + {'nova_object.namespace': 'nova', + 'nova_object.name': 'BandwidthPayload', + 'nova_object.data': + {'network_name': 'private-network', + 'out_bytes': 0, + 'in_bytes': 0}, + 'nova_object.version': '1.0'}] + }, + + # deleting -> deleted + {'state_update.new_task_state': None, + 'state_update.old_task_state': 'deleting', + 'state_update.old_state': 'active', + 'state_update.state': 'deleted', + 'state': 'deleted', + 'task_state': None, + 'terminated_at': '2012-10-29T13:42:11Z', + 'ip_addresses': [], + 'power_state': 'pending', + 'bandwidth': []}, + ] + + self._verify_instance_update_steps(delete_steps, instance_updates, + initial=replacements) diff --git a/nova/tests/unit/notifications/objects/test_notification.py b/nova/tests/unit/notifications/objects/test_notification.py index a698e9ab1666..2f9e2afced4f 100644 --- a/nova/tests/unit/notifications/objects/test_notification.py +++ b/nova/tests/unit/notifications/objects/test_notification.py @@ -255,6 +255,8 @@ class TestNotificationBase(test.NoDBTestCase): notification_object_data = { + 'AuditPeriodPayload': '1.0-28345f72ca9d805eeb61b2c2385805dd', + 'BandwidthPayload': '1.0-49278639296f9939ff2c8947b2078a82', 'EventType': '1.3-6ef678bfe9a4ebfd669c96d2d2c124a5', 'ExceptionNotification': '1.0-a73147b93b520ff0061865849d3dfa56', 'ExceptionPayload': '1.0-4516ae282a55fe2fd5c754967ee6248b', @@ -262,6 +264,9 @@ notification_object_data = { 'InstanceActionNotification': '1.0-a73147b93b520ff0061865849d3dfa56', 'InstanceActionPayload': '1.0-aa6a322cf1a3a19d090259fee65d1094', 'InstancePayload': '1.0-878bbc5a7a20bdeac7c6570f438a53aa', + 'InstanceStateUpdatePayload': '1.0-a934d04e1b314318e42e8062647edd11', + 'InstanceUpdateNotification': '1.0-a73147b93b520ff0061865849d3dfa56', + 'InstanceUpdatePayload': '1.0-c69e17e00400455bfe602e5573a61f0b', 'IpPayload': '1.0-26b40117c41ed95a61ae104f0fcb5fdc', 'NotificationPublisher': '1.0-bbbc1402fb0e443a3eb227cc52b61545', 'ServiceStatusNotification': '1.0-a73147b93b520ff0061865849d3dfa56', diff --git a/nova/tests/unit/test_notifications.py b/nova/tests/unit/test_notifications.py index 97d8fe02a9bb..a99fc25dfb33 100644 --- a/nova/tests/unit/test_notifications.py +++ b/nova/tests/unit/test_notifications.py @@ -200,6 +200,7 @@ class NotificationsTestCase(test.TestCase): notifications.send_update(self.context, old, self.instance) self.assertEqual(0, len(fake_notifier.NOTIFICATIONS)) + self.assertEqual(0, len(fake_notifier.VERSIONED_NOTIFICATIONS)) def test_task_notif(self): @@ -220,12 +221,18 @@ class NotificationsTestCase(test.TestCase): verify_states=True) self.assertEqual(0, len(fake_notifier.NOTIFICATIONS)) + self.assertEqual(0, len(fake_notifier.VERSIONED_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)) + self.assertEqual(1, len(fake_notifier.VERSIONED_NOTIFICATIONS)) + + self.assertEqual( + 'instance.update', + fake_notifier.VERSIONED_NOTIFICATIONS[0]['event_type']) def test_send_no_notif(self): @@ -240,6 +247,7 @@ class NotificationsTestCase(test.TestCase): service="compute", host=None, verify_states=True) self.assertEqual(0, len(fake_notifier.NOTIFICATIONS)) + self.assertEqual(0, len(fake_notifier.VERSIONED_NOTIFICATIONS)) def test_send_on_vm_change(self): old = obj_base.obj_to_primitive(self.instance) @@ -253,6 +261,14 @@ class NotificationsTestCase(test.TestCase): notif = fake_notifier.NOTIFICATIONS[0] self.assertEqual('compute.testhost', notif.publisher_id) + self.assertEqual(1, len(fake_notifier.VERSIONED_NOTIFICATIONS)) + self.assertEqual( + 'nova-compute:testhost', + fake_notifier.VERSIONED_NOTIFICATIONS[0]['publisher_id']) + self.assertEqual( + 'instance.update', + fake_notifier.VERSIONED_NOTIFICATIONS[0]['event_type']) + def test_send_on_task_change(self): old = obj_base.obj_to_primitive(self.instance) @@ -262,6 +278,10 @@ class NotificationsTestCase(test.TestCase): notifications.send_update(self.context, old, self.instance) self.assertEqual(1, len(fake_notifier.NOTIFICATIONS)) + self.assertEqual(1, len(fake_notifier.VERSIONED_NOTIFICATIONS)) + self.assertEqual( + 'instance.update', + fake_notifier.VERSIONED_NOTIFICATIONS[0]['event_type']) def test_no_update_with_states(self): @@ -269,84 +289,146 @@ class NotificationsTestCase(test.TestCase): vm_states.BUILDING, vm_states.BUILDING, task_states.SPAWNING, task_states.SPAWNING, verify_states=True) self.assertEqual(0, len(fake_notifier.NOTIFICATIONS)) + self.assertEqual(0, len(fake_notifier.VERSIONED_NOTIFICATIONS)) - def test_vm_update_with_states(self): + def get_fake_bandwidth(self): + usage = objects.BandwidthUsage(context=self.context) + usage.create( + self.instance.uuid, + mac='DE:AD:BE:EF:00:01', + bw_in=1, + bw_out=2, + last_ctr_in=0, + last_ctr_out=0, + start_period='2012-10-29T13:42:11Z') + return usage + + @mock.patch.object(objects.BandwidthUsageList, 'get_by_uuids') + def test_vm_update_with_states(self, mock_bandwidth_list): + mock_bandwidth_list.return_value = [self.get_fake_bandwidth()] + fake_net_info = fake_network.fake_get_instance_nw_info(self, 1, 1) + self.instance.info_cache.network_info = fake_net_info notifications.send_update_with_states(self.context, self.instance, vm_states.BUILDING, vm_states.ACTIVE, task_states.SPAWNING, task_states.SPAWNING, verify_states=True) + + self._verify_notification() + + def _verify_notification(self, expected_state=vm_states.ACTIVE, + expected_new_task_state=task_states.SPAWNING): self.assertEqual(1, len(fake_notifier.NOTIFICATIONS)) - notif = fake_notifier.NOTIFICATIONS[0] - payload = notif.payload + self.assertEqual(1, len(fake_notifier.VERSIONED_NOTIFICATIONS)) + self.assertEqual( + 'instance.update', + fake_notifier.VERSIONED_NOTIFICATIONS[0]['event_type']) access_ip_v4 = str(self.instance.access_ip_v4) access_ip_v6 = str(self.instance.access_ip_v6) display_name = self.instance.display_name hostname = self.instance.hostname node = self.instance.node - + payload = fake_notifier.NOTIFICATIONS[0].payload self.assertEqual(vm_states.BUILDING, payload["old_state"]) - self.assertEqual(vm_states.ACTIVE, payload["state"]) + self.assertEqual(expected_state, payload["state"]) self.assertEqual(task_states.SPAWNING, payload["old_task_state"]) - self.assertEqual(task_states.SPAWNING, payload["new_task_state"]) + self.assertEqual(expected_new_task_state, payload["new_task_state"]) self.assertEqual(payload["access_ip_v4"], access_ip_v4) self.assertEqual(payload["access_ip_v6"], access_ip_v6) self.assertEqual(payload["display_name"], display_name) self.assertEqual(payload["hostname"], hostname) self.assertEqual(payload["node"], node) + payload = fake_notifier.VERSIONED_NOTIFICATIONS[0][ + 'payload']['nova_object.data'] + state_update = payload['state_update']['nova_object.data'] + self.assertEqual(vm_states.BUILDING, state_update['old_state']) + self.assertEqual(expected_state, state_update["state"]) + self.assertEqual(task_states.SPAWNING, state_update["old_task_state"]) + self.assertEqual(expected_new_task_state, + state_update["new_task_state"]) + self.assertEqual(payload["display_name"], display_name) + self.assertEqual(payload["host_name"], hostname) + self.assertEqual(payload["node"], node) + flavor = payload['flavor']['nova_object.data'] + self.assertEqual(flavor['flavorid'], '1') + self.assertEqual(payload['image_uuid'], uuids.image_ref) - def test_task_update_with_states(self): + net_info = self.instance.info_cache.network_info + vif = net_info[0] + ip_addresses = payload['ip_addresses'] + + self.assertEqual(len(ip_addresses), 2) + for actual_ip, expected_ip in zip(ip_addresses, vif.fixed_ips()): + actual_ip = actual_ip['nova_object.data'] + self.assertEqual(actual_ip['label'], vif['network']['label']) + self.assertEqual(actual_ip['mac'], vif['address'].lower()) + self.assertEqual(actual_ip['port_uuid'], vif['id']) + self.assertEqual(actual_ip['device_name'], vif['devname']) + self.assertEqual(actual_ip['version'], expected_ip['version']) + self.assertEqual(actual_ip['address'], expected_ip['address']) + + bandwidth = payload['bandwidth'] + self.assertEqual(len(bandwidth), 1) + bandwidth = bandwidth[0]['nova_object.data'] + self.assertEqual(bandwidth['in_bytes'], 1) + self.assertEqual(bandwidth['out_bytes'], 2) + self.assertEqual(bandwidth['network_name'], 'test1') + + @mock.patch.object(objects.BandwidthUsageList, 'get_by_uuids') + def test_task_update_with_states(self, mock_bandwidth_list): self.flags(notify_on_state_change="vm_and_task_state") + mock_bandwidth_list.return_value = [self.get_fake_bandwidth()] + fake_net_info = fake_network.fake_get_instance_nw_info(self, 1, 1) + self.instance.info_cache.network_info = fake_net_info notifications.send_update_with_states(self.context, self.instance, vm_states.BUILDING, vm_states.BUILDING, task_states.SPAWNING, None, verify_states=True) - 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) - display_name = self.instance.display_name - hostname = self.instance.hostname - - self.assertEqual(vm_states.BUILDING, payload["old_state"]) - self.assertEqual(vm_states.BUILDING, payload["state"]) - self.assertEqual(task_states.SPAWNING, payload["old_task_state"]) - self.assertIsNone(payload["new_task_state"]) - self.assertEqual(payload["access_ip_v4"], access_ip_v4) - self.assertEqual(payload["access_ip_v6"], access_ip_v6) - self.assertEqual(payload["display_name"], display_name) - self.assertEqual(payload["hostname"], hostname) + self._verify_notification(expected_state=vm_states.BUILDING, + expected_new_task_state=None) def test_update_no_service_name(self): notifications.send_update_with_states(self.context, self.instance, vm_states.BUILDING, vm_states.BUILDING, task_states.SPAWNING, None) self.assertEqual(1, len(fake_notifier.NOTIFICATIONS)) + self.assertEqual(1, len(fake_notifier.VERSIONED_NOTIFICATIONS)) # service name should default to 'compute' notif = fake_notifier.NOTIFICATIONS[0] self.assertEqual('compute.testhost', notif.publisher_id) + # in the versioned notification it defaults to nova-compute + notif = fake_notifier.VERSIONED_NOTIFICATIONS[0] + self.assertEqual('nova-compute:testhost', notif['publisher_id']) + def test_update_with_service_name(self): notifications.send_update_with_states(self.context, self.instance, vm_states.BUILDING, vm_states.BUILDING, task_states.SPAWNING, None, service="testservice") self.assertEqual(1, len(fake_notifier.NOTIFICATIONS)) + self.assertEqual(1, len(fake_notifier.VERSIONED_NOTIFICATIONS)) # service name should default to 'compute' notif = fake_notifier.NOTIFICATIONS[0] self.assertEqual('testservice.testhost', notif.publisher_id) + notif = fake_notifier.VERSIONED_NOTIFICATIONS[0] + self.assertEqual('testservice:testhost', notif['publisher_id']) + def test_update_with_host_name(self): notifications.send_update_with_states(self.context, self.instance, vm_states.BUILDING, vm_states.BUILDING, task_states.SPAWNING, None, host="someotherhost") self.assertEqual(1, len(fake_notifier.NOTIFICATIONS)) + self.assertEqual(1, len(fake_notifier.VERSIONED_NOTIFICATIONS)) # service name should default to 'compute' notif = fake_notifier.NOTIFICATIONS[0] self.assertEqual('compute.someotherhost', notif.publisher_id) + notif = fake_notifier.VERSIONED_NOTIFICATIONS[0] + self.assertEqual('nova-compute:someotherhost', notif['publisher_id']) + def test_payload_has_fixed_ip_labels(self): info = notifications.info_from_instance(self.context, self.instance, self.net_info, None) @@ -404,13 +486,18 @@ class NotificationsTestCase(test.TestCase): 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 + self.assertEqual(1, len(fake_notifier.VERSIONED_NOTIFICATIONS)) + 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) + for payload in [ + fake_notifier.NOTIFICATIONS[0].payload, + fake_notifier.VERSIONED_NOTIFICATIONS[0][ + 'payload']['nova_object.data']]: + + self.assertEqual(payload["old_display_name"], old_display_name) + self.assertEqual(payload["display_name"], new_display_name) def test_send_no_state_change(self): called = [False] diff --git a/nova/virt/fake.py b/nova/virt/fake.py index 01fd586c6c00..c00b73dd6128 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -367,6 +367,11 @@ class FakeDriver(driver.ComputeDriver): running VM. """ bw = [] + for instance in instances: + bw.append({'uuid': instance.uuid, + 'mac_address': 'fa:16:3e:4c:2c:30', + 'bw_in': 0, + 'bw_out': 0}) return bw def get_all_volume_usage(self, context, compute_host_bdms):