From ad56be6d225ef67892ecd29c0fff0b197c01c43a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9la=20Vancsics?= Date: Wed, 14 Jun 2017 07:28:07 +0200 Subject: [PATCH] Transform keypair.create notification The keypair.create.start and keypair.create.end notifications has been transformed to the versioned notification framework. Change-Id: I71e9d8dae55653ad3ee70f708a6d92c98ed20c1c Implements: bp versioned-notification-transformation-pike --- doc/ext/versioned_notifications.py | 1 + .../keypair-create-end.json | 17 +++++++ .../keypair-create-start.json | 17 +++++++ nova/compute/api.py | 23 +++++++-- nova/compute/utils.py | 22 ++++++++ nova/notifications/objects/keypair.py | 51 +++++++++++++++++++ nova/tests/functional/api/client.py | 5 +- .../notification_sample_tests/test_keypair.py | 40 +++++++++++++++ nova/tests/unit/compute/test_keypairs.py | 9 +++- .../objects/test_notification.py | 2 + 10 files changed, 180 insertions(+), 7 deletions(-) create mode 100644 doc/notification_samples/keypair-create-end.json create mode 100644 doc/notification_samples/keypair-create-start.json create mode 100644 nova/notifications/objects/keypair.py create mode 100644 nova/tests/functional/notification_sample_tests/test_keypair.py diff --git a/doc/ext/versioned_notifications.py b/doc/ext/versioned_notifications.py index c3fe54aca030..4f36b1c89bce 100644 --- a/doc/ext/versioned_notifications.py +++ b/doc/ext/versioned_notifications.py @@ -30,6 +30,7 @@ from nova.objects import base from nova.notifications.objects import exception from nova.notifications.objects import flavor from nova.notifications.objects import instance +from nova.notifications.objects import keypair from nova.notifications.objects import service diff --git a/doc/notification_samples/keypair-create-end.json b/doc/notification_samples/keypair-create-end.json new file mode 100644 index 000000000000..52ac6cb9fb43 --- /dev/null +++ b/doc/notification_samples/keypair-create-end.json @@ -0,0 +1,17 @@ +{ + "priority": "INFO", + "payload": { + "nova_object.version": "1.0", + "nova_object.namespace": "nova", + "nova_object.name": "KeypairPayload", + "nova_object.data": { + "user_id": "fake", + "name": "my-key", + "fingerprint": "1e:2c:9b:56:79:4b:45:77:f9:ca:7a:98:2c:b0:d5:3c", + "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDx8nkQv/zgGgB4rMYmIf+6A4l6Rr+o/6lHBQdW5aYd44bd8JttDCE/F/pNRr0lRE+PiqSPO8nDPHw0010JeMH9gYgnnFlyY3/OcJ02RhIPyyxYpv9FhY+2YiUkpwFOcLImyrxEsYXpD/0d3ac30bNH6Sw9JD9UZHYcpSxsIbECHw== Generated-by-Nova", + "type": "ssh" + } + }, + "event_type": "keypair.create.end", + "publisher_id": "nova-api:fake-mini" +} diff --git a/doc/notification_samples/keypair-create-start.json b/doc/notification_samples/keypair-create-start.json new file mode 100644 index 000000000000..ccf4d9a90989 --- /dev/null +++ b/doc/notification_samples/keypair-create-start.json @@ -0,0 +1,17 @@ +{ + "priority": "INFO", + "payload": { + "nova_object.version": "1.0", + "nova_object.namespace": "nova", + "nova_object.name": "KeypairPayload", + "nova_object.data": { + "user_id": "fake", + "name": "my-key", + "fingerprint": null, + "public_key": null, + "type": "ssh" + } + }, + "event_type": "keypair.create.start", + "publisher_id": "nova-api:fake-mini" +} diff --git a/nova/compute/api.py b/nova/compute/api.py index e6c8fca74a77..cfdc06d1e9ab 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -4925,18 +4925,31 @@ class KeypairAPI(base.Base): """Create a new key pair.""" self._validate_new_key_pair(context, user_id, key_name, key_type) - self._notify(context, 'create.start', key_name) - - private_key, public_key, fingerprint = self._generate_key_pair( - user_id, key_type) - keypair = objects.KeyPair(context) keypair.user_id = user_id keypair.name = key_name keypair.type = key_type + keypair.fingerprint = None + keypair.public_key = None + + self._notify(context, 'create.start', key_name) + compute_utils.notify_about_keypair_action( + context=context, + keypair=keypair, + action=fields_obj.NotificationAction.CREATE, + phase=fields_obj.NotificationPhase.START) + + private_key, public_key, fingerprint = self._generate_key_pair( + user_id, key_type) + keypair.fingerprint = fingerprint keypair.public_key = public_key keypair.create() + compute_utils.notify_about_keypair_action( + context=context, + keypair=keypair, + action=fields_obj.NotificationAction.CREATE, + phase=fields_obj.NotificationPhase.END) self._notify(context, 'create.end', key_name) diff --git a/nova/compute/utils.py b/nova/compute/utils.py index 4f5ba4b390e1..e41dbc6f8752 100644 --- a/nova/compute/utils.py +++ b/nova/compute/utils.py @@ -35,6 +35,7 @@ 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 +from nova.notifications.objects import keypair as keypair_notification from nova import objects from nova.objects import fields from nova import rpc @@ -394,6 +395,27 @@ def notify_about_volume_attach_detach(context, instance, host, action, phase, notification.emit(context) +def notify_about_keypair_action(context, keypair, action, phase): + """Send versioned notification about the keypair action on the instance + + :param context: the request context + :param keypair: the keypair which the action performed on + :param action: the name of the action + :param phase: the phase of the action + """ + payload = keypair_notification.KeypairPayload(keypair=keypair) + notification = keypair_notification.KeypairNotification( + priority=fields.NotificationPriority.INFO, + publisher=notification_base.NotificationPublisher( + host=CONF.host, binary='nova-api'), + event_type=notification_base.EventType( + object='keypair', + action=action, + phase=phase), + payload=payload) + notification.emit(context) + + def notify_about_volume_swap(context, instance, host, action, phase, old_volume_id, new_volume_id, exception=None): """Send versioned notification about the volume swap action diff --git a/nova/notifications/objects/keypair.py b/nova/notifications/objects/keypair.py new file mode 100644 index 000000000000..44df1a0732b2 --- /dev/null +++ b/nova/notifications/objects/keypair.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 KeypairPayload(base.NotificationPayloadBase): + SCHEMA = { + 'user_id': ('keypair', 'user_id'), + 'name': ('keypair', 'name'), + 'public_key': ('keypair', 'public_key'), + 'fingerprint': ('keypair', 'fingerprint'), + 'type': ('keypair', 'type') + } + # Version 1.0: Initial version + VERSION = '1.0' + fields = { + 'user_id': fields.StringField(nullable=True), + 'name': fields.StringField(nullable=False), + 'fingerprint': fields.StringField(nullable=True), + 'public_key': fields.StringField(nullable=True), + 'type': fields.StringField(nullable=False), + } + + def __init__(self, keypair, **kwargs): + super(KeypairPayload, self).__init__(**kwargs) + self.populate_schema(keypair=keypair) + + +@base.notification_sample('keypair-create-start.json') +@base.notification_sample('keypair-create-end.json') +@nova_base.NovaObjectRegistry.register_notification +class KeypairNotification(base.NotificationBase): + # Version 1.0: Initial version + VERSION = '1.0' + + fields = { + 'payload': fields.ObjectField('KeypairPayload') + } diff --git a/nova/tests/functional/api/client.py b/nova/tests/functional/api/client.py index 1abe5008e8d8..c5414bd587c9 100644 --- a/nova/tests/functional/api/client.py +++ b/nova/tests/functional/api/client.py @@ -217,7 +217,7 @@ class TestOpenStackClient(object): headers['Content-Type'] = 'application/json' kwargs['body'] = jsonutils.dumps(body) - kwargs.setdefault('check_response_status', [200, 202]) + kwargs.setdefault('check_response_status', [200, 201, 202]) return APIResponse(self.api_request(relative_uri, **kwargs)) def api_put(self, relative_uri, body, **kwargs): @@ -423,3 +423,6 @@ class TestOpenStackClient(object): def get_services(self): return self.api_get('/os-services').body['services'] + + def post_keypair(self, keypair): + return self.api_post('/os-keypairs', keypair).body['keypair'] diff --git a/nova/tests/functional/notification_sample_tests/test_keypair.py b/nova/tests/functional/notification_sample_tests/test_keypair.py new file mode 100644 index 000000000000..195e4b79a325 --- /dev/null +++ b/nova/tests/functional/notification_sample_tests/test_keypair.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 TestKeypairNotificationSample( + notification_sample_base.NotificationSampleTestBase): + + def test_keypair_create(self): + keypair_req = { + "keypair": { + "name": "my-key", + "user_id": "fake", + "type": "ssh" + }} + keypair = self.api.post_keypair(keypair_req) + + self.assertEqual(2, len(fake_notifier.VERSIONED_NOTIFICATIONS)) + self._verify_notification( + 'keypair-create-start', + replacements={}, + actual=fake_notifier.VERSIONED_NOTIFICATIONS[0]) + self._verify_notification( + 'keypair-create-end', + replacements={ + "fingerprint": keypair['fingerprint'], + "public_key": keypair['public_key'] + }, + actual=fake_notifier.VERSIONED_NOTIFICATIONS[1]) diff --git a/nova/tests/unit/compute/test_keypairs.py b/nova/tests/unit/compute/test_keypairs.py index 9bdf44f8faf8..48c5e816c001 100644 --- a/nova/tests/unit/compute/test_keypairs.py +++ b/nova/tests/unit/compute/test_keypairs.py @@ -14,6 +14,7 @@ # under the License. """Tests for keypair API.""" +import mock from oslo_concurrency import processutils from oslo_config import cfg import six @@ -162,11 +163,17 @@ class CreateImportSharedTestMixIn(object): class CreateKeypairTestCase(KeypairAPITestCase, CreateImportSharedTestMixIn): func_name = 'create_key_pair' - def _check_success(self): + @mock.patch('nova.compute.utils.notify_about_keypair_action') + def _check_success(self, mock_notify): keypair, private_key = self.keypair_api.create_key_pair( self.ctxt, self.ctxt.user_id, 'foo', key_type=self.keypair_type) self.assertEqual('foo', keypair['name']) self.assertEqual(self.keypair_type, keypair['type']) + mock_notify.assert_has_calls([ + mock.call(context=self.ctxt, keypair=keypair, + action='create', phase='start'), + mock.call(context=self.ctxt, keypair=keypair, + action='create', phase='end')]) self._check_notifications() def test_success_ssh(self): diff --git a/nova/tests/unit/notifications/objects/test_notification.py b/nova/tests/unit/notifications/objects/test_notification.py index 52a6ff3ff511..3d753913cd7c 100644 --- a/nova/tests/unit/notifications/objects/test_notification.py +++ b/nova/tests/unit/notifications/objects/test_notification.py @@ -386,6 +386,8 @@ notification_object_data = { 'InstanceUpdateNotification': '1.0-a73147b93b520ff0061865849d3dfa56', 'InstanceUpdatePayload': '1.3-5bf5f18ed1232b1d8884fa784b77728f', 'IpPayload': '1.0-8ecf567a99e516d4af094439a7632d34', + 'KeypairNotification': '1.0-a73147b93b520ff0061865849d3dfa56', + 'KeypairPayload': '1.0-6daebbbde0e1bf35c1556b1ecd9385c1', 'NotificationPublisher': '1.0-bbbc1402fb0e443a3eb227cc52b61545', 'ServiceStatusNotification': '1.0-a73147b93b520ff0061865849d3dfa56', 'ServiceStatusPayload': '1.1-7b6856bd879db7f3ecbcd0ca9f35f92f',