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
This commit is contained in:
Balazs Gibizer 2016-11-07 16:21:49 +01:00
parent 190d759274
commit 8ee3e30bd1
10 changed files with 172 additions and 3 deletions

View File

@ -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"
}

View File

@ -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"
}

View File

@ -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.

View File

@ -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')
}

View File

@ -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):

View File

@ -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']

View File

@ -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])

View File

@ -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])

View File

@ -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)

View File

@ -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',