1207 lines
53 KiB
Python
1207 lines
53 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_config import cfg
|
|
from oslo_messaging.rpc import dispatcher as rpc
|
|
|
|
from senlin.common import consts
|
|
from senlin.common import exception as exc
|
|
from senlin.common.i18n import _
|
|
from senlin.common import utils as common_utils
|
|
from senlin.conductor import service
|
|
from senlin.engine.actions import base as action_mod
|
|
from senlin.engine import dispatcher
|
|
from senlin.engine import environment
|
|
from senlin.engine import node as node_mod
|
|
from senlin.objects import cluster as co
|
|
from senlin.objects import node as no
|
|
from senlin.objects import profile as po
|
|
from senlin.objects.requests import nodes as orno
|
|
from senlin.profiles import base as pb
|
|
from senlin.tests.unit.common import base
|
|
from senlin.tests.unit.common import utils
|
|
|
|
|
|
class NodeTest(base.SenlinTestCase):
|
|
def setUp(self):
|
|
super(NodeTest, self).setUp()
|
|
self.ctx = utils.dummy_context(project='node_test_project')
|
|
self.svc = service.ConductorService('host-a', 'topic-a')
|
|
|
|
@mock.patch.object(no.Node, 'get_all')
|
|
def test_node_list(self, mock_get):
|
|
obj_1 = mock.Mock()
|
|
obj_1.to_dict.return_value = {'k': 'v1'}
|
|
obj_2 = mock.Mock()
|
|
obj_2.to_dict.return_value = {'k': 'v2'}
|
|
mock_get.return_value = [obj_1, obj_2]
|
|
|
|
req = orno.NodeListRequest()
|
|
result = self.svc.node_list(self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual([{'k': 'v1'}, {'k': 'v2'}], result)
|
|
mock_get.assert_called_once_with(self.ctx, project_safe=True)
|
|
|
|
@mock.patch.object(co.Cluster, 'find')
|
|
@mock.patch.object(no.Node, 'get_all')
|
|
def test_node_list_with_cluster_id(self, mock_get, mock_find):
|
|
obj_1 = mock.Mock()
|
|
obj_1.to_dict.return_value = {'k': 'v1'}
|
|
obj_2 = mock.Mock()
|
|
obj_2.to_dict.return_value = {'k': 'v2'}
|
|
mock_get.return_value = [obj_1, obj_2]
|
|
mock_find.return_value = mock.Mock(id='CLUSTER_ID')
|
|
|
|
req = orno.NodeListRequest(cluster_id='MY_CLUSTER_NAME',
|
|
project_safe=True)
|
|
result = self.svc.node_list(self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual([{'k': 'v1'}, {'k': 'v2'}], result)
|
|
mock_find.assert_called_once_with(self.ctx, 'MY_CLUSTER_NAME')
|
|
mock_get.assert_called_once_with(self.ctx, cluster_id='CLUSTER_ID',
|
|
project_safe=True)
|
|
|
|
@mock.patch.object(no.Node, 'get_all')
|
|
def test_node_list_with_params(self, mock_get):
|
|
obj_1 = mock.Mock()
|
|
obj_1.to_dict.return_value = {'k': 'v1'}
|
|
obj_2 = mock.Mock()
|
|
obj_2.to_dict.return_value = {'k': 'v2'}
|
|
mock_get.return_value = [obj_1, obj_2]
|
|
|
|
MARKER_UUID = '2fd5b45f-bae4-4cdb-b283-a71e9f9805c7'
|
|
req = orno.NodeListRequest(status=['ACTIVE'], sort='status',
|
|
limit=123, marker=MARKER_UUID,
|
|
project_safe=True)
|
|
result = self.svc.node_list(self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual([{'k': 'v1'}, {'k': 'v2'}], result)
|
|
mock_get.assert_called_once_with(self.ctx, sort='status', limit=123,
|
|
marker=MARKER_UUID, project_safe=True,
|
|
filters={'status': ['ACTIVE']})
|
|
|
|
@mock.patch.object(co.Cluster, 'find')
|
|
def test_node_list_cluster_not_found(self, mock_find):
|
|
mock_find.side_effect = exc.ResourceNotFound(type='cluster',
|
|
id='BOGUS')
|
|
|
|
req = orno.NodeListRequest(cluster_id='BOGUS', project_safe=True)
|
|
|
|
ex = self.assertRaises(rpc.ExpectedException,
|
|
self.svc.node_list,
|
|
self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual(exc.BadRequest, ex.exc_info[0])
|
|
self.assertEqual('Cannot find the given cluster: BOGUS.',
|
|
str(ex.exc_info[1]))
|
|
mock_find.assert_called_once_with(self.ctx, 'BOGUS')
|
|
|
|
@mock.patch.object(no.Node, 'get_all')
|
|
def test_node_list_with_project_safe(self, mock_get):
|
|
mock_get.return_value = []
|
|
|
|
req = orno.NodeListRequest(project_safe=True)
|
|
result = self.svc.node_list(self.ctx, req.obj_to_primitive())
|
|
self.assertEqual([], result)
|
|
mock_get.assert_called_once_with(self.ctx, project_safe=True)
|
|
mock_get.reset_mock()
|
|
|
|
req = orno.NodeListRequest(project_safe=False)
|
|
ex = self.assertRaises(rpc.ExpectedException,
|
|
self.svc.node_list,
|
|
self.ctx, req.obj_to_primitive())
|
|
self.assertEqual(exc.Forbidden, ex.exc_info[0])
|
|
|
|
self.ctx.is_admin = True
|
|
req = orno.NodeListRequest(project_safe=False)
|
|
result = self.svc.node_list(self.ctx, req.obj_to_primitive())
|
|
self.assertEqual([], result)
|
|
mock_get.assert_called_once_with(self.ctx, project_safe=False)
|
|
mock_get.reset_mock()
|
|
|
|
@mock.patch.object(no.Node, 'get_all')
|
|
def test_node_list_empty(self, mock_get):
|
|
mock_get.return_value = []
|
|
|
|
req = orno.NodeListRequest()
|
|
result = self.svc.node_list(self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual([], result)
|
|
mock_get.assert_called_once_with(self.ctx, project_safe=True)
|
|
|
|
@mock.patch.object(action_mod.Action, 'create')
|
|
@mock.patch.object(no.Node, 'create')
|
|
@mock.patch.object(po.Profile, 'find')
|
|
@mock.patch.object(dispatcher, 'start_action')
|
|
def test_node_create(self, notify, mock_profile, mock_node, mock_action):
|
|
mock_profile.return_value = mock.Mock(id='PROFILE_ID')
|
|
x_node = mock.Mock(id='NODE_ID')
|
|
x_node.to_dict.return_value = {'foo': 'bar'}
|
|
mock_node.return_value = x_node
|
|
mock_action.return_value = 'ACTION_ID'
|
|
req = orno.NodeCreateRequestBody(name='NODE1',
|
|
profile_id='PROFILE_NAME')
|
|
|
|
result = self.svc.node_create(self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual({'foo': 'bar', 'action': 'ACTION_ID'}, result)
|
|
mock_profile.assert_called_once_with(self.ctx, 'PROFILE_NAME')
|
|
mock_node.assert_called_once_with(
|
|
self.ctx,
|
|
{
|
|
'name': 'NODE1',
|
|
'profile_id': 'PROFILE_ID',
|
|
'cluster_id': '',
|
|
'index': -1,
|
|
'role': '',
|
|
'metadata': {},
|
|
'user': self.ctx.user_id,
|
|
'project': self.ctx.project_id,
|
|
'domain': self.ctx.domain_id,
|
|
'data': {},
|
|
'init_at': mock.ANY,
|
|
'dependents': {},
|
|
'physical_id': None,
|
|
'status': 'INIT',
|
|
'status_reason': 'Initializing',
|
|
})
|
|
mock_action.assert_called_once_with(
|
|
self.ctx, 'NODE_ID', consts.NODE_CREATE,
|
|
name='node_create_NODE_ID',
|
|
cluster_id='',
|
|
cause=consts.CAUSE_RPC,
|
|
status=action_mod.Action.READY)
|
|
notify.assert_called_once_with()
|
|
|
|
@mock.patch.object(common_utils, 'format_node_name')
|
|
@mock.patch.object(action_mod.Action, 'create')
|
|
@mock.patch.object(no.Node, 'create')
|
|
@mock.patch.object(co.Cluster, 'get_next_index')
|
|
@mock.patch.object(co.Cluster, 'find')
|
|
@mock.patch.object(po.Profile, 'find')
|
|
@mock.patch.object(dispatcher, 'start_action')
|
|
def test_node_create_same_profile(self, notify, mock_profile,
|
|
mock_cluster, mock_index,
|
|
mock_node, mock_action,
|
|
mock_node_name):
|
|
mock_profile.return_value = mock.Mock(id='PROFILE_ID',
|
|
type='PROFILE_TYPE')
|
|
x_cluster = mock.Mock(id='CLUSTER_ID', profile_id='PROFILE_ID',
|
|
config={})
|
|
mock_cluster.return_value = x_cluster
|
|
mock_index.return_value = 12345
|
|
x_node = mock.Mock(id='NODE_ID')
|
|
x_node.to_dict.return_value = {'foo': 'bar'}
|
|
mock_node.return_value = x_node
|
|
mock_action.return_value = 'ACTION_ID'
|
|
mock_node_name.return_value = "GENERATED_NAME"
|
|
req = orno.NodeCreateRequestBody(name='NODE1',
|
|
profile_id='PROFILE_NAME',
|
|
cluster_id='FAKE_CLUSTER')
|
|
|
|
result = self.svc.node_create(self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual({'foo': 'bar', 'action': 'ACTION_ID'}, result)
|
|
mock_cluster.assert_called_once_with(self.ctx, 'FAKE_CLUSTER')
|
|
mock_profile.assert_called_once_with(self.ctx, 'PROFILE_NAME')
|
|
mock_index.assert_called_once_with(self.ctx, 'CLUSTER_ID')
|
|
mock_node.assert_called_once_with(
|
|
self.ctx,
|
|
{
|
|
'name': 'GENERATED_NAME',
|
|
'profile_id': 'PROFILE_ID',
|
|
'cluster_id': 'CLUSTER_ID',
|
|
'index': 12345,
|
|
'role': '',
|
|
'metadata': {},
|
|
'user': self.ctx.user_id,
|
|
'project': self.ctx.project_id,
|
|
'domain': self.ctx.domain_id,
|
|
'data': {},
|
|
'init_at': mock.ANY,
|
|
'dependents': {},
|
|
'physical_id': None,
|
|
'status': 'INIT',
|
|
'status_reason': 'Initializing',
|
|
})
|
|
mock_action.assert_called_once_with(
|
|
self.ctx, 'NODE_ID', consts.NODE_CREATE,
|
|
cluster_id='CLUSTER_ID',
|
|
name='node_create_NODE_ID',
|
|
cause=consts.CAUSE_RPC,
|
|
status=action_mod.Action.READY)
|
|
notify.assert_called_once_with()
|
|
|
|
@mock.patch.object(common_utils, "format_node_name")
|
|
@mock.patch.object(action_mod.Action, 'create')
|
|
@mock.patch.object(no.Node, 'create')
|
|
@mock.patch.object(co.Cluster, 'get_next_index')
|
|
@mock.patch.object(co.Cluster, 'find')
|
|
@mock.patch.object(po.Profile, 'find')
|
|
@mock.patch.object(dispatcher, 'start_action')
|
|
def test_node_create_same_profile_type(self, notify, mock_profile,
|
|
mock_cluster, mock_index,
|
|
mock_node, mock_action,
|
|
mock_node_name):
|
|
mock_profile.side_effect = [
|
|
mock.Mock(id='NODE_PROFILE_ID', type='PROFILE_TYPE'),
|
|
mock.Mock(id='CLUSTER_PROFILE_ID', type='PROFILE_TYPE'),
|
|
]
|
|
x_cluster = mock.Mock(id='CLUSTER_ID', profile_id='CLUSTER_PROFILE_ID',
|
|
config={})
|
|
mock_cluster.return_value = x_cluster
|
|
mock_index.return_value = 12345
|
|
x_node = mock.Mock(id='NODE_ID')
|
|
x_node.to_dict.return_value = {'foo': 'bar'}
|
|
mock_node.return_value = x_node
|
|
mock_action.return_value = 'ACTION_ID'
|
|
mock_node_name.return_value = 'GENERATED_NAME'
|
|
req = orno.NodeCreateRequestBody(name='NODE1',
|
|
profile_id='PROFILE_NAME',
|
|
cluster_id='FAKE_CLUSTER')
|
|
|
|
result = self.svc.node_create(self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual({'foo': 'bar', 'action': 'ACTION_ID'}, result)
|
|
mock_cluster.assert_called_once_with(self.ctx, 'FAKE_CLUSTER')
|
|
mock_profile.assert_has_calls([
|
|
mock.call(self.ctx, 'PROFILE_NAME'), # for node
|
|
mock.call(self.ctx, 'CLUSTER_PROFILE_ID'), # for cluster
|
|
])
|
|
mock_index.assert_called_once_with(self.ctx, 'CLUSTER_ID')
|
|
mock_node.assert_called_once_with(
|
|
self.ctx,
|
|
{
|
|
'name': 'GENERATED_NAME',
|
|
'profile_id': 'NODE_PROFILE_ID',
|
|
'cluster_id': 'CLUSTER_ID',
|
|
'physical_id': None,
|
|
'index': 12345,
|
|
'role': '',
|
|
'metadata': {},
|
|
'status': 'INIT',
|
|
'status_reason': 'Initializing',
|
|
'user': self.ctx.user_id,
|
|
'project': self.ctx.project_id,
|
|
'domain': self.ctx.domain_id,
|
|
'data': {},
|
|
'dependents': {},
|
|
'init_at': mock.ANY,
|
|
})
|
|
mock_action.assert_called_once_with(
|
|
self.ctx, 'NODE_ID', consts.NODE_CREATE,
|
|
name='node_create_NODE_ID',
|
|
cluster_id='CLUSTER_ID',
|
|
cause=consts.CAUSE_RPC,
|
|
status=action_mod.Action.READY)
|
|
notify.assert_called_once_with()
|
|
|
|
@mock.patch.object(po.Profile, 'find')
|
|
@mock.patch.object(no.Node, 'get_by_name')
|
|
def test_node_create_name_conflict(self, mock_find, mock_get):
|
|
cfg.CONF.set_override('name_unique', True)
|
|
mock_get.return_value = mock.Mock()
|
|
req = orno.NodeCreateRequestBody(name='NODE1',
|
|
profile_id='PROFILE_NAME')
|
|
|
|
ex = self.assertRaises(rpc.ExpectedException,
|
|
self.svc.node_create,
|
|
self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual(exc.BadRequest, ex.exc_info[0])
|
|
self.assertEqual(_("The node named (NODE1) already exists."),
|
|
str(ex.exc_info[1]))
|
|
|
|
@mock.patch.object(po.Profile, 'find')
|
|
def test_node_create_profile_not_found(self, mock_profile):
|
|
mock_profile.side_effect = exc.ResourceNotFound(type='profile',
|
|
id='Bogus')
|
|
req = orno.NodeCreateRequestBody(name='NODE1',
|
|
profile_id='Bogus')
|
|
|
|
ex = self.assertRaises(rpc.ExpectedException,
|
|
self.svc.node_create,
|
|
self.ctx, req.obj_to_primitive())
|
|
self.assertEqual(exc.BadRequest, ex.exc_info[0])
|
|
self.assertEqual("The specified profile 'Bogus' could not be "
|
|
"found.", str(ex.exc_info[1]))
|
|
mock_profile.assert_called_once_with(self.ctx, 'Bogus')
|
|
|
|
@mock.patch.object(co.Cluster, 'find')
|
|
@mock.patch.object(po.Profile, 'find')
|
|
def test_node_create_cluster_not_found(self, mock_profile, mock_cluster):
|
|
mock_cluster.side_effect = exc.ResourceNotFound(type='cluster',
|
|
id='Bogus')
|
|
req = orno.NodeCreateRequestBody(name='NODE1',
|
|
profile_id='PROFILE_NAME',
|
|
cluster_id='Bogus')
|
|
|
|
ex = self.assertRaises(rpc.ExpectedException,
|
|
self.svc.node_create,
|
|
self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual(exc.BadRequest, ex.exc_info[0])
|
|
self.assertEqual("The specified cluster 'Bogus' could not be found.",
|
|
str(ex.exc_info[1]))
|
|
mock_cluster.assert_called_once_with(self.ctx, 'Bogus')
|
|
|
|
@mock.patch.object(co.Cluster, 'find')
|
|
@mock.patch.object(po.Profile, 'find')
|
|
def test_node_create_profile_type_not_match(self, mock_profile,
|
|
mock_cluster):
|
|
mock_profile.side_effect = [
|
|
mock.Mock(id='NODE_PROFILE_ID', type='TYPE-A'),
|
|
mock.Mock(id='CLUSTER_PROFILE_ID', type='TYPE-B'),
|
|
]
|
|
mock_cluster.return_value = mock.Mock(id='CLUSTER_ID',
|
|
profile_id='CLUSTER_PROFILE_ID')
|
|
req = orno.NodeCreateRequestBody(name='NODE1',
|
|
profile_id='NODE_PROFILE',
|
|
cluster_id='FAKE_CLUSTER')
|
|
|
|
ex = self.assertRaises(rpc.ExpectedException,
|
|
self.svc.node_create,
|
|
self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual(exc.BadRequest, ex.exc_info[0])
|
|
self.assertEqual("Node and cluster have different profile "
|
|
"type, operation aborted.",
|
|
str(ex.exc_info[1]))
|
|
mock_profile.assert_has_calls([
|
|
mock.call(self.ctx, 'NODE_PROFILE'),
|
|
mock.call(self.ctx, 'CLUSTER_PROFILE_ID'),
|
|
])
|
|
mock_cluster.assert_called_once_with(self.ctx, 'FAKE_CLUSTER')
|
|
|
|
@mock.patch.object(no.Node, 'find')
|
|
def test_node_get(self, mock_find):
|
|
x_obj = mock.Mock(physical_id='PHYSICAL_ID')
|
|
x_obj.to_dict.return_value = {'foo': 'bar'}
|
|
mock_find.return_value = x_obj
|
|
req = orno.NodeGetRequest(identity='NODE1', show_details=False)
|
|
|
|
result = self.svc.node_get(self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual({'foo': 'bar'}, result)
|
|
mock_find.assert_called_once_with(self.ctx, 'NODE1')
|
|
x_obj.to_dict.assert_called_once_with()
|
|
|
|
@mock.patch.object(node_mod.Node, 'load')
|
|
@mock.patch.object(no.Node, 'find')
|
|
def test_node_get_with_details(self, mock_find, mock_load):
|
|
x_obj = mock.Mock(physical_id='PHYSICAL_ID')
|
|
x_obj.to_dict.return_value = {'foo': 'bar'}
|
|
mock_find.return_value = x_obj
|
|
x_node = mock.Mock()
|
|
x_node.get_details.return_value = {'info': 'blahblah'}
|
|
mock_load.return_value = x_node
|
|
|
|
req = orno.NodeGetRequest(identity='NODE1', show_details=True)
|
|
result = self.svc.node_get(self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual({'foo': 'bar', 'details': {'info': 'blahblah'}},
|
|
result)
|
|
mock_find.assert_called_once_with(self.ctx, 'NODE1')
|
|
mock_load.assert_called_once_with(self.ctx, db_node=x_obj)
|
|
x_obj.to_dict.assert_called_once_with()
|
|
x_node.get_details.assert_called_once_with(self.ctx)
|
|
|
|
@mock.patch.object(no.Node, 'find')
|
|
def test_node_get_node_not_found(self, mock_find):
|
|
mock_find.side_effect = exc.ResourceNotFound(type='node', id='Bogus')
|
|
req = orno.NodeGetRequest(identity='Bogus', show_details=False)
|
|
|
|
ex = self.assertRaises(rpc.ExpectedException,
|
|
self.svc.node_get,
|
|
self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual(exc.ResourceNotFound, ex.exc_info[0])
|
|
self.assertEqual("The node 'Bogus' could not be found.",
|
|
str(ex.exc_info[1]))
|
|
mock_find.assert_called_once_with(self.ctx, 'Bogus')
|
|
|
|
@mock.patch.object(no.Node, 'find')
|
|
def test_node_get_no_physical_id(self, mock_find):
|
|
x_obj = mock.Mock(physical_id=None)
|
|
x_obj.to_dict.return_value = {'foo': 'bar'}
|
|
mock_find.return_value = x_obj
|
|
req = orno.NodeGetRequest(identity='NODE1', show_details=True)
|
|
|
|
result = self.svc.node_get(self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual({'foo': 'bar'}, result)
|
|
mock_find.assert_called_once_with(self.ctx, 'NODE1')
|
|
x_obj.to_dict.assert_called_once_with()
|
|
|
|
@mock.patch.object(dispatcher, 'start_action')
|
|
@mock.patch.object(action_mod.Action, 'create')
|
|
@mock.patch.object(no.Node, 'find')
|
|
def test_node_update(self, mock_find, mock_action, mock_start):
|
|
x_obj = mock.Mock(id='FAKE_NODE_ID', name='NODE1', role='ROLE1',
|
|
cluster_id='FAKE_CLUSTER_ID',
|
|
metadata={'KEY': 'VALUE'})
|
|
x_obj.to_dict.return_value = {'foo': 'bar'}
|
|
mock_find.return_value = x_obj
|
|
mock_action.return_value = 'ACTION_ID'
|
|
|
|
req = orno.NodeUpdateRequest(identity='FAKE_NODE',
|
|
name='NODE2',
|
|
role='NEW_ROLE',
|
|
metadata={'foo1': 'bar1'})
|
|
|
|
# all properties changed except profile id
|
|
result = self.svc.node_update(self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual({'foo': 'bar', 'action': 'ACTION_ID'}, result)
|
|
mock_find.assert_called_once_with(self.ctx, 'FAKE_NODE')
|
|
mock_action.assert_called_once_with(
|
|
self.ctx, 'FAKE_NODE_ID', consts.NODE_UPDATE,
|
|
name='node_update_FAKE_NOD',
|
|
cluster_id='FAKE_CLUSTER_ID',
|
|
cause=consts.CAUSE_RPC,
|
|
status=action_mod.Action.READY,
|
|
inputs={
|
|
'name': 'NODE2',
|
|
'role': 'NEW_ROLE',
|
|
'metadata': {
|
|
'foo1': 'bar1',
|
|
}
|
|
})
|
|
mock_start.assert_called_once_with()
|
|
|
|
@mock.patch.object(dispatcher, 'start_action')
|
|
@mock.patch.object(action_mod.Action, 'create')
|
|
@mock.patch.object(po.Profile, 'find')
|
|
@mock.patch.object(no.Node, 'find')
|
|
# @mock.patch.object(co.Cluster, 'find')
|
|
def test_node_update_new_profile(self, mock_find, mock_profile,
|
|
mock_action, mock_start):
|
|
x_obj = mock.Mock(id='FAKE_NODE_ID', role='ROLE1',
|
|
cluster_id='FAKE_CLUSTER_ID',
|
|
metadata={'KEY': 'VALUE'},
|
|
profile_id='OLD_PROFILE_ID')
|
|
x_obj.name = 'NODE1'
|
|
x_obj.to_dict.return_value = {'foo': 'bar'}
|
|
mock_find.return_value = x_obj
|
|
# Same profile type
|
|
mock_profile.side_effect = [
|
|
mock.Mock(id='NEW_PROFILE_ID', type='PROFILE_TYPE'),
|
|
mock.Mock(id='OLD_PROFILE_ID', type='PROFILE_TYPE'),
|
|
]
|
|
mock_action.return_value = 'ACTION_ID'
|
|
# all properties are filtered out except for profile_id
|
|
req = orno.NodeUpdateRequest(identity='FAKE_NODE',
|
|
name='NODE1',
|
|
role='ROLE1',
|
|
metadata={'KEY': 'VALUE'},
|
|
profile_id='NEW_PROFILE')
|
|
result = self.svc.node_update(self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual({'foo': 'bar', 'action': 'ACTION_ID'}, result)
|
|
mock_find.assert_called_once_with(self.ctx, 'FAKE_NODE')
|
|
mock_profile.assert_has_calls([
|
|
mock.call(self.ctx, 'NEW_PROFILE'),
|
|
mock.call(self.ctx, 'OLD_PROFILE_ID'),
|
|
])
|
|
mock_action.assert_called_once_with(
|
|
self.ctx, 'FAKE_NODE_ID', consts.NODE_UPDATE,
|
|
name='node_update_FAKE_NOD',
|
|
cluster_id='FAKE_CLUSTER_ID',
|
|
cause=consts.CAUSE_RPC,
|
|
status=action_mod.Action.READY,
|
|
inputs={
|
|
'new_profile_id': 'NEW_PROFILE_ID',
|
|
})
|
|
mock_start.assert_called_once_with()
|
|
|
|
@mock.patch.object(no.Node, 'find')
|
|
def test_node_update_node_not_found(self, mock_find):
|
|
mock_find.side_effect = exc.ResourceNotFound(type='node', id='Bogus')
|
|
|
|
req = orno.NodeUpdateRequest(identity='Bogus')
|
|
ex = self.assertRaises(rpc.ExpectedException,
|
|
self.svc.node_update, self.ctx,
|
|
req.obj_to_primitive())
|
|
|
|
self.assertEqual(exc.ResourceNotFound, ex.exc_info[0])
|
|
self.assertEqual("The node 'Bogus' could not be found.",
|
|
str(ex.exc_info[1]))
|
|
mock_find.assert_called_once_with(self.ctx, 'Bogus')
|
|
|
|
@mock.patch.object(po.Profile, 'find')
|
|
@mock.patch.object(no.Node, 'find')
|
|
def test_node_update_profile_not_found(self, mock_find, mock_profile):
|
|
mock_find.return_value = mock.Mock()
|
|
mock_profile.side_effect = exc.ResourceNotFound(type='profile',
|
|
id='Bogus')
|
|
|
|
req = orno.NodeUpdateRequest(identity='FAKE_NODE',
|
|
profile_id='Bogus')
|
|
ex = self.assertRaises(rpc.ExpectedException,
|
|
self.svc.node_update,
|
|
self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual(exc.BadRequest, ex.exc_info[0])
|
|
self.assertEqual("The specified profile 'Bogus' could not be "
|
|
"found.", str(ex.exc_info[1]))
|
|
mock_find.assert_called_once_with(self.ctx, 'FAKE_NODE')
|
|
mock_profile.assert_called_once_with(self.ctx, 'Bogus')
|
|
|
|
@mock.patch.object(po.Profile, 'find')
|
|
@mock.patch.object(no.Node, 'find')
|
|
def test_node_update_diff_profile_type(self, mock_find, mock_profile):
|
|
mock_find.return_value = mock.Mock(profile_id='OLD_PROFILE_ID')
|
|
mock_profile.side_effect = [
|
|
mock.Mock(id='NEW_PROFILE_ID', type='NEW_PROFILE_TYPE'),
|
|
mock.Mock(id='OLD_PROFILE_ID', type='OLD_PROFILE_TYPE'),
|
|
]
|
|
|
|
req = orno.NodeUpdateRequest(identity='FAKE_NODE',
|
|
profile_id='NEW_PROFILE')
|
|
ex = self.assertRaises(rpc.ExpectedException,
|
|
self.svc.node_update,
|
|
self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual(exc.BadRequest, ex.exc_info[0])
|
|
self.assertEqual("Cannot update a node to a different "
|
|
"profile type, operation aborted.",
|
|
str(ex.exc_info[1]))
|
|
mock_find.assert_called_once_with(self.ctx, 'FAKE_NODE')
|
|
mock_profile.assert_has_calls([
|
|
mock.call(self.ctx, 'NEW_PROFILE'),
|
|
mock.call(self.ctx, 'OLD_PROFILE_ID'),
|
|
])
|
|
|
|
@mock.patch.object(po.Profile, 'find')
|
|
@mock.patch.object(no.Node, 'find')
|
|
def test_node_update_dumplicated_profile(self, mock_find, mock_profile):
|
|
mock_find.return_value = mock.Mock(profile_id='OLD_PROFILE_ID')
|
|
mock_profile.side_effect = [
|
|
mock.Mock(id='OLD_PROFILE_ID', type='PROFILE_TYPE'),
|
|
mock.Mock(id='OLD_PROFILE_ID', type='PROFILE_TYPE'),
|
|
]
|
|
|
|
req = orno.NodeUpdateRequest(identity='FAKE_NODE',
|
|
profile_id='OLD_PROFILE_ID')
|
|
ex = self.assertRaises(rpc.ExpectedException,
|
|
self.svc.node_update,
|
|
self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual(exc.BadRequest, ex.exc_info[0])
|
|
self.assertEqual("No property needs an update.",
|
|
str(ex.exc_info[1]))
|
|
mock_find.assert_called_once_with(self.ctx, 'FAKE_NODE')
|
|
mock_profile.assert_has_calls([
|
|
mock.call(self.ctx, 'OLD_PROFILE_ID'),
|
|
mock.call(self.ctx, 'OLD_PROFILE_ID'),
|
|
])
|
|
|
|
@mock.patch.object(no.Node, 'find')
|
|
def test_node_update_no_property_for_update(self, mock_find):
|
|
x_obj = mock.Mock(id='FAKE_NODE_ID', name='NODE1', role='ROLE1',
|
|
metadata={'KEY': 'VALUE'})
|
|
mock_find.return_value = x_obj
|
|
|
|
# no property has been specified for update
|
|
req = orno.NodeUpdateRequest(identity='FAKE_NODE')
|
|
ex = self.assertRaises(rpc.ExpectedException,
|
|
self.svc.node_update,
|
|
self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual(exc.BadRequest, ex.exc_info[0])
|
|
self.assertEqual("No property needs an update.",
|
|
str(ex.exc_info[1]))
|
|
mock_find.assert_called_once_with(self.ctx, 'FAKE_NODE')
|
|
|
|
@mock.patch.object(dispatcher, 'start_action')
|
|
@mock.patch.object(action_mod.Action, 'create')
|
|
@mock.patch.object(no.Node, 'find')
|
|
def test_node_delete(self, mock_find, mock_action, mock_start):
|
|
mock_find.return_value = mock.Mock(id='12345678AB', status='ACTIVE',
|
|
cluster_id='',
|
|
dependents={})
|
|
mock_action.return_value = 'ACTION_ID'
|
|
|
|
req = orno.NodeDeleteRequest(identity='FAKE_NODE', force=False)
|
|
result = self.svc.node_delete(self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual({'action': 'ACTION_ID'}, result)
|
|
mock_find.assert_called_once_with(self.ctx, 'FAKE_NODE')
|
|
mock_action.assert_called_once_with(
|
|
self.ctx, '12345678AB', consts.NODE_DELETE,
|
|
name='node_delete_12345678',
|
|
cluster_id='',
|
|
cause=consts.CAUSE_RPC,
|
|
status=action_mod.Action.READY)
|
|
mock_start.assert_called_once_with()
|
|
|
|
@mock.patch.object(no.Node, 'find')
|
|
def test_node_delete_node_not_found(self, mock_find):
|
|
mock_find.side_effect = exc.ResourceNotFound(type='node', id='Bogus')
|
|
|
|
req = orno.NodeDeleteRequest(identity='Bogus', force=False)
|
|
ex = self.assertRaises(rpc.ExpectedException,
|
|
self.svc.node_delete, self.ctx,
|
|
req.obj_to_primitive())
|
|
|
|
self.assertEqual(exc.ResourceNotFound, ex.exc_info[0])
|
|
self.assertEqual("The node 'Bogus' could not be found.",
|
|
str(ex.exc_info[1]))
|
|
mock_find.assert_called_once_with(self.ctx, 'Bogus')
|
|
|
|
@mock.patch.object(no.Node, 'find')
|
|
def test_node_delete_improper_status(self, mock_find):
|
|
for bad_status in [consts.NS_CREATING, consts.NS_UPDATING,
|
|
consts.NS_DELETING, consts.NS_RECOVERING]:
|
|
fake_node = mock.Mock(id='12345678AB', status=bad_status)
|
|
mock_find.return_value = fake_node
|
|
req = orno.NodeDeleteRequest(identity='BUSY', force=False)
|
|
ex = self.assertRaises(rpc.ExpectedException,
|
|
self.svc.node_delete,
|
|
self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual(exc.ActionInProgress, ex.exc_info[0])
|
|
self.assertEqual("The node 'BUSY' is in status %s." % bad_status,
|
|
str(ex.exc_info[1]))
|
|
# skipping assertion on mock_find
|
|
|
|
@mock.patch.object(no.Node, 'find')
|
|
def test_node_delete_have_dependency(self, mock_find):
|
|
dependents = {'nodes': ['NODE1']}
|
|
node = mock.Mock(id='NODE_ID', status='ACTIVE', dependents=dependents)
|
|
mock_find.return_value = node
|
|
req = orno.NodeDeleteRequest(identity='node1', force=False)
|
|
ex = self.assertRaises(rpc.ExpectedException,
|
|
self.svc.node_delete,
|
|
self.ctx, req.obj_to_primitive())
|
|
self.assertEqual(exc.ResourceInUse, ex.exc_info[0])
|
|
self.assertEqual("The node 'node1' cannot be deleted: still depended "
|
|
"by other clusters and/or nodes.",
|
|
str(ex.exc_info[1]))
|
|
|
|
@mock.patch.object(dispatcher, 'start_action')
|
|
@mock.patch.object(action_mod.Action, 'create')
|
|
@mock.patch.object(no.Node, 'find')
|
|
def test_node_delete_force(self, mock_find, mock_action, mock_start):
|
|
for bad_status in [consts.NS_CREATING, consts.NS_UPDATING,
|
|
consts.NS_DELETING, consts.NS_RECOVERING]:
|
|
|
|
mock_find.return_value = mock.Mock(id='12345678AB',
|
|
status=bad_status,
|
|
dependents={},
|
|
cluster_id='',)
|
|
mock_action.return_value = 'ACTION_ID'
|
|
|
|
req = orno.NodeDeleteRequest(identity='FAKE_NODE', force=True)
|
|
result = self.svc.node_delete(self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual({'action': 'ACTION_ID'}, result)
|
|
mock_find.assert_called_with(self.ctx, 'FAKE_NODE')
|
|
mock_action.assert_called_with(
|
|
self.ctx, '12345678AB', consts.NODE_DELETE,
|
|
name='node_delete_12345678',
|
|
cluster_id='',
|
|
cause=consts.CAUSE_RPC,
|
|
status=action_mod.Action.READY)
|
|
mock_start.assert_called_with()
|
|
|
|
@mock.patch.object(environment.Environment, 'get_profile')
|
|
@mock.patch.object(pb.Profile, 'adopt_node')
|
|
def test_node_adopt_preview_with_profile(self, mock_adopt, mock_profile):
|
|
class FakeProfile(object):
|
|
pass
|
|
|
|
req = mock.Mock(
|
|
identity="FAKE_NODE",
|
|
type="TestProfile-1.0",
|
|
overrides="foo",
|
|
snapshot=True
|
|
)
|
|
mock_adopt.return_value = {'prop': 'value'}
|
|
mock_profile.return_value = FakeProfile
|
|
|
|
c, s = self.svc._node_adopt_preview(self.ctx, req)
|
|
|
|
req.obj_set_defaults.assert_called_once_with()
|
|
mock_profile.assert_called_once_with("TestProfile-1.0")
|
|
self.assertEqual(FakeProfile, c)
|
|
mock_adopt.assert_called_once_with(
|
|
self.ctx, mock.ANY, 'TestProfile-1.0',
|
|
overrides="foo", snapshot=True)
|
|
fake_node = mock_adopt.call_args[0][1]
|
|
self.assertIsInstance(fake_node, node_mod.Node)
|
|
self.assertEqual('adopt', fake_node.name)
|
|
self.assertEqual('TBD', fake_node.profile_id)
|
|
self.assertEqual('FAKE_NODE', fake_node.physical_id)
|
|
expected = {
|
|
'type': 'TestProfile',
|
|
'version': '1.0',
|
|
'properties': {'prop': 'value'}
|
|
}
|
|
self.assertEqual(expected, s)
|
|
|
|
@mock.patch.object(pb.Profile, 'adopt_node')
|
|
def test_node_adopt_preview_bad_type(self, mock_adopt):
|
|
req = mock.Mock(
|
|
identity="FAKE_NODE",
|
|
type="TestProfile-1.0",
|
|
overrides="foo",
|
|
snapshot=True
|
|
)
|
|
|
|
ex = self.assertRaises(exc.BadRequest,
|
|
self.svc._node_adopt_preview,
|
|
self.ctx, req)
|
|
|
|
req.obj_set_defaults.assert_called_once_with()
|
|
self.assertEqual("The profile_type 'TestProfile-1.0' could not be "
|
|
"found.", str(ex))
|
|
|
|
@mock.patch.object(environment.Environment, 'get_profile')
|
|
@mock.patch.object(pb.Profile, 'adopt_node')
|
|
def test_node_adopt_preview_failed_adopt(self, mock_adopt, mock_profile):
|
|
class FakeProfile(object):
|
|
pass
|
|
|
|
req = mock.Mock(
|
|
identity="FAKE_NODE",
|
|
type="TestProfile-1.0",
|
|
overrides="foo",
|
|
snapshot=True
|
|
)
|
|
mock_profile.return_value = FakeProfile
|
|
mock_adopt.return_value = {
|
|
'Error': {'code': 502, 'message': 'something is bad'}
|
|
}
|
|
|
|
ex = self.assertRaises(exc.ProfileOperationFailed,
|
|
self.svc._node_adopt_preview,
|
|
self.ctx, req)
|
|
|
|
req.obj_set_defaults.assert_called_once_with()
|
|
mock_profile.assert_called_once_with("TestProfile-1.0")
|
|
mock_adopt.assert_called_once_with(
|
|
self.ctx, mock.ANY, 'TestProfile-1.0',
|
|
overrides="foo", snapshot=True)
|
|
|
|
self.assertEqual('502: something is bad', str(ex))
|
|
|
|
@mock.patch.object(service.ConductorService, '_node_adopt_preview')
|
|
def test_node_adopt_preview(self, mock_preview):
|
|
spec = {'foo': 'bar'}
|
|
mock_preview.return_value = mock.Mock(), spec
|
|
req = orno.NodeAdoptPreviewRequest(identity='FAKE_ID',
|
|
type='FAKE_TYPE')
|
|
|
|
res = self.svc.node_adopt_preview(self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual({'node_preview': {'foo': 'bar'}}, res)
|
|
mock_preview.assert_called_once_with(self.ctx, mock.ANY)
|
|
self.assertIsInstance(mock_preview.call_args[0][1],
|
|
orno.NodeAdoptPreviewRequest)
|
|
|
|
@mock.patch.object(service.ConductorService, '_node_adopt_preview')
|
|
def test_node_adopt_preview_with_exception(self, mock_preview):
|
|
mock_preview.side_effect = exc.BadRequest(msg="boom")
|
|
req = orno.NodeAdoptPreviewRequest(identity='FAKE_ID',
|
|
type='FAKE_TYPE')
|
|
|
|
ex = self.assertRaises(rpc.ExpectedException,
|
|
self.svc.node_adopt_preview,
|
|
self.ctx, req.obj_to_primitive())
|
|
|
|
mock_preview.assert_called_once_with(self.ctx, mock.ANY)
|
|
self.assertIsInstance(mock_preview.call_args[0][1],
|
|
orno.NodeAdoptPreviewRequest)
|
|
self.assertEqual(exc.BadRequest, ex.exc_info[0])
|
|
self.assertEqual('boom.', str(ex.exc_info[1]))
|
|
|
|
@mock.patch.object(no.Node, 'create')
|
|
@mock.patch.object(service.ConductorService, '_node_adopt_preview')
|
|
def test_node_adopt(self, mock_preview, mock_create):
|
|
class FakeProfile(object):
|
|
|
|
@classmethod
|
|
def create(cls, ctx, name, spec):
|
|
obj = mock.Mock(spec=spec, id='PROFILE_ID')
|
|
obj.name = name
|
|
return obj
|
|
|
|
req = orno.NodeAdoptRequest(identity='FAKE_ID', type='FAKE_TYPE')
|
|
mock_preview.return_value = FakeProfile, {'foo': 'bar'}
|
|
fake_node = mock.Mock()
|
|
fake_node.to_dict = mock.Mock(return_value={'attr': 'value'})
|
|
mock_create.return_value = fake_node
|
|
|
|
res = self.svc.node_adopt(self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual({'attr': 'value'}, res)
|
|
mock_preview.assert_called_once_with(self.ctx, mock.ANY)
|
|
self.assertIsInstance(mock_preview.call_args[0][1],
|
|
orno.NodeAdoptRequest)
|
|
attrs = {
|
|
'name': mock.ANY,
|
|
'data': {},
|
|
'dependents': {},
|
|
'profile_id': 'PROFILE_ID',
|
|
'physical_id': 'FAKE_ID',
|
|
'cluster_id': '',
|
|
'index': -1,
|
|
'role': '',
|
|
'metadata': {},
|
|
'status': consts.NS_ACTIVE,
|
|
'status_reason': 'Node adopted successfully',
|
|
'init_at': mock.ANY,
|
|
'created_at': mock.ANY,
|
|
'user': self.ctx.user_id,
|
|
'project': self.ctx.project_id,
|
|
'domain': self.ctx.domain_id
|
|
}
|
|
mock_create.assert_called_once_with(self.ctx, attrs)
|
|
|
|
@mock.patch.object(no.Node, 'get_by_name')
|
|
def test_node_adopt_name_not_unique(self, mock_get):
|
|
cfg.CONF.set_override('name_unique', True)
|
|
req = orno.NodeAdoptRequest(
|
|
name='FAKE_NAME', preview=False,
|
|
identity='FAKE_ID', type='FAKE_TYPE')
|
|
mock_get.return_value = mock.Mock()
|
|
|
|
ex = self.assertRaises(rpc.ExpectedException,
|
|
self.svc.node_adopt,
|
|
self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual(exc.BadRequest, ex.exc_info[0])
|
|
self.assertEqual("The node named (FAKE_NAME) already exists.",
|
|
str(ex.exc_info[1]))
|
|
|
|
@mock.patch.object(no.Node, 'create')
|
|
@mock.patch.object(service.ConductorService, '_node_adopt_preview')
|
|
def test_node_adopt_failed_preview(self, mock_preview, mock_create):
|
|
req = orno.NodeAdoptRequest(identity='FAKE_ID', type='FAKE_TYPE')
|
|
mock_preview.side_effect = exc.BadRequest(msg='boom')
|
|
|
|
ex = self.assertRaises(rpc.ExpectedException,
|
|
self.svc.node_adopt,
|
|
self.ctx, req.obj_to_primitive())
|
|
|
|
mock_preview.assert_called_once_with(self.ctx, mock.ANY)
|
|
self.assertIsInstance(mock_preview.call_args[0][1],
|
|
orno.NodeAdoptRequest)
|
|
|
|
self.assertEqual(exc.BadRequest, ex.exc_info[0])
|
|
self.assertEqual("boom.", str(ex.exc_info[1]))
|
|
|
|
@mock.patch.object(dispatcher, 'start_action')
|
|
@mock.patch.object(action_mod.Action, 'create')
|
|
@mock.patch.object(no.Node, 'find')
|
|
def test_node_check(self, mock_find, mock_action, mock_start):
|
|
mock_find.return_value = mock.Mock(id='12345678AB',
|
|
cluster_id='FAKE_CLUSTER_ID')
|
|
mock_action.return_value = 'ACTION_ID'
|
|
|
|
params = {'k1': 'v1'}
|
|
req = orno.NodeCheckRequest(identity='FAKE_NODE', params=params)
|
|
result = self.svc.node_check(self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual({'action': 'ACTION_ID'}, result)
|
|
mock_find.assert_called_once_with(self.ctx, 'FAKE_NODE')
|
|
mock_action.assert_called_once_with(
|
|
self.ctx, '12345678AB', consts.NODE_CHECK,
|
|
name='node_check_12345678',
|
|
cluster_id='FAKE_CLUSTER_ID',
|
|
cause=consts.CAUSE_RPC,
|
|
status=action_mod.Action.READY,
|
|
inputs={'k1': 'v1'})
|
|
mock_start.assert_called_once_with()
|
|
|
|
@mock.patch.object(no.Node, 'find')
|
|
def test_node_check_not_found(self, mock_find):
|
|
mock_find.side_effect = exc.ResourceNotFound(type='node', id='Bogus')
|
|
|
|
req = orno.NodeCheckRequest(identity='Bogus')
|
|
ex = self.assertRaises(rpc.ExpectedException,
|
|
self.svc.node_check,
|
|
self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual(exc.ResourceNotFound, ex.exc_info[0])
|
|
self.assertEqual("The node 'Bogus' could not be found.",
|
|
str(ex.exc_info[1]))
|
|
mock_find.assert_called_once_with(self.ctx, 'Bogus')
|
|
|
|
@mock.patch.object(dispatcher, 'start_action')
|
|
@mock.patch.object(action_mod.Action, 'create')
|
|
@mock.patch.object(no.Node, 'find')
|
|
def test_node_recover(self, mock_find, mock_action, mock_start):
|
|
mock_find.return_value = mock.Mock(
|
|
id='12345678AB', cluster_id='FAKE_CLUSTER_ID')
|
|
mock_action.return_value = 'ACTION_ID'
|
|
|
|
params = {'operation': 'REBOOT'}
|
|
req = orno.NodeRecoverRequest(identity='FAKE_NODE', params=params)
|
|
result = self.svc.node_recover(self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual({'action': 'ACTION_ID'}, result)
|
|
mock_find.assert_called_once_with(self.ctx, 'FAKE_NODE')
|
|
mock_action.assert_called_once_with(
|
|
self.ctx, '12345678AB', consts.NODE_RECOVER,
|
|
name='node_recover_12345678',
|
|
cluster_id='FAKE_CLUSTER_ID',
|
|
cause=consts.CAUSE_RPC,
|
|
status=action_mod.Action.READY,
|
|
inputs={'operation': 'REBOOT'})
|
|
mock_start.assert_called_once_with()
|
|
|
|
@mock.patch.object(dispatcher, 'start_action')
|
|
@mock.patch.object(action_mod.Action, 'create')
|
|
@mock.patch.object(no.Node, 'find')
|
|
def test_node_recover_with_check(self, mock_find, mock_action, mock_start):
|
|
mock_find.return_value = mock.Mock(id='12345678AB', cluster_id='')
|
|
mock_action.return_value = 'ACTION_ID'
|
|
|
|
params = {'check': True, 'operation': 'REBUILD'}
|
|
req = orno.NodeRecoverRequest(identity='FAKE_NODE', params=params)
|
|
result = self.svc.node_recover(self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual({'action': 'ACTION_ID'}, result)
|
|
mock_find.assert_called_once_with(self.ctx, 'FAKE_NODE')
|
|
mock_action.assert_called_once_with(
|
|
self.ctx, '12345678AB', consts.NODE_RECOVER,
|
|
name='node_recover_12345678',
|
|
cluster_id='',
|
|
cause=consts.CAUSE_RPC,
|
|
status=action_mod.Action.READY,
|
|
inputs={'check': True, 'operation': 'REBUILD'})
|
|
mock_start.assert_called_once_with()
|
|
|
|
@mock.patch.object(dispatcher, 'start_action')
|
|
@mock.patch.object(action_mod.Action, 'create')
|
|
@mock.patch.object(no.Node, 'find')
|
|
def test_node_recover_with_delete_timeout(self, mock_find, mock_action,
|
|
mock_start):
|
|
mock_find.return_value = mock.Mock(id='12345678AB', cluster_id='',)
|
|
mock_action.return_value = 'ACTION_ID'
|
|
|
|
params = {'delete_timeout': 20, 'operation': 'RECREATE'}
|
|
req = orno.NodeRecoverRequest(identity='FAKE_NODE', params=params)
|
|
result = self.svc.node_recover(self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual({'action': 'ACTION_ID'}, result)
|
|
mock_find.assert_called_once_with(self.ctx, 'FAKE_NODE')
|
|
mock_action.assert_called_once_with(
|
|
self.ctx, '12345678AB', consts.NODE_RECOVER,
|
|
name='node_recover_12345678',
|
|
cluster_id='',
|
|
cause=consts.CAUSE_RPC,
|
|
status=action_mod.Action.READY,
|
|
inputs={'delete_timeout': 20,
|
|
'operation': 'RECREATE'})
|
|
mock_start.assert_called_once_with()
|
|
|
|
@mock.patch.object(dispatcher, 'start_action')
|
|
@mock.patch.object(action_mod.Action, 'create')
|
|
@mock.patch.object(no.Node, 'find')
|
|
def test_node_recover_with_force_recreate(self, mock_find, mock_action,
|
|
mock_start):
|
|
mock_find.return_value = mock.Mock(
|
|
id='12345678AB', cluster_id='FAKE_CLUSTER_ID')
|
|
mock_action.return_value = 'ACTION_ID'
|
|
|
|
params = {'force_recreate': True, 'operation': 'reboot',
|
|
'operation_params': {'type': 'soft'}}
|
|
req = orno.NodeRecoverRequest(identity='FAKE_NODE', params=params)
|
|
result = self.svc.node_recover(self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual({'action': 'ACTION_ID'}, result)
|
|
mock_find.assert_called_once_with(self.ctx, 'FAKE_NODE')
|
|
mock_action.assert_called_once_with(
|
|
self.ctx, '12345678AB', consts.NODE_RECOVER,
|
|
name='node_recover_12345678',
|
|
cluster_id='FAKE_CLUSTER_ID',
|
|
cause=consts.CAUSE_RPC,
|
|
status=action_mod.Action.READY,
|
|
inputs={'force_recreate': True,
|
|
'operation': 'reboot',
|
|
'operation_params': {'type': 'soft'}})
|
|
mock_start.assert_called_once_with()
|
|
|
|
@mock.patch.object(no.Node, 'find')
|
|
def test_node_recover_not_found(self, mock_find):
|
|
mock_find.side_effect = exc.ResourceNotFound(type='node', id='Bogus')
|
|
|
|
req = orno.NodeRecoverRequest(identity='Bogus')
|
|
ex = self.assertRaises(rpc.ExpectedException,
|
|
self.svc.node_recover,
|
|
self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual(exc.ResourceNotFound, ex.exc_info[0])
|
|
self.assertEqual("The node 'Bogus' could not be found.",
|
|
str(ex.exc_info[1]))
|
|
mock_find.assert_called_once_with(self.ctx, 'Bogus')
|
|
|
|
@mock.patch.object(action_mod.Action, 'create')
|
|
@mock.patch.object(no.Node, 'find')
|
|
def test_node_recover_unknown_operation(self, mock_find, mock_action):
|
|
mock_find.return_value = mock.Mock(id='12345678AB')
|
|
mock_action.return_value = 'ACTION_ID'
|
|
params = {'bogus': 'illegal'}
|
|
req = orno.NodeRecoverRequest(identity='FAKE_NODE', params=params)
|
|
|
|
ex = self.assertRaises(rpc.ExpectedException,
|
|
self.svc.node_recover,
|
|
self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual(exc.BadRequest, ex.exc_info[0])
|
|
self.assertEqual("Action parameter ['bogus'] is not recognizable.",
|
|
str(ex.exc_info[1]))
|
|
mock_find.assert_called_once_with(self.ctx, 'FAKE_NODE')
|
|
self.assertEqual(0, mock_action.call_count)
|
|
|
|
@mock.patch.object(action_mod.Action, 'create')
|
|
@mock.patch.object(no.Node, 'find')
|
|
def test_node_recover_invalid_operation(self, mock_find, mock_action):
|
|
mock_find.return_value = mock.Mock(id='12345678AB')
|
|
mock_action.return_value = 'ACTION_ID'
|
|
params = {'force_recreate': True, 'operation': 'blah',
|
|
'operation_params': {'type': 'soft'}}
|
|
req = orno.NodeRecoverRequest(identity='FAKE_NODE', params=params)
|
|
|
|
ex = self.assertRaises(rpc.ExpectedException,
|
|
self.svc.node_recover,
|
|
self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual(exc.BadRequest, ex.exc_info[0])
|
|
self.assertEqual("Operation value 'blah' has to be one of the "
|
|
"following: REBOOT, REBUILD, RECREATE.",
|
|
str(ex.exc_info[1]))
|
|
mock_find.assert_called_once_with(self.ctx, 'FAKE_NODE')
|
|
self.assertEqual(0, mock_action.call_count)
|
|
|
|
@mock.patch.object(action_mod.Action, 'create')
|
|
@mock.patch.object(no.Node, 'find')
|
|
def test_node_recover_invalid_operation_params(self, mock_find,
|
|
mock_action):
|
|
mock_find.return_value = mock.Mock(id='12345678AB')
|
|
mock_action.return_value = 'ACTION_ID'
|
|
params = {'force_recreate': True, 'operation': 'REBOOT',
|
|
'operation_params': {'type': 'blah'}}
|
|
req = orno.NodeRecoverRequest(identity='FAKE_NODE', params=params)
|
|
|
|
ex = self.assertRaises(rpc.ExpectedException,
|
|
self.svc.node_recover,
|
|
self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual(exc.BadRequest, ex.exc_info[0])
|
|
self.assertEqual("Type field 'blah' in operation_params has to be one "
|
|
"of the following: SOFT, HARD.",
|
|
str(ex.exc_info[1]))
|
|
mock_find.assert_called_once_with(self.ctx, 'FAKE_NODE')
|
|
self.assertEqual(0, mock_action.call_count)
|
|
|
|
@mock.patch.object(dispatcher, 'start_action')
|
|
@mock.patch.object(action_mod.Action, 'create')
|
|
@mock.patch.object(node_mod.Node, 'load')
|
|
@mock.patch.object(no.Node, 'find')
|
|
def test_node_op(self, mock_find, mock_node, mock_action, mock_start):
|
|
x_db_node = mock.Mock(id='12345678AB', cluster_id='FAKE_CLUSTER_ID')
|
|
mock_find.return_value = x_db_node
|
|
x_schema = mock.Mock()
|
|
x_profile = mock.Mock(OPERATIONS={'dance': x_schema})
|
|
x_node = mock.Mock()
|
|
x_node.rt = {'profile': x_profile}
|
|
mock_node.return_value = x_node
|
|
mock_action.return_value = 'ACTION_ID'
|
|
params = {'style': 'tango'}
|
|
req = orno.NodeOperationRequest(identity='FAKE_NODE',
|
|
operation='dance',
|
|
params=params)
|
|
|
|
result = self.svc.node_op(self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual({'action': 'ACTION_ID'}, result)
|
|
mock_find.assert_called_once_with(self.ctx, 'FAKE_NODE')
|
|
mock_node.assert_called_once_with(self.ctx, db_node=x_db_node)
|
|
x_schema.validate.assert_called_once_with({'style': 'tango'})
|
|
mock_action.assert_called_once_with(
|
|
self.ctx, '12345678AB', consts.NODE_OPERATION,
|
|
name='node_dance_12345678',
|
|
cluster_id='FAKE_CLUSTER_ID',
|
|
cause=consts.CAUSE_RPC,
|
|
status=action_mod.Action.READY,
|
|
inputs={'operation': 'dance', 'params': {'style': 'tango'}})
|
|
mock_start.assert_called_once_with()
|
|
|
|
@mock.patch.object(no.Node, 'find')
|
|
def test_node_op_node_not_found(self, mock_find):
|
|
mock_find.side_effect = exc.ResourceNotFound(type='node', id='Bogus')
|
|
|
|
req = orno.NodeOperationRequest(identity='Bogus', operation='dance',
|
|
params={})
|
|
ex = self.assertRaises(rpc.ExpectedException,
|
|
self.svc.node_op,
|
|
self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual(exc.ResourceNotFound, ex.exc_info[0])
|
|
self.assertEqual("The node 'Bogus' could not be found.",
|
|
str(ex.exc_info[1]))
|
|
mock_find.assert_called_once_with(self.ctx, 'Bogus')
|
|
|
|
@mock.patch.object(node_mod.Node, 'load')
|
|
@mock.patch.object(no.Node, 'find')
|
|
def test_node_op_unsupported_operation(self, mock_find, mock_node):
|
|
x_db_node = mock.Mock(id='12345678AB')
|
|
mock_find.return_value = x_db_node
|
|
x_schema = mock.Mock()
|
|
x_profile = mock.Mock(OPERATIONS={'dance': x_schema}, type='cow')
|
|
x_node = mock.Mock()
|
|
x_node.rt = {'profile': x_profile}
|
|
mock_node.return_value = x_node
|
|
|
|
req = orno.NodeOperationRequest(identity='node1', operation='swim',
|
|
params={})
|
|
ex = self.assertRaises(rpc.ExpectedException,
|
|
self.svc.node_op,
|
|
self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual(exc.BadRequest, ex.exc_info[0])
|
|
self.assertEqual("The requested operation 'swim' is not "
|
|
"supported by the profile type 'cow'.",
|
|
str(ex.exc_info[1]))
|
|
mock_find.assert_called_once_with(self.ctx, 'node1')
|
|
mock_node.assert_called_once_with(self.ctx, db_node=x_db_node)
|
|
|
|
@mock.patch.object(node_mod.Node, 'load')
|
|
@mock.patch.object(no.Node, 'find')
|
|
def test_node_op_bad_parameters(self, mock_find, mock_node):
|
|
x_db_node = mock.Mock(id='12345678AB')
|
|
mock_find.return_value = x_db_node
|
|
x_schema = mock.Mock()
|
|
x_schema.validate.side_effect = exc.ESchema(message='Boom')
|
|
x_profile = mock.Mock(OPERATIONS={'dance': x_schema})
|
|
x_node = mock.Mock()
|
|
x_node.rt = {'profile': x_profile}
|
|
mock_node.return_value = x_node
|
|
|
|
req = orno.NodeOperationRequest(identity='node1', operation='dance',
|
|
params={'style': 'tango'})
|
|
ex = self.assertRaises(rpc.ExpectedException,
|
|
self.svc.node_op,
|
|
self.ctx, req.obj_to_primitive())
|
|
|
|
self.assertEqual(exc.BadRequest, ex.exc_info[0])
|
|
self.assertEqual("Boom.", str(ex.exc_info[1]))
|
|
mock_find.assert_called_once_with(self.ctx, 'node1')
|
|
mock_node.assert_called_once_with(self.ctx, db_node=x_db_node)
|
|
x_schema.validate.assert_called_once_with({'style': 'tango'})
|