Merge "Add audit.planner events"

This commit is contained in:
Jenkins
2016-11-16 16:52:13 +00:00
committed by Gerrit Code Review
10 changed files with 331 additions and 68 deletions

View File

@@ -0,0 +1,70 @@
{
"priority": "INFO",
"payload": {
"watcher_object.data": {
"audit_type": "ONESHOT",
"parameters": {
"para2": "hello",
"para1": 3.2
},
"state": "ONGOING",
"updated_at": null,
"deleted_at": null,
"fault": 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": "AuditActionPayload",
"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.planner.end",
"message_id": "cbcf9f2c-7c53-4b4d-91ec-db49cca024b6"
}

View File

@@ -0,0 +1,80 @@
{
"priority": "ERROR",
"payload": {
"watcher_object.data": {
"audit_type": "ONESHOT",
"parameters": {
"para2": "hello",
"para1": 3.2
},
"state": "ONGOING",
"updated_at": null,
"deleted_at": null,
"fault": {
"watcher_object.data": {
"exception": "WatcherException",
"exception_message": "TEST",
"function_name": "test_send_audit_action_with_error",
"module_name": "watcher.tests.notifications.test_audit_notification"
},
"watcher_object.name": "ExceptionPayload",
"watcher_object.namespace": "watcher",
"watcher_object.version": "1.0"
},
"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": "AuditActionPayload",
"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.planner.error",
"message_id": "cbcf9f2c-7c53-4b4d-91ec-db49cca024b6"
}

View File

@@ -0,0 +1,70 @@
{
"priority": "INFO",
"payload": {
"watcher_object.data": {
"audit_type": "ONESHOT",
"parameters": {
"para2": "hello",
"para1": 3.2
},
"state": "ONGOING",
"updated_at": null,
"deleted_at": null,
"fault": 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": "AuditActionPayload",
"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.planner.start",
"message_id": "cbcf9f2c-7c53-4b4d-91ec-db49cca024b6"
}

View File

@@ -24,13 +24,16 @@ from oslo_log import log
from watcher.decision_engine.planner import manager as planner_manager from watcher.decision_engine.planner import manager as planner_manager
from watcher.decision_engine.strategy.context import default as default_context from watcher.decision_engine.strategy.context import default as default_context
from watcher import notifications
from watcher import objects from watcher import objects
from watcher.objects import fields
LOG = log.getLogger(__name__) LOG = log.getLogger(__name__)
@six.add_metaclass(abc.ABCMeta) @six.add_metaclass(abc.ABCMeta)
class BaseAuditHandler(object): class BaseAuditHandler(object):
@abc.abstractmethod @abc.abstractmethod
def execute(self, audit_uuid, request_context): def execute(self, audit_uuid, request_context):
raise NotImplementedError() raise NotImplementedError()
@@ -70,6 +73,25 @@ class AuditHandler(BaseAuditHandler):
def strategy_context(self): def strategy_context(self):
return self._strategy_context return self._strategy_context
def do_schedule(self, request_context, audit, solution):
try:
notifications.audit.send_action_notification(
request_context, audit,
action=fields.NotificationAction.PLANNER,
phase=fields.NotificationPhase.START)
self.planner.schedule(request_context, audit.id, solution)
notifications.audit.send_action_notification(
request_context, audit,
action=fields.NotificationAction.PLANNER,
phase=fields.NotificationPhase.END)
except Exception:
notifications.audit.send_action_notification(
request_context, audit,
action=fields.NotificationAction.PLANNER,
priority=fields.NotificationPriority.ERROR,
phase=fields.NotificationPhase.ERROR)
raise
@staticmethod @staticmethod
def update_audit_state(audit, state): def update_audit_state(audit, state):
LOG.debug("Update audit state: %s", state) LOG.debug("Update audit state: %s", state)
@@ -82,8 +104,7 @@ class AuditHandler(BaseAuditHandler):
self.update_audit_state(audit, objects.audit.State.ONGOING) self.update_audit_state(audit, objects.audit.State.ONGOING)
def post_execute(self, audit, solution, request_context): def post_execute(self, audit, solution, request_context):
self.planner.schedule(request_context, audit.id, solution) self.do_schedule(request_context, audit, solution)
# change state of the audit to SUCCEEDED # change state of the audit to SUCCEEDED
self.update_audit_state(audit, objects.audit.State.SUCCEEDED) self.update_audit_state(audit, objects.audit.State.SUCCEEDED)

View File

@@ -91,7 +91,7 @@ class ContinuousAuditHandler(base.AuditHandler):
self.execute(audit, request_context) self.execute(audit, request_context)
def post_execute(self, audit, solution, request_context): def post_execute(self, audit, solution, request_context):
self.planner.schedule(request_context, audit.id, solution) self.do_schedule(request_context, audit, solution)
def launch_audits_periodically(self): def launch_audits_periodically(self):
audit_context = context.RequestContext(is_admin=True) audit_context = context.RequestContext(is_admin=True)

View File

@@ -58,7 +58,8 @@ class EventType(NotificationObject):
# Version 1.0: Initial version # Version 1.0: Initial version
# Version 1.1: Added STRATEGY action in NotificationAction enum # Version 1.1: Added STRATEGY action in NotificationAction enum
VERSION = '1.1' # Version 1.2: Added PLANNER action in NotificationAction enum
VERSION = '1.2'
fields = { fields = {
'object': wfields.StringField(), 'object': wfields.StringField(),

View File

@@ -127,8 +127,9 @@ class NotificationAction(BaseWatcherEnum):
DELETE = 'delete' DELETE = 'delete'
STRATEGY = 'strategy' STRATEGY = 'strategy'
PLANNER = 'planner'
ALL = (CREATE, UPDATE, EXCEPTION, DELETE, STRATEGY) ALL = (CREATE, UPDATE, EXCEPTION, DELETE, STRATEGY, PLANNER)
class NotificationPriorityField(BaseEnumField): class NotificationPriorityField(BaseEnumField):

View File

@@ -65,6 +65,12 @@ class TestOneShotAuditHandler(base.DbTestCase):
phase=objects.fields.NotificationPhase.START), phase=objects.fields.NotificationPhase.START),
mock.call(self.context, self.audit, mock.call(self.context, self.audit,
action=objects.fields.NotificationAction.STRATEGY, action=objects.fields.NotificationAction.STRATEGY,
phase=objects.fields.NotificationPhase.END),
mock.call(self.context, self.audit,
action=objects.fields.NotificationAction.PLANNER,
phase=objects.fields.NotificationPhase.START),
mock.call(self.context, self.audit,
action=objects.fields.NotificationAction.PLANNER,
phase=objects.fields.NotificationPhase.END)] phase=objects.fields.NotificationPhase.END)]
self.assertEqual( self.assertEqual(
@@ -106,6 +112,12 @@ class TestOneShotAuditHandler(base.DbTestCase):
phase=objects.fields.NotificationPhase.START), phase=objects.fields.NotificationPhase.START),
mock.call(self.context, self.audit, mock.call(self.context, self.audit,
action=objects.fields.NotificationAction.STRATEGY, action=objects.fields.NotificationAction.STRATEGY,
phase=objects.fields.NotificationPhase.END),
mock.call(self.context, self.audit,
action=objects.fields.NotificationAction.PLANNER,
phase=objects.fields.NotificationPhase.START),
mock.call(self.context, self.audit,
action=objects.fields.NotificationAction.PLANNER,
phase=objects.fields.NotificationPhase.END)] phase=objects.fields.NotificationPhase.END)]
self.assertEqual( self.assertEqual(
@@ -125,6 +137,12 @@ class TestOneShotAuditHandler(base.DbTestCase):
phase=objects.fields.NotificationPhase.START), phase=objects.fields.NotificationPhase.START),
mock.call(self.context, self.audit, mock.call(self.context, self.audit,
action=objects.fields.NotificationAction.STRATEGY, action=objects.fields.NotificationAction.STRATEGY,
phase=objects.fields.NotificationPhase.END),
mock.call(self.context, self.audit,
action=objects.fields.NotificationAction.PLANNER,
phase=objects.fields.NotificationPhase.START),
mock.call(self.context, self.audit,
action=objects.fields.NotificationAction.PLANNER,
phase=objects.fields.NotificationPhase.END)] phase=objects.fields.NotificationPhase.END)]
self.assertEqual( self.assertEqual(

View File

@@ -14,43 +14,58 @@
import freezegun import freezegun
import mock import mock
import oslo_messaging as om
from watcher.common import exception from watcher.common import exception
from watcher.common import rpc
from watcher import notifications from watcher import notifications
from watcher import objects from watcher import objects
from watcher.tests.db import base from watcher.tests.db import base
from watcher.tests.objects import utils from watcher.tests.objects import utils
@freezegun.freeze_time('2016-10-18T09:52:05.219414')
class TestAuditNotification(base.DbTestCase): class TestAuditNotification(base.DbTestCase):
@mock.patch.object(notifications.audit.AuditUpdateNotification, '_emit') def setUp(self):
def test_send_version_invalid_audit(self, mock_emit): super(TestAuditNotification, self).setUp()
p_get_notifier = mock.patch.object(rpc, 'get_notifier')
m_get_notifier = p_get_notifier.start()
self.addCleanup(p_get_notifier.stop)
self.m_notifier = mock.Mock(spec=om.Notifier)
def fake_get_notifier(publisher_id):
self.m_notifier.publisher_id = publisher_id
return self.m_notifier
m_get_notifier.side_effect = fake_get_notifier
self.goal = utils.create_test_goal(mock.Mock())
self.strategy = utils.create_test_strategy(mock.Mock())
def test_send_invalid_audit(self):
audit = utils.get_test_audit(mock.Mock(), state='DOESNOTMATTER', audit = utils.get_test_audit(mock.Mock(), state='DOESNOTMATTER',
goal_id=1) goal_id=1)
self.assertRaises( self.assertRaises(
exception.InvalidAudit, exception.InvalidAudit,
notifications.audit.send_update, notifications.audit.send_update,
mock.MagicMock(), audit, 'host', 'node0') mock.MagicMock(), audit, host='node0')
@freezegun.freeze_time('2016-10-18T09:52:05.219414') def test_send_audit_update_with_strategy(self):
@mock.patch.object(notifications.audit.AuditUpdateNotification, '_emit')
def test_send_version_audit_update_with_strategy(self, mock_emit):
goal = utils.create_test_goal(mock.Mock())
strategy = utils.create_test_strategy(mock.Mock())
audit = utils.create_test_audit( audit = utils.create_test_audit(
mock.Mock(), state=objects.audit.State.ONGOING, mock.Mock(), state=objects.audit.State.ONGOING,
goal_id=goal.id, strategy_id=strategy.id, goal_id=self.goal.id, strategy_id=self.strategy.id,
goal=goal, strategy=strategy) goal=self.goal, strategy=self.strategy)
notifications.audit.send_update( notifications.audit.send_update(
mock.MagicMock(), audit, 'host', 'node0', mock.MagicMock(), audit, host='node0',
old_state=objects.audit.State.PENDING) old_state=objects.audit.State.PENDING)
self.assertEqual(1, mock_emit.call_count) # The 1st notification is because we created the object.
notification = mock_emit.call_args_list[0][1] self.assertEqual(2, self.m_notifier.info.call_count)
notification = self.m_notifier.info.call_args[1]
payload = notification['payload'] payload = notification['payload']
self.assertEqual("infra-optim:node0", self.m_notifier.publisher_id)
self.assertDictEqual( self.assertDictEqual(
{ {
"watcher_object.namespace": "watcher", "watcher_object.namespace": "watcher",
@@ -108,21 +123,19 @@ class TestAuditNotification(base.DbTestCase):
payload payload
) )
@freezegun.freeze_time('2016-10-18T09:52:05.219414') def test_send_audit_update_without_strategy(self):
@mock.patch.object(notifications.audit.AuditUpdateNotification, '_emit')
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( audit = utils.get_test_audit(
mock.Mock(), state=objects.audit.State.ONGOING, mock.Mock(), state=objects.audit.State.ONGOING,
goal_id=goal.id, goal=goal) goal_id=self.goal.id, goal=self.goal)
notifications.audit.send_update( notifications.audit.send_update(
mock.MagicMock(), audit, 'host', 'node0', mock.MagicMock(), audit, host='node0',
old_state=objects.audit.State.PENDING) old_state=objects.audit.State.PENDING)
self.assertEqual(1, mock_emit.call_count) self.assertEqual(1, self.m_notifier.info.call_count)
notification = mock_emit.call_args_list[0][1] notification = self.m_notifier.info.call_args[1]
payload = notification['payload'] payload = notification['payload']
self.assertEqual("infra-optim:node0", self.m_notifier.publisher_id)
self.assertDictEqual( self.assertDictEqual(
{ {
"watcher_object.namespace": "watcher", "watcher_object.namespace": "watcher",
@@ -167,22 +180,19 @@ class TestAuditNotification(base.DbTestCase):
payload payload
) )
@freezegun.freeze_time('2016-10-18T09:52:05.219414') def test_send_audit_create(self):
@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( audit = utils.get_test_audit(
mock.Mock(), state=objects.audit.State.PENDING, mock.Mock(), state=objects.audit.State.PENDING,
goal_id=goal.id, strategy_id=strategy.id, goal_id=self.goal.id, strategy_id=self.strategy.id,
goal=goal.as_dict(), strategy=strategy.as_dict()) goal=self.goal.as_dict(), strategy=self.strategy.as_dict())
notifications.audit.send_create( notifications.audit.send_create(
mock.MagicMock(), audit, 'host', 'node0') mock.MagicMock(), audit, host='node0')
self.assertEqual(1, mock_emit.call_count) self.assertEqual(1, self.m_notifier.info.call_count)
notification = mock_emit.call_args_list[0][1] notification = self.m_notifier.info.call_args[1]
payload = notification['payload'] payload = notification['payload']
self.assertEqual("infra-optim:node0", self.m_notifier.publisher_id)
self.assertDictEqual( self.assertDictEqual(
{ {
"watcher_object.namespace": "watcher", "watcher_object.namespace": "watcher",
@@ -231,21 +241,19 @@ class TestAuditNotification(base.DbTestCase):
payload payload
) )
@freezegun.freeze_time('2016-10-18T09:52:05.219414') def test_send_audit_delete(self):
@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( audit = utils.create_test_audit(
mock.Mock(), state=objects.audit.State.DELETED, mock.Mock(), state=objects.audit.State.DELETED,
goal_id=goal.id, strategy_id=strategy.id) goal_id=self.goal.id, strategy_id=self.strategy.id)
notifications.audit.send_delete( notifications.audit.send_delete(
mock.MagicMock(), audit, 'host', 'node0') mock.MagicMock(), audit, host='node0')
self.assertEqual(1, mock_emit.call_count) # The 1st notification is because we created the object.
notification = mock_emit.call_args_list[0][1] self.assertEqual(2, self.m_notifier.info.call_count)
notification = self.m_notifier.info.call_args[1]
payload = notification['payload'] payload = notification['payload']
self.assertEqual("infra-optim:node0", self.m_notifier.publisher_id)
self.assertDictEqual( self.assertDictEqual(
{ {
"watcher_object.namespace": "watcher", "watcher_object.namespace": "watcher",
@@ -294,22 +302,21 @@ class TestAuditNotification(base.DbTestCase):
payload payload
) )
@freezegun.freeze_time('2016-10-18T09:52:05.219414') def test_send_audit_action(self):
@mock.patch.object(notifications.audit.AuditActionNotification, '_emit')
def test_send_audit_action(self, mock_emit):
goal = utils.create_test_goal(mock.Mock())
strategy = utils.create_test_strategy(mock.Mock())
audit = utils.create_test_audit( audit = utils.create_test_audit(
mock.Mock(), state=objects.audit.State.ONGOING, mock.Mock(), state=objects.audit.State.ONGOING,
goal_id=goal.id, strategy_id=strategy.id, goal_id=self.goal.id, strategy_id=self.strategy.id,
goal=goal, strategy=strategy) goal=self.goal, strategy=self.strategy)
notifications.audit.send_action_notification( notifications.audit.send_action_notification(
mock.MagicMock(), audit, host='node0', mock.MagicMock(), audit, host='node0',
action='strategy', phase='start') action='strategy', phase='start')
self.assertEqual(1, mock_emit.call_count) # The 1st notification is because we created the object.
notification = mock_emit.call_args_list[0][1] self.assertEqual(2, self.m_notifier.info.call_count)
notification = self.m_notifier.info.call_args[1]
notification = self.m_notifier.info.call_args[1]
self.assertEqual("infra-optim:node0", self.m_notifier.publisher_id)
self.assertDictEqual( self.assertDictEqual(
{ {
"event_type": "audit.strategy.start", "event_type": "audit.strategy.start",
@@ -357,21 +364,16 @@ class TestAuditNotification(base.DbTestCase):
"watcher_object.name": "AuditActionPayload", "watcher_object.name": "AuditActionPayload",
"watcher_object.namespace": "watcher", "watcher_object.namespace": "watcher",
"watcher_object.version": "1.0" "watcher_object.version": "1.0"
}, }
"publisher_id": "infra-optim:node0"
}, },
notification notification
) )
@freezegun.freeze_time('2016-10-18T09:52:05.219414') def test_send_audit_action_with_error(self):
@mock.patch.object(notifications.audit.AuditActionNotification, '_emit')
def test_send_audit_action_with_error(self, mock_emit):
goal = utils.create_test_goal(mock.Mock())
strategy = utils.create_test_strategy(mock.Mock())
audit = utils.create_test_audit( audit = utils.create_test_audit(
mock.Mock(), state=objects.audit.State.ONGOING, mock.Mock(), state=objects.audit.State.ONGOING,
goal_id=goal.id, strategy_id=strategy.id, goal_id=self.goal.id, strategy_id=self.strategy.id,
goal=goal, strategy=strategy) goal=self.goal, strategy=self.strategy)
try: try:
# This is to load the exception in sys.exc_info() # This is to load the exception in sys.exc_info()
@@ -381,8 +383,9 @@ class TestAuditNotification(base.DbTestCase):
mock.MagicMock(), audit, host='node0', mock.MagicMock(), audit, host='node0',
action='strategy', priority='error', phase='error') action='strategy', priority='error', phase='error')
self.assertEqual(1, mock_emit.call_count) self.assertEqual(1, self.m_notifier.error.call_count)
notification = mock_emit.call_args_list[0][1] notification = self.m_notifier.error.call_args[1]
self.assertEqual("infra-optim:node0", self.m_notifier.publisher_id)
self.assertDictEqual( self.assertDictEqual(
{ {
"event_type": "audit.strategy.error", "event_type": "audit.strategy.error",
@@ -442,8 +445,7 @@ class TestAuditNotification(base.DbTestCase):
"watcher_object.name": "AuditActionPayload", "watcher_object.name": "AuditActionPayload",
"watcher_object.namespace": "watcher", "watcher_object.namespace": "watcher",
"watcher_object.version": "1.0" "watcher_object.version": "1.0"
}, }
"publisher_id": "infra-optim:node0"
}, },
notification notification
) )

View File

@@ -250,7 +250,7 @@ class TestNotificationBase(testbase.TestCase):
expected_notification_fingerprints = { expected_notification_fingerprints = {
'EventType': '1.1-652f407fcf72d2045d65974d23c78173', 'EventType': '1.2-633c2d32fa849d2a6f8bda3b0db88332',
'ExceptionNotification': '1.0-9b69de0724fda8310d05e18418178866', 'ExceptionNotification': '1.0-9b69de0724fda8310d05e18418178866',
'ExceptionPayload': '1.0-4516ae282a55fe2fd5c754967ee6248b', 'ExceptionPayload': '1.0-4516ae282a55fe2fd5c754967ee6248b',
'NotificationPublisher': '1.0-bbbc1402fb0e443a3eb227cc52b61545', 'NotificationPublisher': '1.0-bbbc1402fb0e443a3eb227cc52b61545',