diff --git a/doc/notification_samples/audit-delete.json b/doc/notification_samples/audit-delete.json new file mode 100644 index 000000000..70b66cc35 --- /dev/null +++ b/doc/notification_samples/audit-delete.json @@ -0,0 +1,69 @@ +{ + "priority": "INFO", + "payload": { + "watcher_object.data": { + "audit_type": "ONESHOT", + "parameters": { + "para2": "hello", + "para1": 3.2 + }, + "state": "DELETED", + "updated_at": null, + "deleted_at": null, + "goal": { + "watcher_object.data": { + "uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a", + "name": "dummy", + "updated_at": null, + "deleted_at": null, + "efficacy_specification": [], + "created_at": "2016-11-04T16:25:35Z", + "display_name": "Dummy goal" + }, + "watcher_object.name": "GoalPayload", + "watcher_object.version": "1.0", + "watcher_object.namespace": "watcher" + }, + "interval": null, + "scope": [], + "strategy": { + "watcher_object.data": { + "parameters_spec": { + "properties": { + "para2": { + "type": "string", + "default": "hello", + "description": "string parameter example" + }, + "para1": { + "description": "number parameter example", + "maximum": 10.2, + "type": "number", + "default": 3.2, + "minimum": 1.0 + } + } + }, + "name": "dummy", + "uuid": "75234dfe-87e3-4f11-a0e0-3c3305d86a39", + "updated_at": null, + "deleted_at": null, + "created_at": "2016-11-04T16:25:35Z", + "display_name": "Dummy strategy" + }, + "watcher_object.name": "StrategyPayload", + "watcher_object.version": "1.0", + "watcher_object.namespace": "watcher" + }, + "created_at": "2016-11-04T16:29:20Z", + "uuid": "4a97b9dd-2023-43dc-b713-815bdd94d4d6" + }, + "watcher_object.name": "AuditDeletePayload", + "watcher_object.version": "1.0", + "watcher_object.namespace": "watcher" + }, + "publisher_id": "infra-optim:localhost", + "timestamp": "2016-11-04 16:31:36.264673 ", + "event_type": "audit.delete", + "message_id": "cbcf9f2c-7c53-4b4d-91ec-db49cca024b6" +} diff --git a/watcher/notifications/audit.py b/watcher/notifications/audit.py index 0b7c8f938..63d8663e0 100644 --- a/watcher/notifications/audit.py +++ b/watcher/notifications/audit.py @@ -111,6 +111,19 @@ class AuditUpdatePayload(AuditPayload): strategy=strategy) +@base.WatcherObjectRegistry.register_notification +class AuditDeletePayload(AuditPayload): + # Version 1.0: Initial version + VERSION = '1.0' + fields = {} + + def __init__(self, audit, goal, strategy): + super(AuditDeletePayload, self).__init__( + audit=audit, + goal=goal, + strategy=strategy) + + # @notificationbase.notification_sample('audit-create.json') # @notificationbase.notification_sample('audit-delete.json') # @base.WatcherObjectRegistry.register_notification @@ -145,6 +158,17 @@ class AuditUpdateNotification(notificationbase.NotificationBase): } +@notificationbase.notification_sample('audit-delete.json') +@base.WatcherObjectRegistry.register_notification +class AuditDeleteNotification(notificationbase.NotificationBase): + # Version 1.0: Initial version + VERSION = '1.0' + + fields = { + 'payload': wfields.ObjectField('AuditDeletePayload') + } + + def _get_common_payload(audit): goal = None strategy = None @@ -215,3 +239,25 @@ def send_update(context, audit, service='infra-optim', payload=versioned_payload) notification.emit(context) + + +def send_delete(context, audit, service='infra-optim', host=None): + goal_payload, strategy_payload = _get_common_payload(audit) + + versioned_payload = AuditDeletePayload( + audit=audit, + goal=goal_payload, + strategy=strategy_payload, + ) + + notification = AuditDeleteNotification( + priority=wfields.NotificationPriority.INFO, + event_type=notificationbase.EventType( + object='audit', + action=wfields.NotificationAction.DELETE), + publisher=notificationbase.NotificationPublisher( + host=host or CONF.host, + binary=service), + payload=versioned_payload) + + notification.emit(context) diff --git a/watcher/objects/audit.py b/watcher/objects/audit.py index 0bf415133..7a73f5f92 100644 --- a/watcher/objects/audit.py +++ b/watcher/objects/audit.py @@ -283,3 +283,8 @@ class Audit(base.WatcherPersistentObject, base.WatcherObject, self.dbapi.soft_delete_audit(self.uuid) self.state = State.DELETED self.save() + + def _notify(): + notifications.audit.send_delete(self._context, self) + + _notify() diff --git a/watcher/tests/notifications/test_audit_notification.py b/watcher/tests/notifications/test_audit_notification.py index 8d7425b79..29afb24c4 100644 --- a/watcher/tests/notifications/test_audit_notification.py +++ b/watcher/tests/notifications/test_audit_notification.py @@ -17,6 +17,7 @@ import mock from watcher.common import exception from watcher import notifications +from watcher import objects from watcher.tests.db import base from watcher.tests.objects import utils @@ -39,11 +40,12 @@ class TestAuditNotification(base.DbTestCase): goal = utils.create_test_goal(mock.Mock()) strategy = utils.create_test_strategy(mock.Mock()) audit = utils.create_test_audit( - mock.Mock(), state='ONGOING', + mock.Mock(), state=objects.audit.State.ONGOING, goal_id=goal.id, strategy_id=strategy.id, goal=goal, strategy=strategy) notifications.audit.send_update( - mock.MagicMock(), audit, 'host', 'node0', old_state='PENDING') + mock.MagicMock(), audit, 'host', 'node0', + old_state=objects.audit.State.PENDING) self.assertEqual(1, mock_emit.call_count) notification = mock_emit.call_args_list[0][1] @@ -111,9 +113,11 @@ class TestAuditNotification(base.DbTestCase): def test_send_version_audit_update_without_strategy(self, mock_emit): goal = utils.create_test_goal(mock.Mock(), id=1) audit = utils.get_test_audit( - mock.Mock(), state='ONGOING', goal_id=goal.id, goal=goal) + mock.Mock(), state=objects.audit.State.ONGOING, + goal_id=goal.id, goal=goal) notifications.audit.send_update( - mock.MagicMock(), audit, 'host', 'node0', old_state='PENDING') + mock.MagicMock(), audit, 'host', 'node0', + old_state=objects.audit.State.PENDING) self.assertEqual(1, mock_emit.call_count) notification = mock_emit.call_args_list[0][1] @@ -169,7 +173,7 @@ class TestAuditNotification(base.DbTestCase): goal = utils.create_test_goal(mock.Mock()) strategy = utils.create_test_strategy(mock.Mock()) audit = utils.get_test_audit( - mock.Mock(), state='PENDING', + mock.Mock(), state=objects.audit.State.PENDING, goal_id=goal.id, strategy_id=strategy.id, goal=goal.as_dict(), strategy=strategy.as_dict()) notifications.audit.send_create( @@ -226,3 +230,66 @@ class TestAuditNotification(base.DbTestCase): }, payload ) + + @freezegun.freeze_time('2016-10-18T09:52:05.219414') + @mock.patch.object(notifications.audit.AuditDeleteNotification, '_emit') + def test_send_version_audit_delete(self, mock_emit): + goal = utils.create_test_goal(mock.Mock()) + strategy = utils.create_test_strategy(mock.Mock()) + audit = utils.create_test_audit( + mock.Mock(), state=objects.audit.State.DELETED, + goal_id=goal.id, strategy_id=strategy.id) + notifications.audit.send_delete( + mock.MagicMock(), audit, 'host', 'node0') + + self.assertEqual(1, mock_emit.call_count) + notification = mock_emit.call_args_list[0][1] + payload = notification['payload'] + + self.assertDictEqual( + { + "watcher_object.namespace": "watcher", + "watcher_object.version": "1.0", + "watcher_object.data": { + "interval": 3600, + "strategy": { + "watcher_object.namespace": "watcher", + "watcher_object.version": "1.0", + "watcher_object.data": { + "updated_at": None, + "uuid": "cb3d0b58-4415-4d90-b75b-1e96878730e3", + "name": "TEST", + "parameters_spec": {}, + "created_at": "2016-10-18T09:52:05Z", + "display_name": "test strategy", + "deleted_at": None + }, + "watcher_object.name": "StrategyPayload" + }, + "parameters": {}, + "uuid": "10a47dd1-4874-4298-91cf-eff046dbdb8d", + "goal": { + "watcher_object.namespace": "watcher", + "watcher_object.version": "1.0", + "watcher_object.data": { + "updated_at": None, + "uuid": "f7ad87ae-4298-91cf-93a0-f35a852e3652", + "name": "TEST", + "efficacy_specification": [], + "created_at": "2016-10-18T09:52:05Z", + "display_name": "test goal", + "deleted_at": None + }, + "watcher_object.name": "GoalPayload" + }, + "deleted_at": None, + "scope": [], + "state": "DELETED", + "updated_at": None, + "created_at": "2016-10-18T09:52:05Z", + "audit_type": "ONESHOT" + }, + "watcher_object.name": "AuditDeletePayload" + }, + payload + ) diff --git a/watcher/tests/notifications/test_notification.py b/watcher/tests/notifications/test_notification.py index 9de8db691..3cc0ced2a 100644 --- a/watcher/tests/notifications/test_notification.py +++ b/watcher/tests/notifications/test_notification.py @@ -260,6 +260,8 @@ expected_notification_fingerprints = { 'AuditUpdatePayload': '1.0-d3aace28d9eb978c1ecf833e108f61f7', 'AuditCreateNotification': '1.0-9b69de0724fda8310d05e18418178866', 'AuditCreatePayload': '1.0-30c85c834648c8ca11f54fc5e084d86b', + 'AuditDeleteNotification': '1.0-9b69de0724fda8310d05e18418178866', + 'AuditDeletePayload': '1.0-30c85c834648c8ca11f54fc5e084d86b', 'GoalPayload': '1.0-fa1fecb8b01dd047eef808ded4d50d1a', 'StrategyPayload': '1.0-94f01c137b083ac236ae82573c1fcfc1', } diff --git a/watcher/tests/objects/test_audit.py b/watcher/tests/objects/test_audit.py index eb00a513b..2cfc75b5c 100644 --- a/watcher/tests/objects/test_audit.py +++ b/watcher/tests/objects/test_audit.py @@ -23,6 +23,7 @@ from watcher import notifications from watcher import objects from watcher.tests.db import base from watcher.tests.db import utils +from watcher.tests.objects import utils as objutils class TestAuditObject(base.DbTestCase): @@ -232,3 +233,40 @@ class TestAuditObjectSendNotifications(base.DbTestCase): self.assertEqual(1, self.m_notifier.info.call_count) self.assertEqual('audit.update', self.m_notifier.info.call_args[1]['event_type']) + + @mock.patch.object(db_api.Connection, 'create_audit') + def test_send_create_notification(self, m_create_audit): + audit = objutils.get_test_audit( + self.context, + goal_id=self.fake_goal.id, + strategy_id=self.fake_strategy.id, + goal=self.fake_goal.as_dict(), + strategy=self.fake_strategy.as_dict()) + m_create_audit.return_value = audit + audit.create() + + self.assertEqual(1, self.m_notifier.info.call_count) + self.assertEqual('audit.create', + self.m_notifier.info.call_args[1]['event_type']) + + @mock.patch.object(db_api.Connection, 'soft_delete_audit', mock.Mock()) + @mock.patch.object(db_api.Connection, 'update_audit', mock.Mock()) + @mock.patch.object(db_api.Connection, 'get_audit_by_uuid') + def test_send_delete_notification(self, m_get_audit): + fake_audit = utils.get_test_audit( + goal=self.fake_goal.as_dict(), + strategy_id=self.fake_strategy.id, + strategy=self.fake_strategy.as_dict()) + m_get_audit.return_value = fake_audit + uuid = fake_audit['uuid'] + + audit = objects.Audit.get_by_uuid(self.context, uuid, eager=True) + audit.soft_delete() + + self.assertEqual(2, self.m_notifier.info.call_count) + self.assertEqual( + 'audit.update', + self.m_notifier.info.call_args_list[0][1]['event_type']) + self.assertEqual( + 'audit.delete', + self.m_notifier.info.call_args_list[1][1]['event_type'])