Merge "Added some test cases for node operations (engine)"
This commit is contained in:
commit
07514e4294
@ -694,6 +694,11 @@ class EngineService(service.Service):
|
|||||||
def node_list(self, context, cluster_id=None, show_deleted=False,
|
def node_list(self, context, cluster_id=None, show_deleted=False,
|
||||||
limit=None, marker=None, sort_keys=None, sort_dir=None,
|
limit=None, marker=None, sort_keys=None, sort_dir=None,
|
||||||
filters=None, tenant_safe=True):
|
filters=None, tenant_safe=True):
|
||||||
|
|
||||||
|
limit = utils.parse_int_param('limit', limit)
|
||||||
|
tenant_safe = utils.parse_bool_param('tenant_safe', tenant_safe)
|
||||||
|
show_deleted = utils.parse_bool_param('show_deleted', show_deleted)
|
||||||
|
|
||||||
# Maybe the cluster_id is a name or a short ID
|
# Maybe the cluster_id is a name or a short ID
|
||||||
if cluster_id is not None:
|
if cluster_id is not None:
|
||||||
db_cluster = self.cluster_find(context, cluster_id)
|
db_cluster = self.cluster_find(context, cluster_id)
|
||||||
@ -710,18 +715,12 @@ class EngineService(service.Service):
|
|||||||
@request_context
|
@request_context
|
||||||
def node_create(self, context, name, profile_id, cluster_id=None,
|
def node_create(self, context, name, profile_id, cluster_id=None,
|
||||||
role=None, tags=None):
|
role=None, tags=None):
|
||||||
db_profile = self.profile_find(context, profile_id)
|
node_profile = self.profile_find(context, profile_id)
|
||||||
if cluster_id is not None:
|
if cluster_id is not None:
|
||||||
db_cluster = self.cluster_find(context, cluster_id)
|
db_cluster = self.cluster_find(context, cluster_id)
|
||||||
cluster_id = db_cluster.id
|
cluster_id = db_cluster.id
|
||||||
|
|
||||||
if context.project_id != db_cluster.project:
|
|
||||||
msg = _('Node and cluster are from different project, '
|
|
||||||
'operation is disallowed.')
|
|
||||||
raise exception.ProjectNotMatch(message=msg)
|
|
||||||
|
|
||||||
if profile_id != db_cluster.profile_id:
|
if profile_id != db_cluster.profile_id:
|
||||||
node_profile = self.profile_find(context, profile_id)
|
|
||||||
cluster_profile = self.profile_find(context,
|
cluster_profile = self.profile_find(context,
|
||||||
db_cluster.profile_id)
|
db_cluster.profile_id)
|
||||||
if node_profile.type != cluster_profile.type:
|
if node_profile.type != cluster_profile.type:
|
||||||
@ -733,7 +732,7 @@ class EngineService(service.Service):
|
|||||||
|
|
||||||
# Create a node instance
|
# Create a node instance
|
||||||
tags = tags or {}
|
tags = tags or {}
|
||||||
node = node_mod.Node(name, db_profile.id, cluster_id, context,
|
node = node_mod.Node(name, node_profile.id, cluster_id, context,
|
||||||
role=role, tags=tags)
|
role=role, tags=tags)
|
||||||
node.store(context)
|
node.store(context)
|
||||||
|
|
||||||
|
@ -70,6 +70,7 @@ def dummy_context(user='test_username', tenant_id='test_tenant_id',
|
|||||||
roles = roles or []
|
roles = roles or []
|
||||||
return context.RequestContext.from_dict({
|
return context.RequestContext.from_dict({
|
||||||
'tenant_id': tenant_id,
|
'tenant_id': tenant_id,
|
||||||
|
'project_id': tenant_id,
|
||||||
'tenant': 'test_tenant',
|
'tenant': 'test_tenant',
|
||||||
'username': user,
|
'username': user,
|
||||||
'user_id': user_id,
|
'user_id': user_id,
|
||||||
|
@ -150,7 +150,8 @@ class ClusterTest(base.SenlinTestCase):
|
|||||||
self.assertIsNotNone(result)
|
self.assertIsNotNone(result)
|
||||||
self.assertEqual(self.profile['id'], result['profile_id'])
|
self.assertEqual(self.profile['id'], result['profile_id'])
|
||||||
|
|
||||||
self.eng.cluster_create(self.ctx, 'c-2', 0, self.profile['name'])
|
result = self.eng.cluster_create(self.ctx, 'c-2', 0,
|
||||||
|
self.profile['name'])
|
||||||
self.assertIsNotNone(result)
|
self.assertIsNotNone(result)
|
||||||
self.assertEqual(self.profile['id'], result['profile_id'])
|
self.assertEqual(self.profile['id'], result['profile_id'])
|
||||||
|
|
||||||
@ -176,10 +177,10 @@ class ClusterTest(base.SenlinTestCase):
|
|||||||
self.assertIsInstance(result, list)
|
self.assertIsInstance(result, list)
|
||||||
names = [c['name'] for c in result]
|
names = [c['name'] for c in result]
|
||||||
ids = [c['id'] for c in result]
|
ids = [c['id'] for c in result]
|
||||||
self.assertIn(c1['name'], names[0])
|
self.assertEqual(c1['name'], names[0])
|
||||||
self.assertIn(c2['name'], names[1])
|
self.assertEqual(c2['name'], names[1])
|
||||||
self.assertIn(c1['id'], ids[0])
|
self.assertEqual(c1['id'], ids[0])
|
||||||
self.assertIn(c2['id'], ids[1])
|
self.assertEqual(c2['id'], ids[1])
|
||||||
|
|
||||||
@mock.patch.object(dispatcher, 'notify')
|
@mock.patch.object(dispatcher, 'notify')
|
||||||
def test_cluster_list_with_limit_marker(self, notify):
|
def test_cluster_list_with_limit_marker(self, notify):
|
||||||
|
307
senlin/tests/engine/test_nodes.py
Normal file
307
senlin/tests/engine/test_nodes.py
Normal file
@ -0,0 +1,307 @@
|
|||||||
|
# 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.db import api as db_api
|
||||||
|
from senlin.engine.actions import base as action_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 NodeTest(base.SenlinTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(NodeTest, self).setUp()
|
||||||
|
self.ctx = utils.dummy_context(tenant_id='node_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)
|
||||||
|
|
||||||
|
self.profile = self.eng.profile_create(
|
||||||
|
self.ctx, 'p-test', 'TestProfile',
|
||||||
|
spec={'INT': 10, 'STR': 'string'}, perm='1111')
|
||||||
|
|
||||||
|
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_node_create_default(self, notify):
|
||||||
|
node = self.eng.node_create(self.ctx, 'n-1', self.profile['id'])
|
||||||
|
self.assertIsNotNone(node)
|
||||||
|
self.assertEqual('n-1', node['name'])
|
||||||
|
self.assertEqual(-1, node['index'])
|
||||||
|
self.assertEqual(self.profile['id'], node['profile_id'])
|
||||||
|
self.assertIsNone(node['cluster_id'])
|
||||||
|
self.assertIsNone(node['role'])
|
||||||
|
self.assertEqual({}, node['tags'])
|
||||||
|
|
||||||
|
action_id = node['action']
|
||||||
|
action = db_api.action_get(self.ctx, action_id)
|
||||||
|
self.assertIsNotNone(action)
|
||||||
|
self._verify_action(action, 'NODE_CREATE',
|
||||||
|
'node_create_%s' % node['id'][:8],
|
||||||
|
node['id'],
|
||||||
|
cause=action_mod.CAUSE_RPC)
|
||||||
|
notify.assert_called_once_with(self.ctx,
|
||||||
|
self.eng.dispatcher.NEW_ACTION,
|
||||||
|
None, action_id=action_id)
|
||||||
|
|
||||||
|
def test_node_create_profile_not_found(self):
|
||||||
|
ex = self.assertRaises(rpc.ExpectedException,
|
||||||
|
self.eng.node_create,
|
||||||
|
self.ctx, 'n-1', 'Bogus')
|
||||||
|
self.assertEqual(exception.ProfileNotFound, ex.exc_info[0])
|
||||||
|
|
||||||
|
@mock.patch.object(dispatcher, 'notify')
|
||||||
|
def test_cluster_create_with_role_and_tags(self, notify):
|
||||||
|
node = self.eng.node_create(self.ctx, 'n-1', self.profile['id'],
|
||||||
|
role='master', tags={'k': 'v'})
|
||||||
|
|
||||||
|
self.assertIsNotNone(node)
|
||||||
|
self.assertEqual('n-1', node['name'])
|
||||||
|
self.assertEqual('master', node['role'])
|
||||||
|
self.assertEqual({'k': 'v'}, node['tags'])
|
||||||
|
|
||||||
|
@mock.patch.object(dispatcher, 'notify')
|
||||||
|
def test_node_create_with_profile_name_or_short_id(self, notify):
|
||||||
|
node = self.eng.node_create(self.ctx, 'n-1', self.profile['id'][:8])
|
||||||
|
self.assertIsNotNone(node)
|
||||||
|
self.assertEqual(self.profile['id'], node['profile_id'])
|
||||||
|
|
||||||
|
node = self.eng.node_create(self.ctx, 'n-2', self.profile['name'])
|
||||||
|
self.assertIsNotNone(node)
|
||||||
|
self.assertEqual(self.profile['id'], node['profile_id'])
|
||||||
|
|
||||||
|
def test_node_create_with_cluster_id_not_found(self):
|
||||||
|
ex = self.assertRaises(rpc.ExpectedException,
|
||||||
|
self.eng.node_create,
|
||||||
|
self.ctx, 'n-1', self.profile['id'],
|
||||||
|
cluster_id='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_node_create_project_not_match(self, notify):
|
||||||
|
ctx_cluster = utils.dummy_context(tenant_id='a-different-tenant')
|
||||||
|
cluster = self.eng.cluster_create(ctx_cluster, 'c-1', 0,
|
||||||
|
self.profile['id'])
|
||||||
|
|
||||||
|
ex = self.assertRaises(rpc.ExpectedException,
|
||||||
|
self.eng.node_create,
|
||||||
|
self.ctx, 'n-1', self.profile['id'],
|
||||||
|
cluster_id=cluster['id'])
|
||||||
|
|
||||||
|
self.assertEqual(exception.ClusterNotFound, ex.exc_info[0])
|
||||||
|
self.assertEqual("The cluster (%s) could not be found."
|
||||||
|
"" % cluster['id'],
|
||||||
|
six.text_type(ex.exc_info[1]))
|
||||||
|
|
||||||
|
@mock.patch.object(dispatcher, 'notify')
|
||||||
|
def test_node_create_profile_type_not_match(self, notify):
|
||||||
|
env = environment.global_env()
|
||||||
|
env.register_profile('SecondProfile', fakes.TestProfile)
|
||||||
|
cluster_profile = self.eng.profile_create(
|
||||||
|
self.ctx, 'cluster-profile', 'SecondProfile',
|
||||||
|
spec={'INT': 20, 'STR': 'string'})
|
||||||
|
|
||||||
|
cluster = self.eng.cluster_create(self.ctx, 'c-1', 0,
|
||||||
|
cluster_profile['id'])
|
||||||
|
|
||||||
|
ex = self.assertRaises(rpc.ExpectedException,
|
||||||
|
self.eng.node_create,
|
||||||
|
self.ctx, 'n-1', self.profile['id'],
|
||||||
|
cluster_id=cluster['id'])
|
||||||
|
|
||||||
|
self.assertEqual(exception.ProfileTypeNotMatch, ex.exc_info[0])
|
||||||
|
self.assertEqual("Node and cluster have different profile type, "
|
||||||
|
"operation aborted.",
|
||||||
|
six.text_type(ex.exc_info[1]))
|
||||||
|
|
||||||
|
@mock.patch.object(dispatcher, 'notify')
|
||||||
|
def test_node_get(self, notify):
|
||||||
|
node = self.eng.node_create(self.ctx, 'n-1', self.profile['id'])
|
||||||
|
|
||||||
|
for identity in [node['id'], node['id'][:6], 'n-1']:
|
||||||
|
result = self.eng.node_get(self.ctx, identity)
|
||||||
|
self.assertIsInstance(result, dict)
|
||||||
|
self.assertEqual(node['id'], result['id'])
|
||||||
|
|
||||||
|
ex = self.assertRaises(rpc.ExpectedException,
|
||||||
|
self.eng.node_get, self.ctx, 'Bogus')
|
||||||
|
self.assertEqual(exception.NodeNotFound, ex.exc_info[0])
|
||||||
|
|
||||||
|
@mock.patch.object(dispatcher, 'notify')
|
||||||
|
def test_node_list(self, notify):
|
||||||
|
node1 = self.eng.node_create(self.ctx, 'n1', self.profile['id'])
|
||||||
|
node2 = self.eng.node_create(self.ctx, 'n2', self.profile['id'])
|
||||||
|
result = self.eng.node_list(self.ctx)
|
||||||
|
|
||||||
|
self.assertIsInstance(result, list)
|
||||||
|
names = [n['name'] for n in result]
|
||||||
|
ids = [n['id'] for n in result]
|
||||||
|
self.assertEqual(node1['name'], names[0])
|
||||||
|
self.assertEqual(node2['name'], names[1])
|
||||||
|
self.assertEqual(node1['id'], ids[0])
|
||||||
|
self.assertEqual(node2['id'], ids[1])
|
||||||
|
|
||||||
|
@mock.patch.object(dispatcher, 'notify')
|
||||||
|
def test_node_list_with_limit_marker(self, notify):
|
||||||
|
node1 = self.eng.node_create(self.ctx, 'n1', self.profile['id'])
|
||||||
|
node2 = self.eng.node_create(self.ctx, 'n2', self.profile['id'])
|
||||||
|
result = self.eng.node_list(self.ctx, limit=0)
|
||||||
|
|
||||||
|
self.assertEqual(0, len(result))
|
||||||
|
result = self.eng.node_list(self.ctx, limit=1)
|
||||||
|
self.assertEqual(1, len(result))
|
||||||
|
result = self.eng.node_list(self.ctx, limit=2)
|
||||||
|
self.assertEqual(2, len(result))
|
||||||
|
result = self.eng.node_list(self.ctx, limit=3)
|
||||||
|
self.assertEqual(2, len(result))
|
||||||
|
|
||||||
|
result = self.eng.node_list(self.ctx, marker=node1['id'])
|
||||||
|
self.assertEqual(1, len(result))
|
||||||
|
result = self.eng.node_list(self.ctx, marker=node2['id'])
|
||||||
|
self.assertEqual(0, len(result))
|
||||||
|
|
||||||
|
self.eng.node_create(self.ctx, 'n3', self.profile['id'])
|
||||||
|
|
||||||
|
result = self.eng.node_list(self.ctx, limit=1, marker=node1['id'])
|
||||||
|
self.assertEqual(1, len(result))
|
||||||
|
result = self.eng.node_list(self.ctx, limit=2, marker=node1['id'])
|
||||||
|
self.assertEqual(2, len(result))
|
||||||
|
|
||||||
|
@mock.patch.object(dispatcher, 'notify')
|
||||||
|
def test_node_list_with_sort_keys(self, notify):
|
||||||
|
node1 = self.eng.node_create(self.ctx, 'CC', self.profile['id'])
|
||||||
|
node2 = self.eng.node_create(self.ctx, 'BB', self.profile['id'])
|
||||||
|
|
||||||
|
# default by created_time
|
||||||
|
result = self.eng.node_list(self.ctx)
|
||||||
|
self.assertEqual(node1['id'], result[0]['id'])
|
||||||
|
self.assertEqual(node2['id'], result[1]['id'])
|
||||||
|
|
||||||
|
# use name for sorting
|
||||||
|
result = self.eng.node_list(self.ctx, sort_keys=['name'])
|
||||||
|
self.assertEqual(node2['id'], result[0]['id'])
|
||||||
|
self.assertEqual(node1['id'], result[1]['id'])
|
||||||
|
|
||||||
|
# unknown keys will be ignored
|
||||||
|
result = self.eng.node_list(self.ctx, sort_keys=['duang'])
|
||||||
|
self.assertIsNotNone(result)
|
||||||
|
|
||||||
|
@mock.patch.object(dispatcher, 'notify')
|
||||||
|
def test_node_list_with_sort_dir(self, notify):
|
||||||
|
node1 = self.eng.node_create(self.ctx, 'BB', self.profile['id'])
|
||||||
|
node2 = self.eng.node_create(self.ctx, 'AA', self.profile['id'])
|
||||||
|
node3 = self.eng.node_create(self.ctx, 'CC', self.profile['id'])
|
||||||
|
|
||||||
|
# default by created_time, ascending
|
||||||
|
result = self.eng.node_list(self.ctx)
|
||||||
|
self.assertEqual(node1['id'], result[0]['id'])
|
||||||
|
self.assertEqual(node2['id'], result[1]['id'])
|
||||||
|
|
||||||
|
# sort by created_time, descending
|
||||||
|
result = self.eng.node_list(self.ctx, sort_dir='desc')
|
||||||
|
self.assertEqual(node3['id'], result[0]['id'])
|
||||||
|
self.assertEqual(node2['id'], result[1]['id'])
|
||||||
|
|
||||||
|
# use name for sorting, descending
|
||||||
|
result = self.eng.node_list(self.ctx, sort_keys=['name'],
|
||||||
|
sort_dir='desc')
|
||||||
|
self.assertEqual(node3['id'], result[0]['id'])
|
||||||
|
self.assertEqual(node1['id'], result[1]['id'])
|
||||||
|
|
||||||
|
# use permission for sorting
|
||||||
|
ex = self.assertRaises(ValueError,
|
||||||
|
self.eng.node_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_node_list_show_deleted(self, notify):
|
||||||
|
node = self.eng.node_create(self.ctx, 'n1', self.profile['id'])
|
||||||
|
result = self.eng.node_list(self.ctx)
|
||||||
|
self.assertEqual(1, len(result))
|
||||||
|
self.assertEqual(node['id'], result[0]['id'])
|
||||||
|
|
||||||
|
db_api.node_delete(self.ctx, node['id'])
|
||||||
|
|
||||||
|
result = self.eng.node_list(self.ctx)
|
||||||
|
self.assertEqual(0, len(result))
|
||||||
|
|
||||||
|
result = self.eng.node_list(self.ctx, show_deleted=True)
|
||||||
|
self.assertEqual(1, len(result))
|
||||||
|
self.assertEqual(node['id'], result[0]['id'])
|
||||||
|
|
||||||
|
@mock.patch.object(dispatcher, 'notify')
|
||||||
|
def test_cluster_list_with_cluster_id(self, notify):
|
||||||
|
c = self.eng.cluster_create(self.ctx, 'c-1', 0, self.profile['id'])
|
||||||
|
node = self.eng.node_create(self.ctx, 'n1', self.profile['id'],
|
||||||
|
cluster_id=c['id'])
|
||||||
|
|
||||||
|
result = self.eng.node_list(self.ctx, cluster_id=c['id'])
|
||||||
|
self.assertEqual(1, len(result))
|
||||||
|
self.assertEqual(node['id'], result[0]['id'])
|
||||||
|
|
||||||
|
@mock.patch.object(dispatcher, 'notify')
|
||||||
|
def test_node_list_with_filters(self, notify):
|
||||||
|
self.eng.node_create(self.ctx, 'BB', self.profile['id'])
|
||||||
|
self.eng.node_create(self.ctx, 'AA', self.profile['id'])
|
||||||
|
self.eng.node_create(self.ctx, 'CC', self.profile['id'])
|
||||||
|
|
||||||
|
result = self.eng.node_list(self.ctx, filters={'name': 'BB'})
|
||||||
|
self.assertEqual(1, len(result))
|
||||||
|
self.assertEqual('BB', result[0]['name'])
|
||||||
|
|
||||||
|
result = self.eng.node_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,
|
||||||
|
tenant_safe='no')
|
||||||
|
self.assertEqual(exception.InvalidParameter, ex.exc_info[0])
|
||||||
|
|
||||||
|
def test_cluster_list_empty(self):
|
||||||
|
result = self.eng.node_list(self.ctx)
|
||||||
|
self.assertIsInstance(result, list)
|
||||||
|
self.assertEqual(0, len(result))
|
Loading…
Reference in New Issue
Block a user