Merge "Add Action Notification"

This commit is contained in:
Jenkins
2017-03-08 09:58:46 +00:00
committed by Gerrit Code Review
22 changed files with 1188 additions and 120 deletions

View File

@@ -26,6 +26,7 @@ from watcher.applier.actions import factory
from watcher.applier.workflow_engine import default as tflow
from watcher.common import exception
from watcher.common import utils
from watcher import notifications
from watcher import objects
from watcher.tests.db import base
@@ -71,19 +72,19 @@ class TestDefaultWorkFlowEngine(base.DbTestCase):
except Exception as exc:
self.fail(exc)
def create_action(self, action_type, parameters, parents, uuid=None):
def create_action(self, action_type, parameters, parents=None, uuid=None):
action = {
'uuid': uuid or utils.generate_uuid(),
'action_plan_id': 0,
'action_type': action_type,
'input_parameters': parameters,
'state': objects.action.State.PENDING,
'parents': parents,
'parents': parents or [],
}
new_action = objects.Action(self.context, **action)
new_action.create()
new_action.save()
with mock.patch.object(notifications.action, 'send_create'):
new_action.create()
return new_action
@@ -106,8 +107,11 @@ class TestDefaultWorkFlowEngine(base.DbTestCase):
except Exception as exc:
self.fail(exc)
def test_execute_with_one_action(self):
actions = [self.create_action("nop", {'message': 'test'}, None)]
@mock.patch.object(notifications.action, 'send_execution_notification')
@mock.patch.object(notifications.action, 'send_update')
def test_execute_with_one_action(self, mock_send_update,
mock_execution_notification):
actions = [self.create_action("nop", {'message': 'test'})]
try:
self.engine.execute(actions)
self.check_actions_state(actions, objects.action.State.SUCCEEDED)
@@ -115,12 +119,15 @@ class TestDefaultWorkFlowEngine(base.DbTestCase):
except Exception as exc:
self.fail(exc)
def test_execute_nop_sleep(self):
@mock.patch.object(notifications.action, 'send_execution_notification')
@mock.patch.object(notifications.action, 'send_update')
def test_execute_nop_sleep(self, mock_send_update,
mock_execution_notification):
actions = []
first_nop = self.create_action("nop", {'message': 'test'}, [])
second_nop = self.create_action("nop", {'message': 'second test'}, [])
first_nop = self.create_action("nop", {'message': 'test'})
second_nop = self.create_action("nop", {'message': 'second test'})
sleep = self.create_action("sleep", {'duration': 0.0},
[first_nop.uuid, second_nop.uuid])
parents=[first_nop.uuid, second_nop.uuid])
actions.extend([first_nop, second_nop, sleep])
try:
@@ -130,19 +137,23 @@ class TestDefaultWorkFlowEngine(base.DbTestCase):
except Exception as exc:
self.fail(exc)
def test_execute_with_parents(self):
@mock.patch.object(notifications.action, 'send_execution_notification')
@mock.patch.object(notifications.action, 'send_update')
def test_execute_with_parents(self, mock_send_update,
mock_execution_notification):
actions = []
first_nop = self.create_action(
"nop", {'message': 'test'}, [],
"nop", {'message': 'test'},
uuid='bc7eee5c-4fbe-4def-9744-b539be55aa19')
second_nop = self.create_action(
"nop", {'message': 'second test'}, [],
"nop", {'message': 'second test'},
uuid='0565bd5c-aa00-46e5-8d81-2cb5cc1ffa23')
first_sleep = self.create_action(
"sleep", {'duration': 0.0}, [first_nop.uuid, second_nop.uuid],
"sleep", {'duration': 0.0}, parents=[first_nop.uuid,
second_nop.uuid],
uuid='be436531-0da3-4dad-a9c0-ea1d2aff6496')
second_sleep = self.create_action(
"sleep", {'duration': 0.0}, [first_sleep.uuid],
"sleep", {'duration': 0.0}, parents=[first_sleep.uuid],
uuid='9eb51e14-936d-4d12-a500-6ba0f5e0bb1c')
actions.extend([first_nop, second_nop, first_sleep, second_sleep])
@@ -194,10 +205,12 @@ class TestDefaultWorkFlowEngine(base.DbTestCase):
except Exception as exc:
self.fail(exc)
def test_execute_with_two_actions(self):
@mock.patch.object(notifications.action, 'send_execution_notification')
@mock.patch.object(notifications.action, 'send_update')
def test_execute_with_two_actions(self, m_send_update, m_execution):
actions = []
second = self.create_action("sleep", {'duration': 0.0}, None)
first = self.create_action("nop", {'message': 'test'}, None)
second = self.create_action("sleep", {'duration': 0.0})
first = self.create_action("nop", {'message': 'test'})
actions.append(first)
actions.append(second)
@@ -209,12 +222,14 @@ class TestDefaultWorkFlowEngine(base.DbTestCase):
except Exception as exc:
self.fail(exc)
def test_execute_with_three_actions(self):
@mock.patch.object(notifications.action, 'send_execution_notification')
@mock.patch.object(notifications.action, 'send_update')
def test_execute_with_three_actions(self, m_send_update, m_execution):
actions = []
third = self.create_action("nop", {'message': 'next'}, None)
second = self.create_action("sleep", {'duration': 0.0}, None)
first = self.create_action("nop", {'message': 'hello'}, None)
third = self.create_action("nop", {'message': 'next'})
second = self.create_action("sleep", {'duration': 0.0})
first = self.create_action("nop", {'message': 'hello'})
self.check_action_state(first, objects.action.State.PENDING)
self.check_action_state(second, objects.action.State.PENDING)
@@ -231,12 +246,14 @@ class TestDefaultWorkFlowEngine(base.DbTestCase):
except Exception as exc:
self.fail(exc)
def test_execute_with_exception(self):
@mock.patch.object(notifications.action, 'send_execution_notification')
@mock.patch.object(notifications.action, 'send_update')
def test_execute_with_exception(self, m_send_update, m_execution):
actions = []
third = self.create_action("no_exist", {'message': 'next'}, None)
second = self.create_action("sleep", {'duration': 0.0}, None)
first = self.create_action("nop", {'message': 'hello'}, None)
third = self.create_action("no_exist", {'message': 'next'})
second = self.create_action("sleep", {'duration': 0.0})
first = self.create_action("nop", {'message': 'hello'})
self.check_action_state(first, objects.action.State.PENDING)
self.check_action_state(second, objects.action.State.PENDING)
@@ -253,9 +270,12 @@ class TestDefaultWorkFlowEngine(base.DbTestCase):
self.check_action_state(second, objects.action.State.SUCCEEDED)
self.check_action_state(third, objects.action.State.FAILED)
@mock.patch.object(notifications.action, 'send_execution_notification')
@mock.patch.object(notifications.action, 'send_update')
@mock.patch.object(factory.ActionFactory, "make_action")
def test_execute_with_action_exception(self, m_make_action):
actions = [self.create_action("fake_action", {}, None)]
def test_execute_with_action_exception(self, m_make_action, m_send_update,
m_send_execution):
actions = [self.create_action("fake_action", {})]
m_make_action.return_value = FakeAction(mock.Mock())
exc = self.assertRaises(exception.WorkflowExecutionException,

View File

@@ -120,7 +120,7 @@ class TestActionScheduling(base.DbTestCase):
self.context, self.audit.id, solution)
self.assertIsNotNone(action_plan.uuid)
self.assertEqual(1, m_create_scheduled_actions.call_count)
action_graph = m_create_scheduled_actions.call_args[0][1]
action_graph = m_create_scheduled_actions.call_args[0][0]
expected_edges = []
@@ -175,7 +175,7 @@ class TestActionScheduling(base.DbTestCase):
self.context, self.audit.id, solution)
self.assertIsNotNone(action_plan.uuid)
self.assertEqual(1, m_create_scheduled_actions.call_count)
action_graph = m_create_scheduled_actions.call_args[0][1]
action_graph = m_create_scheduled_actions.call_args[0][0]
expected_edges = \
[({'action_type': 'nop',
@@ -231,7 +231,7 @@ class TestActionScheduling(base.DbTestCase):
self.context, self.audit.id, solution)
self.assertIsNotNone(action_plan.uuid)
self.assertEqual(1, m_create_scheduled_actions.call_count)
action_graph = m_create_scheduled_actions.call_args[0][1]
action_graph = m_create_scheduled_actions.call_args[0][0]
expected_edges = \
[({'action_type': 'new_action_type',
@@ -294,7 +294,7 @@ class TestActionScheduling(base.DbTestCase):
self.context, self.audit.id, solution)
self.assertIsNotNone(action_plan.uuid)
self.assertEqual(1, m_create_scheduled_actions.call_count)
action_graph = m_create_scheduled_actions.call_args[0][1]
action_graph = m_create_scheduled_actions.call_args[0][0]
expected_edges = \
[({'action_type': 'migrate',
@@ -372,7 +372,7 @@ class TestActionScheduling(base.DbTestCase):
self.context, self.audit.id, solution)
self.assertIsNotNone(action_plan.uuid)
self.assertEqual(1, m_create_scheduled_actions.call_count)
action_graph = m_create_scheduled_actions.call_args[0][1]
action_graph = m_create_scheduled_actions.call_args[0][0]
expected_edges = \
[({'action_type': 'migrate',
@@ -468,7 +468,7 @@ class TestActionScheduling(base.DbTestCase):
self.context, self.audit.id, solution)
self.assertIsNotNone(action_plan.uuid)
self.assertEqual(1, m_create_scheduled_actions.call_count)
action_graph = m_create_scheduled_actions.call_args[0][1]
action_graph = m_create_scheduled_actions.call_args[0][0]
expected_edges = \
[({'action_type': 'migrate',
@@ -567,7 +567,7 @@ class TestActionScheduling(base.DbTestCase):
self.context, self.audit.id, solution)
self.assertIsNotNone(action_plan.uuid)
self.assertEqual(1, m_create_scheduled_actions.call_count)
action_graph = m_create_scheduled_actions.call_args[0][1]
action_graph = m_create_scheduled_actions.call_args[0][0]
expected_edges = \
[({'action_type': 'resize',
@@ -672,7 +672,7 @@ class TestActionScheduling(base.DbTestCase):
self.context, self.audit.id, solution)
self.assertIsNotNone(action_plan.uuid)
self.assertEqual(1, m_create_scheduled_actions.call_count)
action_graph = m_create_scheduled_actions.call_args[0][1]
action_graph = m_create_scheduled_actions.call_args[0][0]
expected_edges = \
[({'action_type': 'migrate',
@@ -803,7 +803,7 @@ class TestActionScheduling(base.DbTestCase):
self.context, self.audit.id, solution)
self.assertIsNotNone(action_plan.uuid)
self.assertEqual(1, m_create_scheduled_actions.call_count)
action_graph = m_create_scheduled_actions.call_args[0][1]
action_graph = m_create_scheduled_actions.call_args[0][0]
expected_edges = \
[({'action_type': 'migrate',

View File

@@ -0,0 +1,355 @@
# All Rights Reserved.
#
# 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 freezegun
import mock
import oslo_messaging as om
from watcher.common import exception
from watcher.common import rpc
from watcher import notifications
from watcher import objects
from watcher.tests.db import base
from watcher.tests.objects import utils
@freezegun.freeze_time('2016-10-18T09:52:05.219414')
class TestActionNotification(base.DbTestCase):
def setUp(self):
super(TestActionNotification, 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())
self.audit = utils.create_test_audit(mock.Mock(),
strategy_id=self.strategy.id)
self.action_plan = utils.create_test_action_plan(mock.Mock())
def test_send_invalid_action_plan(self):
action_plan = utils.get_test_action_plan(
mock.Mock(), state='DOESNOTMATTER', audit_id=1)
self.assertRaises(
exception.InvalidActionPlan,
notifications.action_plan.send_update,
mock.MagicMock(), action_plan, host='node0')
def test_send_action_update(self):
action = utils.create_test_action(
mock.Mock(), state=objects.action.State.ONGOING,
action_type='nop', input_parameters={'param1': 1, 'param2': 2},
parents=[], action_plan_id=self.action_plan.id)
notifications.action.send_update(
mock.MagicMock(), action, host='node0',
old_state=objects.action.State.PENDING)
# The 1st notification is because we created the object.
# The 2nd notification is because we created the action plan object.
self.assertEqual(4, self.m_notifier.info.call_count)
notification = self.m_notifier.info.call_args[1]
payload = notification['payload']
self.assertEqual("infra-optim:node0", self.m_notifier.publisher_id)
self.assertDictEqual(
{
'watcher_object.namespace': 'watcher',
'watcher_object.version': '1.0',
'watcher_object.name': 'ActionUpdatePayload',
'watcher_object.data': {
'uuid': '10a47dd1-4874-4298-91cf-eff046dbdb8d',
'input_parameters': {
'param2': 2,
'param1': 1
},
'created_at': '2016-10-18T09:52:05Z',
'updated_at': None,
'state_update': {
'watcher_object.namespace': 'watcher',
'watcher_object.version': '1.0',
'watcher_object.name': 'ActionStateUpdatePayload',
'watcher_object.data': {
'old_state': 'PENDING',
'state': 'ONGOING'
}
},
'state': 'ONGOING',
'action_plan': {
'watcher_object.namespace': 'watcher',
'watcher_object.version': '1.0',
'watcher_object.name': 'TerseActionPlanPayload',
'watcher_object.data': {
'uuid': '76be87bd-3422-43f9-93a0-e85a577e3061',
'global_efficacy': {},
'created_at': '2016-10-18T09:52:05Z',
'updated_at': None,
'state': 'ONGOING',
'audit_uuid': '10a47dd1-4874-4298'
'-91cf-eff046dbdb8d',
'strategy_uuid': 'cb3d0b58-4415-4d90'
'-b75b-1e96878730e3',
'deleted_at': None
}
},
'parents': [],
'action_type': 'nop',
'deleted_at': None
}
},
payload
)
def test_send_action_plan_create(self):
action = utils.create_test_action(
mock.Mock(), state=objects.action.State.PENDING,
action_type='nop', input_parameters={'param1': 1, 'param2': 2},
parents=[], action_plan_id=self.action_plan.id)
notifications.action.send_create(mock.MagicMock(), action,
host='node0')
self.assertEqual(4, self.m_notifier.info.call_count)
notification = self.m_notifier.info.call_args[1]
payload = notification['payload']
self.assertEqual("infra-optim:node0", self.m_notifier.publisher_id)
self.assertDictEqual(
{
'watcher_object.namespace': 'watcher',
'watcher_object.version': '1.0',
'watcher_object.name': 'ActionCreatePayload',
'watcher_object.data': {
'uuid': '10a47dd1-4874-4298-91cf-eff046dbdb8d',
'input_parameters': {
'param2': 2,
'param1': 1
},
'created_at': '2016-10-18T09:52:05Z',
'updated_at': None,
'state': 'PENDING',
'action_plan': {
'watcher_object.namespace': 'watcher',
'watcher_object.version': '1.0',
'watcher_object.name': 'TerseActionPlanPayload',
'watcher_object.data': {
'uuid': '76be87bd-3422-43f9-93a0-e85a577e3061',
'global_efficacy': {},
'created_at': '2016-10-18T09:52:05Z',
'updated_at': None,
'state': 'ONGOING',
'audit_uuid': '10a47dd1-4874-4298'
'-91cf-eff046dbdb8d',
'strategy_uuid': 'cb3d0b58-4415-4d90'
'-b75b-1e96878730e3',
'deleted_at': None
}
},
'parents': [],
'action_type': 'nop',
'deleted_at': None
}
},
payload
)
def test_send_action_delete(self):
action = utils.create_test_action(
mock.Mock(), state=objects.action.State.DELETED,
action_type='nop', input_parameters={'param1': 1, 'param2': 2},
parents=[], action_plan_id=self.action_plan.id)
notifications.action.send_delete(mock.MagicMock(), action,
host='node0')
# The 1st notification is because we created the audit object.
# The 2nd notification is because we created the action plan object.
self.assertEqual(4, self.m_notifier.info.call_count)
notification = self.m_notifier.info.call_args[1]
payload = notification['payload']
self.assertEqual("infra-optim:node0", self.m_notifier.publisher_id)
self.assertDictEqual(
{
'watcher_object.namespace': 'watcher',
'watcher_object.version': '1.0',
'watcher_object.name': 'ActionDeletePayload',
'watcher_object.data': {
'uuid': '10a47dd1-4874-4298-91cf-eff046dbdb8d',
'input_parameters': {
'param2': 2,
'param1': 1
},
'created_at': '2016-10-18T09:52:05Z',
'updated_at': None,
'state': 'DELETED',
'action_plan': {
'watcher_object.namespace': 'watcher',
'watcher_object.version': '1.0',
'watcher_object.name': 'TerseActionPlanPayload',
'watcher_object.data': {
'uuid': '76be87bd-3422-43f9-93a0-e85a577e3061',
'global_efficacy': {},
'created_at': '2016-10-18T09:52:05Z',
'updated_at': None,
'state': 'ONGOING',
'audit_uuid': '10a47dd1-4874-4298'
'-91cf-eff046dbdb8d',
'strategy_uuid': 'cb3d0b58-4415-4d90'
'-b75b-1e96878730e3',
'deleted_at': None
}
},
'parents': [],
'action_type': 'nop',
'deleted_at': None
}
},
payload
)
def test_send_action_execution(self):
action = utils.create_test_action(
mock.Mock(), state=objects.action.State.PENDING,
action_type='nop', input_parameters={'param1': 1, 'param2': 2},
parents=[], action_plan_id=self.action_plan.id)
notifications.action.send_execution_notification(
mock.MagicMock(), action, 'execution', phase='start', host='node0')
# The 1st notification is because we created the audit object.
# The 2nd notification is because we created the action plan object.
self.assertEqual(4, self.m_notifier.info.call_count)
notification = self.m_notifier.info.call_args[1]
self.assertEqual("infra-optim:node0", self.m_notifier.publisher_id)
self.assertDictEqual(
{
'event_type': 'action.execution.start',
'payload': {
'watcher_object.namespace': 'watcher',
'watcher_object.version': '1.0',
'watcher_object.name': 'ActionExecutionPayload',
'watcher_object.data': {
'uuid': '10a47dd1-4874-4298-91cf-eff046dbdb8d',
'input_parameters': {
'param2': 2,
'param1': 1
},
'created_at': '2016-10-18T09:52:05Z',
'fault': None,
'updated_at': None,
'state': 'PENDING',
'action_plan': {
'watcher_object.namespace': 'watcher',
'watcher_object.version': '1.0',
'watcher_object.name': 'TerseActionPlanPayload',
'watcher_object.data': {
'uuid': '76be87bd-3422-43f9-93a0-e85a577e3061',
'global_efficacy': {},
'created_at': '2016-10-18T09:52:05Z',
'updated_at': None,
'state': 'ONGOING',
'audit_uuid': '10a47dd1-4874-4298'
'-91cf-eff046dbdb8d',
'strategy_uuid': 'cb3d0b58-4415-4d90'
'-b75b-1e96878730e3',
'deleted_at': None
}
},
'parents': [],
'action_type': 'nop',
'deleted_at': None
}
}
},
notification
)
def test_send_action_execution_with_error(self):
action = utils.create_test_action(
mock.Mock(), state=objects.action.State.FAILED,
action_type='nop', input_parameters={'param1': 1, 'param2': 2},
parents=[], action_plan_id=self.action_plan.id)
try:
# This is to load the exception in sys.exc_info()
raise exception.WatcherException("TEST")
except exception.WatcherException:
notifications.action.send_execution_notification(
mock.MagicMock(), action, 'execution', phase='error',
host='node0', priority='error')
self.assertEqual(1, self.m_notifier.error.call_count)
notification = self.m_notifier.error.call_args[1]
self.assertEqual("infra-optim:node0", self.m_notifier.publisher_id)
self.assertDictEqual(
{
'event_type': 'action.execution.error',
'payload': {
'watcher_object.namespace': 'watcher',
'watcher_object.version': '1.0',
'watcher_object.name': 'ActionExecutionPayload',
'watcher_object.data': {
'uuid': '10a47dd1-4874-4298-91cf-eff046dbdb8d',
'input_parameters': {
'param2': 2,
'param1': 1
},
'created_at': '2016-10-18T09:52:05Z',
'fault': {
'watcher_object.data': {
'exception': u'WatcherException',
'exception_message': u'TEST',
'function_name': (
'test_send_action_execution_with_error'),
'module_name': (
'watcher.tests.notifications.'
'test_action_notification')
},
'watcher_object.name': 'ExceptionPayload',
'watcher_object.namespace': 'watcher',
'watcher_object.version': '1.0'
},
'updated_at': None,
'state': 'FAILED',
'action_plan': {
'watcher_object.namespace': 'watcher',
'watcher_object.version': '1.0',
'watcher_object.name': 'TerseActionPlanPayload',
'watcher_object.data': {
'uuid': '76be87bd-3422-43f9-93a0-e85a577e3061',
'global_efficacy': {},
'created_at': '2016-10-18T09:52:05Z',
'updated_at': None,
'state': 'ONGOING',
'audit_uuid': '10a47dd1-4874-4298'
'-91cf-eff046dbdb8d',
'strategy_uuid': 'cb3d0b58-4415-4d90'
'-b75b-1e96878730e3',
'deleted_at': None
}
},
'parents': [],
'action_type': 'nop',
'deleted_at': None
}
}
},
notification
)

View File

@@ -267,16 +267,27 @@ expected_notification_fingerprints = {
'AuditActionPayload': '1.0-09f5d005f94ba9e5f6b9200170332c52',
'GoalPayload': '1.0-fa1fecb8b01dd047eef808ded4d50d1a',
'StrategyPayload': '1.0-94f01c137b083ac236ae82573c1fcfc1',
'ActionPlanActionPayload': '1.0-34871caf18e9b43a28899953c1c9733a',
'ActionPlanActionPayload': '1.0-d9f134708e06cf2ff2d3b8d522ac2aa8',
'ActionPlanCreateNotification': '1.0-9b69de0724fda8310d05e18418178866',
'ActionPlanCreatePayload': '1.0-ffc3087acd73351b14f3dcc30e105027',
'ActionPlanCreatePayload': '1.0-23d0abbfa43acfd49b2b3097770efdce',
'ActionPlanDeleteNotification': '1.0-9b69de0724fda8310d05e18418178866',
'ActionPlanDeletePayload': '1.0-ffc3087acd73351b14f3dcc30e105027',
'ActionPlanPayload': '1.0-ffc3087acd73351b14f3dcc30e105027',
'ActionPlanDeletePayload': '1.0-23d0abbfa43acfd49b2b3097770efdce',
'ActionPlanPayload': '1.0-23d0abbfa43acfd49b2b3097770efdce',
'ActionPlanStateUpdatePayload': '1.0-1a1b606bf14a2c468800c2b010801ce5',
'ActionPlanUpdateNotification': '1.0-9b69de0724fda8310d05e18418178866',
'ActionPlanUpdatePayload': '1.0-7912a45fe53775c721f42aa87f06a023',
'ActionPlanUpdatePayload': '1.0-3e1a348a0579c6c43c1c3d7257e3f26b',
'ActionPlanActionNotification': '1.0-9b69de0724fda8310d05e18418178866',
'ActionCreateNotification': '1.0-9b69de0724fda8310d05e18418178866',
'ActionCreatePayload': '1.0-519b93b7450319d8928b4b6e6362df31',
'ActionDeleteNotification': '1.0-9b69de0724fda8310d05e18418178866',
'ActionDeletePayload': '1.0-519b93b7450319d8928b4b6e6362df31',
'ActionExecutionNotification': '1.0-9b69de0724fda8310d05e18418178866',
'ActionExecutionPayload': '1.0-bff9f820a2abf7bb6d7027b7450157df',
'ActionPayload': '1.0-519b93b7450319d8928b4b6e6362df31',
'ActionStateUpdatePayload': '1.0-1a1b606bf14a2c468800c2b010801ce5',
'ActionUpdateNotification': '1.0-9b69de0724fda8310d05e18418178866',
'ActionUpdatePayload': '1.0-03306c7e7f4d49ac328c261eff6b30b8',
'TerseActionPlanPayload': '1.0-42bf7a5585cc111a9a4dbc008a04c67e',
}

View File

@@ -19,7 +19,9 @@ import iso8601
import mock
from watcher.common import exception
from watcher.common import utils as c_utils
from watcher.db.sqlalchemy import api as db_api
from watcher import notifications
from watcher import objects
from watcher.tests.db import base
from watcher.tests.db import utils
@@ -47,6 +49,13 @@ class TestActionObject(base.DbTestCase):
def setUp(self):
super(TestActionObject, self).setUp()
p_action_notifications = mock.patch.object(
notifications, 'action_plan', autospec=True)
self.m_action_notifications = p_action_notifications.start()
self.addCleanup(p_action_notifications.stop)
self.m_send_update = self.m_action_notifications.send_update
self.fake_action_plan = utils.create_test_action_plan(
id=self.action_plan_id)
@@ -73,6 +82,7 @@ class TestActionObject(base.DbTestCase):
self.context, action_id, eager=self.eager)
self.assertEqual(self.context, action._context)
self.eager_action_assert(action)
self.assertEqual(0, self.m_send_update.call_count)
@mock.patch.object(db_api.Connection, 'get_action_by_uuid')
def test_get_by_uuid(self, mock_get_action):
@@ -82,6 +92,7 @@ class TestActionObject(base.DbTestCase):
mock_get_action.assert_called_once_with(
self.context, uuid, eager=self.eager)
self.assertEqual(self.context, action._context)
self.assertEqual(0, self.m_send_update.call_count)
def test_get_bad_id_and_uuid(self):
self.assertRaises(exception.InvalidIdentity,
@@ -98,19 +109,31 @@ class TestActionObject(base.DbTestCase):
self.assertEqual(self.context, actions[0]._context)
for action in actions:
self.eager_action_assert(action)
self.assertEqual(0, self.m_send_update.call_count)
@mock.patch.object(objects.Strategy, 'get')
@mock.patch.object(objects.Audit, 'get')
@mock.patch.object(db_api.Connection, 'update_action')
@mock.patch.object(db_api.Connection, 'get_action_by_uuid')
def test_save(self, mock_get_action, mock_update_action):
def test_save(self, mock_get_action, mock_update_action, mock_get_audit,
mock_get_strategy):
mock_get_action.return_value = self.fake_action
fake_saved_action = self.fake_action.copy()
mock_get_audit.return_value = mock.PropertyMock(
uuid=c_utils.generate_uuid())
mock_get_strategy.return_value = mock.PropertyMock(
uuid=c_utils.generate_uuid())
fake_saved_action['updated_at'] = datetime.datetime.utcnow()
mock_update_action.return_value = fake_saved_action
uuid = self.fake_action['uuid']
action = objects.Action.get_by_uuid(
self.context, uuid, eager=self.eager)
action.state = objects.action.State.SUCCEEDED
action.save()
if not self.eager:
self.assertRaises(exception.EagerlyLoadedActionRequired,
action.save)
else:
action.save()
expected_update_at = fake_saved_action['updated_at'].replace(
tzinfo=iso8601.iso8601.Utc())
@@ -121,6 +144,7 @@ class TestActionObject(base.DbTestCase):
uuid, {'state': objects.action.State.SUCCEEDED})
self.assertEqual(self.context, action._context)
self.assertEqual(expected_update_at, action.updated_at)
self.assertEqual(0, self.m_send_update.call_count)
@mock.patch.object(db_api.Connection, 'get_action_by_uuid')
def test_refresh(self, mock_get_action):
@@ -137,6 +161,7 @@ class TestActionObject(base.DbTestCase):
self.assertEqual(expected, mock_get_action.call_args_list)
self.assertEqual(self.context, action._context)
self.eager_action_assert(action)
self.assertEqual(0, self.m_send_update.call_count)
class TestCreateDeleteActionObject(base.DbTestCase):
@@ -160,11 +185,14 @@ class TestCreateDeleteActionObject(base.DbTestCase):
mock_create_action.assert_called_once_with(expected_action)
self.assertEqual(self.context, action._context)
@mock.patch.object(notifications.action, 'send_delete')
@mock.patch.object(notifications.action, 'send_update')
@mock.patch.object(db_api.Connection, 'update_action')
@mock.patch.object(db_api.Connection, 'soft_delete_action')
@mock.patch.object(db_api.Connection, 'get_action_by_uuid')
def test_soft_delete(self, mock_get_action,
mock_soft_delete_action, mock_update_action):
mock_soft_delete_action, mock_update_action,
mock_send_update, mock_send_delete):
mock_get_action.return_value = self.fake_action
fake_deleted_action = self.fake_action.copy()
fake_deleted_action['deleted_at'] = datetime.datetime.utcnow()