From 0c0a9c84d68081adb887a42c914219e029f835f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincent=20Fran=C3=A7oise?= Date: Thu, 20 Oct 2016 09:37:49 +0200 Subject: [PATCH] Implemented audit.create notification In this changeset, I implemented the audit.create notification. Change-Id: Ia092ca3a3dc951e3313a07f15a98aec5818e9ab0 Partially-Implements: blueprint audit-versioned-notifications-api --- doc/notification_samples/audit-create.json | 69 ++++++++++++ watcher/notifications/audit.py | 71 +++++++++++- watcher/notifications/base.py | 6 + watcher/objects/audit.py | 5 + watcher/objects/base.py | 2 +- watcher/tests/api/utils.py | 9 +- watcher/tests/api/v1/test_actions.py | 26 ++--- watcher/tests/api/v1/test_actions_plans.py | 12 +- watcher/tests/api/v1/test_audit_templates.py | 21 ++-- watcher/tests/db/base.py | 5 + watcher/tests/db/utils.py | 45 +++++--- .../strategy/context/test_strategy_context.py | 15 +-- .../notifications/test_audit_notification.py | 103 ++++++++++++++---- .../tests/notifications/test_notification.py | 2 + watcher/tests/objects/test_audit.py | 11 +- watcher/tests/objects/utils.py | 18 ++- 16 files changed, 332 insertions(+), 88 deletions(-) create mode 100644 doc/notification_samples/audit-create.json diff --git a/doc/notification_samples/audit-create.json b/doc/notification_samples/audit-create.json new file mode 100644 index 000000000..f74516b78 --- /dev/null +++ b/doc/notification_samples/audit-create.json @@ -0,0 +1,69 @@ +{ + "priority": "INFO", + "payload": { + "watcher_object.data": { + "audit_type": "ONESHOT", + "parameters": { + "para2": "hello", + "para1": 3.2 + }, + "state": "PENDING", + "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": "AuditCreatePayload", + "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.create", + "message_id": "cbcf9f2c-7c53-4b4d-91ec-db49cca024b6" +} diff --git a/watcher/notifications/audit.py b/watcher/notifications/audit.py index 01be8e14b..0b7c8f938 100644 --- a/watcher/notifications/audit.py +++ b/watcher/notifications/audit.py @@ -82,6 +82,19 @@ class AuditStateUpdatePayload(notificationbase.NotificationPayloadBase): } +@base.WatcherObjectRegistry.register_notification +class AuditCreatePayload(AuditPayload): + # Version 1.0: Initial version + VERSION = '1.0' + fields = {} + + def __init__(self, audit, goal, strategy): + super(AuditCreatePayload, self).__init__( + audit=audit, + goal=goal, + strategy=strategy) + + @base.WatcherObjectRegistry.register_notification class AuditUpdatePayload(AuditPayload): # Version 1.0: Initial version @@ -98,6 +111,29 @@ class AuditUpdatePayload(AuditPayload): strategy=strategy) +# @notificationbase.notification_sample('audit-create.json') +# @notificationbase.notification_sample('audit-delete.json') +# @base.WatcherObjectRegistry.register_notification +# class AuditActionNotification(notificationbase.NotificationBase): +# # Version 1.0: Initial version +# VERSION = '1.0' + +# fields = { +# 'payload': wfields.ObjectField('AuditActionPayload') +# } + + +@notificationbase.notification_sample('audit-create.json') +@base.WatcherObjectRegistry.register_notification +class AuditCreateNotification(notificationbase.NotificationBase): + # Version 1.0: Initial version + VERSION = '1.0' + + fields = { + 'payload': wfields.ObjectField('AuditCreatePayload') + } + + @notificationbase.notification_sample('audit-update.json') @base.WatcherObjectRegistry.register_notification class AuditUpdateNotification(notificationbase.NotificationBase): @@ -109,9 +145,7 @@ class AuditUpdateNotification(notificationbase.NotificationBase): } -def send_update(context, audit, service='infra-optim', - host=None, old_state=None): - """Emit an audit.update notification.""" +def _get_common_payload(audit): goal = None strategy = None try: @@ -128,6 +162,37 @@ def send_update(context, audit, service='infra-optim', strategy_payload = strategy_notifications.StrategyPayload( strategy=strategy) + return goal_payload, strategy_payload + + +def send_create(context, audit, service='infra-optim', host=None): + """Emit an audit.create notification.""" + goal_payload, strategy_payload = _get_common_payload(audit) + + versioned_payload = AuditCreatePayload( + audit=audit, + goal=goal_payload, + strategy=strategy_payload, + ) + + notification = AuditCreateNotification( + priority=wfields.NotificationPriority.INFO, + event_type=notificationbase.EventType( + object='audit', + action=wfields.NotificationAction.CREATE), + publisher=notificationbase.NotificationPublisher( + host=host or CONF.host, + binary=service), + payload=versioned_payload) + + notification.emit(context) + + +def send_update(context, audit, service='infra-optim', + host=None, old_state=None): + """Emit an audit.update notification.""" + goal_payload, strategy_payload = _get_common_payload(audit) + state_update = AuditStateUpdatePayload( old_state=old_state, state=audit.state if old_state else None) diff --git a/watcher/notifications/base.py b/watcher/notifications/base.py index fa56247d2..ec0a3b966 100644 --- a/watcher/notifications/base.py +++ b/watcher/notifications/base.py @@ -145,6 +145,12 @@ class NotificationBase(NotificationObject): 'publisher': wfields.ObjectField('NotificationPublisher'), } + def save(self, context): + raise exception.UnsupportedError() + + def obj_load_attr(self, attrname): + raise exception.UnsupportedError() + def _should_notify(self): """Determine whether the notification should be sent. diff --git a/watcher/objects/audit.py b/watcher/objects/audit.py index 507ad968d..0bf415133 100644 --- a/watcher/objects/audit.py +++ b/watcher/objects/audit.py @@ -236,6 +236,11 @@ class Audit(base.WatcherPersistentObject, base.WatcherObject, # notifications containing information about the related relationships self._from_db_object(self, db_audit, eager=True) + def _notify(): + notifications.audit.send_create(self._context, self) + + _notify() + @base.remotable def destroy(self): """Delete the Audit from the DB.""" diff --git a/watcher/objects/base.py b/watcher/objects/base.py index a78e1d3ea..eac07bb1c 100644 --- a/watcher/objects/base.py +++ b/watcher/objects/base.py @@ -155,7 +155,7 @@ class WatcherPersistentObject(object): if obj[rel_id] ) for obj_field, related_obj_cls, rel_id in loadable_fields: - if db_object.get(obj_field) and obj[rel_id]: + if getattr(db_object, obj_field, None) and obj[rel_id]: # The object field data was eagerly loaded alongside # the main object data obj[obj_field] = related_obj_cls._from_db_object( diff --git a/watcher/tests/api/utils.py b/watcher/tests/api/utils.py index 71c0b21aa..221eb7697 100644 --- a/watcher/tests/api/utils.py +++ b/watcher/tests/api/utils.py @@ -88,9 +88,12 @@ def audit_post_data(**kw): def audit_template_post_data(**kw): - audit_template = db_utils.get_test_audit_template(**kw) - internal = audit_template_ctrl.AuditTemplatePatchType.internal_attrs() - return remove_internal(audit_template, internal) + attrs = audit_template_ctrl.AuditTemplatePostType._wsme_attributes + audit_template = db_utils.get_test_audit_template() + fields = [field.key for field in attrs] + post_data = {k: v for k, v in audit_template.items() if k in fields} + post_data.update({k: v for k, v in kw.items() if k in fields}) + return post_data def action_post_data(**kw): diff --git a/watcher/tests/api/v1/test_actions.py b/watcher/tests/api/v1/test_actions.py index 10be8bc45..bd2c07ffb 100644 --- a/watcher/tests/api/v1/test_actions.py +++ b/watcher/tests/api/v1/test_actions.py @@ -189,7 +189,8 @@ class TestListAction(api_base.FunctionalTest): def test_filter_by_action_plan_uuid(self): action_plan_1 = obj_utils.create_test_action_plan( self.context, - uuid=utils.generate_uuid()) + uuid=utils.generate_uuid(), + audit_id=self.audit.id) action_list = [] for id_ in range(3): @@ -201,7 +202,8 @@ class TestListAction(api_base.FunctionalTest): action_plan_2 = obj_utils.create_test_action_plan( self.context, - uuid=utils.generate_uuid()) + uuid=utils.generate_uuid(), + audit_id=self.audit.id) for id_ in range(4, 5, 6): obj_utils.create_test_action( @@ -221,14 +223,12 @@ class TestListAction(api_base.FunctionalTest): self.assertEqual(action_plan_2.uuid, action['action_plan_uuid']) def test_details_and_filter_by_action_plan_uuid(self): - audit = obj_utils.create_test_audit(self.context, - uuid=utils.generate_uuid()) action_plan = obj_utils.create_test_action_plan( self.context, uuid=utils.generate_uuid(), - audit_id=audit.id) + audit_id=self.audit.id) - for id_ in range(3): + for id_ in range(1, 3): action = obj_utils.create_test_action( self.context, id=id_, action_plan_id=action_plan.id, @@ -240,33 +240,29 @@ class TestListAction(api_base.FunctionalTest): self.assertEqual(action_plan.uuid, action['action_plan_uuid']) def test_details_and_filter_by_audit_uuid(self): - audit = obj_utils.create_test_audit(self.context, - uuid=utils.generate_uuid()) action_plan = obj_utils.create_test_action_plan( self.context, uuid=utils.generate_uuid(), - audit_id=audit.id) + audit_id=self.audit.id) - for id_ in range(3): + for id_ in range(1, 3): action = obj_utils.create_test_action( self.context, id=id_, action_plan_id=action_plan.id, uuid=utils.generate_uuid()) response = self.get_json( - '/actions/detail?audit_uuid=%s' % audit.uuid) + '/actions/detail?audit_uuid=%s' % self.audit.uuid) for action in response['actions']: self.assertEqual(action_plan.uuid, action['action_plan_uuid']) def test_filter_by_action_plan_and_audit_uuids(self): - audit = obj_utils.create_test_audit( - self.context, uuid=utils.generate_uuid()) action_plan = obj_utils.create_test_action_plan( self.context, uuid=utils.generate_uuid(), - audit_id=audit.id) + audit_id=self.audit.id) url = '/actions?action_plan_uuid=%s&audit_uuid=%s' % ( - action_plan.uuid, audit.uuid) + action_plan.uuid, self.audit.uuid) response = self.get_json(url, expect_errors=True) self.assertEqual(400, response.status_int) diff --git a/watcher/tests/api/v1/test_actions_plans.py b/watcher/tests/api/v1/test_actions_plans.py index 5c316c451..7f1602eaa 100644 --- a/watcher/tests/api/v1/test_actions_plans.py +++ b/watcher/tests/api/v1/test_actions_plans.py @@ -172,8 +172,9 @@ class TestListActionPlan(api_base.FunctionalTest): def test_many_with_audit_uuid(self): action_plan_list = [] audit = obj_utils.create_test_audit(self.context, + id=2, uuid=utils.generate_uuid()) - for id_ in range(5): + for id_ in range(2, 5): action_plan = obj_utils.create_test_action_plan( self.context, id=id_, uuid=utils.generate_uuid(), audit_id=audit.id) @@ -186,14 +187,16 @@ class TestListActionPlan(api_base.FunctionalTest): def test_many_with_audit_uuid_filter(self): action_plan_list1 = [] audit1 = obj_utils.create_test_audit(self.context, + id=2, uuid=utils.generate_uuid()) - for id_ in range(5): + for id_ in range(2, 5): action_plan = obj_utils.create_test_action_plan( self.context, id=id_, uuid=utils.generate_uuid(), audit_id=audit1.id) action_plan_list1.append(action_plan.uuid) audit2 = obj_utils.create_test_audit(self.context, + id=3, uuid=utils.generate_uuid()) action_plan_list2 = [] for id_ in [5, 6, 7]: @@ -241,8 +244,9 @@ class TestListActionPlan(api_base.FunctionalTest): def test_many_with_sort_key_audit_uuid(self): audit_list = [] - for id_ in range(5): + for id_ in range(2, 5): audit = obj_utils.create_test_audit(self.context, + id=id_, uuid=utils.generate_uuid()) obj_utils.create_test_action_plan( self.context, id=id_, uuid=utils.generate_uuid(), @@ -251,7 +255,7 @@ class TestListActionPlan(api_base.FunctionalTest): response = self.get_json('/action_plans/?sort_key=audit_uuid') - self.assertEqual(5, len(response['action_plans'])) + self.assertEqual(3, len(response['action_plans'])) uuids = [s['audit_uuid'] for s in response['action_plans']] self.assertEqual(sorted(audit_list), uuids) diff --git a/watcher/tests/api/v1/test_audit_templates.py b/watcher/tests/api/v1/test_audit_templates.py index 651b06b14..0f5bda059 100644 --- a/watcher/tests/api/v1/test_audit_templates.py +++ b/watcher/tests/api/v1/test_audit_templates.py @@ -32,14 +32,11 @@ from watcher.tests.objects import utils as obj_utils def post_get_test_audit_template(**kw): - audit_template = api_utils.audit_template_post_data(**kw) goal = db_utils.get_test_goal() strategy = db_utils.get_test_strategy(goal_id=goal['id']) - del audit_template['uuid'] - del audit_template['goal_id'] - del audit_template['strategy_id'] - audit_template['goal'] = kw.get('goal', goal['uuid']) - audit_template['strategy'] = kw.get('strategy', strategy['uuid']) + kw['goal'] = kw.get('goal', goal['uuid']) + kw['strategy'] = kw.get('strategy', strategy['uuid']) + audit_template = api_utils.audit_template_post_data(**kw) return audit_template @@ -57,20 +54,16 @@ class FunctionalTestWithSetup(api_base.FunctionalTest): def setUp(self): super(FunctionalTestWithSetup, self).setUp() - self.fake_goal1 = obj_utils.get_test_goal( + self.fake_goal1 = obj_utils.create_test_goal( self.context, id=1, uuid=utils.generate_uuid(), name="dummy_1") - self.fake_goal2 = obj_utils.get_test_goal( + self.fake_goal2 = obj_utils.create_test_goal( self.context, id=2, uuid=utils.generate_uuid(), name="dummy_2") - self.fake_goal1.create() - self.fake_goal2.create() - self.fake_strategy1 = obj_utils.get_test_strategy( + self.fake_strategy1 = obj_utils.create_test_strategy( self.context, id=1, uuid=utils.generate_uuid(), name="strategy_1", goal_id=self.fake_goal1.id) - self.fake_strategy2 = obj_utils.get_test_strategy( + self.fake_strategy2 = obj_utils.create_test_strategy( self.context, id=2, uuid=utils.generate_uuid(), name="strategy_2", goal_id=self.fake_goal2.id) - self.fake_strategy1.create() - self.fake_strategy2.create() class TestListAuditTemplate(FunctionalTestWithSetup): diff --git a/watcher/tests/db/base.py b/watcher/tests/db/base.py index f869c5cd8..2370659cd 100644 --- a/watcher/tests/db/base.py +++ b/watcher/tests/db/base.py @@ -23,6 +23,7 @@ from watcher.db.sqlalchemy import api as sqla_api from watcher.db.sqlalchemy import migration from watcher.db.sqlalchemy import models from watcher.tests import base +from watcher.tests.db import utils CONF = cfg.CONF @@ -65,6 +66,9 @@ class Database(fixtures.Fixture): class DbTestCase(base.TestCase): + def get_next_id(self): + return next(self._id_gen) + def setUp(self): cfg.CONF.set_override("enable_authentication", False, enforce_type=True) @@ -81,3 +85,4 @@ class DbTestCase(base.TestCase): _DB_CACHE = Database(sqla_api, migration, sql_connection=CONF.database.connection) self.useFixture(_DB_CACHE) + self._id_gen = utils.id_generator() diff --git a/watcher/tests/db/utils.py b/watcher/tests/db/utils.py index dba00d1fc..1e796bfda 100644 --- a/watcher/tests/db/utils.py +++ b/watcher/tests/db/utils.py @@ -17,9 +17,30 @@ from oslo_utils import timeutils from watcher.db import api as db_api +from watcher.db.sqlalchemy import models from watcher import objects +def id_generator(): + id_ = 1 + while True: + yield id_ + id_ += 1 + + +def _load_relationships(model, db_data): + rel_data = {} + relationships = db_api.get_instance()._get_relationships(model) + for name, relationship in relationships.items(): + related_model = relationship.argument + if not db_data.get(name): + rel_data[name] = None + else: + rel_data[name] = related_model(**db_data.get(name)) + + return rel_data + + def get_test_audit_template(**kwargs): audit_template_data = { 'id': kwargs.get('id', 1), @@ -36,10 +57,8 @@ def get_test_audit_template(**kwargs): # ObjectField doesn't allow None nor dict, so if we want to simulate a # non-eager object loading, the field should not be referenced at all. - if kwargs.get('goal'): - audit_template_data['goal'] = kwargs.get('goal') - if kwargs.get('strategy'): - audit_template_data['strategy'] = kwargs.get('strategy') + audit_template_data.update( + _load_relationships(models.AuditTemplate, kwargs)) return audit_template_data @@ -77,10 +96,7 @@ def get_test_audit(**kwargs): } # ObjectField doesn't allow None nor dict, so if we want to simulate a # non-eager object loading, the field should not be referenced at all. - if kwargs.get('goal'): - audit_data['goal'] = kwargs.get('goal') - if kwargs.get('strategy'): - audit_data['strategy'] = kwargs.get('strategy') + audit_data.update(_load_relationships(models.Audit, kwargs)) return audit_data @@ -121,8 +137,7 @@ def get_test_action(**kwargs): # ObjectField doesn't allow None nor dict, so if we want to simulate a # non-eager object loading, the field should not be referenced at all. - if kwargs.get('action_plan'): - action_data['action_plan'] = kwargs.get('action_plan') + action_data.update(_load_relationships(models.Action, kwargs)) return action_data @@ -158,10 +173,7 @@ def get_test_action_plan(**kwargs): # ObjectField doesn't allow None nor dict, so if we want to simulate a # non-eager object loading, the field should not be referenced at all. - if kwargs.get('audit'): - action_plan_data['audit'] = kwargs.get('audit') - if kwargs.get('strategy'): - action_plan_data['strategy'] = kwargs.get('strategy') + action_plan_data.update(_load_relationships(models.ActionPlan, kwargs)) return action_plan_data @@ -244,10 +256,9 @@ def get_test_strategy(**kwargs): 'parameters_spec': kwargs.get('parameters_spec', {}), } - # goal ObjectField doesn't allow None nor dict, so if we want to simulate a + # ObjectField doesn't allow None nor dict, so if we want to simulate a # non-eager object loading, the field should not be referenced at all. - if kwargs.get('goal'): - strategy_data['goal'] = kwargs.get('goal') + strategy_data.update(_load_relationships(models.Strategy, kwargs)) return strategy_data diff --git a/watcher/tests/decision_engine/strategy/context/test_strategy_context.py b/watcher/tests/decision_engine/strategy/context/test_strategy_context.py index 3f376042c..f22781595 100644 --- a/watcher/tests/decision_engine/strategy/context/test_strategy_context.py +++ b/watcher/tests/decision_engine/strategy/context/test_strategy_context.py @@ -50,17 +50,17 @@ class TestStrategyContext(base.DbTestCase): @mock.patch.object(manager.CollectorManager, "get_cluster_model_collector", mock.Mock()) def test_execute_force_dummy(self): - obj_utils.create_test_goal(self.context, id=50, - uuid=utils.generate_uuid(), - name="my_goal") + goal = obj_utils.create_test_goal( + self.context, id=50, uuid=utils.generate_uuid(), name="my_goal") - strategy = obj_utils.create_test_strategy(self.context, - id=42, - uuid=utils.generate_uuid(), - name="dummy") + strategy = obj_utils.create_test_strategy( + self.context, id=42, uuid=utils.generate_uuid(), name="dummy", + goal_id=goal.id) audit = obj_utils.create_test_audit( self.context, + id=2, + goal_id=goal.id, strategy_id=strategy.id, uuid=utils.generate_uuid(), ) @@ -87,6 +87,7 @@ class TestStrategyContext(base.DbTestCase): audit = obj_utils.create_test_audit( self.context, + id=2, strategy_id=strategy.id, uuid=utils.generate_uuid(), ) diff --git a/watcher/tests/notifications/test_audit_notification.py b/watcher/tests/notifications/test_audit_notification.py index e7a06d661..8d7425b79 100644 --- a/watcher/tests/notifications/test_audit_notification.py +++ b/watcher/tests/notifications/test_audit_notification.py @@ -16,32 +16,33 @@ import freezegun import mock from watcher.common import exception -from watcher.notifications import audit as auditnotifs -from watcher.tests import base as testbase +from watcher import notifications +from watcher.tests.db import base from watcher.tests.objects import utils -class TestAuditNotification(testbase.TestCase): +class TestAuditNotification(base.DbTestCase): - @mock.patch.object(auditnotifs.AuditUpdateNotification, '_emit') + @mock.patch.object(notifications.audit.AuditUpdateNotification, '_emit') def test_send_version_invalid_audit(self, mock_emit): audit = utils.get_test_audit(mock.Mock(), state='DOESNOTMATTER', goal_id=1) self.assertRaises( exception.InvalidAudit, - auditnotifs.send_update, + notifications.audit.send_update, mock.MagicMock(), audit, 'host', 'node0') @freezegun.freeze_time('2016-10-18T09:52:05.219414') - @mock.patch.object(auditnotifs.AuditUpdateNotification, '_emit') + @mock.patch.object(notifications.audit.AuditUpdateNotification, '_emit') def test_send_version_audit_update_with_strategy(self, mock_emit): - goal = utils.get_test_goal(mock.Mock(), id=1) - strategy = utils.get_test_strategy(mock.Mock(), id=1) - audit = utils.get_test_audit(mock.Mock(), state='ONGOING', - goal_id=goal.id, strategy_id=strategy.id, - goal=goal, strategy=strategy) - auditnotifs.send_update( + goal = utils.create_test_goal(mock.Mock()) + strategy = utils.create_test_strategy(mock.Mock()) + audit = utils.create_test_audit( + mock.Mock(), 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') self.assertEqual(1, mock_emit.call_count) @@ -62,7 +63,7 @@ class TestAuditNotification(testbase.TestCase): "uuid": "cb3d0b58-4415-4d90-b75b-1e96878730e3", "name": "TEST", "parameters_spec": {}, - "created_at": None, + "created_at": "2016-10-18T09:52:05Z", "display_name": "test strategy", "deleted_at": None }, @@ -78,7 +79,7 @@ class TestAuditNotification(testbase.TestCase): "uuid": "f7ad87ae-4298-91cf-93a0-f35a852e3652", "name": "TEST", "efficacy_specification": [], - "created_at": None, + "created_at": "2016-10-18T09:52:05Z", "display_name": "test goal", "deleted_at": None }, @@ -88,7 +89,7 @@ class TestAuditNotification(testbase.TestCase): "scope": [], "state": "ONGOING", "updated_at": None, - "created_at": None, + "created_at": "2016-10-18T09:52:05Z", "state_update": { "watcher_object.namespace": "watcher", "watcher_object.version": "1.0", @@ -106,12 +107,12 @@ class TestAuditNotification(testbase.TestCase): ) @freezegun.freeze_time('2016-10-18T09:52:05.219414') - @mock.patch.object(auditnotifs.AuditUpdateNotification, '_emit') + @mock.patch.object(notifications.audit.AuditUpdateNotification, '_emit') def test_send_version_audit_update_without_strategy(self, mock_emit): - goal = utils.get_test_goal(mock.Mock(), id=1) + goal = utils.create_test_goal(mock.Mock(), id=1) audit = utils.get_test_audit( mock.Mock(), state='ONGOING', goal_id=goal.id, goal=goal) - auditnotifs.send_update( + notifications.audit.send_update( mock.MagicMock(), audit, 'host', 'node0', old_state='PENDING') self.assertEqual(1, mock_emit.call_count) @@ -134,7 +135,7 @@ class TestAuditNotification(testbase.TestCase): "uuid": "f7ad87ae-4298-91cf-93a0-f35a852e3652", "name": "TEST", "efficacy_specification": [], - "created_at": None, + "created_at": "2016-10-18T09:52:05Z", "display_name": "test goal", "deleted_at": None }, @@ -161,3 +162,67 @@ class TestAuditNotification(testbase.TestCase): }, payload ) + + @freezegun.freeze_time('2016-10-18T09:52:05.219414') + @mock.patch.object(notifications.audit.AuditCreateNotification, '_emit') + def test_send_version_audit_create(self, mock_emit): + goal = utils.create_test_goal(mock.Mock()) + strategy = utils.create_test_strategy(mock.Mock()) + audit = utils.get_test_audit( + mock.Mock(), state='PENDING', + goal_id=goal.id, strategy_id=strategy.id, + goal=goal.as_dict(), strategy=strategy.as_dict()) + notifications.audit.send_create( + 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": "PENDING", + "updated_at": None, + "created_at": None, + "audit_type": "ONESHOT" + }, + "watcher_object.name": "AuditCreatePayload" + }, + payload + ) diff --git a/watcher/tests/notifications/test_notification.py b/watcher/tests/notifications/test_notification.py index 084e9a9bc..9de8db691 100644 --- a/watcher/tests/notifications/test_notification.py +++ b/watcher/tests/notifications/test_notification.py @@ -258,6 +258,8 @@ expected_notification_fingerprints = { 'AuditStateUpdatePayload': '1.0-1a1b606bf14a2c468800c2b010801ce5', 'AuditUpdateNotification': '1.0-9b69de0724fda8310d05e18418178866', 'AuditUpdatePayload': '1.0-d3aace28d9eb978c1ecf833e108f61f7', + 'AuditCreateNotification': '1.0-9b69de0724fda8310d05e18418178866', + 'AuditCreatePayload': '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 5ba6b6ca4..eb00a513b 100644 --- a/watcher/tests/objects/test_audit.py +++ b/watcher/tests/objects/test_audit.py @@ -217,9 +217,14 @@ class TestAuditObjectSendNotifications(base.DbTestCase): @mock.patch.object(db_api.Connection, 'update_audit', mock.Mock()) @mock.patch.object(db_api.Connection, 'get_audit_by_uuid') - def test_send_update_notification(self, mock_get_audit): - mock_get_audit.return_value = self.fake_audit - uuid = self.fake_audit['uuid'] + def test_send_update_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.state = objects.audit.State.ONGOING audit.save() diff --git a/watcher/tests/objects/utils.py b/watcher/tests/objects/utils.py index 6123087a7..dfba81608 100644 --- a/watcher/tests/objects/utils.py +++ b/watcher/tests/objects/utils.py @@ -18,6 +18,18 @@ from watcher import objects from watcher.tests.db import utils as db_utils +def _load_related_objects(context, cls, db_data): + """Replace the DB data with its object counterpart""" + obj_data = db_data.copy() + for name, (obj_cls, _) in cls.object_fields.items(): + if obj_data.get(name): + obj_data[name] = obj_cls(context, **obj_data.get(name).as_dict()) + else: + del obj_data[name] + + return obj_data + + def get_test_audit_template(context, **kw): """Return a AuditTemplate object with appropriate attributes. @@ -53,12 +65,14 @@ def get_test_audit(context, **kw): that a create() could be used to commit it to the DB. """ db_audit = db_utils.get_test_audit(**kw) + obj_data = _load_related_objects(context, objects.Audit, db_audit) + # Let DB generate ID if it isn't specified explicitly if 'id' not in kw: del db_audit['id'] audit = objects.Audit(context) - for key in db_audit: - setattr(audit, key, db_audit[key]) + for key in obj_data: + setattr(audit, key, obj_data[key]) return audit