senlin/senlin/tests/unit/engine/actions/test_node_action.py

576 lines
25 KiB
Python

# 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 mock
from senlin.common import scaleutils
from senlin.engine.actions import base as base_action
from senlin.engine.actions import node_action
from senlin.engine import cluster as cluster_mod
from senlin.engine import event as event_mod
from senlin.engine import node as node_mod
from senlin.engine import senlin_lock as lock
from senlin.policies import base as policy_mod
from senlin.tests.unit.common import base
from senlin.tests.unit.common import utils
@mock.patch.object(node_mod.Node, 'load')
class NodeActionTest(base.SenlinTestCase):
def setUp(self):
super(NodeActionTest, self).setUp()
self.ctx = utils.dummy_context()
def test_do_create(self, mock_load):
node = mock.Mock()
node.id = 'NID'
node.do_create = mock.Mock(return_value=None)
mock_load.return_value = node
action = node_action.NodeAction(node.id, 'ACTION', self.ctx)
# Test node creation failure path
res_code, res_msg = action.do_create()
self.assertEqual(action.RES_ERROR, res_code)
self.assertEqual('Node creation failed.', res_msg)
node.do_create.assert_called_once_with(action.context)
node.reset_mock()
# Test node creation success path
node.do_create = mock.Mock(return_value=mock.Mock())
res_code, res_msg = action.do_create()
self.assertEqual(action.RES_OK, res_code)
self.assertEqual('Node created successfully.', res_msg)
node.do_create.assert_called_once_with(action.context)
@mock.patch.object(scaleutils, 'check_size_params')
@mock.patch.object(cluster_mod.Cluster, 'load')
def test_do_create_with_cluster_id_specified(self, mock_c_load,
mock_check, mock_load):
cluster = mock.Mock()
cluster.id = 'CID'
cluster.desired_capacity = 0
mock_c_load.return_value = cluster
node = mock.Mock()
node.id = 'NID'
node.do_create = mock.Mock(return_value=None)
node.cluster_id = cluster.id
mock_load.return_value = node
mock_check.return_value = ''
action = node_action.NodeAction(node.id, 'ACTION', self.ctx,
cause=base_action.CAUSE_RPC)
node.do_create = mock.Mock(return_value=mock.Mock())
res_code, res_msg = action.do_create()
self.assertEqual(action.RES_OK, res_code)
mock_check.assert_called_once_with(cluster, 1, None, None, True)
mock_c_load.assert_called_once_with(action.context, 'CID')
cluster.store.assert_called_once_with(action.context)
self.assertEqual(1, cluster.desired_capacity)
cluster.add_node.assert_called_once_with(node)
def test_do_delete(self, mock_load):
node = mock.Mock()
node.id = 'NID'
node.do_delete = mock.Mock(return_value=None)
mock_load.return_value = node
action = node_action.NodeAction('ID', 'ACTION', self.ctx)
# Test failed node deletion path
res_code, res_msg = action.do_delete()
self.assertEqual(action.RES_ERROR, res_code)
self.assertEqual('Node deletion failed.', res_msg)
node.do_delete.assert_called_once_with(action.context)
node.reset_mock()
# Test node deletion success path
node.do_delete = mock.Mock(return_value=mock.Mock())
res_code, res_msg = action.do_delete()
self.assertEqual(action.RES_OK, res_code)
self.assertEqual('Node deleted successfully.', res_msg)
node.do_delete.assert_called_once_with(action.context)
@mock.patch.object(scaleutils, 'check_size_params')
@mock.patch.object(cluster_mod.Cluster, 'load')
def test_do_delete_with_cluster_id_specified(self, mock_c_load,
mock_check, mock_load):
cluster = mock.Mock()
cluster.id = 'CID'
cluster.desired_capacity = 1
mock_c_load.return_value = cluster
node = mock.Mock()
node.id = 'NID'
node.do_delete = mock.Mock(return_value=None)
node.cluster_id = cluster.id
mock_load.return_value = node
mock_check.return_value = ''
action = node_action.NodeAction(node.id, 'ACTION', self.ctx,
cause=base_action.CAUSE_RPC)
node.do_delete = mock.Mock(return_value=mock.Mock())
res_code, res_msg = action.do_delete()
self.assertEqual(action.RES_OK, res_code)
mock_check.assert_called_once_with(cluster, 0, None, None, True)
mock_c_load.assert_called_once_with(action.context, 'CID')
cluster.store.assert_called_once_with(action.context)
self.assertEqual(0, cluster.desired_capacity)
cluster.remove_node.assert_called_once_with(node.id)
def test_do_update(self, mock_load):
node = mock.Mock()
node.id = 'NID'
mock_load.return_value = node
inputs = {"new_profile_id": "FAKE_PROFILE_ID"}
action = node_action.NodeAction(node.id, 'ACTION', self.ctx,
inputs=inputs)
# Test failed node update path
node.do_update = mock.Mock(return_value=None)
res_code, res_msg = action.do_update()
self.assertEqual(action.RES_ERROR, res_code)
self.assertEqual('Node update failed.', res_msg)
node.do_update.assert_called_once_with(action.context, inputs)
node.reset_mock()
# Test node update success path
node.do_update = mock.Mock(return_value=mock.Mock())
res_code, res_msg = action.do_update()
self.assertEqual(action.RES_OK, res_code)
self.assertEqual('Node updated successfully.', res_msg)
node.do_update.assert_called_once_with(action.context, inputs)
@mock.patch.object(cluster_mod.Cluster, 'load')
@mock.patch.object(scaleutils, 'check_size_params')
def test_do_join_success(self, mock_check, mock_c_load, mock_load):
node = mock.Mock()
node.id = 'NID'
mock_load.return_value = node
inputs = {"cluster_id": "FAKE_ID"}
action = node_action.NodeAction(node.id, 'NODE_JOIN', self.ctx,
inputs=inputs)
cluster = mock.Mock()
cluster.desired_capacity = 100
mock_c_load.return_value = cluster
mock_check.return_value = ''
node.do_join = mock.Mock(return_value=True)
# Test failed node join path
res_code, res_msg = action.do_join()
self.assertEqual(action.RES_OK, res_code)
self.assertEqual('Node successfully joined cluster.', res_msg)
node.do_join.assert_called_once_with(action.context, 'FAKE_ID')
mock_calls = [mock.call(action.context, 'FAKE_ID'),
mock.call(action.context, 'FAKE_ID')]
mock_c_load.assert_has_calls(mock_calls)
mock_check.assert_called_once_with(cluster, 101, None, None, True)
self.assertEqual(101, cluster.desired_capacity)
cluster.store.assert_called_once_with(action.context)
cluster.add_node.assert_called_once_with(node)
@mock.patch.object(cluster_mod.Cluster, 'load')
@mock.patch.object(scaleutils, 'check_size_params')
def test_do_join_fail_size_check(self, mock_check, mock_c_load, mock_load):
node = mock.Mock()
node.id = 'NID'
mock_load.return_value = node
inputs = {"cluster_id": "FAKE_ID"}
action = node_action.NodeAction(node.id, 'NODE_JOIN', self.ctx,
inputs=inputs)
cluster = mock.Mock()
cluster.desired_capacity = 100
mock_c_load.return_value = cluster
mock_check.return_value = 'Size limits'
node.do_join = mock.Mock(return_value=True)
# Test failed node join path
res_code, res_msg = action.do_join()
self.assertEqual(action.RES_ERROR, res_code)
self.assertEqual('Size limits', res_msg)
mock_c_load.assert_called_once_with(action.context, 'FAKE_ID')
mock_check.assert_called_once_with(cluster, 101, None, None, True)
self.assertEqual(0, node.do_join.call_count)
@mock.patch.object(cluster_mod.Cluster, 'load')
@mock.patch.object(scaleutils, 'check_size_params')
def test_do_join_failed_do_join(self, mock_check, mock_c_load, mock_load):
node = mock.Mock()
node.id = 'NID'
mock_load.return_value = node
inputs = {"cluster_id": "FAKE_ID"}
action = node_action.NodeAction(node.id, 'NODE_JOIN', self.ctx,
inputs=inputs)
cluster = mock.Mock()
cluster.desired_capacity = 100
mock_c_load.return_value = cluster
mock_check.return_value = ''
node.do_join = mock.Mock(return_value=False)
# Test failed node join path
res_code, res_msg = action.do_join()
self.assertEqual(action.RES_ERROR, res_code)
self.assertEqual('Node failed in joining cluster.', res_msg)
mock_c_load.assert_called_once_with(action.context, 'FAKE_ID')
mock_check.assert_called_once_with(cluster, 101, None, None, True)
node.do_join.assert_called_once_with(action.context, 'FAKE_ID')
@mock.patch.object(cluster_mod.Cluster, 'load')
@mock.patch.object(scaleutils, 'check_size_params')
def test_do_leave_success(self, mock_check, mock_c_load, mock_load):
node = mock.Mock()
node.id = 'NID'
mock_load.return_value = node
node.cluster_id = 'FAKE_ID'
action = node_action.NodeAction(node.id, 'NODE_LEAVE', self.ctx)
cluster = mock.Mock()
cluster.id = 'FAKE_ID'
cluster.desired_capacity = 100
mock_c_load.return_value = cluster
mock_check.return_value = ''
node.do_leave = mock.Mock(return_value=True)
# Test failed node join path
res_code, res_msg = action.do_leave()
self.assertEqual(action.RES_OK, res_code)
self.assertEqual('Node successfully left cluster.', res_msg)
node.do_leave.assert_called_once_with(action.context)
mock_calls = [mock.call(action.context, 'FAKE_ID'),
mock.call(action.context, 'FAKE_ID')]
mock_c_load.assert_has_calls(mock_calls)
mock_check.assert_called_once_with(cluster, 99, None, None, True)
self.assertEqual(99, cluster.desired_capacity)
cluster.store.assert_called_once_with(action.context)
cluster.remove_node.assert_called_once_with('NID')
@mock.patch.object(cluster_mod.Cluster, 'load')
@mock.patch.object(scaleutils, 'check_size_params')
def test_do_leave_failed_check(self, mock_check, mock_c_load, mock_load):
node = mock.Mock()
node.id = 'NID'
node.cluster_id = 'FAKE_ID'
mock_load.return_value = node
action = node_action.NodeAction(node.id, 'NODE_LEAVE', self.ctx)
cluster = mock.Mock()
cluster.desired_capacity = 100
mock_c_load.return_value = cluster
mock_check.return_value = 'Size limits'
node.do_leave = mock.Mock(return_value=True)
# Test failed node join path
res_code, res_msg = action.do_leave()
self.assertEqual(action.RES_ERROR, res_code)
self.assertEqual('Size limits', res_msg)
mock_c_load.assert_called_once_with(action.context, 'FAKE_ID')
mock_check.assert_called_once_with(cluster, 99, None, None, True)
self.assertEqual(0, node.do_leave.call_count)
@mock.patch.object(cluster_mod.Cluster, 'load')
@mock.patch.object(scaleutils, 'check_size_params')
def test_do_leave_failed_do_leave(self, mock_check, mock_c_load,
mock_load):
node = mock.Mock()
node.id = 'NID'
node.cluster_id = 'FAKE_ID'
mock_load.return_value = node
action = node_action.NodeAction(node.id, 'NODE_LEAVE', self.ctx)
cluster = mock.Mock()
cluster.desired_capacity = 100
mock_c_load.return_value = cluster
mock_check.return_value = ''
node.do_leave = mock.Mock(return_value=False)
# Test failed node join path
res_code, res_msg = action.do_leave()
self.assertEqual(action.RES_ERROR, res_code)
self.assertEqual('Node failed in leaving cluster.', res_msg)
node.do_leave.assert_called_once_with(action.context)
mock_c_load.assert_called_once_with(action.context, 'FAKE_ID')
mock_check.assert_called_once_with(cluster, 99, None, None, True)
def test_execute(self, mock_load):
node = mock.Mock()
node.id = 'NID'
mock_load.return_value = node
action = node_action.NodeAction(node.id, 'NODE_SING', self.ctx)
action.do_sing = mock.Mock(return_value=(action.RES_OK, 'GOOD'))
res_code, res_msg = action._execute()
self.assertEqual(action.RES_OK, res_code)
self.assertEqual('GOOD', res_msg)
action.do_sing.assert_called_once_with()
@mock.patch.object(event_mod, 'error')
def test_execute_bad_action(self, mock_error, mock_load):
node = mock.Mock()
node.id = 'NID'
mock_load.return_value = node
action = node_action.NodeAction(node.id, 'NODE_DANCE', self.ctx)
res_code, res_msg = action._execute()
self.assertEqual(action.RES_ERROR, res_code)
reason = 'Unsupported action: NODE_DANCE'
self.assertEqual(reason, res_msg)
mock_error.assert_called_once_with(action.context, node,
action.action, 'Failed',
reason)
@mock.patch.object(lock, 'cluster_lock_acquire')
def test_execute_failed_lock_cluster(self, mock_acquire, mock_load):
node = mock.Mock()
node.cluster_id = 'FAKE_CLUSTER'
node.id = 'NID'
mock_load.return_value = node
action = node_action.NodeAction('NODE_ID', 'NODE_FLY', self.ctx,
cause='RPC Request')
action.id = 'ACTION_ID'
mock_acquire.return_value = None
res_code, res_msg = action.execute()
reason = 'Failed in locking cluster'
self.assertEqual(action.RES_RETRY, res_code)
self.assertEqual(reason, res_msg)
mock_load.assert_called_once_with(action.context, node_id='NODE_ID')
mock_acquire.assert_called_once_with(self.ctx, 'FAKE_CLUSTER',
'ACTION_ID',
lock.NODE_SCOPE, False)
@mock.patch.object(lock, 'cluster_lock_acquire')
@mock.patch.object(lock, 'cluster_lock_release')
@mock.patch.object(base_action.Action, 'policy_check')
def test_execute_failed_policy_check(self, mock_check, mock_release,
mock_acquire, mock_load):
node = mock.Mock()
node.id = 'NID'
node.cluster_id = 'FAKE_CLUSTER'
mock_load.return_value = node
action = node_action.NodeAction('NODE_ID', 'NODE_FLY', self.ctx,
cause='RPC Request')
action.id = 'ACTION_ID'
action.data = {
'status': policy_mod.CHECK_ERROR,
'reason': 'Failed policy checking'
}
mock_acquire.return_value = action.id
res_code, res_msg = action.execute()
reason = 'Policy check: Failed policy checking'
self.assertEqual(action.RES_ERROR, res_code)
self.assertEqual(reason, res_msg)
mock_load.assert_called_once_with(action.context, node_id='NODE_ID')
mock_acquire.assert_called_once_with(self.ctx, 'FAKE_CLUSTER',
'ACTION_ID',
lock.NODE_SCOPE, False)
mock_release.assert_called_once_with('FAKE_CLUSTER', 'ACTION_ID',
lock.NODE_SCOPE)
mock_check.assert_called_once_with('FAKE_CLUSTER', 'BEFORE')
@mock.patch.object(lock, 'cluster_lock_acquire')
@mock.patch.object(lock, 'cluster_lock_release')
@mock.patch.object(base_action.Action, 'policy_check')
@mock.patch.object(lock, 'node_lock_acquire')
@mock.patch.object(lock, 'node_lock_release')
def test_execute_failed_locking_node(self, mock_release_node,
mock_acquire_node, mock_check,
mock_release, mock_acquire,
mock_load):
node = mock.Mock()
node.cluster_id = 'FAKE_CLUSTER'
node.id = 'NODE_ID'
mock_load.return_value = node
action = node_action.NodeAction('NODE_ID', 'NODE_FLY', self.ctx,
cause='RPC Request')
action.id = 'ACTION_ID'
action.data = {
'status': policy_mod.CHECK_OK,
'reason': 'Policy checking passed'
}
mock_acquire.return_value = 'ACTION_ID'
mock_acquire_node.return_value = None
res_code, res_msg = action.execute()
reason = 'Failed in locking node'
self.assertEqual(action.RES_ERROR, res_code)
self.assertEqual(reason, res_msg)
mock_load.assert_called_once_with(action.context, node_id='NODE_ID')
mock_acquire.assert_called_once_with(self.ctx, 'FAKE_CLUSTER',
'ACTION_ID',
lock.NODE_SCOPE, False)
mock_release.assert_called_once_with('FAKE_CLUSTER', 'ACTION_ID',
lock.NODE_SCOPE)
mock_check.assert_called_once_with('FAKE_CLUSTER', 'BEFORE')
mock_acquire_node.assert_called_once_with(self.ctx, 'NODE_ID',
'ACTION_ID', False)
mock_release_node.assert_called_once_with('NODE_ID', 'ACTION_ID')
@mock.patch.object(lock, 'cluster_lock_acquire')
@mock.patch.object(lock, 'cluster_lock_release')
@mock.patch.object(base_action.Action, 'policy_check')
@mock.patch.object(lock, 'node_lock_acquire')
@mock.patch.object(lock, 'node_lock_release')
def test_execute_success(self, mock_release_node, mock_acquire_node,
mock_check, mock_release, mock_acquire,
mock_load):
def fake_execute():
node.cluster_id = None
return (action.RES_OK, 'Execution ok')
node = mock.Mock()
node.cluster_id = 'FAKE_CLUSTER'
node.id = 'NODE_ID'
mock_load.return_value = node
action = node_action.NodeAction(node.id, 'NODE_FLY', self.ctx,
cause='RPC Request')
action.id = 'ACTION_ID'
# check result
action.data = {
'status': policy_mod.CHECK_OK,
'reason': 'Policy checking passed'
}
self.patchobject(action, '_execute', side_effect=fake_execute)
mock_acquire.return_value = 'ACTION_ID'
mock_acquire_node.return_value = 'ACTION_ID'
res_code, res_msg = action.execute()
reason = 'Execution ok'
self.assertEqual(action.RES_OK, res_code)
self.assertEqual(reason, res_msg)
mock_load.assert_called_once_with(action.context, node_id='NODE_ID')
mock_acquire.assert_called_once_with(self.ctx, 'FAKE_CLUSTER',
'ACTION_ID',
lock.NODE_SCOPE, False)
mock_release.assert_called_once_with('FAKE_CLUSTER', 'ACTION_ID',
lock.NODE_SCOPE)
mock_acquire_node.assert_called_once_with(self.ctx, 'NODE_ID',
'ACTION_ID', False)
mock_release_node.assert_called_once_with('NODE_ID', 'ACTION_ID')
check_calls = [
mock.call('FAKE_CLUSTER', 'BEFORE'),
mock.call('FAKE_CLUSTER', 'AFTER')
]
mock_check.assert_has_calls(check_calls)
@mock.patch.object(lock, 'cluster_lock_acquire')
@mock.patch.object(lock, 'cluster_lock_release')
@mock.patch.object(base_action.Action, 'policy_check')
@mock.patch.object(lock, 'node_lock_acquire')
@mock.patch.object(lock, 'node_lock_release')
def test_execute_failed_execute(self, mock_release_node, mock_acquire_node,
mock_check, mock_release, mock_acquire,
mock_load):
node = mock.Mock()
node.cluster_id = 'FAKE_CLUSTER'
node.id = 'NODE_ID'
mock_load.return_value = node
action = node_action.NodeAction(node.id, 'NODE_FLY', self.ctx,
cause='RPC Request')
action.id = 'ACTION_ID'
# check result
action.data = {
'status': policy_mod.CHECK_OK,
'reason': 'Policy checking passed'
}
self.patchobject(action, '_execute',
return_value=(action.RES_ERROR, 'Execution Failed'))
mock_acquire.return_value = 'ACTION_ID'
mock_acquire_node.return_value = 'ACTION_ID'
res_code, res_msg = action.execute()
reason = 'Execution Failed'
self.assertEqual(action.RES_ERROR, res_code)
self.assertEqual(reason, res_msg)
mock_load.assert_called_once_with(action.context, node_id='NODE_ID')
mock_acquire.assert_called_once_with(self.ctx, 'FAKE_CLUSTER',
'ACTION_ID',
lock.NODE_SCOPE, False)
mock_release.assert_called_once_with('FAKE_CLUSTER', 'ACTION_ID',
lock.NODE_SCOPE)
mock_acquire_node.assert_called_once_with(self.ctx, 'NODE_ID',
'ACTION_ID', False)
mock_release_node.assert_called_once_with('NODE_ID', 'ACTION_ID')
mock_check.assert_called_once_with('FAKE_CLUSTER', 'BEFORE')
@mock.patch.object(lock, 'cluster_lock_acquire')
@mock.patch.object(lock, 'cluster_lock_release')
@mock.patch.object(lock, 'node_lock_acquire')
@mock.patch.object(lock, 'node_lock_release')
def test_execute_failed_post_check(self, mock_release_node,
mock_acquire_node,
mock_release, mock_acquire,
mock_load):
def fake_check(cluster_id, target):
if target == 'BEFORE':
action.data = {
'status': policy_mod.CHECK_OK,
'reason': 'Policy checking passed'
}
else:
action.data = {
'status': policy_mod.CHECK_ERROR,
'reason': 'Policy checking failed'
}
node = mock.Mock()
node.cluster_id = 'FAKE_CLUSTER'
node.id = 'NODE_ID'
mock_load.return_value = node
action = node_action.NodeAction('NODE_ID', 'NODE_FLY', self.ctx,
cause='RPC Request')
action.id = 'ACTION_ID'
mock_check = self.patchobject(action, 'policy_check',
side_effect=fake_check)
# check result
self.patchobject(action, '_execute',
return_value=(action.RES_OK, 'Ignored'))
mock_acquire.return_value = 'ACTION_ID'
mock_acquire_node.return_value = 'ACTION_ID'
res_code, res_msg = action.execute()
reason = 'Policy check: Policy checking failed'
self.assertEqual(action.RES_ERROR, res_code)
self.assertEqual(reason, res_msg)
mock_load.assert_called_once_with(action.context, node_id='NODE_ID')
mock_acquire.assert_called_once_with(self.ctx, 'FAKE_CLUSTER',
'ACTION_ID',
lock.NODE_SCOPE, False)
mock_release.assert_called_once_with('FAKE_CLUSTER', 'ACTION_ID',
lock.NODE_SCOPE)
check_calls = [
mock.call('FAKE_CLUSTER', 'BEFORE'),
mock.call('FAKE_CLUSTER', 'AFTER')
]
mock_check.assert_has_calls(check_calls)
mock_acquire_node.assert_called_once_with(self.ctx, 'NODE_ID',
'ACTION_ID', False)
mock_release_node.assert_called_once_with('NODE_ID', 'ACTION_ID')