senlin/senlin/tests/engine/test_clusters.py

926 lines
39 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 oslo_messaging.rpc import dispatcher as rpc
import six
from senlin.common import exception
from senlin.common.i18n import _
from senlin.db import api as db_api
from senlin.engine.actions import base as action_mod
from senlin.engine import cluster as cluster_mod
from senlin.engine import dispatcher
from senlin.engine import environment
from senlin.engine import service
from senlin.tests.common import base
from senlin.tests.common import utils
from senlin.tests import fakes
class ClusterTest(base.SenlinTestCase):
def setUp(self):
super(ClusterTest, self).setUp()
self.ctx = utils.dummy_context(tenant_id='cluster_test_tenant')
self.eng = service.EngineService('host-a', 'topic-a')
self.eng.init_tgm()
self.eng.dispatcher = mock.Mock()
env = environment.global_env()
env.register_profile('TestProfile', fakes.TestProfile)
env.register_policy('TestPolicy', fakes.TestPolicy)
self.profile = self.eng.profile_create(
self.ctx, 'p-test', 'TestProfile',
spec={'INT': 10, 'STR': 'string'}, perm='1111')
self.policy = self.eng.policy_create(
self.ctx, 'policy_1', 'TestPolicy',
spec={'KEY1': 'string'}, cooldown=60, level=50)
def _verify_action(self, obj, action, name, target, cause, inputs=None):
if inputs is None:
inputs = {}
self.assertEqual(action, obj['action'])
self.assertEqual(name, obj['name'])
self.assertEqual(target, obj['target'])
self.assertEqual(cause, obj['cause'])
self.assertEqual(inputs, obj['inputs'])
@mock.patch.object(dispatcher, 'notify')
def test_cluster_create_default(self, notify):
result = self.eng.cluster_create(self.ctx, 'c-1', 0,
self.profile['id'])
self.assertIsNotNone(result)
self.assertEqual('c-1', result['name'])
self.assertEqual(0, result['size'])
self.assertEqual(self.profile['id'], result['profile_id'])
self.assertEqual(self.ctx.user_id, result['user'])
self.assertEqual('cluster_test_tenant', result['project'])
self.assertIsNone(result['parent'])
self.assertIsNone(result['timeout'])
self.assertIsNone(result['tags'])
action_id = result['action']
action = db_api.action_get(self.ctx, result['action'])
self.assertIsNotNone(action)
self._verify_action(action, 'CLUSTER_CREATE',
'cluster_create_%s' % result['id'][:8],
result['id'],
cause=action_mod.CAUSE_RPC)
notify.assert_called_once_with(self.ctx,
self.eng.dispatcher.NEW_ACTION,
None, action_id=action_id)
@mock.patch.object(dispatcher, 'notify')
def test_cluster_create_with_timeout(self, notify):
result = self.eng.cluster_create(self.ctx, 'c-1', 0,
self.profile['id'],
timeout=120)
self.assertIsNotNone(result)
self.assertEqual('c-1', result['name'])
self.assertEqual(120, result['timeout'])
ex = self.assertRaises(rpc.ExpectedException,
self.eng.cluster_create,
self.ctx, 'c-1', 0,
self.profile['id'],
timeout='N/A')
self.assertEqual(exception.InvalidParameter, ex.exc_info[0])
@mock.patch.object(dispatcher, 'notify')
def test_cluster_create_with_size(self, notify):
result = self.eng.cluster_create(self.ctx, 'c-1', 2,
self.profile['id'])
self.assertIsNotNone(result)
self.assertEqual('c-1', result['name'])
self.assertEqual(2, result['size'])
ex = self.assertRaises(rpc.ExpectedException,
self.eng.cluster_create,
self.ctx, 'c-1', 'Big',
self.profile['id'])
self.assertEqual(exception.InvalidParameter, ex.exc_info[0])
@mock.patch.object(dispatcher, 'notify')
def test_cluster_create_with_parent(self, notify):
result = self.eng.cluster_create(self.ctx, 'c-1', 2,
self.profile['id'],
parent='fake id')
self.assertIsNotNone(result)
self.assertEqual('c-1', result['name'])
self.assertEqual('fake id', result['parent'])
@mock.patch.object(dispatcher, 'notify')
def test_cluster_create_with_tags(self, notify):
result = self.eng.cluster_create(self.ctx, 'c-1', 2,
self.profile['id'],
tags={'k': 'v'})
self.assertIsNotNone(result)
self.assertEqual('c-1', result['name'])
self.assertEqual({'k': 'v'}, result['tags'])
def test_cluster_create_profile_not_found(self):
ex = self.assertRaises(rpc.ExpectedException,
self.eng.cluster_create,
self.ctx, 'c-1', 0, 'Bogus')
self.assertEqual(exception.ProfileNotFound, ex.exc_info[0])
@mock.patch.object(dispatcher, 'notify')
def test_cluster_create_with_profile_name_or_short_id(self, notify):
result = self.eng.cluster_create(self.ctx, 'c-1', 0,
self.profile['id'][:8])
self.assertIsNotNone(result)
self.assertEqual(self.profile['id'], result['profile_id'])
result = self.eng.cluster_create(self.ctx, 'c-2', 0,
self.profile['name'])
self.assertIsNotNone(result)
self.assertEqual(self.profile['id'], result['profile_id'])
@mock.patch.object(dispatcher, 'notify')
def test_cluster_get(self, notify):
c = self.eng.cluster_create(self.ctx, 'c-1', 0,
self.profile['id'])
for identity in [c['id'], c['id'][:6], 'c-1']:
result = self.eng.cluster_get(self.ctx, identity)
self.assertIsInstance(result, dict)
self.assertEqual(c['id'], result['id'])
ex = self.assertRaises(rpc.ExpectedException,
self.eng.cluster_get, self.ctx, 'Bogus')
self.assertEqual(exception.ClusterNotFound, ex.exc_info[0])
@mock.patch.object(dispatcher, 'notify')
def test_cluster_list(self, notify):
c1 = self.eng.cluster_create(self.ctx, 'c-1', 0, self.profile['id'])
c2 = self.eng.cluster_create(self.ctx, 'c-2', 0, self.profile['id'])
result = self.eng.cluster_list(self.ctx)
self.assertIsInstance(result, list)
names = [c['name'] for c in result]
ids = [c['id'] for c in result]
self.assertEqual(c1['name'], names[0])
self.assertEqual(c2['name'], names[1])
self.assertEqual(c1['id'], ids[0])
self.assertEqual(c2['id'], ids[1])
@mock.patch.object(dispatcher, 'notify')
def test_cluster_list_with_limit_marker(self, notify):
c1 = self.eng.cluster_create(self.ctx, 'c-1', 0, self.profile['id'])
c2 = self.eng.cluster_create(self.ctx, 'c-2', 0, self.profile['id'])
result = self.eng.cluster_list(self.ctx, limit=0)
self.assertEqual(0, len(result))
result = self.eng.cluster_list(self.ctx, limit=1)
self.assertEqual(1, len(result))
result = self.eng.cluster_list(self.ctx, limit=2)
self.assertEqual(2, len(result))
result = self.eng.cluster_list(self.ctx, limit=3)
self.assertEqual(2, len(result))
result = self.eng.cluster_list(self.ctx, marker=c1['id'])
self.assertEqual(1, len(result))
result = self.eng.cluster_list(self.ctx, marker=c2['id'])
self.assertEqual(0, len(result))
self.eng.cluster_create(self.ctx, 'c-3', 0, self.profile['id'])
result = self.eng.cluster_list(self.ctx, limit=1, marker=c1['id'])
self.assertEqual(1, len(result))
result = self.eng.cluster_list(self.ctx, limit=2, marker=c1['id'])
self.assertEqual(2, len(result))
@mock.patch.object(dispatcher, 'notify')
def test_cluster_list_with_sort_keys(self, notify):
c1 = self.eng.cluster_create(self.ctx, 'CC', 0, self.profile['id'])
c2 = self.eng.cluster_create(self.ctx, 'BB', 0, self.profile['id'])
# default by created_time
result = self.eng.cluster_list(self.ctx)
self.assertEqual(c1['id'], result[0]['id'])
self.assertEqual(c2['id'], result[1]['id'])
# use name for sorting
result = self.eng.cluster_list(self.ctx, sort_keys=['name'])
self.assertEqual(c2['id'], result[0]['id'])
self.assertEqual(c1['id'], result[1]['id'])
# unknown keys will be ignored
result = self.eng.cluster_list(self.ctx, sort_keys=['duang'])
self.assertIsNotNone(result)
@mock.patch.object(dispatcher, 'notify')
def test_cluster_list_with_sort_dir(self, notify):
c1 = self.eng.cluster_create(self.ctx, 'BB', 0, self.profile['id'])
c2 = self.eng.cluster_create(self.ctx, 'AA', 0, self.profile['id'])
c3 = self.eng.cluster_create(self.ctx, 'CC', 0, self.profile['id'])
# default by created_time, ascending
result = self.eng.cluster_list(self.ctx)
self.assertEqual(c1['id'], result[0]['id'])
self.assertEqual(c2['id'], result[1]['id'])
# sort by created_time, descending
result = self.eng.cluster_list(self.ctx, sort_dir='desc')
self.assertEqual(c3['id'], result[0]['id'])
self.assertEqual(c2['id'], result[1]['id'])
# use name for sorting, descending
result = self.eng.cluster_list(self.ctx, sort_keys=['name'],
sort_dir='desc')
self.assertEqual(c3['id'], result[0]['id'])
self.assertEqual(c1['id'], result[1]['id'])
# use permission for sorting
ex = self.assertRaises(ValueError,
self.eng.cluster_list, self.ctx,
sort_dir='Bogus')
self.assertEqual("Unknown sort direction, must be "
"'desc' or 'asc'", six.text_type(ex))
@mock.patch.object(dispatcher, 'notify')
def test_cluster_list_show_deleted(self, notify):
c = self.eng.cluster_create(self.ctx, 'c-1', 0, self.profile['id'])
result = self.eng.cluster_list(self.ctx)
self.assertEqual(1, len(result))
self.assertEqual(c['id'], result[0]['id'])
db_api.cluster_delete(self.ctx, c['id'])
result = self.eng.cluster_list(self.ctx)
self.assertEqual(0, len(result))
result = self.eng.cluster_list(self.ctx, show_deleted=True)
self.assertEqual(1, len(result))
self.assertEqual(c['id'], result[0]['id'])
@mock.patch.object(dispatcher, 'notify')
def test_cluster_list_show_nested(self, notify):
c = self.eng.cluster_create(self.ctx, 'c-1', 0, self.profile['id'],
parent='other-cluster')
result = self.eng.cluster_list(self.ctx)
self.assertEqual(0, len(result))
result = self.eng.cluster_list(self.ctx, show_nested=True)
self.assertEqual(1, len(result))
self.assertEqual(c['id'], result[0]['id'])
@mock.patch.object(dispatcher, 'notify')
def test_cluster_list_tenant_safe(self, notify):
c1 = self.eng.cluster_create(self.ctx, 'c1', 0, self.profile['id'])
new_ctx = utils.dummy_context(tenant_id='a_diff_tenant')
c2 = self.eng.cluster_create(new_ctx, 'c2', 0, self.profile['id'])
# default is tenant_safe
result = self.eng.cluster_list(self.ctx)
self.assertIsInstance(result, list)
self.assertEqual(1, len(result))
self.assertEqual(c1['id'], result[0]['id'])
result = self.eng.cluster_list(new_ctx)
self.assertIsInstance(result, list)
self.assertEqual(1, len(result))
self.assertEqual(c2['id'], result[0]['id'])
# try tenant_safe set to False
result = self.eng.cluster_list(self.ctx, tenant_safe=False)
self.assertIsInstance(result, list)
self.assertEqual(2, len(result))
self.assertEqual(c1['id'], result[0]['id'])
self.assertEqual(c2['id'], result[1]['id'])
@mock.patch.object(dispatcher, 'notify')
def test_cluster_list_with_filters(self, notify):
self.eng.cluster_create(self.ctx, 'BB', 0, self.profile['id'])
self.eng.cluster_create(self.ctx, 'AA', 0, self.profile['id'])
self.eng.cluster_create(self.ctx, 'CC', 0, self.profile['id'])
result = self.eng.cluster_list(self.ctx, filters={'name': 'BB'})
self.assertEqual(1, len(result))
self.assertEqual('BB', result[0]['name'])
result = self.eng.cluster_list(self.ctx, filters={'name': 'DD'})
self.assertEqual(0, len(result))
def test_cluster_list_bad_param(self):
ex = self.assertRaises(rpc.ExpectedException,
self.eng.cluster_list, self.ctx, limit='no')
self.assertEqual(exception.InvalidParameter, ex.exc_info[0])
ex = self.assertRaises(rpc.ExpectedException,
self.eng.cluster_list, self.ctx,
show_deleted='no')
self.assertEqual(exception.InvalidParameter, ex.exc_info[0])
ex = self.assertRaises(rpc.ExpectedException,
self.eng.cluster_list, self.ctx,
show_nested='no')
self.assertEqual(exception.InvalidParameter, ex.exc_info[0])
ex = self.assertRaises(rpc.ExpectedException,
self.eng.cluster_list, self.ctx,
tenant_safe='no')
self.assertEqual(exception.InvalidParameter, ex.exc_info[0])
def test_cluster_list_empty(self):
result = self.eng.cluster_list(self.ctx)
self.assertIsInstance(result, list)
self.assertEqual(0, len(result))
@mock.patch.object(dispatcher, 'notify')
def test_cluster_find(self, notify):
c = self.eng.cluster_create(self.ctx, 'c-1', 0, self.profile['id'])
cid = c['id']
result = self.eng.cluster_find(self.ctx, cid)
self.assertIsNotNone(result)
# short id
result = self.eng.cluster_find(self.ctx, cid[:5])
self.assertIsNotNone(result)
# name
result = self.eng.cluster_find(self.ctx, 'c-1')
self.assertIsNotNone(result)
# others
self.assertRaises(exception.ClusterNotFound,
self.eng.cluster_find, self.ctx, 'Bogus')
@mock.patch.object(dispatcher, 'notify')
def test_cluster_find_show_deleted(self, notify):
c = self.eng.cluster_create(self.ctx, 'c-1', 0, self.profile['id'])
cid = c['id']
db_api.cluster_delete(self.ctx, cid)
for identity in [cid, cid[:6], 'c-1']:
self.assertRaises(exception.ClusterNotFound,
self.eng.cluster_find, self.ctx, identity)
# short id and name based finding does not support show_deleted
for identity in [cid[:6], 'c-1']:
self.assertRaises(exception.ClusterNotFound,
self.eng.cluster_find, self.ctx, identity)
# ID based finding is okay with show_deleted
result = self.eng.cluster_find(self.ctx, cid, show_deleted=True)
self.assertIsNotNone(result)
@mock.patch.object(dispatcher, 'notify')
def test_cluster_update_simple_success(self, notify):
c1 = self.eng.cluster_create(self.ctx, 'c-1', 0, self.profile['id'])
cid = c1['id']
# 1. update name
self.eng.cluster_update(self.ctx, cid, name='c-2')
c = self.eng.cluster_get(self.ctx, cid)
self.assertEqual(cid, c['id'])
self.assertEqual('c-2', c['name'])
# 2. update parent
p = self.eng.cluster_create(self.ctx, 'parent', 0, self.profile['id'])
self.eng.cluster_update(self.ctx, cid, parent=p['id'])
c = self.eng.cluster_get(self.ctx, cid)
self.assertEqual(cid, c['id'])
self.assertEqual(p['id'], c['parent'])
# 3. update tags
self.eng.cluster_update(self.ctx, cid, tags={'k': 'v'})
c = self.eng.cluster_get(self.ctx, cid)
self.assertEqual(cid, c['id'])
self.assertEqual({'k': 'v'}, c['tags'])
# 4. update timeout
self.eng.cluster_update(self.ctx, cid, timeout=119)
c = self.eng.cluster_get(self.ctx, cid)
self.assertEqual(cid, c['id'])
self.assertEqual(119, c['timeout'])
def test_cluster_update_cluster_not_found(self):
ex = self.assertRaises(rpc.ExpectedException,
self.eng.cluster_update, self.ctx, 'Bogus')
self.assertEqual(exception.ClusterNotFound, ex.exc_info[0])
@mock.patch.object(dispatcher, 'notify')
def test_cluster_update_cluster_bad_status(self, notify):
c = self.eng.cluster_create(self.ctx, 'c-1', 0, self.profile['id'])
cluster = cluster_mod.Cluster.load(self.ctx, c['id'])
cluster.set_status(self.ctx, cluster.DELETED, reason='test')
ex = self.assertRaises(rpc.ExpectedException,
self.eng.cluster_update, self.ctx, c['id'],
name='new name')
self.assertEqual(exception.ClusterNotFound, ex.exc_info[0])
@mock.patch.object(dispatcher, 'notify')
def test_cluster_update_parent_not_found(self, notify):
c = self.eng.cluster_create(self.ctx, 'c-1', 0, self.profile['id'])
ex = self.assertRaises(rpc.ExpectedException,
self.eng.cluster_update, self.ctx, c['id'],
parent='Bogus')
self.assertEqual(exception.ClusterNotFound, ex.exc_info[0])
@mock.patch.object(dispatcher, 'notify')
def test_cluster_update_timeout_not_integer(self, notify):
c = self.eng.cluster_create(self.ctx, 'c-1', 0, self.profile['id'])
ex = self.assertRaises(rpc.ExpectedException,
self.eng.cluster_update, self.ctx, c['id'],
timeout='Long')
self.assertEqual(exception.InvalidParameter, ex.exc_info[0])
@mock.patch.object(dispatcher, 'notify')
def test_cluster_update_cluster_status_error(self, notify):
c = self.eng.cluster_create(self.ctx, 'c-1', 0, self.profile['id'])
cluster = cluster_mod.Cluster.load(self.ctx, c['id'])
cluster.set_status(self.ctx, cluster.ERROR, reason='test')
ex = self.assertRaises(rpc.ExpectedException,
self.eng.cluster_update, self.ctx, c['id'],
profile_id='good_profile')
self.assertEqual(exception.NotSupported, ex.exc_info[0])
@mock.patch.object(dispatcher, 'notify')
def test_cluster_update_update_to_same_profile(self, notify):
c = self.eng.cluster_create(self.ctx, 'c-1', 0, self.profile['id'])
self.eng.cluster_update(self.ctx, c['id'],
profile_id=self.profile['id'])
result = self.eng.cluster_get(self.ctx, c['id'])
self.assertEqual(c['id'], result['id'])
self.assertEqual(c['profile_id'], result['profile_id'])
# notify is only called once, because the 'cluster_update' operation
# was not causing any new action to be dispatched
notify.assert_called_once()
@mock.patch.object(dispatcher, 'notify')
def test_cluster_update_update_to_diff_profile_type(self, notify):
# Register a different profile
env = environment.global_env()
env.register_profile('DiffProfileType', fakes.TestProfile)
new_profile = self.eng.profile_create(
self.ctx, 'p-test', 'DiffProfileType',
spec={'INT': 10, 'STR': 'string'}, perm='1111')
c = self.eng.cluster_create(self.ctx, 'c-1', 0, self.profile['id'])
ex = self.assertRaises(rpc.ExpectedException,
self.eng.cluster_update,
self.ctx, c['id'], profile_id=new_profile['id'])
self.assertEqual(exception.ProfileTypeNotMatch, ex.exc_info[0])
@mock.patch.object(dispatcher, 'notify')
def test_cluster_update_profile_not_found(self, notify):
c = self.eng.cluster_create(self.ctx, 'c-1', 0, self.profile['id'])
ex = self.assertRaises(rpc.ExpectedException,
self.eng.cluster_update,
self.ctx, c['id'], profile_id='Bogus')
self.assertEqual(exception.ProfileNotFound, ex.exc_info[0])
@mock.patch.object(dispatcher, 'notify')
def test_cluster_update_profile_normal(self, notify):
new_profile = self.eng.profile_create(
self.ctx, 'p-new', 'TestProfile',
spec={'INT': 20, 'STR': 'string new'})
c = self.eng.cluster_create(self.ctx, 'c-1', 0, self.profile['id'])
self.eng.cluster_update(self.ctx, c['id'],
profile_id=new_profile['id'])
# TODO(anyone): uncomment the following lines when cluster-update
# is implemented
# action_id = result['action']
# action = self.eng.action_get(self.ctx, action_id)
# self._verify_action(action, 'CLUSTER_UPDATE',
# 'cluster_update_%s' % c['id'][:8],
# result['id'],
# cause=action_mod.CAUSE_RPC)
# notify.assert_called_once_with(self.ctx,
# self.eng.dispatcher.NEW_ACTION,
# None, action_id=action_id)
@mock.patch.object(dispatcher, 'notify')
def test_cluster_delete(self, notify):
c = self.eng.cluster_create(self.ctx, 'c-1', 0, self.profile['id'])
cid = c['id']
result = self.eng.cluster_delete(self.ctx, cid)
self.assertIsNotNone(result)
# verify action is fired
action_id = result['action']
action = self.eng.action_get(self.ctx, action_id)
self._verify_action(action, 'CLUSTER_DELETE',
'cluster_delete_%s' % c['id'][:8],
c['id'],
cause=action_mod.CAUSE_RPC)
expected_call = mock.call(self.ctx,
self.eng.dispatcher.NEW_ACTION,
None, action_id=mock.ANY)
# two calls: one for create, the other for delete
notify.assert_has_calls([expected_call] * 2)
def test_cluster_delete_not_found(self):
ex = self.assertRaises(rpc.ExpectedException,
self.eng.cluster_delete, self.ctx, 'Bogus')
self.assertEqual(exception.ClusterNotFound, ex.exc_info[0])
self.assertEqual('The cluster (Bogus) could not be found.',
six.text_type(ex.exc_info[1]))
def _prepare_nodes(self, ctx, count=3, profile_id=None, **kwargs):
'''Prepare nodes for add or delete.'''
nodes = []
for i in range(count):
values = {
'name': 'test_node_name',
'physical_id': 'fake-phy-id-%s' % (i + 1),
'cluster_id': None,
'profile_id': profile_id or self.profile['id'],
'project': ctx.tenant_id,
'index': i + 1,
'role': None,
'created_time': None,
'updated_time': None,
'deleted_time': None,
'status': 'ACTIVE',
'status_reason': 'create complete',
'tags': {'foo': '123'},
'data': {'key1': 'value1'},
}
values.update(kwargs)
db_node = db_api.node_create(ctx, values)
nodes.append(six.text_type(db_node.id))
return nodes
@mock.patch.object(dispatcher, 'notify')
def test_cluster_add_nodes(self, notify):
c = self.eng.cluster_create(self.ctx, 'c-1', 0, self.profile['id'])
cid = c['id']
nodes = self._prepare_nodes(self.ctx)
result = self.eng.cluster_add_nodes(self.ctx, cid, nodes)
# verify action is fired
action_id = result['action']
action = self.eng.action_get(self.ctx, action_id)
self._verify_action(action, 'CLUSTER_ADD_NODES',
'cluster_add_nodes_%s' % cid[:8],
cid, cause=action_mod.CAUSE_RPC,
inputs={'nodes': nodes})
expected_call = mock.call(self.ctx,
self.eng.dispatcher.NEW_ACTION,
None, action_id=mock.ANY)
# two calls: one for create, the other for adding nodes
notify.assert_has_calls([expected_call] * 2)
def test_cluster_add_nodes_cluster_not_found(self):
ex = self.assertRaises(rpc.ExpectedException,
self.eng.cluster_add_nodes,
self.ctx, 'Bogus', ['n1', 'n2'])
self.assertEqual(exception.ClusterNotFound, ex.exc_info[0])
self.assertEqual('The cluster (Bogus) could not be found.',
six.text_type(ex.exc_info[1]))
@mock.patch.object(dispatcher, 'notify')
def test_cluster_add_nodes_empty_list(self, notify):
c = self.eng.cluster_create(self.ctx, 'c-1', 0, self.profile['id'])
cid = c['id']
ex = self.assertRaises(rpc.ExpectedException,
self.eng.cluster_add_nodes,
self.ctx, cid, [])
self.assertEqual(exception.SenlinBadRequest, ex.exc_info[0])
self.assertEqual('The request is malformed: No nodes to add: []',
six.text_type(ex.exc_info[1]))
@mock.patch.object(dispatcher, 'notify')
def test_cluster_add_nodes_node_not_found(self, notify):
c = self.eng.cluster_create(self.ctx, 'c-1', 0, self.profile['id'])
cid = c['id']
ex = self.assertRaises(rpc.ExpectedException,
self.eng.cluster_add_nodes,
self.ctx, cid, ['Bogus'])
self.assertEqual(exception.SenlinBadRequest, ex.exc_info[0])
self.assertEqual("The request is malformed: Nodes not found: "
"['Bogus']", six.text_type(ex.exc_info[1]))
@mock.patch.object(dispatcher, 'notify')
def test_cluster_add_nodes_node_not_active(self, notify):
c = self.eng.cluster_create(self.ctx, 'c-1', 0, self.profile['id'])
cid = c['id']
nodes = self._prepare_nodes(self.ctx, count=1, status='ERROR')
ex = self.assertRaises(rpc.ExpectedException,
self.eng.cluster_add_nodes,
self.ctx, cid, nodes)
self.assertEqual(exception.SenlinBadRequest, ex.exc_info[0])
msg = _("Nodes are not ACTIVE: %s") % nodes
self.assertEqual(_("The request is malformed: %(msg)s") % {'msg': msg},
six.text_type(ex.exc_info[1]))
@mock.patch.object(dispatcher, 'notify')
def test_cluster_add_nodes_node_already_owned(self, notify):
c1 = self.eng.cluster_create(self.ctx, 'c-1', 0, self.profile['id'])
cid1 = c1['id']
c2 = self.eng.cluster_create(self.ctx, 'c-2', 0, self.profile['id'])
cid2 = c2['id']
nodes1 = self._prepare_nodes(self.ctx, count=1, cluster_id=cid1)
nodes2 = self._prepare_nodes(self.ctx, count=1, cluster_id=cid2)
ex = self.assertRaises(rpc.ExpectedException,
self.eng.cluster_add_nodes,
self.ctx, cid1, nodes1)
# adding from the same cluster is not allowed
self.assertEqual(exception.SenlinBadRequest, ex.exc_info[0])
msg = _("Nodes %s owned by other cluster, need to delete them from "
"those clusters first.") % nodes1
self.assertEqual(_("The request is malformed: %(msg)s") % {'msg': msg},
six.text_type(ex.exc_info[1]))
ex = self.assertRaises(rpc.ExpectedException,
self.eng.cluster_add_nodes,
self.ctx, cid1, nodes2)
# adding from a different cluster is not allowed either
self.assertEqual(exception.SenlinBadRequest, ex.exc_info[0])
msg = _("Nodes %s owned by other cluster, need to delete them from "
"those clusters first.") % nodes2
self.assertEqual(_("The request is malformed: %(msg)s") % {'msg': msg},
six.text_type(ex.exc_info[1]))
@mock.patch.object(dispatcher, 'notify')
def test_cluster_del_nodes(self, notify):
c = self.eng.cluster_create(self.ctx, 'c-1', 0, self.profile['id'])
cid = c['id']
nodes = self._prepare_nodes(self.ctx, count=1, cluster_id=cid)
result = self.eng.cluster_del_nodes(self.ctx, cid, nodes)
# verify action is fired
action_id = result['action']
action = self.eng.action_get(self.ctx, action_id)
self._verify_action(action, 'CLUSTER_DEL_NODES',
'cluster_del_nodes_%s' % cid[:8],
cid, cause=action_mod.CAUSE_RPC,
inputs={'nodes': nodes})
expected_call = mock.call(self.ctx,
self.eng.dispatcher.NEW_ACTION,
None, action_id=mock.ANY)
# two calls: one for create, the other for adding nodes
notify.assert_has_calls([expected_call] * 2)
def test_cluster_del_nodes_cluster_not_found(self):
ex = self.assertRaises(rpc.ExpectedException,
self.eng.cluster_del_nodes,
self.ctx, 'Bogus', ['n1', 'n2'])
self.assertEqual(exception.ClusterNotFound, ex.exc_info[0])
self.assertEqual('The cluster (Bogus) could not be found.',
six.text_type(ex.exc_info[1]))
@mock.patch.object(dispatcher, 'notify')
def test_cluster_del_nodes_empty_list(self, notify):
c = self.eng.cluster_create(self.ctx, 'c-1', 0, self.profile['id'])
cid = c['id']
ex = self.assertRaises(rpc.ExpectedException,
self.eng.cluster_del_nodes,
self.ctx, cid, [])
self.assertEqual(exception.SenlinBadRequest, ex.exc_info[0])
self.assertEqual('The request is malformed: No nodes specified.',
six.text_type(ex.exc_info[1]))
@mock.patch.object(dispatcher, 'notify')
def test_cluster_del_nodes_node_not_found(self, notify):
c = self.eng.cluster_create(self.ctx, 'c-1', 0, self.profile['id'])
cid = c['id']
ex = self.assertRaises(rpc.ExpectedException,
self.eng.cluster_del_nodes,
self.ctx, cid, ['Bogus'])
self.assertEqual(exception.SenlinBadRequest, ex.exc_info[0])
self.assertEqual("The request is malformed: Nodes not found: "
"['Bogus']", six.text_type(ex.exc_info[1]))
@mock.patch.object(dispatcher, 'notify')
def test_cluster_del_nodes_node_in_other_cluster(self, notify):
c = self.eng.cluster_create(self.ctx, 'c-1', 0, self.profile['id'])
c2 = self.eng.cluster_create(self.ctx, 'c-2', 0, self.profile['id'])
cid = c['id']
nodes = self._prepare_nodes(self.ctx, count=1, cluster_id=c2['id'])
ex = self.assertRaises(rpc.ExpectedException,
self.eng.cluster_del_nodes,
self.ctx, cid, nodes)
self.assertEqual(exception.SenlinBadRequest, ex.exc_info[0])
self.assertEqual("The request is malformed: Nodes not members of "
"specified cluster: %s" % nodes,
six.text_type(ex.exc_info[1]))
@mock.patch.object(dispatcher, 'notify')
def test_cluster_del_nodes_orphan_nodes(self, notify):
c = self.eng.cluster_create(self.ctx, 'c-1', 0, self.profile['id'])
cid = c['id']
nodes = self._prepare_nodes(self.ctx, count=1)
ex = self.assertRaises(rpc.ExpectedException,
self.eng.cluster_del_nodes,
self.ctx, cid, nodes)
self.assertEqual(exception.SenlinBadRequest, ex.exc_info[0])
self.assertEqual("The request is malformed: Nodes not members of "
"specified cluster: %s" % nodes,
six.text_type(ex.exc_info[1]))
@mock.patch.object(dispatcher, 'notify')
def test_cluster_scale_out(self, notify):
c = self.eng.cluster_create(self.ctx, 'c-1', 0, self.profile['id'])
cid = c['id']
result = self.eng.cluster_scale_out(self.ctx, cid, count=1)
# verify action is fired
action_id = result['action']
action = self.eng.action_get(self.ctx, action_id)
self._verify_action(action, 'CLUSTER_SCALE_OUT',
'cluster_scale_out_%s' % cid[:8],
cid, cause=action_mod.CAUSE_RPC,
inputs={'count': 1})
expected_call = mock.call(self.ctx,
self.eng.dispatcher.NEW_ACTION,
None, action_id=mock.ANY)
# two calls: one for create, the other for scaling operation
notify.assert_has_calls([expected_call] * 2)
@mock.patch.object(dispatcher, 'notify')
def test_cluster_scale_out_cluster_not_found(self, notify):
ex = self.assertRaises(rpc.ExpectedException,
self.eng.cluster_scale_out,
self.ctx, 'Bogus')
self.assertEqual(exception.ClusterNotFound, ex.exc_info[0])
self.assertEqual('The cluster (Bogus) could not be found.',
six.text_type(ex.exc_info[1]))
@mock.patch.object(dispatcher, 'notify')
def test_cluster_scale_out_count_is_none(self, notify):
c = self.eng.cluster_create(self.ctx, 'c-1', 0, self.profile['id'])
cid = c['id']
result = self.eng.cluster_scale_out(self.ctx, cid)
# verify action is fired
action_id = result['action']
action = self.eng.action_get(self.ctx, action_id)
self._verify_action(action, 'CLUSTER_SCALE_OUT',
'cluster_scale_out_%s' % cid[:8],
cid, cause=action_mod.CAUSE_RPC,
inputs={})
expected_call = mock.call(self.ctx,
self.eng.dispatcher.NEW_ACTION,
None, action_id=mock.ANY)
# two calls: one for create, the other for scaling operation
notify.assert_has_calls([expected_call] * 2)
@mock.patch.object(dispatcher, 'notify')
def test_cluster_scale_out_count_not_int_or_zero(self, notify):
c = self.eng.cluster_create(self.ctx, 'c-1', 0, self.profile['id'])
cid = c['id']
ex = self.assertRaises(rpc.ExpectedException,
self.eng.cluster_scale_out,
self.ctx, cid, count='one')
self.assertEqual(exception.InvalidParameter, ex.exc_info[0])
self.assertEqual("Invalid value 'one' specified for 'count'",
six.text_type(ex.exc_info[1]))
ex = self.assertRaises(rpc.ExpectedException,
self.eng.cluster_scale_out,
self.ctx, cid, count=0)
self.assertEqual(exception.InvalidParameter, ex.exc_info[0])
self.assertEqual("Invalid value '0' specified for 'count'",
six.text_type(ex.exc_info[1]))
@mock.patch.object(dispatcher, 'notify')
def test_cluster_scale_in(self, notify):
c = self.eng.cluster_create(self.ctx, 'c-1', 2, self.profile['id'])
cid = c['id']
result = self.eng.cluster_scale_in(self.ctx, cid, count=1)
# verify action is fired
action_id = result['action']
action = self.eng.action_get(self.ctx, action_id)
self._verify_action(action, 'CLUSTER_SCALE_IN',
'cluster_scale_in_%s' % cid[:8],
cid, cause=action_mod.CAUSE_RPC,
inputs={'count': 1})
expected_call = mock.call(self.ctx,
self.eng.dispatcher.NEW_ACTION,
None, action_id=mock.ANY)
# two calls: one for create, the other for scaling operation
notify.assert_has_calls([expected_call] * 2)
@mock.patch.object(dispatcher, 'notify')
def test_cluster_scale_in_cluster_not_found(self, notify):
ex = self.assertRaises(rpc.ExpectedException,
self.eng.cluster_scale_in,
self.ctx, 'Bogus')
self.assertEqual(exception.ClusterNotFound, ex.exc_info[0])
self.assertEqual('The cluster (Bogus) could not be found.',
six.text_type(ex.exc_info[1]))
@mock.patch.object(dispatcher, 'notify')
def test_cluster_scale_in_count_is_none(self, notify):
c = self.eng.cluster_create(self.ctx, 'c-1', 2, self.profile['id'])
cid = c['id']
result = self.eng.cluster_scale_in(self.ctx, cid)
# verify action is fired
action_id = result['action']
action = self.eng.action_get(self.ctx, action_id)
self._verify_action(action, 'CLUSTER_SCALE_IN',
'cluster_scale_in_%s' % cid[:8],
cid, cause=action_mod.CAUSE_RPC,
inputs={})
expected_call = mock.call(self.ctx,
self.eng.dispatcher.NEW_ACTION,
None, action_id=mock.ANY)
# two calls: one for create, the other for scaling operation
notify.assert_has_calls([expected_call] * 2)
@mock.patch.object(dispatcher, 'notify')
def test_cluster_scale_in_count_not_int_or_zero(self, notify):
c = self.eng.cluster_create(self.ctx, 'c-1', 2, self.profile['id'])
cid = c['id']
ex = self.assertRaises(rpc.ExpectedException,
self.eng.cluster_scale_in,
self.ctx, cid, count='one')
self.assertEqual(exception.InvalidParameter, ex.exc_info[0])
self.assertEqual("Invalid value 'one' specified for 'count'",
six.text_type(ex.exc_info[1]))
ex = self.assertRaises(rpc.ExpectedException,
self.eng.cluster_scale_in,
self.ctx, cid, count=0)
self.assertEqual(exception.InvalidParameter, ex.exc_info[0])
self.assertEqual("Invalid value '0' specified for 'count'",
six.text_type(ex.exc_info[1]))