diff --git a/doc/notification_samples/instance-create-end.json b/doc/notification_samples/instance-create-end.json index 13975a35a0..6808a9b0e5 100644 --- a/doc/notification_samples/instance-create-end.json +++ b/doc/notification_samples/instance-create-end.json @@ -52,6 +52,7 @@ "state":"active", "task_state":null, "power_state":"running", + "tags":["tag"], "tenant_id":"6f70656e737461636b20342065766572", "terminated_at":null, "auto_disk_config":"MANUAL", @@ -82,7 +83,7 @@ }, "nova_object.name":"InstanceCreatePayload", "nova_object.namespace":"nova", - "nova_object.version":"1.5" + "nova_object.version":"1.6" }, "priority":"INFO", "publisher_id":"nova-compute:compute" diff --git a/doc/notification_samples/instance-create-error.json b/doc/notification_samples/instance-create-error.json index 43fc7bf2e8..e1238cb36d 100644 --- a/doc/notification_samples/instance-create-error.json +++ b/doc/notification_samples/instance-create-error.json @@ -49,6 +49,7 @@ "state":"building", "task_state":null, "power_state":"pending", + "tags":["tag"], "tenant_id":"6f70656e737461636b20342065766572", "terminated_at":null, "auto_disk_config":"MANUAL", @@ -79,7 +80,7 @@ }, "nova_object.name":"InstanceCreatePayload", "nova_object.namespace":"nova", - "nova_object.version":"1.5" + "nova_object.version":"1.6" }, "priority":"ERROR", "publisher_id":"nova-compute:compute" diff --git a/doc/notification_samples/instance-create-start.json b/doc/notification_samples/instance-create-start.json index f0d48520bc..73bb9fcc4c 100644 --- a/doc/notification_samples/instance-create-start.json +++ b/doc/notification_samples/instance-create-start.json @@ -39,6 +39,7 @@ "state":"building", "task_state":null, "power_state":"pending", + "tags":["tag"], "tenant_id":"6f70656e737461636b20342065766572", "terminated_at":null, "auto_disk_config":"MANUAL", @@ -69,7 +70,7 @@ }, "nova_object.name":"InstanceCreatePayload", "nova_object.namespace":"nova", - "nova_object.version":"1.5" + "nova_object.version":"1.6" }, "priority":"INFO", "publisher_id":"nova-compute:compute" diff --git a/nova/conductor/manager.py b/nova/conductor/manager.py index 55ff733a87..dffdfec0bb 100644 --- a/nova/conductor/manager.py +++ b/nova/conductor/manager.py @@ -1011,6 +1011,12 @@ class ComputeTaskManager(base.Base): cell, instance.flavor, instance.uuid, block_device_mapping) instance_tags = self._create_tags(cctxt, instance.uuid, tags) + # TODO(Kevin Zheng): clean this up once instance.create() handles + # tags; we do this so the instance.create notification in + # build_and_run_instance in nova-compute doesn't lazy-load tags + instance.tags = instance_tags if instance_tags \ + else objects.TagList() + # Update mapping for instance. Normally this check is guarded by # a try/except but if we're here we know that a newer nova-api # handled the build process and would have created the mapping diff --git a/nova/notifications/objects/instance.py b/nova/notifications/objects/instance.py index 782ddd85fa..9fe72d2ccd 100644 --- a/nova/notifications/objects/instance.py +++ b/nova/notifications/objects/instance.py @@ -187,10 +187,12 @@ class InstanceCreatePayload(InstanceActionPayload): # 1.3: Add keypairs field # 1.4: Add key_name field to InstancePayload # 1.5: Add BDM related data to InstancePayload - VERSION = '1.5' + # 1.6: Add tags field to InstanceCreatePayload + VERSION = '1.6' fields = { - 'keypairs': fields.ListOfObjectsField('KeypairPayload') + 'keypairs': fields.ListOfObjectsField('KeypairPayload'), + 'tags': fields.ListOfStringsField(), } def __init__(self, instance, fault): @@ -199,6 +201,8 @@ class InstanceCreatePayload(InstanceActionPayload): fault=fault) self.keypairs = [keypair_payload.KeypairPayload(keypair=keypair) for keypair in instance.keypairs] + self.tags = [instance_tag.tag + for instance_tag in instance.tags] @nova_base.NovaObjectRegistry.register_notification diff --git a/nova/tests/functional/notification_sample_tests/test_instance.py b/nova/tests/functional/notification_sample_tests/test_instance.py index f4ec20ea7b..f6190b7e81 100644 --- a/nova/tests/functional/notification_sample_tests/test_instance.py +++ b/nova/tests/functional/notification_sample_tests/test_instance.py @@ -156,7 +156,8 @@ class TestInstanceNotificationSample( def test_create_delete_server(self): server = self._boot_a_server( - extra_params={'networks': [{'port': self.neutron.port_1['id']}]}) + extra_params={'networks': [{'port': self.neutron.port_1['id']}], + 'tags': ['tag']}) self._attach_volume_to_server(server, self.cinder.SWAP_OLD_VOL) self.api.delete_server(server['id']) self._wait_until_deleted(server) @@ -191,7 +192,8 @@ class TestInstanceNotificationSample( server = self._boot_a_server( expected_status='ERROR', - extra_params={'networks': [{'port': self.neutron.port_1['id']}]}) + extra_params={'networks': [{'port': self.neutron.port_1['id']}], + 'tags': ['tag']}) self.assertEqual(2, len(fake_notifier.VERSIONED_NOTIFICATIONS)) diff --git a/nova/tests/unit/compute/test_compute_mgr.py b/nova/tests/unit/compute/test_compute_mgr.py index d722233809..66725ac6f1 100644 --- a/nova/tests/unit/compute/test_compute_mgr.py +++ b/nova/tests/unit/compute/test_compute_mgr.py @@ -5265,7 +5265,7 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase): mock.ANY, 'expected_task_state': 'spawning'} expected_call = mock.call(self.context, self.instance.uuid, updates, columns_to_join=['metadata', 'system_metadata', - 'info_cache']) + 'info_cache', 'tags']) last_update_call = mock_db_update.call_args_list[ mock_db_update.call_count - 1] self.assertEqual(expected_call, last_update_call) diff --git a/nova/tests/unit/compute/test_compute_utils.py b/nova/tests/unit/compute/test_compute_utils.py index ffaee73e06..1c098cd1d1 100644 --- a/nova/tests/unit/compute/test_compute_utils.py +++ b/nova/tests/unit/compute/test_compute_utils.py @@ -603,6 +603,47 @@ class UsageInfoTestCase(test.TestCase): self.assertEqual(uuids.fake_image_ref, payload['image_uuid']) + def test_notify_about_instance_create_with_tags(self): + instance = create_instance(self.context) + + # TODO(Kevin Zheng): clean this up to pass tags as params to + # create_instance() once instance.create() handles tags. + instance.tags = objects.TagList( + objects=[objects.Tag(self.context, tag='tag1')]) + + compute_utils.notify_about_instance_create( + self.context, + instance, + host='fake-compute', + phase='start') + + self.assertEqual(1, len(fake_notifier.VERSIONED_NOTIFICATIONS)) + notification = fake_notifier.VERSIONED_NOTIFICATIONS[0] + + self.assertEqual('INFO', notification['priority']) + self.assertEqual('instance.create.start', notification['event_type']) + self.assertEqual('nova-compute:fake-compute', + notification['publisher_id']) + + payload = notification['payload']['nova_object.data'] + self.assertEqual('fake', payload['tenant_id']) + self.assertEqual('fake', payload['user_id']) + self.assertEqual(instance.uuid, payload['uuid']) + + flavorid = flavors.get_flavor_by_name('m1.tiny')['flavorid'] + flavor = payload['flavor']['nova_object.data'] + self.assertEqual(flavorid, str(flavor['flavorid'])) + + self.assertEqual(0, len(payload['keypairs'])) + for attr in ('display_name', 'created_at', 'launched_at', + 'state', 'task_state', 'display_description', 'locked', + 'auto_disk_config', 'tags'): + self.assertIn(attr, payload, "Key %s not in payload" % attr) + + self.assertEqual(1, len(payload['tags'])) + self.assertEqual('tag1', payload['tags'][0]) + self.assertEqual(uuids.fake_image_ref, payload['image_uuid']) + def test_notify_about_volume_swap(self): instance = create_instance(self.context) diff --git a/nova/tests/unit/fake_instance.py b/nova/tests/unit/fake_instance.py index fd0abad7e2..934fdd2441 100644 --- a/nova/tests/unit/fake_instance.py +++ b/nova/tests/unit/fake_instance.py @@ -122,6 +122,7 @@ def fake_instance_obj(context, obj_instance_class=None, **updates): obj_instance_class(), fake_db_instance(**updates), expected_attrs=expected_attrs) inst.keypairs = objects.KeyPairList(objects=[]) + inst.tags = objects.TagList() if flavor: inst.flavor = flavor # This is needed for instance quota counting until we have the diff --git a/nova/tests/unit/notifications/objects/test_notification.py b/nova/tests/unit/notifications/objects/test_notification.py index cc45e34f16..fd483016fc 100644 --- a/nova/tests/unit/notifications/objects/test_notification.py +++ b/nova/tests/unit/notifications/objects/test_notification.py @@ -385,7 +385,7 @@ notification_object_data = { '1.0-a73147b93b520ff0061865849d3dfa56', 'InstanceActionVolumeSwapPayload': '1.4-8b82cef523c62020c24b3eb1c39ea2ef', 'InstanceCreateNotification': '1.0-a73147b93b520ff0061865849d3dfa56', - 'InstanceCreatePayload': '1.5-97e9c0f516a68f20b25ce2f994cab081', + 'InstanceCreatePayload': '1.6-b117dd709616d60cf5e126b983c72cd2', 'InstancePayload': '1.4-46d922bd0a5cce46398b0cf7e8735fc4', 'InstanceStateUpdatePayload': '1.0-07e111c0fa0f6db0f79b0726d593e3da', 'InstanceUpdateNotification': '1.0-a73147b93b520ff0061865849d3dfa56', diff --git a/nova/tests/unit/objects/test_build_request.py b/nova/tests/unit/objects/test_build_request.py index 7f15e88074..a124581076 100644 --- a/nova/tests/unit/objects/test_build_request.py +++ b/nova/tests/unit/objects/test_build_request.py @@ -140,6 +140,9 @@ class _TestBuildRequestObject(object): # on build_request fake_req = fake_build_request.fake_db_req() fields = jsonutils.loads(fake_req['instance'])['nova_object.data'] + # TODO(Kevin Zheng): clean up this workaround once + # build_request.get_new_instance() can handle tags. + fields.pop('tags', None) build_request = objects.BuildRequest._from_db_object( self.context, objects.BuildRequest(), fake_req) self.assertEqual(0, len(build_request.instance.obj_what_changed())) diff --git a/nova/tests/unit/objects/test_instance.py b/nova/tests/unit/objects/test_instance.py index 63afeebee5..3cebfaab67 100644 --- a/nova/tests/unit/objects/test_instance.py +++ b/nova/tests/unit/objects/test_instance.py @@ -733,11 +733,11 @@ class _TestInstanceObject(object): mock.call(self.context, inst.uuid, {'vm_state': 'foo', 'task_state': 'bar', 'cell_name': 'foo!bar@baz'}, - columns_to_join=['system_metadata', 'extra', - 'extra.flavor']), + columns_to_join=['tags', 'system_metadata', + 'extra', 'extra.flavor']), mock.call(self.context, inst.uuid, {'vm_state': 'bar', 'task_state': 'foo'}, - columns_to_join=['system_metadata'])] + columns_to_join=['system_metadata', 'tags'])] mock_db_update.assert_has_calls(expected_calls) def test_skip_cells_api(self):