From 8ee3e30bd19571bc1a9e8a5e9d776fbb1435266d Mon Sep 17 00:00:00 2001 From: Balazs Gibizer Date: Mon, 7 Nov 2016 16:21:49 +0100 Subject: [PATCH] Transform aggregate.create notification The aggregate.create.start and aggregate.create.end notifications has been transformed to the versioned notification framework. The notification payload contains the 'id' of the aggregate which is the db primary key. It is added because the REST API also uses the db id instead of the uuid in the os-aggregates' requests and responses. Implements: bp versioned-notification-transformation-ocata Change-Id: I92fe504a8f7dc19b0e1df5884045d4bc0d9e4f98 --- .../aggregate-create-end.json | 19 +++++++ .../aggregate-create-start.json | 17 +++++++ nova/compute/utils.py | 15 ++++++ nova/notifications/objects/aggregate.py | 51 +++++++++++++++++++ nova/objects/aggregate.py | 12 +++++ nova/tests/functional/api/client.py | 3 ++ .../test_aggregate.py | 40 +++++++++++++++ .../test_exception_notification.py | 6 ++- nova/tests/unit/compute/test_compute.py | 10 +++- .../objects/test_notification.py | 2 + 10 files changed, 172 insertions(+), 3 deletions(-) create mode 100644 doc/notification_samples/aggregate-create-end.json create mode 100644 doc/notification_samples/aggregate-create-start.json create mode 100644 nova/notifications/objects/aggregate.py create mode 100644 nova/tests/functional/notification_sample_tests/test_aggregate.py diff --git a/doc/notification_samples/aggregate-create-end.json b/doc/notification_samples/aggregate-create-end.json new file mode 100644 index 0000000000..ded886cab8 --- /dev/null +++ b/doc/notification_samples/aggregate-create-end.json @@ -0,0 +1,19 @@ +{ + "priority": "INFO", + "payload": { + "nova_object.version": "1.0", + "nova_object.namespace": "nova", + "nova_object.name": "AggregatePayload", + "nova_object.data": { + "name": "my-aggregate", + "metadata": { + "availability_zone": "nova" + }, + "hosts": [], + "id": 1, + "uuid": "788608ec-ebdc-45c5-bc7f-e5f24ab92c80" + } + }, + "event_type": "aggregate.create.end", + "publisher_id": "nova-api:fake-mini" +} diff --git a/doc/notification_samples/aggregate-create-start.json b/doc/notification_samples/aggregate-create-start.json new file mode 100644 index 0000000000..9fc18d260f --- /dev/null +++ b/doc/notification_samples/aggregate-create-start.json @@ -0,0 +1,17 @@ +{ + "priority": "INFO", + "payload": { + "nova_object.version": "1.0", + "nova_object.namespace": "nova", + "nova_object.name": "AggregatePayload", + "nova_object.data": { + "name": "my-aggregate", + "metadata": { + "availability_zone": "nova" + }, + "uuid": "788608ec-ebdc-45c5-bc7f-e5f24ab92c80" + } + }, + "event_type": "aggregate.create.start", + "publisher_id": "nova-api:fake-mini" +} diff --git a/nova/compute/utils.py b/nova/compute/utils.py index 76744f0716..595a0a4ec2 100644 --- a/nova/compute/utils.py +++ b/nova/compute/utils.py @@ -32,6 +32,7 @@ from nova import exception from nova.i18n import _LW from nova.network import model as network_model from nova import notifications +from nova.notifications.objects import aggregate as aggregate_notification from nova.notifications.objects import base as notification_base from nova.notifications.objects import exception as notification_exception from nova.notifications.objects import instance as instance_notification @@ -451,6 +452,20 @@ def notify_about_aggregate_update(context, event_suffix, aggregate_payload): notifier.info(context, 'aggregate.%s' % event_suffix, aggregate_payload) +def notify_about_aggregate_action(context, aggregate, action, phase): + payload = aggregate_notification.AggregatePayload(aggregate) + notification = aggregate_notification.AggregateNotification( + priority=fields.NotificationPriority.INFO, + publisher=notification_base.NotificationPublisher( + context=context, host=CONF.host, binary='nova-api'), + event_type=notification_base.EventType( + object='aggregate', + action=action, + phase=phase), + payload=payload) + notification.emit(context) + + def notify_about_host_update(context, event_suffix, host_payload): """Send a notification about host update. diff --git a/nova/notifications/objects/aggregate.py b/nova/notifications/objects/aggregate.py new file mode 100644 index 0000000000..5d1160acc3 --- /dev/null +++ b/nova/notifications/objects/aggregate.py @@ -0,0 +1,51 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# 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.notifications.objects import base +from nova.objects import base as nova_base +from nova.objects import fields + + +@nova_base.NovaObjectRegistry.register_notification +class AggregatePayload(base.NotificationPayloadBase): + SCHEMA = { + 'id': ('aggregate', 'id'), + 'uuid': ('aggregate', 'uuid'), + 'name': ('aggregate', 'name'), + 'hosts': ('aggregate', 'hosts'), + 'metadata': ('aggregate', 'metadata'), + } + # Version 1.0: Initial version + VERSION = '1.0' + fields = { + 'id': fields.IntegerField(), + 'uuid': fields.UUIDField(nullable=False), + 'name': fields.StringField(), + 'hosts': fields.ListOfStringsField(nullable=True), + 'metadata': fields.DictOfStringsField(nullable=True), + } + + def __init__(self, aggregate, **kwargs): + super(AggregatePayload, self).__init__(**kwargs) + self.populate_schema(aggregate=aggregate) + + +@base.notification_sample('aggregate-create-start.json') +@base.notification_sample('aggregate-create-end.json') +@nova_base.NovaObjectRegistry.register_notification +class AggregateNotification(base.NotificationBase): + # Version 1.0: Initial version + VERSION = '1.0' + + fields = { + 'payload': fields.ObjectField('AggregatePayload') + } diff --git a/nova/objects/aggregate.py b/nova/objects/aggregate.py index a16455afce..03909bf6ef 100644 --- a/nova/objects/aggregate.py +++ b/nova/objects/aggregate.py @@ -360,11 +360,18 @@ class Aggregate(base.NovaPersistentObject, base.NovaObject): payload['meta_data'] = payload.pop('metadata') if 'uuid' not in updates: updates['uuid'] = uuidutils.generate_uuid() + self.uuid = updates['uuid'] LOG.debug('Generated uuid %(uuid)s for aggregate', dict(uuid=updates['uuid'])) compute_utils.notify_about_aggregate_update(self._context, "create.start", payload) + compute_utils.notify_about_aggregate_action( + context=self._context, + aggregate=self, + action=fields.NotificationAction.CREATE, + phase=fields.NotificationPhase.START) + metadata = updates.pop('metadata', None) db_aggregate = _aggregate_create_in_db(self._context, updates, metadata=metadata) @@ -373,6 +380,11 @@ class Aggregate(base.NovaPersistentObject, base.NovaObject): compute_utils.notify_about_aggregate_update(self._context, "create.end", payload) + compute_utils.notify_about_aggregate_action( + context=self._context, + aggregate=self, + action=fields.NotificationAction.CREATE, + phase=fields.NotificationPhase.END) @base.remotable def save(self): diff --git a/nova/tests/functional/api/client.py b/nova/tests/functional/api/client.py index b919d7f4fb..436b45643b 100644 --- a/nova/tests/functional/api/client.py +++ b/nova/tests/functional/api/client.py @@ -388,3 +388,6 @@ class TestOpenStackClient(object): def get_instance_actions(self, server_id): return self.api_get('/servers/%s/os-instance-actions' % (server_id)).body['instanceActions'] + + def post_aggregate(self, aggregate): + return self.api_post('/os-aggregates', aggregate).body['aggregate'] diff --git a/nova/tests/functional/notification_sample_tests/test_aggregate.py b/nova/tests/functional/notification_sample_tests/test_aggregate.py new file mode 100644 index 0000000000..6fc4fdbda7 --- /dev/null +++ b/nova/tests/functional/notification_sample_tests/test_aggregate.py @@ -0,0 +1,40 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# 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.tests.functional.notification_sample_tests \ + import notification_sample_base +from nova.tests.unit import fake_notifier + + +class TestAggregateNotificationSample( + notification_sample_base.NotificationSampleTestBase): + + def test_aggregate_create_delete(self): + aggregate_req = { + "aggregate": { + "name": "my-aggregate", + "availability_zone": "nova"}} + aggregate = self.admin_api.post_aggregate(aggregate_req) + + self.assertEqual(2, len(fake_notifier.VERSIONED_NOTIFICATIONS)) + # The uuid hasn't been exposed on the REST API yet so we have no way to + # match it here, now. + self._verify_notification( + 'aggregate-create-start', + replacements={ + 'uuid': self.ANY}, + actual=fake_notifier.VERSIONED_NOTIFICATIONS[0]) + self._verify_notification( + 'aggregate-create-end', + replacements={ + 'uuid': self.ANY, + 'id': aggregate['id']}, + actual=fake_notifier.VERSIONED_NOTIFICATIONS[1]) diff --git a/nova/tests/functional/notification_sample_tests/test_exception_notification.py b/nova/tests/functional/notification_sample_tests/test_exception_notification.py index 10cf66365b..c13176855a 100644 --- a/nova/tests/functional/notification_sample_tests/test_exception_notification.py +++ b/nova/tests/functional/notification_sample_tests/test_exception_notification.py @@ -33,5 +33,7 @@ class TestExceptionNotificationSample( self.assertRaises(api_client.OpenStackApiException, self.admin_api.api_post, 'os-aggregates', post) - self.assertEqual(1, len(fake_notifier.VERSIONED_NOTIFICATIONS)) - self._verify_notification('compute-exception') + self.assertEqual(4, len(fake_notifier.VERSIONED_NOTIFICATIONS)) + self._verify_notification( + 'compute-exception', + actual=fake_notifier.VERSIONED_NOTIFICATIONS[3]) diff --git a/nova/tests/unit/compute/test_compute.py b/nova/tests/unit/compute/test_compute.py index 0e29c7cce3..9ba39ac49a 100644 --- a/nova/tests/unit/compute/test_compute.py +++ b/nova/tests/unit/compute/test_compute.py @@ -10573,10 +10573,18 @@ class ComputeAPIAggrTestCase(BaseTestCase): self.stub_out('oslo_messaging.rpc.client.call', fake_rpc_method) self.stub_out('oslo_messaging.rpc.client.cast', fake_rpc_method) - def test_aggregate_no_zone(self): + @mock.patch('nova.compute.utils.notify_about_aggregate_action') + def test_aggregate_no_zone(self, mock_notify): # Ensure we can create an aggregate without an availability zone aggr = self.api.create_aggregate(self.context, 'fake_aggregate', None) + + mock_notify.assert_has_calls([ + mock.call(context=self.context, aggregate=aggr, + action='create', phase='start'), + mock.call(context=self.context, aggregate=aggr, + action='create', phase='end')]) + self.api.delete_aggregate(self.context, aggr.id) self.assertRaises(exception.AggregateNotFound, self.api.delete_aggregate, self.context, aggr.id) diff --git a/nova/tests/unit/notifications/objects/test_notification.py b/nova/tests/unit/notifications/objects/test_notification.py index 3721c4b72d..2926f26dc7 100644 --- a/nova/tests/unit/notifications/objects/test_notification.py +++ b/nova/tests/unit/notifications/objects/test_notification.py @@ -257,6 +257,8 @@ class TestNotificationBase(test.NoDBTestCase): notification_object_data = { + 'AggregateNotification': '1.0-a73147b93b520ff0061865849d3dfa56', + 'AggregatePayload': '1.0-2550af604410af7b4ad5d46fb29ba45b', 'AuditPeriodPayload': '1.0-2b429dd307b8374636703b843fa3f9cb', 'BandwidthPayload': '1.0-ee2616a7690ab78406842a2b68e34130', 'EventType': '1.4-da0f0fbcda143ca96c2ac1b93937c22c',