From 5537840edf22f57331b6d83f8b295753b17f45f0 Mon Sep 17 00:00:00 2001 From: ShunliZhou Date: Thu, 6 Apr 2017 15:47:15 +0800 Subject: [PATCH] Transform servergroup.create notification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Transform server group create notification to the versioned notification framework. Co-Authored-By: Gábor Antal Change-Id: I781975293ccef40670cf474dd1d475cb427dba63 Implements: bp versioned-notification-transformation-queens --- .../server_group-create.json | 19 ++++++ nova/compute/utils.py | 14 +++++ nova/notifications/objects/server_group.py | 60 +++++++++++++++++++ nova/objects/instance_group.py | 4 ++ .../test_server_group.py | 30 ++++++++++ nova/tests/unit/compute/test_compute_utils.py | 43 +++++++++++++ .../objects/test_notification.py | 2 + .../tests/unit/objects/test_instance_group.py | 24 +++++++- 8 files changed, 195 insertions(+), 1 deletion(-) create mode 100644 doc/notification_samples/server_group-create.json create mode 100644 nova/notifications/objects/server_group.py create mode 100644 nova/tests/functional/notification_sample_tests/test_server_group.py diff --git a/doc/notification_samples/server_group-create.json b/doc/notification_samples/server_group-create.json new file mode 100644 index 000000000000..318713c2ec08 --- /dev/null +++ b/doc/notification_samples/server_group-create.json @@ -0,0 +1,19 @@ +{ + "priority": "INFO", + "payload": { + "nova_object.version": "1.0", + "nova_object.namespace": "nova", + "nova_object.name": "ServerGroupPayload", + "nova_object.data": { + "uuid": "788608ec-ebdc-45c5-bc7f-e5f24ab92c80", + "name": "test-server-group", + "project_id": "6f70656e737461636b20342065766572", + "user_id": "fake", + "policies": ["anti-affinity"], + "members": [], + "hosts": null + } + }, + "event_type": "server_group.create", + "publisher_id": "nova-api:fake-mini" +} diff --git a/nova/compute/utils.py b/nova/compute/utils.py index d89ed1863c5b..74faadfae19b 100644 --- a/nova/compute/utils.py +++ b/nova/compute/utils.py @@ -37,6 +37,7 @@ 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 from nova.notifications.objects import keypair as keypair_notification +from nova.notifications.objects import server_group as sg_notification from nova import objects from nova.objects import fields from nova import rpc @@ -557,6 +558,19 @@ def notify_about_host_update(context, event_suffix, host_payload): notifier.info(context, 'HostAPI.%s' % event_suffix, host_payload) +def notify_about_server_group_action(context, group, action): + payload = sg_notification.ServerGroupPayload(group) + notification = sg_notification.ServerGroupNotification( + priority=fields.NotificationPriority.INFO, + publisher=notification_base.NotificationPublisher( + host=CONF.host, source=fields.NotificationSource.API), + event_type=notification_base.EventType( + object='server_group', + action=action), + payload=payload) + notification.emit(context) + + def refresh_info_cache_for_instance(context, instance): """Refresh the info cache for an instance. diff --git a/nova/notifications/objects/server_group.py b/nova/notifications/objects/server_group.py new file mode 100644 index 000000000000..e42ef722f260 --- /dev/null +++ b/nova/notifications/objects/server_group.py @@ -0,0 +1,60 @@ +# 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. + +import copy + +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 ServerGroupPayload(base.NotificationPayloadBase): + SCHEMA = { + 'uuid': ('group', 'uuid'), + 'name': ('group', 'name'), + 'user_id': ('group', 'user_id'), + 'project_id': ('group', 'project_id'), + 'policies': ('group', 'policies'), + 'members': ('group', 'members'), + 'hosts': ('group', 'hosts'), + } + # Version 1.0: Initial version + VERSION = '1.0' + fields = { + 'uuid': fields.UUIDField(), + 'name': fields.StringField(nullable=True), + 'user_id': fields.StringField(nullable=True), + 'project_id': fields.StringField(nullable=True), + 'policies': fields.ListOfStringsField(nullable=True), + 'members': fields.ListOfStringsField(nullable=True), + 'hosts': fields.ListOfStringsField(nullable=True), + } + + def __init__(self, group): + super(ServerGroupPayload, self).__init__() + # Note: The group is orphaned here to avoid triggering lazy-loading of + # the group.hosts field. + cgroup = copy.deepcopy(group) + cgroup._context = None + self.populate_schema(group=cgroup) + + +@base.notification_sample('server_group-create.json') +@nova_base.NovaObjectRegistry.register_notification +class ServerGroupNotification(base.NotificationBase): + # Version 1.0: Initial version + VERSION = '1.0' + + fields = { + 'payload': fields.ObjectField('ServerGroupPayload') + } diff --git a/nova/objects/instance_group.py b/nova/objects/instance_group.py index 4f111f2293b7..ae9fedebf128 100644 --- a/nova/objects/instance_group.py +++ b/nova/objects/instance_group.py @@ -417,6 +417,10 @@ class InstanceGroup(base.NovaPersistentObject, base.NovaObject, payload['server_group_id'] = self.uuid compute_utils.notify_about_server_group_update(self._context, "create", payload) + compute_utils.notify_about_server_group_action( + context=self._context, + group=self, + action=fields.NotificationAction.CREATE) @base.remotable def create(self): diff --git a/nova/tests/functional/notification_sample_tests/test_server_group.py b/nova/tests/functional/notification_sample_tests/test_server_group.py new file mode 100644 index 000000000000..3f1290680c6b --- /dev/null +++ b/nova/tests/functional/notification_sample_tests/test_server_group.py @@ -0,0 +1,30 @@ +# 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 TestServerGroupNotificationSample( + notification_sample_base.NotificationSampleTestBase): + + def test_server_group_create(self): + group_req = { + "name": "test-server-group", + "policies": ["anti-affinity"]} + group = self.api.post_server_groups(group_req) + + self.assertEqual(1, len(fake_notifier.VERSIONED_NOTIFICATIONS)) + self._verify_notification( + 'server_group-create', + replacements={'uuid': group['id']}, + actual=fake_notifier.VERSIONED_NOTIFICATIONS[0]) diff --git a/nova/tests/unit/compute/test_compute_utils.py b/nova/tests/unit/compute/test_compute_utils.py index 1c098cd1d196..99c602e2e076 100644 --- a/nova/tests/unit/compute/test_compute_utils.py +++ b/nova/tests/unit/compute/test_compute_utils.py @@ -969,6 +969,49 @@ class ComputeUtilsTestCase(test.NoDBTestCase): mock_notify_usage.assert_has_calls(expected_notify_calls) +class ServerGroupTestCase(test.TestCase): + def setUp(self): + super(ServerGroupTestCase, self).setUp() + fake_notifier.stub_notifier(self) + self.addCleanup(fake_notifier.reset) + self.user_id = 'fake' + self.project_id = 'fake' + self.context = context.RequestContext(self.user_id, self.project_id) + + def test_notify_about_server_group_action(self): + uuid = uuids.instance + group = objects.InstanceGroup(context=self.context, + id=1, + uuid=uuid, + user_id=self.user_id, + project_id=self.project_id, + name="test-server-group", + policies=["anti-affinity"]) + compute_utils.notify_about_server_group_action(self.context, + group, 'create') + self.assertEqual(len(fake_notifier.VERSIONED_NOTIFICATIONS), 1) + notification = fake_notifier.VERSIONED_NOTIFICATIONS[0] + expected = {'priority': 'INFO', + 'event_type': u'server_group.create', + 'publisher_id': u'nova-api:fake-mini', + 'payload': { + 'nova_object.data': { + 'name': u'test-server-group', + 'policies': [u'anti-affinity'], + 'project_id': u'fake', + 'user_id': u'fake', + 'uuid': uuid, + 'hosts': None, + 'members': None + }, + 'nova_object.name': 'ServerGroupPayload', + 'nova_object.namespace': 'nova', + 'nova_object.version': '1.0' + } + } + self.assertEqual(notification, expected) + + class ComputeUtilsQuotaTestCase(test.TestCase): def setUp(self): super(ComputeUtilsQuotaTestCase, self).setUp() diff --git a/nova/tests/unit/notifications/objects/test_notification.py b/nova/tests/unit/notifications/objects/test_notification.py index 0f003eb53886..975fffa8e515 100644 --- a/nova/tests/unit/notifications/objects/test_notification.py +++ b/nova/tests/unit/notifications/objects/test_notification.py @@ -395,6 +395,8 @@ notification_object_data = { 'KeypairNotification': '1.0-a73147b93b520ff0061865849d3dfa56', 'KeypairPayload': '1.0-6daebbbde0e1bf35c1556b1ecd9385c1', 'NotificationPublisher': '2.1-9f89fe4abb80f9a7b726e59800c905de', + 'ServerGroupNotification': '1.0-a73147b93b520ff0061865849d3dfa56', + 'ServerGroupPayload': '1.0-eb4bd1738b4670cfe1b7c30344c143c3', 'ServiceStatusNotification': '1.0-a73147b93b520ff0061865849d3dfa56', 'ServiceStatusPayload': '1.1-7b6856bd879db7f3ecbcd0ca9f35f92f', } diff --git a/nova/tests/unit/objects/test_instance_group.py b/nova/tests/unit/objects/test_instance_group.py index d542c18afc5f..be7f08978975 100644 --- a/nova/tests/unit/objects/test_instance_group.py +++ b/nova/tests/unit/objects/test_instance_group.py @@ -20,8 +20,10 @@ from oslo_utils import timeutils from nova import exception from nova import objects from nova.tests.unit.objects import test_objects +from nova.tests.unit import utils as test_utils from nova.tests import uuidsentinel as uuids + _TS_NOW = timeutils.utcnow(with_timezone=True) # o.vo.fields.DateTimeField converts to tz-aware and # in process we lose microsecond resolution. @@ -122,10 +124,11 @@ class _TestInstanceGroupObject(object): self.assertFalse(mock_db_update.called) self.assertFalse(mock_notify.called) + @mock.patch('nova.compute.utils.notify_about_server_group_action') @mock.patch('nova.compute.utils.notify_about_server_group_update') @mock.patch('nova.objects.InstanceGroup._create_in_db', return_value=_INST_GROUP_DB) - def test_create(self, mock_db_create, mock_notify): + def test_create(self, mock_db_create, mock_notify, mock_notify_action): obj = objects.InstanceGroup(context=self.context) obj.uuid = _DB_UUID obj.name = _INST_GROUP_DB['name'] @@ -165,7 +168,26 @@ class _TestInstanceGroupObject(object): 'policies': _INST_GROUP_DB['policies'], 'server_group_id': _DB_UUID}) + def _group_matcher(group): + """Custom mock call matcher method.""" + return (group.uuid == _DB_UUID and + group.name == _INST_GROUP_DB['name'] and + group.user_id == _INST_GROUP_DB['user_id'] and + group.project_id == _INST_GROUP_DB['project_id'] and + group.created_at == _TS_NOW and + group.updated_at == _TS_NOW and + group.deleted_at is None and + group.deleted is False and + group.members == _INST_GROUP_DB['members'] and + group.policies == _INST_GROUP_DB['policies'] and + group.id == 1) + + group_matcher = test_utils.CustomMockCallMatcher(_group_matcher) + self.assertRaises(exception.ObjectActionError, obj.create) + mock_notify_action.assert_called_once_with(context=self.context, + group=group_matcher, + action='create') @mock.patch('nova.compute.utils.notify_about_server_group_update') @mock.patch('nova.objects.InstanceGroup._destroy_in_db')