senlin/senlin/tests/unit/engine/test_node.py

1218 lines
50 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.
from unittest import mock
from oslo_serialization import jsonutils
from oslo_utils import uuidutils
from senlin.common import consts
from senlin.common import exception
from senlin.engine import node as nodem
from senlin.objects import node as node_obj
from senlin.profiles import base as pb
from senlin.tests.unit.common import base
from senlin.tests.unit.common import utils
PROFILE_ID = 'aa5f86b8-e52b-4f2b-828a-4c14c770938d'
CLUSTER_ID = '2c5139a6-24ba-4a6f-bd53-a268f61536de'
NODE_ID = '60efdaa1-06c2-4fcf-ae44-17a2d85ff3ea'
class TestNode(base.SenlinTestCase):
def setUp(self):
super(TestNode, self).setUp()
self.context = utils.dummy_context(project='node_test_project')
self.profile = utils.create_profile(self.context, PROFILE_ID)
self.cluster = utils.create_cluster(self.context, CLUSTER_ID,
PROFILE_ID)
def test_node_init(self):
node = nodem.Node('node1', PROFILE_ID, CLUSTER_ID, role='first_node')
self.assertIsNone(node.id)
self.assertEqual('node1', node.name)
self.assertIsNone(node.physical_id)
self.assertEqual(PROFILE_ID, node.profile_id)
self.assertEqual('', node.user)
self.assertEqual('', node.project)
self.assertEqual('', node.domain)
self.assertEqual(CLUSTER_ID, node.cluster_id)
self.assertEqual(-1, node.index)
self.assertEqual('first_node', node.role)
self.assertIsNone(node.init_at)
self.assertIsNone(node.created_at)
self.assertIsNone(node.updated_at)
self.assertEqual('INIT', node.status)
self.assertEqual('Initializing', node.status_reason)
self.assertEqual({}, node.data)
self.assertEqual({}, node.metadata)
self.assertEqual({}, node.rt)
def test_node_init_random_name(self):
node = nodem.Node(None, PROFILE_ID, None)
self.assertIsNotNone(node.name)
self.assertEqual(13, len(node.name))
def test_node_store_init(self):
node = nodem.Node('node1', PROFILE_ID, CLUSTER_ID, self.context,
role='first_node', index=1)
self.assertIsNone(node.id)
node_id = node.store(self.context)
self.assertIsNotNone(node_id)
node_info = node_obj.Node.get(self.context, node_id)
self.assertIsNotNone(node_info)
self.assertEqual('node1', node_info.name)
self.assertIsNone(node_info.physical_id)
self.assertEqual(CLUSTER_ID, node_info.cluster_id)
self.assertEqual(PROFILE_ID, node_info.profile_id)
self.assertEqual(self.context.user_id, node_info.user)
self.assertEqual(self.context.project_id, node_info.project)
self.assertEqual(self.context.domain_id, node_info.domain)
self.assertEqual(1, node_info.index)
self.assertEqual('first_node', node.role)
self.assertIsNotNone(node_info.init_at)
self.assertIsNone(node_info.created_at)
self.assertIsNone(node_info.updated_at)
self.assertEqual('INIT', node_info.status)
self.assertEqual('Initializing', node_info.status_reason)
self.assertEqual({}, node_info.metadata)
self.assertEqual({}, node_info.data)
def test_node_store_update(self):
node = nodem.Node('node1', PROFILE_ID, "", user=self.context.user_id,
project=self.context.project_id)
node_id = node.store(self.context)
node.name = 'new_name'
new_node_id = node.store(self.context)
self.assertEqual(node_id, new_node_id)
def test_node_load(self):
ex = self.assertRaises(exception.ResourceNotFound,
nodem.Node.load,
self.context, 'non-existent', None)
self.assertEqual("The node 'non-existent' could not be found.",
str(ex))
x_node_id = 'ee96c490-2dee-40c8-8919-4c64b89e326c'
node = utils.create_node(self.context, x_node_id, PROFILE_ID,
CLUSTER_ID)
node_info = nodem.Node.load(self.context, x_node_id)
self.assertEqual(node.id, node_info.id)
self.assertEqual(node.name, node_info.name)
self.assertEqual(node.physical_id, node_info.physical_id)
self.assertEqual(node.cluster_id, node_info.cluster_id)
self.assertEqual(node.profile_id, node_info.profile_id)
self.assertEqual(node.user, node_info.user)
self.assertEqual(node.project, node_info.project)
self.assertEqual(node.domain, node_info.domain)
self.assertEqual(node.index, node_info.index)
self.assertEqual(node.role, node_info.role)
self.assertEqual(node.init_at, node_info.init_at)
self.assertEqual(node.created_at, node_info.created_at)
self.assertEqual(node.updated_at, node_info.updated_at)
self.assertEqual(node.status, node_info.status)
self.assertEqual(node.status_reason, node_info.status_reason)
self.assertEqual(node.metadata, node_info.metadata)
self.assertEqual(node.data, node_info.data)
self.assertEqual(self.profile.name, node_info.rt['profile'].name)
def test_node_load_diff_project(self):
x_node_id = 'c06840c5-f4e4-49ae-8143-9da5b4c73f38'
utils.create_node(self.context, x_node_id, PROFILE_ID, CLUSTER_ID)
new_ctx = utils.dummy_context(project='a-different-project')
ex = self.assertRaises(exception.ResourceNotFound,
nodem.Node.load,
new_ctx, x_node_id, None)
self.assertEqual("The node '%s' could not be found." % x_node_id,
str(ex))
res = nodem.Node.load(new_ctx, x_node_id, project_safe=False)
self.assertIsNotNone(res)
self.assertEqual(x_node_id, res.id)
@mock.patch.object(nodem.Node, '_from_object')
@mock.patch.object(node_obj.Node, 'get_all')
def test_node_load_all(self, mock_get, mock_init):
x_obj_1 = mock.Mock()
x_obj_2 = mock.Mock()
mock_get.return_value = [x_obj_1, x_obj_2]
x_node_1 = mock.Mock()
x_node_2 = mock.Mock()
mock_init.side_effect = [x_node_1, x_node_2]
result = nodem.Node.load_all(self.context)
self.assertEqual([x_node_1, x_node_2], [n for n in result])
mock_get.assert_called_once_with(self.context, cluster_id=None,
limit=None, marker=None,
sort=None, filters=None,
project_safe=True)
mock_init.assert_has_calls([
mock.call(self.context, x_obj_1),
mock.call(self.context, x_obj_2)])
def test_node_set_status(self):
node = nodem.Node('node1', PROFILE_ID, CLUSTER_ID, self.context)
node.store(self.context)
self.assertEqual(nodem.consts.NS_INIT, node.status)
self.assertIsNotNone(node.init_at)
self.assertIsNone(node.created_at)
self.assertIsNone(node.updated_at)
# create
node.set_status(self.context, consts.NS_CREATING,
reason='Creation in progress')
self.assertEqual('CREATING', node.status)
self.assertEqual('Creation in progress', node.status_reason)
self.assertIsNone(node.created_at)
self.assertIsNone(node.updated_at)
node.set_status(self.context, consts.NS_ACTIVE,
reason='Creation succeeded')
self.assertEqual('ACTIVE', node.status)
self.assertEqual('Creation succeeded', node.status_reason)
self.assertIsNotNone(node.created_at)
self.assertIsNotNone(node.updated_at)
# update
node.set_status(self.context, consts.NS_UPDATING,
reason='Update in progress')
self.assertEqual('UPDATING', node.status)
self.assertEqual('Update in progress', node.status_reason)
self.assertIsNotNone(node.created_at)
node.set_status(self.context, consts.NS_ACTIVE,
reason='Update succeeded')
self.assertEqual('ACTIVE', node.status)
self.assertEqual('Update succeeded', node.status_reason)
self.assertIsNotNone(node.created_at)
self.assertIsNotNone(node.updated_at)
node.set_status(self.context, consts.NS_ACTIVE)
self.assertEqual('ACTIVE', node.status)
self.assertIsNotNone(node.created_at)
self.assertIsNotNone(node.updated_at)
# delete
node.set_status(self.context, consts.NS_DELETING,
reason='Deletion in progress')
self.assertEqual('DELETING', node.status)
self.assertEqual('Deletion in progress', node.status_reason)
self.assertIsNotNone(node.created_at)
@mock.patch.object(pb.Profile, 'get_details')
def test_node_get_details(self, mock_details):
node = nodem.Node('node1', CLUSTER_ID, None)
for physical_id in (None, ''):
node.physical_id = physical_id
self.assertEqual({}, node.get_details(self.context))
self.assertEqual(0, mock_details.call_count)
fake_id = 'd94d6333-82e6-4f87-b7ab-b786776df9d1'
node.physical_id = fake_id
mock_details.return_value = {'foo': 'bar'}
res = node.get_details(self.context)
mock_details.assert_called_once_with(self.context, node)
self.assertEqual({'foo': 'bar'}, res)
@mock.patch.object(nodem.Node, 'set_status')
@mock.patch.object(pb.Profile, 'create_object')
def test_node_create(self, mock_create, mock_status):
node = nodem.Node('node1', PROFILE_ID, CLUSTER_ID, self.context)
physical_id = 'd94d6333-82e6-4f87-b7ab-b786776df9d1'
mock_create.return_value = physical_id
res = node.do_create(self.context)
self.assertTrue(res)
mock_status.assert_any_call(self.context, consts.NS_CREATING,
'Creation in progress')
mock_status.assert_any_call(self.context, consts.NS_ACTIVE,
'Creation succeeded',
physical_id=physical_id)
@mock.patch.object(nodem.Node, 'set_status')
def test_node_create_not_init(self, mock_status):
node = nodem.Node('node1', PROFILE_ID, CLUSTER_ID, self.context)
node.status = 'NOT_INIT'
res, reason = node.do_create(self.context)
self.assertFalse(res)
self.assertEqual('Node must be in INIT status', reason)
mock_status.assert_any_call(self.context, consts.NS_ERROR,
'Node must be in INIT status')
@mock.patch.object(nodem.Node, 'set_status')
@mock.patch.object(pb.Profile, 'create_object')
def test_node_create_not_created(self, mock_create, mock_status):
node = nodem.Node('node1', PROFILE_ID, CLUSTER_ID, self.context)
mock_create.side_effect = exception.EResourceCreation(
type='PROFILE', message='Boom', resource_id='test_id')
res, reason = node.do_create(self.context)
self.assertFalse(res)
self.assertEqual(str(reason), 'Failed in creating PROFILE: Boom.')
mock_status.assert_any_call(self.context, consts.NS_CREATING,
'Creation in progress')
mock_status.assert_any_call(self.context, consts.NS_ERROR,
'Failed in creating PROFILE: Boom.',
physical_id='test_id')
@mock.patch.object(node_obj.Node, 'delete')
@mock.patch.object(nodem.Node, 'set_status')
@mock.patch.object(pb.Profile, 'delete_object')
def test_node_delete(self, mock_delete, mock_status, mock_db_delete):
node = nodem.Node('node1', PROFILE_ID, CLUSTER_ID, self.context)
node.physical_id = uuidutils.generate_uuid()
node.id = uuidutils.generate_uuid()
mock_db_delete.return_value = True
res = node.do_delete(self.context)
self.assertTrue(res)
mock_delete.assert_called_once_with(self.context, node)
mock_db_delete.assert_called_once_with(self.context, node.id)
mock_status.assert_called_once_with(self.context, consts.NS_DELETING,
'Deletion in progress')
@mock.patch.object(node_obj.Node, 'delete')
@mock.patch.object(nodem.Node, 'set_status')
@mock.patch.object(pb.Profile, 'delete_object')
def test_node_delete_no_physical_id(self, mock_delete, mock_status,
mock_db_delete):
node = nodem.Node('node1', PROFILE_ID, CLUSTER_ID, self.context)
node.id = uuidutils.generate_uuid()
self.assertIsNone(node.physical_id)
mock_db_delete.return_value = True
res = node.do_delete(self.context)
self.assertTrue(res)
mock_status.assert_called_once_with(self.context, consts.NS_DELETING,
"Deletion in progress")
self.assertTrue(mock_delete.called)
mock_db_delete.assert_called_once_with(self.context, node.id)
@mock.patch.object(nodem.Node, 'set_status')
@mock.patch.object(pb.Profile, 'delete_object')
def test_node_delete_EResourceDeletion(self, mock_delete, mock_status):
ex = exception.EResourceDeletion(type='PROFILE', id='NODE_ID',
message='Too Bad')
mock_delete.side_effect = ex
node = nodem.Node('node1', PROFILE_ID, CLUSTER_ID, self.context)
node.physical_id = uuidutils.generate_uuid()
res = node.do_delete(self.context)
self.assertFalse(res)
mock_delete.assert_called_once_with(self.context, node)
mock_status.assert_has_calls([
mock.call(self.context, consts.NS_DELETING,
"Deletion in progress"),
mock.call(self.context, consts.NS_ERROR,
"Failed in deleting PROFILE 'NODE_ID': Too Bad.")
])
@mock.patch.object(node_obj.Node, 'update')
@mock.patch.object(pb.Profile, 'update_object')
def test_node_update_new_profile(self, mock_update, mock_db):
node = nodem.Node('node1', PROFILE_ID, CLUSTER_ID, self.context,
physical_id=uuidutils.generate_uuid())
node.id = node.store(self.context)
new_id = uuidutils.generate_uuid()
utils.create_profile(self.context, new_id)
mock_update.return_value = True
res = node.do_update(self.context, {'new_profile_id': new_id})
self.assertTrue(res)
mock_update.assert_called_once_with(self.context, node, new_id)
self.assertEqual(new_id, node.profile_id)
self.assertEqual(new_id, node.rt['profile'].id)
mock_db.assert_has_calls([
mock.call(self.context, node.id,
{'status': consts.NS_UPDATING,
'status_reason': 'Update in progress'}),
mock.call(self.context, node.id,
{'status': consts.NS_ACTIVE,
'status_reason': 'Update succeeded',
'profile_id': new_id,
'updated_at': mock.ANY})
])
@mock.patch.object(pb.Profile, 'update_object')
@mock.patch.object(node_obj.Node, 'update')
def test_node_update_name(self, mock_db, mock_update):
node = nodem.Node('node1', PROFILE_ID, CLUSTER_ID, self.context)
node.store(self.context)
physical_id = uuidutils.generate_uuid()
node.physical_id = physical_id
res = node.do_update(self.context, {'name': 'new_name',
'role': 'new_role',
'metadata': {'k': {'m': 'v'}},
'bogus': 'foo'})
self.assertTrue(res)
self.assertEqual('new_name', node.name)
mock_db.assert_has_calls([
mock.call(self.context, node.id,
{'status': 'UPDATING',
'status_reason': 'Update in progress'}),
mock.call(self.context, node.id,
{'status': 'ACTIVE',
'status_reason': 'Update succeeded',
'name': 'new_name',
'role': 'new_role',
'metadata': {'k': {'m': 'v'}},
'updated_at': mock.ANY})
])
self.assertEqual(0, mock_update.call_count)
def test_node_update_not_created(self):
node = nodem.Node('node1', PROFILE_ID, CLUSTER_ID, self.context)
self.assertIsNone(node.physical_id)
new_profile_id = '71d8f4dd-1ef9-4308-b7ae-03298b04449e'
res = node.do_update(self.context, new_profile_id)
self.assertFalse(res)
@mock.patch.object(pb.Profile, 'update_object')
@mock.patch.object(node_obj.Node, 'update')
def test_node_update_EResourceUpdate(self, mock_db, mock_update):
node = nodem.Node('node1', PROFILE_ID, CLUSTER_ID, self.context)
node.physical_id = uuidutils.generate_uuid()
node.id = uuidutils.generate_uuid()
ex = exception.EResourceUpdate(type='PROFILE', id='ID',
message='reason')
mock_update.side_effect = ex
new_id = uuidutils.generate_uuid()
utils.create_profile(self.context, new_id)
res = node.do_update(self.context, {'new_profile_id': new_id})
self.assertFalse(res)
self.assertNotEqual(new_id, node.profile_id)
mock_db.assert_has_calls([
mock.call(
self.context, node.id,
{"status": "UPDATING", "status_reason": "Update in progress"}
),
mock.call(
self.context, node.id,
{"status": "ERROR",
"status_reason": "Failed in updating PROFILE 'ID': reason.",
"updated_at": mock.ANY}
)
])
self.assertEqual(1, mock_update.call_count)
@mock.patch.object(node_obj.Node, 'migrate')
def test_node_join_same_cluster(self, mock_migrate):
node = nodem.Node('node1', PROFILE_ID, CLUSTER_ID, self.context)
node.index = 1
res = node.do_join(self.context, CLUSTER_ID)
self.assertTrue(res)
self.assertEqual(1, node.index)
self.assertIsNone(node.updated_at)
self.assertFalse(mock_migrate.called)
@mock.patch.object(pb.Profile, 'join_cluster')
@mock.patch.object(node_obj.Node, 'migrate')
def test_node_join(self, mock_migrate, mock_join_cluster):
node = nodem.Node('node1', PROFILE_ID, CLUSTER_ID, self.context)
mock_join_cluster.return_value = True
cluster_id = 'fb8bca7a-a82b-4442-a40f-92d3e3cfb0b9'
res = node.do_join(self.context, cluster_id)
self.assertTrue(res)
mock_migrate.assert_called_once_with(self.context, node.id,
cluster_id, mock.ANY)
mock_join_cluster.assert_called_once_with(self.context, node,
cluster_id)
self.assertEqual(cluster_id, node.cluster_id)
self.assertEqual(mock_migrate.return_value.index, node.index)
self.assertIsNotNone(node.updated_at)
@mock.patch.object(pb.Profile, 'join_cluster')
def test_node_join_fail_profile_call(self, mock_join):
node = nodem.Node('node1', PROFILE_ID, None, self.context)
node.id = uuidutils.generate_uuid()
mock_join.return_value = False
cluster_id = 'fb8bca7a-a82b-4442-a40f-92d3e3cfb0b9'
res = node.do_join(self.context, cluster_id)
self.assertFalse(res)
mock_join.assert_called_once_with(self.context, node, cluster_id)
self.assertEqual(-1, node.index)
@mock.patch.object(node_obj.Node, 'migrate')
def test_node_leave_no_cluster(self, mock_migrate):
node = nodem.Node('node1', PROFILE_ID, '', self.context)
self.assertTrue(node.do_leave(self.context))
self.assertFalse(mock_migrate.called)
self.assertEqual('', node.cluster_id)
self.assertIsNone(node.updated_at)
@mock.patch.object(pb.Profile, 'leave_cluster')
@mock.patch.object(node_obj.Node, 'migrate')
def test_node_leave(self, mock_migrate, mock_leave_cluster):
node = nodem.Node('node1', PROFILE_ID, CLUSTER_ID, self.context)
mock_leave_cluster.return_value = True
res = node.do_leave(self.context)
self.assertTrue(res)
self.assertEqual('', node.cluster_id)
self.assertIsNotNone(node.updated_at)
self.assertEqual(-1, node.index)
mock_migrate.assert_called_once_with(self.context, node.id,
None, mock.ANY)
mock_leave_cluster.assert_called_once_with(self.context, node)
@mock.patch.object(pb.Profile, 'leave_cluster')
def test_node_leave_fail_update_server_metadata(self, mock_leave):
node = nodem.Node('node1', PROFILE_ID, CLUSTER_ID, self.context,
index=1)
mock_leave.return_value = False
res = node.do_leave(self.context)
self.assertFalse(res)
self.assertNotEqual('', node.cluster_id)
self.assertIsNone(node.updated_at)
self.assertEqual(1, node.index)
@mock.patch.object(nodem.Node, 'set_status')
@mock.patch.object(pb.Profile, 'check_object')
def test_node_check(self, mock_check, mock_status):
node = nodem.Node('node1', PROFILE_ID, '')
node.status = consts.NS_ACTIVE
node.physical_id = 'd94d6333-82e6-4f87-b7ab-b786776df9d1'
mock_check.return_value = True
res = node.do_check(self.context)
self.assertTrue(res)
mock_check.assert_called_once_with(self.context, node)
mock_status.assert_called_once_with(self.context, consts.NS_ACTIVE,
'Check: Node is ACTIVE.')
@mock.patch.object(nodem.Node, 'set_status')
@mock.patch.object(pb.Profile, 'check_object')
def test_node_check_warning(self, mock_check, mock_status):
node = nodem.Node('node1', PROFILE_ID, '')
node.status = consts.NS_WARNING
node.status_reason = 'bad news'
node.physical_id = 'd94d6333-82e6-4f87-b7ab-b786776df9d1'
mock_check.return_value = True
res = node.do_check(self.context)
self.assertTrue(res)
mock_check.assert_called_once_with(self.context, node)
msg = ("Check: Physical object is ACTIVE but the node status was "
"WARNING. %s") % node.status_reason
mock_status.assert_called_once_with(self.context, consts.NS_WARNING,
msg)
@mock.patch.object(nodem.Node, 'set_status')
@mock.patch.object(pb.Profile, 'check_object')
def test_node_check_not_active(self, mock_check, mock_status):
node = nodem.Node('node1', PROFILE_ID, '')
node.status = consts.NS_WARNING
node.physical_id = 'd94d6333-82e6-4f87-b7ab-b786776df9d1'
mock_check.return_value = False
res = node.do_check(self.context)
self.assertTrue(res)
mock_status.assert_called_once_with(self.context, consts.NS_ERROR,
'Check: Node is not ACTIVE.')
@mock.patch.object(nodem.Node, 'set_status')
@mock.patch.object(pb.Profile, 'check_object')
def test_node_check_check_with_exc(self, mock_check, mock_status):
node = nodem.Node('node1', PROFILE_ID, '')
node.physical_id = 'd94d6333-82e6-4f87-b7ab-b786776df9d1'
err = exception.EResourceOperation(op='checking', type='server',
id=node.physical_id,
message='failed get')
mock_check.side_effect = err
res = node.do_check(self.context)
self.assertFalse(res)
mock_status.assert_called_once_with(
self.context,
consts.NS_ERROR,
"Failed in checking server '%s': failed get." % node.physical_id)
def test_node_check_no_physical_id(self):
node = nodem.Node('node1', PROFILE_ID, '')
res = node.do_check(self.context)
self.assertFalse(res)
@mock.patch.object(nodem.Node, 'set_status')
@mock.patch.object(pb.Profile, 'check_object')
def test_node_check_no_server(self, mock_check, mock_status):
node = nodem.Node('node1', PROFILE_ID, '')
node.physical_id = 'd94d6333-82e6-4f87-b7ab-b786776df9d1'
err = exception.EServerNotFound(type='server',
id=node.physical_id,
message='No Server found')
mock_check.side_effect = err
res = node.do_check(self.context)
self.assertTrue(res)
mock_status.assert_called_once_with(
self.context, consts.NS_ERROR,
"Failed in found server '%s': No Server found."
% node.physical_id,
physical_id=None)
@mock.patch.object(pb.Profile, 'healthcheck_object')
def test_node_healthcheck(self, mock_healthcheck):
node = nodem.Node('node1', PROFILE_ID, '')
node.status = consts.NS_ACTIVE
node.physical_id = 'd94d6333-82e6-4f87-b7ab-b786776df9d1'
mock_healthcheck.return_value = True
res = node.do_healthcheck(self.context, consts.NODE_STATUS_POLLING)
self.assertTrue(res)
mock_healthcheck.assert_called_once_with(self.context, node,
consts.NODE_STATUS_POLLING)
def test_node_healthcheck_no_physical_id(self):
node = nodem.Node('node1', PROFILE_ID, '')
res = node.do_healthcheck(self.context, consts.NODE_STATUS_POLLING)
self.assertFalse(res)
@mock.patch.object(nodem.Node, 'set_status')
@mock.patch.object(pb.Profile, 'recover_object')
def test_node_recover_new_object(self, mock_recover, mock_status):
def set_status(*args, **kwargs):
if args[1] == 'ACTIVE':
node.physical_id = new_id
node.data = {'recovery': 'RECREATE'}
node = nodem.Node('node1', PROFILE_ID, '')
node.physical_id = 'd94d6333-82e6-4f87-b7ab-b786776df9d1'
new_id = '166db83b-b4a4-49ef-96a8-6c0fdd882d1a'
# action = node_action.NodeAction(node.id, 'ACTION', self.ctx)
mock_recover.return_value = new_id, True
mock_status.side_effect = set_status
action = mock.Mock()
action.inputs = {'operation': 'SWIM'}
res = node.do_recover(self.context, action)
self.assertTrue(res)
mock_recover.assert_called_once_with(
self.context, node, **action.inputs)
self.assertEqual('node1', node.name)
self.assertEqual(new_id, node.physical_id)
self.assertEqual(PROFILE_ID, node.profile_id)
self.assertEqual({'recovery': 'RECREATE'}, node.data)
mock_status.assert_has_calls([
mock.call(self.context, 'RECOVERING',
reason='Recovery in progress'),
mock.call(self.context, consts.NS_ACTIVE,
reason='Recovery succeeded',
physical_id=new_id,
data={'recovery': 'RECREATE'})])
@mock.patch.object(nodem.Node, 'set_status')
@mock.patch.object(pb.Profile, 'recover_object')
def test_node_recover_in_place(self, mock_recover, mock_status):
node = nodem.Node('node1', PROFILE_ID, None)
node.physical_id = 'd94d6333-82e6-4f87-b7ab-b786776df9d1'
mock_recover.return_value = node.physical_id, True
action = mock.Mock(inputs={})
res = node.do_recover(self.context, action)
self.assertTrue(res)
mock_recover.assert_called_once_with(self.context, node)
self.assertEqual('node1', node.name)
self.assertEqual(PROFILE_ID, node.profile_id)
mock_status.assert_has_calls([
mock.call(self.context, 'RECOVERING',
reason='Recovery in progress'),
mock.call(self.context, consts.NS_ACTIVE,
reason='Recovery succeeded')])
@mock.patch.object(nodem.Node, 'set_status')
def test_node_recover_check_active(self, mock_status):
node = nodem.Node('node1', PROFILE_ID, None)
node.physical_id = 'd94d6333-82e6-4f87-b7ab-b786776df9d1'
node.status = 'ACTIVE'
mock_check = self.patchobject(pb.Profile, 'check_object')
mock_check.return_value = True
action = mock.Mock(inputs={'check': True})
res = node.do_recover(self.context, action)
self.assertTrue(res)
mock_check.assert_called_once_with(self.context, node)
mock_status.assert_called_once_with(self.context, consts.NS_ACTIVE,
reason='Recover: Node is ACTIVE.')
@mock.patch.object(nodem.Node, 'set_status')
@mock.patch.object(pb.Profile, 'recover_object')
def test_node_recover_check_error(self, mock_recover, mock_status):
def set_status(*args, **kwargs):
if args[1] == 'ACTIVE':
node.physical_id = new_id
node.data = {'recovery': 'recreate'}
node = nodem.Node('node1', PROFILE_ID, '')
node.physical_id = 'd94d6333-82e6-4f87-b7ab-b786776df9d1'
new_id = '166db83b-b4a4-49ef-96a8-6c0fdd882d1a'
mock_recover.return_value = new_id, True
mock_status.side_effect = set_status
mock_check = self.patchobject(pb.Profile, 'check_object')
mock_check.return_value = False
action = mock.Mock(inputs={'check': True})
res = node.do_recover(self.context, action)
self.assertTrue(res)
mock_check.assert_called_once_with(self.context, node)
mock_recover.assert_called_once_with(
self.context, node, **action.inputs)
self.assertEqual('node1', node.name)
self.assertEqual(new_id, node.physical_id)
self.assertEqual(PROFILE_ID, node.profile_id)
mock_status.assert_has_calls([
mock.call(self.context, 'RECOVERING',
reason='Recovery in progress'),
mock.call(self.context, consts.NS_ACTIVE,
reason='Recovery succeeded',
physical_id=new_id,
data={'recovery': 'RECREATE'})])
@mock.patch.object(nodem.Node, 'set_status')
@mock.patch.object(pb.Profile, 'recover_object')
def test_node_recover_recreate(self, mock_recover, mock_status):
def set_status(*args, **kwargs):
if args[1] == 'ACTIVE':
node.physical_id = new_id
node.data = {'recovery': 'RECREATE'}
node = nodem.Node('node1', PROFILE_ID, '', id='fake')
node.physical_id = 'd94d6333-82e6-4f87-b7ab-b786776df9d1'
new_id = '166db83b-b4a4-49ef-96a8-6c0fdd882d1a'
mock_recover.return_value = new_id, True
mock_status.side_effect = set_status
mock_check = self.patchobject(pb.Profile, 'check_object')
mock_check.return_value = False
action = mock.Mock(
outputs={}, inputs={'operation': 'RECREATE',
'check': True})
res = node.do_recover(self.context, action)
self.assertTrue(res)
mock_check.assert_called_once_with(self.context, node)
mock_recover.assert_called_once_with(
self.context, node, **action.inputs)
self.assertEqual('node1', node.name)
self.assertEqual(new_id, node.physical_id)
self.assertEqual(PROFILE_ID, node.profile_id)
mock_status.assert_has_calls([
mock.call(self.context, 'RECOVERING',
reason='Recovery in progress'),
mock.call(self.context, consts.NS_ACTIVE,
reason='Recovery succeeded',
physical_id=new_id,
data={'recovery': 'RECREATE'})])
@mock.patch.object(nodem.Node, 'set_status')
@mock.patch.object(pb.Profile, 'recover_object')
def test_node_recover_check_exception(self, mock_recover, mock_status):
def set_status(*args, **kwargs):
if args[1] == 'ACTIVE':
node.physical_id = new_id
node.data = {'recovery': 'RECREATE'}
node = nodem.Node('node1', PROFILE_ID, '')
node.physical_id = 'd94d6333-82e6-4f87-b7ab-b786776df9d1'
new_id = '166db83b-b4a4-49ef-96a8-6c0fdd882d1a'
mock_recover.return_value = new_id, True
mock_status.side_effect = set_status
mock_check = self.patchobject(pb.Profile, 'check_object')
mock_check.side_effect = exception.EResourceOperation(
op='checking',
type='server',
id=node.physical_id,
reason='Boom!'
)
action = mock.Mock(inputs={'operation': 'boom',
'check': True})
res = node.do_recover(self.context, action)
self.assertTrue(res)
mock_check.assert_called_once_with(self.context, node)
mock_recover.assert_called_once_with(
self.context, node, **action.inputs)
self.assertEqual('node1', node.name)
self.assertEqual(new_id, node.physical_id)
self.assertEqual(PROFILE_ID, node.profile_id)
mock_status.assert_has_calls([
mock.call(self.context, 'RECOVERING',
reason='Recovery in progress'),
mock.call(self.context, consts.NS_ACTIVE,
reason='Recovery succeeded',
physical_id=new_id,
data={'recovery': 'RECREATE'})])
@mock.patch.object(nodem.Node, 'set_status')
@mock.patch.object(pb.Profile, 'recover_object')
def test_node_recover_failed_recover(self, mock_recover, mock_status):
node = nodem.Node('node1', PROFILE_ID, None)
node.physical_id = 'd94d6333-82e6-4f87-b7ab-b786776df9d1'
mock_recover.return_value = node.physical_id, None
action = mock.Mock(inputs={'operation': 'RECREATE'})
res = node.do_recover(self.context, action)
self.assertFalse(res)
mock_status.assert_has_calls([
mock.call(self.context, 'RECOVERING',
reason='Recovery in progress'),
mock.call(self.context, consts.NS_ERROR,
reason='Recovery failed')])
@mock.patch.object(nodem.Node, 'set_status')
@mock.patch.object(pb.Profile, 'recover_object')
def test_node_recover_failed_recover_with_old_physical_id(self,
mock_recover,
mock_status):
node = nodem.Node('node1', PROFILE_ID, None)
node.physical_id = 'd94d6333-82e6-4f87-b7ab-b786776df9d1'
action = mock.Mock(
inputs={'operation': consts.RECOVER_RECREATE, 'check': True})
mock_recover.side_effect = exception.EResourceOperation(
op=consts.RECOVER_RECREATE,
type='server',
id=node.physical_id,
resource_id=node.physical_id,
reason='Recovery failed',
)
res = node.do_recover(self.context, action)
self.assertFalse(res)
mock_recover.assert_called_once_with(
self.context, node, **action.inputs)
reason = ("Failed in RECREATE server 'd94d6333-82e6-4f87-b7ab-b786776d"
"f9d1': Internal error happened.")
mock_status.assert_has_calls([
mock.call(self.context, 'RECOVERING',
reason='Recovery in progress'),
mock.call(self.context, consts.NS_ERROR,
reason=str(reason),
physical_id=node.physical_id)])
@mock.patch.object(nodem.Node, 'set_status')
@mock.patch.object(pb.Profile, 'recover_object')
def test_node_recover_failed_recover_with_new_physical_id(self,
mock_recover,
mock_status):
def set_status(*args, **kwargs):
if args[1] == consts.NS_ERROR:
node.physical_id = new_id
node = nodem.Node('node1', PROFILE_ID, None)
node.physical_id = 'd94d6333-82e6-4f87-b7ab-b786776df9d1'
new_id = '166db83b-b4a4-49ef-96a8-6c0fdd882d1a'
mock_status.side_effect = set_status
action = mock.Mock(inputs={'operation': consts.RECOVER_RECREATE,
'check': True})
mock_recover.side_effect = exception.EResourceOperation(
op=consts.RECOVER_RECREATE,
type='server',
id=node.physical_id,
resource_id=new_id,
reason='Recovery failed',
)
res = node.do_recover(self.context, action)
self.assertFalse(res)
mock_recover.assert_called_once_with(
self.context, node, **action.inputs)
reason = ("Failed in RECREATE server 'd94d6333-82e6-4f87-b7ab-b786776d"
"f9d1': Internal error happened.")
mock_status.assert_has_calls([
mock.call(self.context, 'RECOVERING',
reason='Recovery in progress'),
mock.call(self.context, consts.NS_ERROR,
reason=str(reason),
physical_id=new_id)])
def test_node_recover_no_physical_id_reboot_op(self):
node = nodem.Node('node1', PROFILE_ID, None)
action = mock.Mock(inputs={'operation': 'REBOOT'})
res = node.do_recover(self.context, action)
self.assertFalse(res)
def test_node_recover_no_physical_id_rebuild_op(self):
node = nodem.Node('node1', PROFILE_ID, None)
action = mock.Mock(inputs={'operation': 'REBUILD'})
res = node.do_recover(self.context, action)
self.assertFalse(res)
@mock.patch.object(nodem.Node, 'set_status')
@mock.patch.object(pb.Profile, 'recover_object')
def test_node_recover_no_physical_id_no_op(self, mock_recover,
mock_status):
def set_status(*args, **kwargs):
if args[1] == 'ACTIVE':
node.physical_id = new_id
node.data = {'recovery': 'RECREATE'}
node = nodem.Node('node1', PROFILE_ID, '', id='fake')
new_id = '166db83b-b4a4-49ef-96a8-6c0fdd882d1a'
mock_recover.return_value = new_id, True
mock_status.side_effect = set_status
mock_check = self.patchobject(pb.Profile, 'check_object')
mock_check.return_value = False
action = mock.Mock(
outputs={}, inputs={})
res = node.do_recover(self.context, action)
self.assertTrue(res)
mock_check.assert_not_called()
mock_recover.assert_called_once_with(
self.context, node, **action.inputs)
self.assertEqual('node1', node.name)
self.assertEqual(new_id, node.physical_id)
self.assertEqual(PROFILE_ID, node.profile_id)
mock_status.assert_has_calls([
mock.call(self.context, 'RECOVERING',
reason='Recovery in progress'),
mock.call(self.context, consts.NS_ACTIVE,
reason='Recovery succeeded',
physical_id=new_id,
data={'recovery': 'RECREATE'})])
@mock.patch.object(nodem.Node, 'set_status')
@mock.patch.object(pb.Profile, 'recover_object')
def test_node_recover_no_physical_id_recreate_op(self, mock_recover,
mock_status):
def set_status(*args, **kwargs):
if args[1] == 'ACTIVE':
node.physical_id = new_id
node.data = {'recovery': 'RECREATE'}
node = nodem.Node('node1', PROFILE_ID, '', id='fake')
new_id = '166db83b-b4a4-49ef-96a8-6c0fdd882d1a'
mock_recover.return_value = new_id, True
mock_status.side_effect = set_status
mock_check = self.patchobject(pb.Profile, 'check_object')
mock_check.return_value = False
action = mock.Mock(
outputs={}, inputs={'operation': 'RECREATE',
'check': True})
res = node.do_recover(self.context, action)
self.assertTrue(res)
mock_check.assert_called_once_with(self.context, node)
mock_recover.assert_called_once_with(
self.context, node, **action.inputs)
self.assertEqual('node1', node.name)
self.assertEqual(new_id, node.physical_id)
self.assertEqual(PROFILE_ID, node.profile_id)
mock_status.assert_has_calls([
mock.call(self.context, 'RECOVERING',
reason='Recovery in progress'),
mock.call(self.context, consts.NS_ACTIVE,
reason='Recovery succeeded',
physical_id=new_id,
data={'recovery': 'RECREATE'})])
@mock.patch.object(nodem.Node, 'set_status')
def test_node_recover_operation_not_support(self, mock_set_status):
node = nodem.Node('node1', PROFILE_ID, None)
node.physical_id = 'd94d6333-82e6-4f87-b7ab-b786776df9d1'
action = mock.Mock(
outputs={}, inputs={'operation': 'foo'})
res = node.do_recover(self.context, action)
self.assertEqual({}, action.outputs)
self.assertFalse(res)
@mock.patch.object(nodem.Node, 'set_status')
def test_node_recover_operation_not_string(self, mock_set_status):
node = nodem.Node('node1', PROFILE_ID, None)
node.physical_id = 'd94d6333-82e6-4f87-b7ab-b786776df9d1'
action = mock.Mock(
outputs={}, inputs={'operation': 'foo'})
res = node.do_recover(self.context, action)
self.assertEqual({}, action.outputs)
self.assertFalse(res)
@mock.patch.object(nodem.Node, 'set_status')
def test_node_operation(self, mock_set_status):
node = nodem.Node('node1', PROFILE_ID, '')
node.physical_id = 'd94d6333-82e6-4f87-b7ab-b786776df9d1'
x_profile = mock.Mock()
x_profile.handle_dance = mock.Mock(return_value=True)
node.rt['profile'] = x_profile
inputs = {'operation': 'dance', 'params': {'style': 'tango'}}
res = node.do_operation(self.context, **inputs)
self.assertTrue(res)
mock_set_status.assert_has_calls([
mock.call(self.context, consts.NS_OPERATING,
reason="Operation 'dance' in progress"),
mock.call(self.context, consts.NS_ACTIVE,
reason="Operation 'dance' succeeded")
])
x_profile.handle_dance.assert_called_once_with(node, style='tango')
def test_node_operation_no_physical_id(self):
node = nodem.Node('node1', PROFILE_ID, None)
inputs = {'operation': 'dance', 'params': {'style': 'tango'}}
res = node.do_operation(self.context, **inputs)
self.assertFalse(res)
@mock.patch.object(nodem.Node, 'set_status')
def test_node_operation_failed_op(self, mock_set_status):
node = nodem.Node('node1', PROFILE_ID, '')
node.physical_id = 'd94d6333-82e6-4f87-b7ab-b786776df9d1'
x_profile = mock.Mock()
err = exception.EResourceOperation(
op='dance', type='container', id='test_id', message='Boom')
x_profile.handle_dance = mock.Mock(side_effect=err)
node.rt['profile'] = x_profile
inputs = {'operation': 'dance', 'params': {'style': 'tango'}}
res = node.do_operation(self.context, **inputs)
self.assertFalse(res)
mock_set_status.assert_has_calls([
mock.call(self.context, consts.NS_OPERATING,
reason="Operation 'dance' in progress"),
mock.call(self.context, consts.NS_ERROR,
reason="Failed in dance container 'test_id': Boom.")
])
x_profile.handle_dance.assert_called_once_with(node, style='tango')
def _verify_execution_create_args(self, expected_name,
expected_inputs_dict, wfc):
wfc.execution_create.assert_called_once_with(mock.ANY, mock.ANY)
actual_call_args, call_kwargs = wfc.execution_create.call_args
# execution_create parameters are name and inputs
actual_call_name, actual_call_inputs = actual_call_args
# actual_call_inputs is string representation of a dictionary.
# convert actual_call_inputs to json, then dump it back as string
# sorted by key
final_actual_call_inputs = jsonutils.dumps(
jsonutils.loads(actual_call_inputs), sort_keys=True)
# dump expected_inputs_dict as string sorted by key
final_expected_inputs = jsonutils.dumps(
expected_inputs_dict, sort_keys=True)
# compare the sorted input strings along with the names
self.assertEqual(actual_call_name, expected_name)
self.assertEqual(final_actual_call_inputs, final_expected_inputs)
def test_run_workflow(self):
node = nodem.Node('node1', PROFILE_ID, 'FAKE_CLUSTER')
node.physical_id = 'FAKE_NODE'
wfc = mock.Mock()
wfc.workflow_find.return_value = None
wfc.workflow_create = mock.Mock()
wfc.execution_create = mock.Mock()
x_profile = mock.Mock()
x_profile.workflow = mock.Mock(return_value=wfc)
node.rt['profile'] = x_profile
options = {
'workflow_name': 'foo',
'inputs': {
'definition': {
'bar': 'baz'
},
'FAKE_KEY1': 'FAKE_VALUE1',
'FAKE_KEY2': 'FAKE_VALUE2',
}
}
res = node.run_workflow(**options)
self.assertTrue(res)
x_profile.workflow.assert_called_once_with(node)
wfc.workflow_find.assert_called_once_with('foo')
wfc.workflow_create.assert_called_once_with(
{'bar': 'baz'}, scope='private')
final_dict = {
'cluster_id': 'FAKE_CLUSTER',
'node_id': 'FAKE_NODE',
'FAKE_KEY1': 'FAKE_VALUE1',
'FAKE_KEY2': 'FAKE_VALUE2',
}
self._verify_execution_create_args('foo', final_dict, wfc)
def test_run_workflow_no_physical_id(self):
node = nodem.Node('node1', PROFILE_ID, 'FAKE_CLUSTER')
node.physical_id = None
res = node.run_workflow()
self.assertFalse(res)
def test_run_workflow_workflow_is_found(self):
node = nodem.Node('node1', PROFILE_ID, 'FAKE_CLUSTER')
node.physical_id = 'FAKE_NODE'
wfc = mock.Mock()
wfc.workflow_find.return_value = mock.Mock(definition={'bar': 'baz'})
wfc.workflow_create = mock.Mock()
wfc.execution_create = mock.Mock()
x_profile = mock.Mock()
x_profile.workflow = mock.Mock(return_value=wfc)
node.rt['profile'] = x_profile
options = {
'workflow_name': 'foo',
'inputs': {
'FAKE_KEY1': 'FAKE_VALUE1',
'FAKE_KEY2': 'FAKE_VALUE2',
}
}
res = node.run_workflow(**options)
self.assertTrue(res)
x_profile.workflow.assert_called_once_with(node)
wfc.workflow_find.assert_called_once_with('foo')
self.assertEqual(0, wfc.workflow_create.call_count)
final_dict = {
'cluster_id': 'FAKE_CLUSTER',
'node_id': 'FAKE_NODE',
'FAKE_KEY1': 'FAKE_VALUE1',
'FAKE_KEY2': 'FAKE_VALUE2',
}
self._verify_execution_create_args('foo', final_dict, wfc)
def test_run_workflow_failed_creation(self):
node = nodem.Node('node1', PROFILE_ID, 'FAKE_CLUSTER')
node.physical_id = 'FAKE_NODE'
wfc = mock.Mock()
wfc.workflow_find.return_value = None
err = exception.InternalError(message='boom')
wfc.workflow_create.side_effect = err
wfc.execution_create = mock.Mock()
x_profile = mock.Mock()
x_profile.workflow = mock.Mock(return_value=wfc)
node.rt['profile'] = x_profile
options = {
'workflow_name': 'foo',
'inputs': {
'definition': {'bar': 'baz'},
'FAKE_KEY1': 'FAKE_VALUE1',
'FAKE_KEY2': 'FAKE_VALUE2',
}
}
ex = self.assertRaises(exception.EResourceOperation,
node.run_workflow,
**options)
self.assertEqual("Failed in executing workflow 'foo': boom.",
str(ex))
x_profile.workflow.assert_called_once_with(node)
wfc.workflow_find.assert_called_once_with('foo')
wfc.workflow_create.assert_called_once_with(
{'bar': 'baz'}, scope='private')
self.assertEqual(0, wfc.execution_create.call_count)
def test_run_workflow_failed_execution(self):
node = nodem.Node('node1', PROFILE_ID, 'FAKE_CLUSTER')
node.physical_id = 'FAKE_NODE'
wfc = mock.Mock()
wfc.workflow_find.return_value = None
wfc.workflow_create = mock.Mock()
err = exception.InternalError(message='boom')
wfc.execution_create.side_effect = err
x_profile = mock.Mock()
x_profile.workflow = mock.Mock(return_value=wfc)
node.rt['profile'] = x_profile
options = {
'workflow_name': 'foo',
'inputs': {
'definition': {'bar': 'baz'},
'FAKE_KEY1': 'FAKE_VALUE1',
'FAKE_KEY2': 'FAKE_VALUE2',
}
}
ex = self.assertRaises(exception.EResourceOperation,
node.run_workflow,
**options)
self.assertEqual("Failed in executing workflow 'foo': boom.",
str(ex))
x_profile.workflow.assert_called_once_with(node)
wfc.workflow_find.assert_called_once_with('foo')
wfc.workflow_create.assert_called_once_with(
{'bar': 'baz'}, scope='private')
final_dict = {
'cluster_id': 'FAKE_CLUSTER',
'node_id': 'FAKE_NODE',
'FAKE_KEY1': 'FAKE_VALUE1',
'FAKE_KEY2': 'FAKE_VALUE2',
}
self._verify_execution_create_args('foo', final_dict, wfc)