fuel-web/nailgun/nailgun/test/unit/test_lcm_transaction_serial...

465 lines
15 KiB
Python

# -*- coding: utf-8 -*-
# Copyright 2016 Mirantis, Inc.
#
# 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
import multiprocessing.dummy
from nailgun import consts
from nailgun import errors
from nailgun import lcm
from nailgun.utils.resolvers import TagResolver
from nailgun.test.base import BaseUnitTest
class TestTransactionSerializer(BaseUnitTest):
@classmethod
def setUpClass(cls):
cls.tasks = [
{
'id': 'task1', 'roles': ['controller'],
'type': 'puppet', 'version': '2.0.0',
'condition': {
'yaql_exp': '$.public_ssl.hostname = localhost'
},
'parameters': {},
'requires': ['task3'],
'required_for': ['task2'],
'cross_depends': [{'name': 'task2', 'role': 'compute'}],
'cross_depended_by': [{'name': 'task3', 'role': 'cinder'}]
},
{
'id': 'task2', 'roles': ['compute', 'controller'],
'type': 'puppet', 'version': '2.0.0',
'condition': {
'yaql_exp': '$.public_ssl.hostname != localhost'
},
'parameters': {},
'cross_depends': [{'name': 'task3', 'role': 'cinder'}]
},
{
'id': 'task3', 'roles': ['cinder', 'controller'],
'type': 'puppet', 'version': '2.0.0',
'condition': 'settings:public_ssl.hostname != "localhost"',
'parameters': {},
'cross_depends': [{'name': 'task3', 'role': '/.*/'}],
'cross_depended_by': [{'name': 'task2', 'role': 'self'}]
},
{
'id': 'task4', 'roles': ['controller'],
'type': 'puppet', 'version': '2.0.0',
'parameters': {},
'cross_depended_by': [{'name': 'task3'}]
}
]
cls.nodes = [
mock.MagicMock(uid='1', roles=['controller']),
mock.MagicMock(uid='2', roles=['compute']),
mock.MagicMock(uid='3', roles=['cinder']),
mock.MagicMock(uid='4', roles=['custom']),
]
cls.context = lcm.TransactionContext({
'common': {
'cluster': {'id': 1},
'release': {'version': 'liberty-9.0'},
'openstack_version': 'liberty-9.0',
'public_ssl': {'hostname': 'localhost'},
},
'nodes': {
'1': {
'attributes': {
'a_str': 'text1',
'a_int': 1
}
},
'2': {
'attributes': {
'a_str': 'text2',
'a_int': 2
}
},
'3': {
'attributes': {
'a_str': 'text3',
'a_int': 3
}
},
'4': {
'attributes': {
'a_str': 'text3',
'a_int': 3
}
}
}
})
with mock.patch('nailgun.utils.resolvers.objects') as m_objects:
m_objects.Node.all_tags = lambda x: x.roles
cls.resolver = TagResolver(cls.nodes)
def test_serialize_integration(self):
serialized = lcm.TransactionSerializer.serialize(
self.context, self.tasks, self.resolver
)[1]
# controller
self.datadiff(
[
{
'id': 'task1', 'type': 'puppet', 'fail_on_error': True,
'parameters': {},
'requires': [
{'node_id': '1', 'name': 'task3'},
{'node_id': '2', 'name': 'task2'},
],
'required_for': [
{'node_id': '1', 'name': 'task2'},
{'node_id': '3', 'name': 'task3'},
]
},
{
'id': 'task2', 'type': 'skipped', 'fail_on_error': False,
'requires': [
{'node_id': '3', 'name': 'task3'},
],
},
{
'id': 'task3', 'type': 'skipped', 'fail_on_error': False,
'requires': [
{'node_id': '3', 'name': 'task3'},
],
'required_for': [
{'node_id': '1', 'name': 'task2'},
]
},
{
'id': 'task4', 'type': 'puppet', 'fail_on_error': True,
'parameters': {},
'required_for': [
{'node_id': '1', 'name': 'task3'},
{'node_id': '3', 'name': 'task3'},
]
}
],
serialized['1'],
ignore_keys=['parameters', 'fail_on_error'],
compare_sorted=True,
)
# compute
self.datadiff(
[
{
'id': 'task2', 'type': 'skipped', 'fail_on_error': True,
'requires': [
{'node_id': '3', 'name': 'task3'},
],
}
],
serialized['2'],
ignore_keys=['parameters', 'fail_on_error'],
compare_sorted=True,
)
# cinder
self.datadiff(
[
{
'id': 'task3', 'type': 'skipped', 'fail_on_error': True,
'requires': [
{'node_id': '1', 'name': 'task3'},
]
}
],
serialized['3'],
ignore_keys=['parameters', 'fail_on_error'],
compare_sorted=True,
)
def test_resolve_nodes(self):
serializer = lcm.TransactionSerializer(
self.context, self.resolver
)
self.assertEqual(
[None],
serializer.resolve_nodes(
{'id': 'deploy_start',
'type': consts.ORCHESTRATOR_TASK_TYPES.stage}
)
)
self.assertItemsEqual(
['2'],
serializer.resolve_nodes(
{'id': 'deploy_start',
'type': consts.ORCHESTRATOR_TASK_TYPES.skipped,
'groups': ['compute']},
)
)
self.assertItemsEqual(
['1'],
serializer.resolve_nodes(
{'id': 'deploy_start',
'type': consts.ORCHESTRATOR_TASK_TYPES.skipped,
'roles': ['controller']}
)
)
def test_dependencies_de_duplication(self):
serializer = lcm.TransactionSerializer(
self.context, self.resolver
)
serializer.tasks_graph = {
None: {},
'1': {
'task1': {
'id': 'task1',
'requires': ['task2'],
'cross_depends': [
{'role': 'self', 'name': 'task2'},
]
},
'task2': {
'id': 'task2',
'required_for': ['task1'],
'cross_depended_by': [{'role': 'self', 'name': 'task1'}]
}
}
}
serializer.resolve_dependencies()
self.datadiff(
{
'task1': {
'id': 'task1',
'requires': [{'node_id': '1', 'name': 'task2'}],
},
'task2': {
'id': 'task2',
'required_for': [{'node_id': '1', 'name': 'task1'}],
}
},
serializer.tasks_graph['1'],
compare_sorted=True
)
def test_tasks_expand_groups(self):
tasks = list(self.tasks)
tasks.append({
'id': 'task4', 'roles': ['/.*/'],
'type': 'puppet', 'version': '2.0.0',
'parameters': {},
'cross_depends': [{'name': 'task2', 'role': 'self'}],
})
tasks.append({
'id': 'custom', 'type': 'group', 'roles': 'custom',
'fault_tolerance': '100%',
'tasks': ['task4', 'task2']
})
tasks.append({
'id': 'controller', 'type': 'group', 'roles': 'controller',
'fault_tolerance': '0%',
'tasks': ['task4', 'task2']
})
tasks.append({
'id': 'compute', 'type': 'group', 'roles': 'compute',
'tasks': ['task4', 'task2']
})
serialized = lcm.TransactionSerializer.serialize(
self.context, tasks, self.resolver
)
tasks_per_node = serialized[1]
self.datadiff(
[
{
'id': 'task2', 'type': 'skipped', 'fail_on_error': True,
'requires': [{'name': 'task3', 'node_id': '3'}]
},
{
'id': 'task4', 'type': 'puppet', 'fail_on_error': True,
'parameters': {},
'requires': [{'name': 'task2', 'node_id': '4'}]
},
],
tasks_per_node['4'],
ignore_keys=['parameters', 'fail_on_error'],
compare_sorted=True
)
tasks_metadata = serialized[2]
self.datadiff(
{
'fault_tolerance_groups': [
{
'name': 'custom',
'node_ids': ['4'],
'fault_tolerance': 1
},
{
'name': 'controller',
'node_ids': ['1'],
'fault_tolerance': 0
},
{
'name': 'compute',
'node_ids': ['2'],
'fault_tolerance': 2
}
]
},
tasks_metadata,
compare_sorted=True
)
def test_expand_dependencies(self):
serializer = lcm.TransactionSerializer(
self.context, self.resolver
)
serializer.tasks_graph = {
'1': {'task1': {}},
'2': {'task2': {}},
None: {'deploy_start': {}, 'deploy_end': {}}
}
self.assertItemsEqual([], serializer.expand_dependencies('1', None))
self.assertItemsEqual([], serializer.expand_dependencies('1', []))
self.assertItemsEqual(
[('deploy_start', None), ('task2', '2')],
serializer.expand_dependencies('2', ['deploy_start', 'task2'])
)
def test_expand_cross_dependencies(self):
serializer = lcm.TransactionSerializer(
self.context, self.resolver
)
serializer.tasks_graph = {
'1': {'task1': {}, 'task2': {}},
'2': {'task3': {}, 'task2': {}, 'task1': {}},
'3': {'task3': {}},
None: {'deploy_start': {}, 'deploy_end': {}}
}
self.assertItemsEqual(
[], serializer.expand_cross_dependencies('task1', '1', None)
)
self.assertItemsEqual(
[], serializer.expand_cross_dependencies('task1', '1', [])
)
self.assertItemsEqual(
[
('deploy_start', None), ('task2', '2'),
('task3', '2'), ('task3', '3')
],
serializer.expand_cross_dependencies(
'task2', '1',
[{'name': 'deploy_start', 'role': None},
{'name': 'task2', 'role': '/.*/'},
{'name': 'task3', 'role': '/.*/'}]
)
)
self.assertItemsEqual(
[('task2', '2'), ('task1', '1')],
serializer.expand_cross_dependencies(
'task2', '1',
[{'name': 'task2'},
{'name': 'task1', 'role': 'self'}]
)
)
def test_need_update_task(self):
serializer = lcm.TransactionSerializer(
self.context, self.resolver
)
self.assertTrue(serializer.need_update_task(
{}, {"id": "task1", "type": "puppet"}
))
self.assertTrue(serializer.need_update_task(
{"task1": {"type": "skipped"}}, {"id": "task1", "type": "puppet"}
))
self.assertFalse(serializer.need_update_task(
{"task1": {"type": "skipped"}}, {"id": "task1", "type": "skipped"}
))
self.assertFalse(serializer.need_update_task(
{"task1": {"type": "puppet"}}, {"id": "task1", "type": "skipped"}
))
@mock.patch(
'nailgun.lcm.transaction_serializer.settings.LCM_CHECK_TASK_VERSION',
new=True
)
def test_ensure_task_based_deploy_allowed_raises_if_version_check(self):
self.assertRaises(
errors.TaskBaseDeploymentNotAllowed,
lcm.TransactionSerializer.ensure_task_based_deploy_allowed,
{'type': consts.ORCHESTRATOR_TASK_TYPES.puppet,
'version': '1.0.0', 'id': 'test'}
)
@mock.patch(
'nailgun.lcm.transaction_serializer.settings.LCM_CHECK_TASK_VERSION',
new=False
)
def test_ensure_task_based_deploy_allowed_if_not_version_check(self):
self.assertNotRaises(
errors.TaskBaseDeploymentNotAllowed,
lcm.TransactionSerializer.ensure_task_based_deploy_allowed,
{'type': consts.ORCHESTRATOR_TASK_TYPES.puppet,
'version': '1.0.0', 'id': 'test'}
)
@mock.patch(
'nailgun.lcm.transaction_serializer.settings'
'.LCM_SERIALIZERS_CONCURRENCY_FACTOR',
new=2
)
@mock.patch(
'nailgun.lcm.transaction_serializer.multiprocessing',
new=multiprocessing.dummy
)
def test_multi_processing_serialization(self):
self.test_serialize_integration()
def test_get_fault_tolerance(self):
self.assertEqual(
11,
lcm.TransactionSerializer.calculate_fault_tolerance(None, 10)
)
self.assertEqual(
10,
lcm.TransactionSerializer.calculate_fault_tolerance('10', 10)
)
self.assertEqual(
10,
lcm.TransactionSerializer.calculate_fault_tolerance(10, 10)
)
self.assertEqual(
1,
lcm.TransactionSerializer.calculate_fault_tolerance('10%', 10)
)
self.assertEqual(
11,
lcm.TransactionSerializer.calculate_fault_tolerance('a%', 10)
)
self.assertEqual(
9,
lcm.TransactionSerializer.calculate_fault_tolerance(' -10%', 10)
)
self.assertEqual(
9,
lcm.TransactionSerializer.calculate_fault_tolerance('-1 ', 10)
)