From 655942069aa77abad90a742cd47690a81987e9d7 Mon Sep 17 00:00:00 2001 From: Balazs Gibizer Date: Fri, 13 Jan 2017 11:00:25 +0100 Subject: [PATCH] handle uninited fields in notification payload Due to the not strict handling of uninitialized fields during notification payload population it is possible that the emitted notification missing some of the fields defined in the schema. There are two problematic cases: 1) no load tirggered for lazy loaded fields. If the field was not loaded it is not added to the payload. 2) uninitialized, not lazy loadable fields are not added to the payload. This patch makes sure that lazy load is triggered. If the field is not lazy loadable and also not initialized then related payload field will be set to None. If the payload field is not nullable the code will fail to make sure that the inconsistency is detected. The following changes cannot be split to different commits because as soon as the generic schema population code is fixed every listed notification starts behaving differently. In some cases the availability_zone field of the Service object is left unitialized by both the constructor and the obj_load_attr function this caused that the availability_zone field of the service.update notification was missing from the emitted notification payload. The extra_specs field of the Flavor object is not lazy loadable and not initialized in the Flavor destroy case. So the extra_specs field of the FlavorPayload needed to be made nullable. The projects field of the Flavor object is lazy loaded but when the Flavor object is loaded as part of an Instance object Flavor is orphaned and no lazy load is allowed on that Flavor object. In this case the projects field of the FlavorPayload will be set to None to signal that the information is not available. This also means that the projects field of the FlavorPayload needed to be changed to nullable. The hosts and id fields of the Aggregate object is not initialized during the create of the Aggregate before the aggregate.create.start needs to be sent. Therefore these fields needs to be nullable. Closes-Bug: #1653221 Change-Id: Ib122cd98ee0cc31938d5ff1d5c753053267a3bd4 --- .../aggregate-create-end.json | 2 +- .../aggregate-create-start.json | 4 +- .../aggregate-delete-end.json | 2 +- .../aggregate-delete-start.json | 2 +- doc/notification_samples/flavor-create.json | 6 +- doc/notification_samples/flavor-delete.json | 6 +- doc/notification_samples/flavor-update.json | 2 +- .../instance-create-end.json | 3 +- .../instance-create-error.json | 3 +- .../instance-create-start.json | 3 +- .../instance-delete-end.json | 3 +- .../instance-delete-start.json | 3 +- .../instance-pause-end.json | 3 +- .../instance-pause-start.json | 3 +- .../instance-power_off-end.json | 3 +- .../instance-power_off-start.json | 3 +- .../instance-power_on-end.json | 3 +- .../instance-power_on-start.json | 3 +- .../instance-resize-end.json | 3 +- .../instance-resize-start.json | 3 +- .../instance-resize_finish-end.json | 3 +- .../instance-resize_finish-start.json | 3 +- .../instance-restore-end.json | 3 +- .../instance-restore-start.json | 3 +- .../instance-resume-end.json | 3 +- .../instance-resume-start.json | 3 +- .../instance-shelve-end.json | 3 +- .../instance-shelve-start.json | 3 +- .../instance-shelve_offload-end.json | 3 +- .../instance-shelve_offload-start.json | 3 +- .../instance-shutdown-end.json | 3 +- .../instance-shutdown-start.json | 3 +- .../instance-snapshot-end.json | 3 +- .../instance-snapshot-start.json | 3 +- .../instance-suspend-end.json | 3 +- .../instance-suspend-start.json | 3 +- .../instance-unpause-end.json | 3 +- .../instance-unpause-start.json | 3 +- .../instance-unshelve-end.json | 3 +- .../instance-unshelve-start.json | 3 +- doc/notification_samples/instance-update.json | 3 +- .../instance-volume_swap-end.json | 3 +- .../instance-volume_swap-error.json | 3 +- .../instance-volume_swap-start.json | 3 +- doc/notification_samples/service-update.json | 3 +- nova/api/openstack/compute/flavor_access.py | 7 ++- nova/notifications/objects/aggregate.py | 7 ++- nova/notifications/objects/base.py | 25 +++++++- nova/notifications/objects/flavor.py | 14 ++++- nova/tests/unit/compute/test_compute.py | 3 +- .../objects/test_notification.py | 58 +++++++++++++++++-- nova/tests/unit/objects/test_flavor.py | 20 +++++-- 52 files changed, 204 insertions(+), 68 deletions(-) diff --git a/doc/notification_samples/aggregate-create-end.json b/doc/notification_samples/aggregate-create-end.json index ded886cab86d..ab48994fa41d 100644 --- a/doc/notification_samples/aggregate-create-end.json +++ b/doc/notification_samples/aggregate-create-end.json @@ -1,7 +1,7 @@ { "priority": "INFO", "payload": { - "nova_object.version": "1.0", + "nova_object.version": "1.1", "nova_object.namespace": "nova", "nova_object.name": "AggregatePayload", "nova_object.data": { diff --git a/doc/notification_samples/aggregate-create-start.json b/doc/notification_samples/aggregate-create-start.json index 9fc18d260f88..775feea7d558 100644 --- a/doc/notification_samples/aggregate-create-start.json +++ b/doc/notification_samples/aggregate-create-start.json @@ -1,7 +1,7 @@ { "priority": "INFO", "payload": { - "nova_object.version": "1.0", + "nova_object.version": "1.1", "nova_object.namespace": "nova", "nova_object.name": "AggregatePayload", "nova_object.data": { @@ -9,6 +9,8 @@ "metadata": { "availability_zone": "nova" }, + "hosts": null, + "id": null, "uuid": "788608ec-ebdc-45c5-bc7f-e5f24ab92c80" } }, diff --git a/doc/notification_samples/aggregate-delete-end.json b/doc/notification_samples/aggregate-delete-end.json index 61dbaa1f7a11..058c3a9e5a6e 100644 --- a/doc/notification_samples/aggregate-delete-end.json +++ b/doc/notification_samples/aggregate-delete-end.json @@ -1,7 +1,7 @@ { "priority": "INFO", "payload": { - "nova_object.version": "1.0", + "nova_object.version": "1.1", "nova_object.namespace": "nova", "nova_object.name": "AggregatePayload", "nova_object.data": { diff --git a/doc/notification_samples/aggregate-delete-start.json b/doc/notification_samples/aggregate-delete-start.json index 8bd25fe43bc8..b4c75abbe032 100644 --- a/doc/notification_samples/aggregate-delete-start.json +++ b/doc/notification_samples/aggregate-delete-start.json @@ -1,7 +1,7 @@ { "priority": "INFO", "payload": { - "nova_object.version": "1.0", + "nova_object.version": "1.1", "nova_object.namespace": "nova", "nova_object.name": "AggregatePayload", "nova_object.data": { diff --git a/doc/notification_samples/flavor-create.json b/doc/notification_samples/flavor-create.json index 556a30503b73..4cca7a0e43aa 100644 --- a/doc/notification_samples/flavor-create.json +++ b/doc/notification_samples/flavor-create.json @@ -2,7 +2,7 @@ "priority": "INFO", "payload": { "nova_object.namespace": "nova", - "nova_object.version": "1.2", + "nova_object.version": "1.3", "nova_object.name": "FlavorPayload", "nova_object.data": { "name": "test_flavor", @@ -15,7 +15,9 @@ "is_public": true, "root_gb": 10, "vcpu_weight": 0, - "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3" + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "extra_specs": null, + "projects": [] } }, "event_type": "flavor.create", diff --git a/doc/notification_samples/flavor-delete.json b/doc/notification_samples/flavor-delete.json index f3b925408b14..5b7b3e7cafcd 100644 --- a/doc/notification_samples/flavor-delete.json +++ b/doc/notification_samples/flavor-delete.json @@ -2,7 +2,7 @@ "priority": "INFO", "payload": { "nova_object.namespace": "nova", - "nova_object.version": "1.2", + "nova_object.version": "1.3", "nova_object.name": "FlavorPayload", "nova_object.data": { "name": "test_flavor", @@ -15,7 +15,9 @@ "is_public": true, "root_gb": 10, "vcpu_weight": 0, - "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3" + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "extra_specs": null, + "projects": [] } }, "event_type": "flavor.delete", diff --git a/doc/notification_samples/flavor-update.json b/doc/notification_samples/flavor-update.json index 0cc4ddf71fb7..c76311a59971 100644 --- a/doc/notification_samples/flavor-update.json +++ b/doc/notification_samples/flavor-update.json @@ -2,7 +2,7 @@ "priority": "INFO", "payload": { "nova_object.namespace": "nova", - "nova_object.version": "1.2", + "nova_object.version": "1.3", "nova_object.name": "FlavorPayload", "nova_object.data": { "name": "test_flavor", diff --git a/doc/notification_samples/instance-create-end.json b/doc/notification_samples/instance-create-end.json index 9ea78d54cf15..a1ea50e678e0 100644 --- a/doc/notification_samples/instance-create-end.json +++ b/doc/notification_samples/instance-create-end.json @@ -54,11 +54,12 @@ "extra_specs": { "hw:watchdog_action": "disabled" }, + "projects": null, "swap": 0, "is_public": true, "vcpu_weight": 0 }, - "nova_object.version": "1.2", + "nova_object.version": "1.3", "nova_object.namespace": "nova" }, "user_id":"fake", diff --git a/doc/notification_samples/instance-create-error.json b/doc/notification_samples/instance-create-error.json index eafecee7c9a3..1d5e39261d68 100644 --- a/doc/notification_samples/instance-create-error.json +++ b/doc/notification_samples/instance-create-error.json @@ -51,11 +51,12 @@ "extra_specs": { "hw:watchdog_action": "disabled" }, + "projects": null, "swap": 0, "is_public": true, "vcpu_weight": 0 }, - "nova_object.version": "1.2", + "nova_object.version": "1.3", "nova_object.namespace": "nova" }, "user_id":"fake", diff --git a/doc/notification_samples/instance-create-start.json b/doc/notification_samples/instance-create-start.json index c72f7b87ab7b..56b7b608de23 100644 --- a/doc/notification_samples/instance-create-start.json +++ b/doc/notification_samples/instance-create-start.json @@ -41,11 +41,12 @@ "extra_specs": { "hw:watchdog_action": "disabled" }, + "projects": null, "swap": 0, "is_public": true, "vcpu_weight": 0 }, - "nova_object.version": "1.2", + "nova_object.version": "1.3", "nova_object.namespace": "nova" }, "user_id":"fake", diff --git a/doc/notification_samples/instance-delete-end.json b/doc/notification_samples/instance-delete-end.json index 1989584aa293..120fa5e2be34 100644 --- a/doc/notification_samples/instance-delete-end.json +++ b/doc/notification_samples/instance-delete-end.json @@ -41,11 +41,12 @@ "extra_specs": { "hw:watchdog_action": "disabled" }, + "projects": null, "swap": 0, "is_public": true, "vcpu_weight": 0 }, - "nova_object.version": "1.2", + "nova_object.version": "1.3", "nova_object.namespace": "nova" }, "user_id":"fake", diff --git a/doc/notification_samples/instance-delete-start.json b/doc/notification_samples/instance-delete-start.json index 2616b7a17218..ba2dee44dbaf 100644 --- a/doc/notification_samples/instance-delete-start.json +++ b/doc/notification_samples/instance-delete-start.json @@ -54,11 +54,12 @@ "extra_specs": { "hw:watchdog_action": "disabled" }, + "projects": null, "swap": 0, "is_public": true, "vcpu_weight": 0 }, - "nova_object.version": "1.2", + "nova_object.version": "1.3", "nova_object.namespace": "nova" }, "user_id":"fake", diff --git a/doc/notification_samples/instance-pause-end.json b/doc/notification_samples/instance-pause-end.json index 1e7f2146b35c..7a4b5692fd17 100644 --- a/doc/notification_samples/instance-pause-end.json +++ b/doc/notification_samples/instance-pause-end.json @@ -54,11 +54,12 @@ "extra_specs": { "hw:watchdog_action": "disabled" }, + "projects": null, "swap": 0, "is_public": true, "vcpu_weight": 0 }, - "nova_object.version": "1.2", + "nova_object.version": "1.3", "nova_object.namespace": "nova" }, "user_id":"fake", diff --git a/doc/notification_samples/instance-pause-start.json b/doc/notification_samples/instance-pause-start.json index 3bdd551caace..dfc3f0465ceb 100644 --- a/doc/notification_samples/instance-pause-start.json +++ b/doc/notification_samples/instance-pause-start.json @@ -54,11 +54,12 @@ "extra_specs": { "hw:watchdog_action": "disabled" }, + "projects": null, "swap": 0, "is_public": true, "vcpu_weight": 0 }, - "nova_object.version": "1.2", + "nova_object.version": "1.3", "nova_object.namespace": "nova" }, "user_id":"fake", diff --git a/doc/notification_samples/instance-power_off-end.json b/doc/notification_samples/instance-power_off-end.json index e0f99a26e67d..1b15f705777e 100644 --- a/doc/notification_samples/instance-power_off-end.json +++ b/doc/notification_samples/instance-power_off-end.json @@ -54,11 +54,12 @@ "extra_specs": { "hw:watchdog_action": "disabled" }, + "projects": null, "swap": 0, "is_public": true, "vcpu_weight": 0 }, - "nova_object.version": "1.2", + "nova_object.version": "1.3", "nova_object.namespace": "nova" }, "user_id":"fake", diff --git a/doc/notification_samples/instance-power_off-start.json b/doc/notification_samples/instance-power_off-start.json index 54de4f029e4a..28dff447be6b 100644 --- a/doc/notification_samples/instance-power_off-start.json +++ b/doc/notification_samples/instance-power_off-start.json @@ -54,11 +54,12 @@ "extra_specs": { "hw:watchdog_action": "disabled" }, + "projects": null, "swap": 0, "is_public": true, "vcpu_weight": 0 }, - "nova_object.version": "1.2", + "nova_object.version": "1.3", "nova_object.namespace": "nova" }, "user_id":"fake", diff --git a/doc/notification_samples/instance-power_on-end.json b/doc/notification_samples/instance-power_on-end.json index f08be50603c4..086ad7c6cc47 100644 --- a/doc/notification_samples/instance-power_on-end.json +++ b/doc/notification_samples/instance-power_on-end.json @@ -54,11 +54,12 @@ "extra_specs": { "hw:watchdog_action": "disabled" }, + "projects": null, "swap": 0, "is_public": true, "vcpu_weight": 0 }, - "nova_object.version": "1.2", + "nova_object.version": "1.3", "nova_object.namespace": "nova" }, "user_id":"fake", diff --git a/doc/notification_samples/instance-power_on-start.json b/doc/notification_samples/instance-power_on-start.json index 92587a4b835f..596c2c610592 100644 --- a/doc/notification_samples/instance-power_on-start.json +++ b/doc/notification_samples/instance-power_on-start.json @@ -54,11 +54,12 @@ "extra_specs": { "hw:watchdog_action": "disabled" }, + "projects": null, "swap": 0, "is_public": true, "vcpu_weight": 0 }, - "nova_object.version": "1.2", + "nova_object.version": "1.3", "nova_object.namespace": "nova" }, "user_id":"fake", diff --git a/doc/notification_samples/instance-resize-end.json b/doc/notification_samples/instance-resize-end.json index 860714f88bc2..b140408f7bd0 100644 --- a/doc/notification_samples/instance-resize-end.json +++ b/doc/notification_samples/instance-resize-end.json @@ -54,11 +54,12 @@ "extra_specs": { "hw:watchdog_action": "disabled" }, + "projects": null, "swap": 0, "is_public": true, "vcpu_weight": 0 }, - "nova_object.version": "1.2", + "nova_object.version": "1.3", "nova_object.namespace": "nova" }, "user_id":"fake", diff --git a/doc/notification_samples/instance-resize-start.json b/doc/notification_samples/instance-resize-start.json index 0971f336c681..f5f16a1dead9 100644 --- a/doc/notification_samples/instance-resize-start.json +++ b/doc/notification_samples/instance-resize-start.json @@ -54,11 +54,12 @@ "extra_specs": { "hw:watchdog_action": "disabled" }, + "projects": null, "swap": 0, "is_public": true, "vcpu_weight": 0 }, - "nova_object.version": "1.2", + "nova_object.version": "1.3", "nova_object.namespace": "nova" }, "user_id":"fake", diff --git a/doc/notification_samples/instance-resize_finish-end.json b/doc/notification_samples/instance-resize_finish-end.json index 65cc35ec87cd..780f4f3ae2fb 100644 --- a/doc/notification_samples/instance-resize_finish-end.json +++ b/doc/notification_samples/instance-resize_finish-end.json @@ -54,11 +54,12 @@ "extra_specs": { "hw:watchdog_action": "reset" }, + "projects": null, "swap": 0, "is_public": true, "vcpu_weight": 0 }, - "nova_object.version": "1.2", + "nova_object.version": "1.3", "nova_object.namespace": "nova" }, "user_id":"fake", diff --git a/doc/notification_samples/instance-resize_finish-start.json b/doc/notification_samples/instance-resize_finish-start.json index 6e6bed20f82d..5dcbf95dc79c 100644 --- a/doc/notification_samples/instance-resize_finish-start.json +++ b/doc/notification_samples/instance-resize_finish-start.json @@ -54,11 +54,12 @@ "extra_specs": { "hw:watchdog_action": "reset" }, + "projects": null, "swap": 0, "is_public": true, "vcpu_weight": 0 }, - "nova_object.version": "1.2", + "nova_object.version": "1.3", "nova_object.namespace": "nova" }, "user_id":"fake", diff --git a/doc/notification_samples/instance-restore-end.json b/doc/notification_samples/instance-restore-end.json index e6d3fbc29535..3ecb98bf9513 100644 --- a/doc/notification_samples/instance-restore-end.json +++ b/doc/notification_samples/instance-restore-end.json @@ -54,11 +54,12 @@ "extra_specs": { "hw:watchdog_action": "disabled" }, + "projects": null, "swap": 0, "is_public": true, "vcpu_weight": 0 }, - "nova_object.version": "1.2", + "nova_object.version": "1.3", "nova_object.namespace": "nova" }, "user_id":"fake", diff --git a/doc/notification_samples/instance-restore-start.json b/doc/notification_samples/instance-restore-start.json index 5393681c6e71..a8cd5171ae9c 100644 --- a/doc/notification_samples/instance-restore-start.json +++ b/doc/notification_samples/instance-restore-start.json @@ -54,11 +54,12 @@ "extra_specs": { "hw:watchdog_action": "disabled" }, + "projects": null, "swap": 0, "is_public": true, "vcpu_weight": 0 }, - "nova_object.version": "1.2", + "nova_object.version": "1.3", "nova_object.namespace": "nova" }, "user_id":"fake", diff --git a/doc/notification_samples/instance-resume-end.json b/doc/notification_samples/instance-resume-end.json index 2bf5d10ae67e..3e646a7fc2e4 100644 --- a/doc/notification_samples/instance-resume-end.json +++ b/doc/notification_samples/instance-resume-end.json @@ -54,11 +54,12 @@ "extra_specs": { "hw:watchdog_action": "disabled" }, + "projects": null, "swap": 0, "is_public": true, "vcpu_weight": 0 }, - "nova_object.version": "1.2", + "nova_object.version": "1.3", "nova_object.namespace": "nova" }, "user_id":"fake", diff --git a/doc/notification_samples/instance-resume-start.json b/doc/notification_samples/instance-resume-start.json index ee83ae5da14c..b96e0c63cf19 100644 --- a/doc/notification_samples/instance-resume-start.json +++ b/doc/notification_samples/instance-resume-start.json @@ -54,11 +54,12 @@ "extra_specs": { "hw:watchdog_action": "disabled" }, + "projects": null, "swap": 0, "is_public": true, "vcpu_weight": 0 }, - "nova_object.version": "1.2", + "nova_object.version": "1.3", "nova_object.namespace": "nova" }, "user_id":"fake", diff --git a/doc/notification_samples/instance-shelve-end.json b/doc/notification_samples/instance-shelve-end.json index d713c62c3d0f..337cddf11f05 100644 --- a/doc/notification_samples/instance-shelve-end.json +++ b/doc/notification_samples/instance-shelve-end.json @@ -54,11 +54,12 @@ "extra_specs": { "hw:watchdog_action": "disabled" }, + "projects": null, "swap": 0, "is_public": true, "vcpu_weight": 0 }, - "nova_object.version": "1.2", + "nova_object.version": "1.3", "nova_object.namespace": "nova" }, "user_id":"fake", diff --git a/doc/notification_samples/instance-shelve-start.json b/doc/notification_samples/instance-shelve-start.json index 34ee9df3af92..1637a816d07e 100644 --- a/doc/notification_samples/instance-shelve-start.json +++ b/doc/notification_samples/instance-shelve-start.json @@ -54,11 +54,12 @@ "extra_specs": { "hw:watchdog_action": "disabled" }, + "projects": null, "swap": 0, "is_public": true, "vcpu_weight": 0 }, - "nova_object.version": "1.2", + "nova_object.version": "1.3", "nova_object.namespace": "nova" }, "user_id":"fake", diff --git a/doc/notification_samples/instance-shelve_offload-end.json b/doc/notification_samples/instance-shelve_offload-end.json index 029eca9f6e79..cc78220f2e1e 100644 --- a/doc/notification_samples/instance-shelve_offload-end.json +++ b/doc/notification_samples/instance-shelve_offload-end.json @@ -54,11 +54,12 @@ "extra_specs": { "hw:watchdog_action": "disabled" }, + "projects": null, "swap": 0, "is_public": true, "vcpu_weight": 0 }, - "nova_object.version": "1.2", + "nova_object.version": "1.3", "nova_object.namespace": "nova" }, "user_id":"fake", diff --git a/doc/notification_samples/instance-shelve_offload-start.json b/doc/notification_samples/instance-shelve_offload-start.json index a60686d14ec3..b3a492c3b017 100644 --- a/doc/notification_samples/instance-shelve_offload-start.json +++ b/doc/notification_samples/instance-shelve_offload-start.json @@ -54,11 +54,12 @@ "extra_specs": { "hw:watchdog_action": "disabled" }, + "projects": null, "swap": 0, "is_public": true, "vcpu_weight": 0 }, - "nova_object.version": "1.2", + "nova_object.version": "1.3", "nova_object.namespace": "nova" }, "user_id":"fake", diff --git a/doc/notification_samples/instance-shutdown-end.json b/doc/notification_samples/instance-shutdown-end.json index 2083b66c2f68..c9e9d0950d98 100644 --- a/doc/notification_samples/instance-shutdown-end.json +++ b/doc/notification_samples/instance-shutdown-end.json @@ -54,11 +54,12 @@ "extra_specs": { "hw:watchdog_action": "disabled" }, + "projects": null, "swap": 0, "is_public": true, "vcpu_weight": 0 }, - "nova_object.version": "1.2", + "nova_object.version": "1.3", "nova_object.namespace": "nova" }, "user_id":"fake", diff --git a/doc/notification_samples/instance-shutdown-start.json b/doc/notification_samples/instance-shutdown-start.json index 09afea752ba0..afbbb01507b8 100644 --- a/doc/notification_samples/instance-shutdown-start.json +++ b/doc/notification_samples/instance-shutdown-start.json @@ -54,11 +54,12 @@ "extra_specs": { "hw:watchdog_action": "disabled" }, + "projects": null, "swap": 0, "is_public": true, "vcpu_weight": 0 }, - "nova_object.version": "1.2", + "nova_object.version": "1.3", "nova_object.namespace": "nova" }, "user_id":"fake", diff --git a/doc/notification_samples/instance-snapshot-end.json b/doc/notification_samples/instance-snapshot-end.json index 623de2c7922c..b3e5fabead93 100644 --- a/doc/notification_samples/instance-snapshot-end.json +++ b/doc/notification_samples/instance-snapshot-end.json @@ -54,11 +54,12 @@ "extra_specs": { "hw:watchdog_action": "disabled" }, + "projects": null, "swap": 0, "is_public": true, "vcpu_weight": 0 }, - "nova_object.version": "1.2", + "nova_object.version": "1.3", "nova_object.namespace": "nova" }, "user_id":"fake", diff --git a/doc/notification_samples/instance-snapshot-start.json b/doc/notification_samples/instance-snapshot-start.json index 1c579b857602..712e494f876d 100644 --- a/doc/notification_samples/instance-snapshot-start.json +++ b/doc/notification_samples/instance-snapshot-start.json @@ -54,11 +54,12 @@ "extra_specs": { "hw:watchdog_action": "disabled" }, + "projects": null, "swap": 0, "is_public": true, "vcpu_weight": 0 }, - "nova_object.version": "1.2", + "nova_object.version": "1.3", "nova_object.namespace": "nova" }, "user_id":"fake", diff --git a/doc/notification_samples/instance-suspend-end.json b/doc/notification_samples/instance-suspend-end.json index f63bc83b205f..a7351ea2d1a9 100644 --- a/doc/notification_samples/instance-suspend-end.json +++ b/doc/notification_samples/instance-suspend-end.json @@ -54,11 +54,12 @@ "extra_specs": { "hw:watchdog_action": "disabled" }, + "projects": null, "swap": 0, "is_public": true, "vcpu_weight": 0 }, - "nova_object.version": "1.2", + "nova_object.version": "1.3", "nova_object.namespace": "nova" }, "user_id":"fake", diff --git a/doc/notification_samples/instance-suspend-start.json b/doc/notification_samples/instance-suspend-start.json index df1b38f6f7f9..91a97ea61865 100644 --- a/doc/notification_samples/instance-suspend-start.json +++ b/doc/notification_samples/instance-suspend-start.json @@ -54,11 +54,12 @@ "extra_specs": { "hw:watchdog_action": "disabled" }, + "projects": null, "swap": 0, "is_public": true, "vcpu_weight": 0 }, - "nova_object.version": "1.2", + "nova_object.version": "1.3", "nova_object.namespace": "nova" }, "user_id":"fake", diff --git a/doc/notification_samples/instance-unpause-end.json b/doc/notification_samples/instance-unpause-end.json index 646b152ae6bc..15b2e070f5cf 100644 --- a/doc/notification_samples/instance-unpause-end.json +++ b/doc/notification_samples/instance-unpause-end.json @@ -54,11 +54,12 @@ "extra_specs": { "hw:watchdog_action": "disabled" }, + "projects": null, "swap": 0, "is_public": true, "vcpu_weight": 0 }, - "nova_object.version": "1.2", + "nova_object.version": "1.3", "nova_object.namespace": "nova" }, "user_id":"fake", diff --git a/doc/notification_samples/instance-unpause-start.json b/doc/notification_samples/instance-unpause-start.json index 23b273ca1d9b..fd31f02d9058 100644 --- a/doc/notification_samples/instance-unpause-start.json +++ b/doc/notification_samples/instance-unpause-start.json @@ -54,11 +54,12 @@ "extra_specs": { "hw:watchdog_action": "disabled" }, + "projects": null, "swap": 0, "is_public": true, "vcpu_weight": 0 }, - "nova_object.version": "1.2", + "nova_object.version": "1.3", "nova_object.namespace": "nova" }, "user_id":"fake", diff --git a/doc/notification_samples/instance-unshelve-end.json b/doc/notification_samples/instance-unshelve-end.json index 9de657c1dca6..6457d0f2bb34 100644 --- a/doc/notification_samples/instance-unshelve-end.json +++ b/doc/notification_samples/instance-unshelve-end.json @@ -54,11 +54,12 @@ "extra_specs": { "hw:watchdog_action": "disabled" }, + "projects": null, "swap": 0, "is_public": true, "vcpu_weight": 0 }, - "nova_object.version": "1.2", + "nova_object.version": "1.3", "nova_object.namespace": "nova" }, "user_id":"fake", diff --git a/doc/notification_samples/instance-unshelve-start.json b/doc/notification_samples/instance-unshelve-start.json index 334f3969b6b6..91d04a630ba8 100644 --- a/doc/notification_samples/instance-unshelve-start.json +++ b/doc/notification_samples/instance-unshelve-start.json @@ -54,11 +54,12 @@ "extra_specs": { "hw:watchdog_action": "disabled" }, + "projects": null, "swap": 0, "is_public": true, "vcpu_weight": 0 }, - "nova_object.version": "1.2", + "nova_object.version": "1.3", "nova_object.namespace": "nova" }, "user_id":"fake", diff --git a/doc/notification_samples/instance-update.json b/doc/notification_samples/instance-update.json index 88381628908f..0aa5fd4e00ca 100644 --- a/doc/notification_samples/instance-update.json +++ b/doc/notification_samples/instance-update.json @@ -59,11 +59,12 @@ "extra_specs": { "hw:watchdog_action": "disabled" }, + "projects": null, "swap": 0, "is_public": true, "vcpu_weight": 0 }, - "nova_object.version": "1.2", + "nova_object.version": "1.3", "nova_object.namespace": "nova" }, "user_id": "fake", diff --git a/doc/notification_samples/instance-volume_swap-end.json b/doc/notification_samples/instance-volume_swap-end.json index af22bc3a09c2..ca8ee9f6f340 100644 --- a/doc/notification_samples/instance-volume_swap-end.json +++ b/doc/notification_samples/instance-volume_swap-end.json @@ -22,13 +22,14 @@ "extra_specs": { "hw:watchdog_action": "disabled" }, + "projects": null, "swap": 0, "is_public": true, "vcpu_weight": 0 }, "nova_object.name": "FlavorPayload", "nova_object.namespace": "nova", - "nova_object.version": "1.2" + "nova_object.version": "1.3" }, "host": "compute", "host_name": "some-server", diff --git a/doc/notification_samples/instance-volume_swap-error.json b/doc/notification_samples/instance-volume_swap-error.json index e55d839ed561..16c602f4be11 100644 --- a/doc/notification_samples/instance-volume_swap-error.json +++ b/doc/notification_samples/instance-volume_swap-error.json @@ -32,13 +32,14 @@ "extra_specs": { "hw:watchdog_action": "disabled" }, + "projects": null, "swap": 0, "is_public": true, "vcpu_weight": 0 }, "nova_object.name": "FlavorPayload", "nova_object.namespace": "nova", - "nova_object.version": "1.2" + "nova_object.version": "1.3" }, "host": "compute", "host_name": "some-server", diff --git a/doc/notification_samples/instance-volume_swap-start.json b/doc/notification_samples/instance-volume_swap-start.json index 0590186044f8..01610f6201e7 100644 --- a/doc/notification_samples/instance-volume_swap-start.json +++ b/doc/notification_samples/instance-volume_swap-start.json @@ -22,13 +22,14 @@ "extra_specs": { "hw:watchdog_action": "disabled" }, + "projects": null, "swap": 0, "is_public": true, "vcpu_weight": 0 }, "nova_object.name": "FlavorPayload", "nova_object.namespace": "nova", - "nova_object.version": "1.2" + "nova_object.version": "1.3" }, "host": "compute", "host_name": "some-server", diff --git a/doc/notification_samples/service-update.json b/doc/notification_samples/service-update.json index 1eacf1324416..f37612d2f07a 100644 --- a/doc/notification_samples/service-update.json +++ b/doc/notification_samples/service-update.json @@ -13,7 +13,8 @@ "disabled_reason": null, "report_count": 1, "forced_down": false, - "version": 17 + "version": 17, + "availability_zone": null } }, "event_type": "service.update", diff --git a/nova/api/openstack/compute/flavor_access.py b/nova/api/openstack/compute/flavor_access.py index aa8c0f053aac..6f20fc742300 100644 --- a/nova/api/openstack/compute/flavor_access.py +++ b/nova/api/openstack/compute/flavor_access.py @@ -25,7 +25,6 @@ from nova.api.openstack import wsgi from nova.api import validation from nova import exception from nova.i18n import _ -from nova import objects from nova.policies import flavor_access as fa_policies ALIAS = 'os-flavor-access' @@ -124,7 +123,11 @@ class FlavorActionController(wsgi.Controller): vals = body['removeTenantAccess'] tenant = vals['tenant'] - flavor = objects.Flavor(context=context, flavorid=id) + # NOTE(gibi): We have to load a flavor from the db here as + # flavor.remove_access() will try to emit a notification and that needs + # a fully loaded flavor. + flavor = common.get_flavor(context, id) + try: flavor.remove_access(tenant) except (exception.FlavorAccessNotFound, diff --git a/nova/notifications/objects/aggregate.py b/nova/notifications/objects/aggregate.py index 67768982708a..88e617db553c 100644 --- a/nova/notifications/objects/aggregate.py +++ b/nova/notifications/objects/aggregate.py @@ -25,9 +25,12 @@ class AggregatePayload(base.NotificationPayloadBase): 'metadata': ('aggregate', 'metadata'), } # Version 1.0: Initial version - VERSION = '1.0' + # 1.1: Making the id field nullable + VERSION = '1.1' fields = { - 'id': fields.IntegerField(), + # NOTE(gibi): id is nullable as aggregate.create.start is sent before + # the id is generated by the db + 'id': fields.IntegerField(nullable=True), 'uuid': fields.UUIDField(nullable=False), 'name': fields.StringField(), 'hosts': fields.ListOfStringsField(nullable=True), diff --git a/nova/notifications/objects/base.py b/nova/notifications/objects/base.py index db40863d9c41..46669a081271 100644 --- a/nova/notifications/objects/base.py +++ b/nova/notifications/objects/base.py @@ -11,11 +11,16 @@ # 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 oslo_log import log as logging +from oslo_versionedobjects import exception as ovo_exception +from nova import exception from nova.objects import base from nova.objects import fields from nova import rpc +LOG = logging.getLogger(__name__) + @base.NovaObjectRegistry.register_if(False) class NotificationObject(base.NovaObject): @@ -96,8 +101,26 @@ class NotificationPayloadBase(NotificationObject): """ for key, (obj, field) in self.SCHEMA.items(): source = kwargs[obj] - if source.obj_attr_is_set(field): + # trigger lazy-load if possible + try: setattr(self, key, getattr(source, field)) + # ObjectActionError - not lazy loadable field + # NotImplementedError - obj_load_attr() is not even defined + # OrphanedObjectError - lazy loadable field but context is None + except (exception.ObjectActionError, + NotImplementedError, + exception.OrphanedObjectError, + ovo_exception.OrphanedObjectError) as e: + LOG.debug(("Defaulting the value of the field '%(field)s' " + "to None in %(payload)s due to '%(exception)s'"), + {'field': key, + 'payload': self.__class__.__name__, + 'exception': e}) + # NOTE(gibi): This will fail if the payload field is not + # nullable, but that means that either the source object is not + # properly initialized or the payload field needs to be defined + # as nullable + setattr(self, key, None) self.populated = True # the schema population will create changed fields but we don't need diff --git a/nova/notifications/objects/flavor.py b/nova/notifications/objects/flavor.py index ed004f5e1d05..a659d1e64f13 100644 --- a/nova/notifications/objects/flavor.py +++ b/nova/notifications/objects/flavor.py @@ -35,7 +35,9 @@ class FlavorPayload(base.NotificationPayloadBase): # Version 1.0: Initial version # Version 1.1: Add other fields for Flavor # Version 1.2: Add extra_specs and projects fields - VERSION = '1.2' + # Version 1.3: Make projects and extra_specs field nullable as they are + # not always available when a notification is emitted. + VERSION = '1.3' # NOTE: if we'd want to rename some fields(memory_mb->ram, root_gb->disk, # ephemeral_gb: ephemeral), bumping to payload version 2.0 will be needed. @@ -67,8 +69,8 @@ class FlavorPayload(base.NotificationPayloadBase): 'vcpu_weight': fields.IntegerField(nullable=True), 'disabled': fields.BooleanField(), 'is_public': fields.BooleanField(), - 'extra_specs': fields.DictOfStringsField(), - 'projects': fields.ListOfStringsField(), + 'extra_specs': fields.DictOfStringsField(nullable=True), + 'projects': fields.ListOfStringsField(nullable=True), } def __init__(self, flavor, **kwargs): @@ -89,3 +91,9 @@ class FlavorPayload(base.NotificationPayloadBase): if target_version < (1, 2): primitive.pop('extra_specs', None) primitive.pop('projects', None) + if target_version < (1, 3): + if 'projects' not in primitive or primitive['projects'] is None: + primitive['projects'] = [] + if ('extra_specs' not in primitive or + primitive['extra_specs'] is None): + primitive['extra_specs'] = {} diff --git a/nova/tests/unit/compute/test_compute.py b/nova/tests/unit/compute/test_compute.py index 6688f427da57..37c0ec36b594 100644 --- a/nova/tests/unit/compute/test_compute.py +++ b/nova/tests/unit/compute/test_compute.py @@ -11169,7 +11169,8 @@ class ComputeAPIAggrCallsSchedulerTestCase(test.NoDBTestCase): @mock.patch.object(scheduler_client.SchedulerClient, 'delete_aggregate') def test_delete_aggregate(self, delete_aggregate): self.api.is_safe_to_update_az = mock.Mock() - agg = objects.Aggregate(hosts=[]) + agg = objects.Aggregate(uuid=uuids.agg, name='fake-aggregate', + hosts=[]) agg.destroy = mock.Mock() with mock.patch.object(objects.Aggregate, 'get_by_id', return_value=agg): diff --git a/nova/tests/unit/notifications/objects/test_notification.py b/nova/tests/unit/notifications/objects/test_notification.py index 9d43419c9ee8..38f574ce7227 100644 --- a/nova/tests/unit/notifications/objects/test_notification.py +++ b/nova/tests/unit/notifications/objects/test_notification.py @@ -17,6 +17,7 @@ import mock from oslo_utils import timeutils from oslo_versionedobjects import fixture +from nova import exception from nova.network import model as network_model from nova.notifications import base as notification_base from nova.notifications.objects import base as notification @@ -25,6 +26,7 @@ from nova.objects import base from nova.objects import fields from nova import test from nova.tests.unit.objects import test_objects +from nova.tests import uuidsentinel as uuids class TestNotificationBase(test.NoDBTestCase): @@ -36,8 +38,17 @@ class TestNotificationBase(test.NoDBTestCase): 'field_1': fields.StringField(), 'field_2': fields.IntegerField(), 'not_important_field': fields.IntegerField(), + 'lazy_field': fields.IntegerField() } + def obj_load_attr(self, attrname): + if attrname == 'lazy_field': + self.lazy_field = 42 + else: + raise exception.ObjectActionError( + action='obj_load_attr', + reason='attribute %s not lazy-loadable' % attrname) + @base.NovaObjectRegistry.register_if(False) class TestNotificationPayload(notification.NotificationPayloadBase): VERSION = '1.0' @@ -45,12 +56,15 @@ class TestNotificationBase(test.NoDBTestCase): SCHEMA = { 'field_1': ('source_field', 'field_1'), 'field_2': ('source_field', 'field_2'), + 'lazy_field': ('source_field', 'lazy_field') } fields = { 'extra_field': fields.StringField(), # filled by ctor - 'field_1': fields.StringField(), # filled by the schema + # filled by the schema + 'field_1': fields.StringField(nullable=True), 'field_2': fields.IntegerField(), # filled by the schema + 'lazy_field': fields.IntegerField() # filled by the schema } def populate_schema(self, source_field): @@ -103,7 +117,8 @@ class TestNotificationBase(test.NoDBTestCase): 'nova_object.data': { 'extra_field': 'test string', 'field_1': 'test1', - 'field_2': 42}, + 'field_2': 42, + 'lazy_field': 42}, 'nova_object.version': '1.0', 'nova_object.namespace': 'nova'} @@ -223,6 +238,32 @@ class TestNotificationBase(test.NoDBTestCase): self.assertRaises(AssertionError, noti.emit, mock_context) self.assertFalse(mock_notifier.called) + def test_lazy_load_source_field(self): + my_obj = self.TestObject(field_1='test1', + field_2=42, + not_important_field=13) + payload = self.TestNotificationPayload(extra_field='test string') + + payload.populate_schema(my_obj) + + self.assertEqual(42, payload.lazy_field) + + def test_uninited_source_field_defaulted_to_none(self): + my_obj = self.TestObject(field_2=42, + not_important_field=13) + payload = self.TestNotificationPayload(extra_field='test string') + + payload.populate_schema(my_obj) + + self.assertIsNone(payload.field_1) + + def test_uninited_source_field_not_nullable_payload_field_fails(self): + my_obj = self.TestObject(field_1='test1', + not_important_field=13) + payload = self.TestNotificationPayload(extra_field='test string') + + self.assertRaises(ValueError, payload.populate_schema, my_obj) + @mock.patch('nova.rpc.NOTIFIER') def test_empty_schema(self, mock_notifier): non_populated_payload = self.TestNotificationPayloadEmptySchema( @@ -258,14 +299,14 @@ class TestNotificationBase(test.NoDBTestCase): notification_object_data = { 'AggregateNotification': '1.0-a73147b93b520ff0061865849d3dfa56', - 'AggregatePayload': '1.0-2550af604410af7b4ad5d46fb29ba45b', + 'AggregatePayload': '1.1-1eb9adcc4440d8627de6ec37c6398746', 'AuditPeriodPayload': '1.0-2b429dd307b8374636703b843fa3f9cb', 'BandwidthPayload': '1.0-ee2616a7690ab78406842a2b68e34130', 'EventType': '1.5-ffa6d332f4462c45a2a363356a14165f', 'ExceptionNotification': '1.0-a73147b93b520ff0061865849d3dfa56', 'ExceptionPayload': '1.0-27db46ee34cd97e39f2643ed92ad0cc5', 'FlavorNotification': '1.0-a73147b93b520ff0061865849d3dfa56', - 'FlavorPayload': '1.2-6f73fa3dc2d5f7abff1a27aac70b8759', + 'FlavorPayload': '1.3-6335e626893d7df5f96f87e6731fef56', 'InstanceActionNotification': '1.0-a73147b93b520ff0061865849d3dfa56', 'InstanceActionPayload': '1.1-8d12efc3251c606b61b3d479a9da51be', 'InstanceActionVolumeSwapNotification': @@ -345,15 +386,20 @@ class TestInstanceNotification(test.NoDBTestCase): def test_send_version_instance_update_uses_flavor(self, mock_emit): # Make sure that the notification payload chooses the values in # instance.flavor.$value instead of instance.$value - test_keys = ['memory_mb', 'vcpus', 'root_gb', 'ephemeral_gb'] + test_keys = ['memory_mb', 'vcpus', 'root_gb', 'ephemeral_gb', 'swap'] flavor_values = {k: 123 for k in test_keys} instance_values = {k: 456 for k in test_keys} - flavor = objects.Flavor(**flavor_values) + flavor = objects.Flavor(flavorid='test-flavor', name='test-flavor', + disabled=False, projects=[], is_public=True, + extra_specs = {}, **flavor_values) info_cache = objects.InstanceInfoCache( network_info=network_model.NetworkInfo()) instance = objects.Instance( flavor=flavor, info_cache=info_cache, + metadata={}, + uuid=uuids.instance1, + locked=False, **instance_values) payload = { 'bandwidth': {}, diff --git a/nova/tests/unit/objects/test_flavor.py b/nova/tests/unit/objects/test_flavor.py index 929cf3718afb..4eb51b156ef9 100644 --- a/nova/tests/unit/objects/test_flavor.py +++ b/nova/tests/unit/objects/test_flavor.py @@ -156,17 +156,19 @@ class _TestFlavor(object): db_flavor['flavorid']) self._compare(self, db_flavor, flavor) - def test_add_access(self): + @mock.patch('nova.objects.Flavor._send_notification') + def test_add_access(self, mock_notify): elevated = self.context.elevated() flavor = flavor_obj.Flavor(context=elevated, id=12345, flavorid='123') with mock.patch.object(db, 'flavor_access_add') as add: flavor.add_access('456') add.assert_called_once_with(elevated, '123', '456') + @mock.patch('nova.objects.Flavor._send_notification') @mock.patch('nova.db.flavor_access_add') @mock.patch('nova.objects.Flavor._flavor_add_project') @mock.patch('nova.objects.Flavor.in_api', new=True) - def test_add_access_api(self, mock_api_add, mock_main_add): + def test_add_access_api(self, mock_api_add, mock_main_add, mock_notify): elevated = self.context.elevated() flavor = flavor_obj.Flavor(context=elevated, id=12345, flavorid='123') flavor.add_access('456') @@ -178,17 +180,19 @@ class _TestFlavor(object): self.assertRaises(exception.ObjectActionError, flavor.add_access, '2') - def test_remove_access(self): + @mock.patch('nova.objects.Flavor._send_notification') + def test_remove_access(self, mock_notify): elevated = self.context.elevated() flavor = flavor_obj.Flavor(context=elevated, id=12345, flavorid='123') with mock.patch.object(db, 'flavor_access_remove') as remove: flavor.remove_access('456') remove.assert_called_once_with(elevated, '123', '456') + @mock.patch('nova.objects.Flavor._send_notification') @mock.patch('nova.db.flavor_access_add') @mock.patch('nova.objects.Flavor._flavor_del_project') @mock.patch('nova.objects.Flavor.in_api', new=True) - def test_remove_access_api(self, mock_api_del, mock_main_del): + def test_remove_access_api(self, mock_api_del, mock_main_del, mock_notify): elevated = self.context.elevated() flavor = flavor_obj.Flavor(context=elevated, id=12345, flavorid='123') flavor.remove_access('456') @@ -246,11 +250,13 @@ class _TestFlavor(object): flavor = objects.Flavor(self.context, **fields) self.assertRaises(exception.FlavorExists, flavor.create) + @mock.patch('nova.objects.Flavor._send_notification') @mock.patch('nova.db.flavor_access_add') @mock.patch('nova.db.flavor_access_remove') @mock.patch('nova.db.flavor_extra_specs_delete') @mock.patch('nova.db.flavor_extra_specs_update_or_create') - def test_save(self, mock_update, mock_delete, mock_remove, mock_add): + def test_save(self, mock_update, mock_delete, mock_remove, mock_add, + mock_notify): ctxt = self.context.elevated() extra_specs = {'key1': 'value1', 'key2': 'value2'} projects = ['project-1', 'project-2'] @@ -289,12 +295,14 @@ class _TestFlavor(object): self.assertEqual(['project-1', 'project-3'], flavor.projects) mock_add.assert_called_once_with(ctxt, 'foo', 'project-3') + @mock.patch('nova.objects.Flavor._send_notification') @mock.patch('nova.objects.Flavor._flavor_add_project') @mock.patch('nova.objects.Flavor._flavor_del_project') @mock.patch('nova.objects.Flavor._flavor_extra_specs_del') @mock.patch('nova.objects.Flavor._flavor_extra_specs_add') @mock.patch('nova.objects.Flavor.in_api', new=True) - def test_save_api(self, mock_update, mock_delete, mock_remove, mock_add): + def test_save_api(self, mock_update, mock_delete, mock_remove, mock_add, + mock_notify): extra_specs = {'key1': 'value1', 'key2': 'value2'} projects = ['project-1', 'project-2'] flavor = flavor_obj.Flavor(context=self.context, flavorid='foo',