88e9f5a209
The main purpose of this commit is to have an ability to split configuration file astute.yaml into common and node parts. Common part is huge and we will dump it once and also there will be only one instance of this data in RAM which saves a lot of memory when you run deploy on many nodes (>100). This patch adds two new variables to context, which can be used in yaql_exp: $node and $common for node and common parts of the context. Functions changed, changed_all, changed_any, added, deleted don't work for these variables. DocImpact Change-Id: I56bf982652a5dc27882e4a401ca9ec124899fed7 Partial-Bug: #1596987
338 lines
12 KiB
Python
338 lines
12 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.
|
|
|
|
from nailgun import consts
|
|
from nailgun.lcm.context import TransactionContext
|
|
from nailgun.lcm import task_serializer
|
|
from nailgun.settings import settings
|
|
|
|
from nailgun.test.base import BaseUnitTest
|
|
|
|
|
|
class TestTaskSerializerContext(BaseUnitTest):
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
cls.transaction = TransactionContext(
|
|
{
|
|
'common': {
|
|
'cluster': {'id': 1},
|
|
'release': {'version': 'liberty-9.0'},
|
|
'openstack_version': 'liberty-9.0',
|
|
'public_ssl': {'hostname': 'localhost'},
|
|
},
|
|
'nodes': {
|
|
'1': {
|
|
'attribute': '1'
|
|
}
|
|
}
|
|
}
|
|
)
|
|
cls.context = task_serializer.Context(cls.transaction)
|
|
|
|
def test_transform_legacy_condition(self):
|
|
cases = [
|
|
(
|
|
'settings:additional_components.ceilometer.value == 1 or ',
|
|
'settings:ceilometer.enabled == 1 or ',
|
|
),
|
|
(
|
|
'settings:common.vms_create.value == 1 or '
|
|
'settings:common.vms_create.value == 2',
|
|
'settings:vms_create == 1 or settings:vms_create == 2',
|
|
),
|
|
(
|
|
'settings:my.id.value == 2 or settings:my.id.value == 1',
|
|
'settings:my.id == 2 or settings:my.id == 1',
|
|
),
|
|
(
|
|
'settings:common.value.id == 2 or '
|
|
'settings:common.value.id == 1',
|
|
'settings:value.id == 2 or settings:value.id == 1',
|
|
),
|
|
(
|
|
'cluster:id == 2 or cluster:id == 1',
|
|
'cluster:id == 2 or cluster:id == 1'
|
|
),
|
|
(
|
|
'cluster:common.id.value == 2 or cluster:common.id.value == 1',
|
|
'cluster:common.id.value == 2 or cluster:common.id.value == 1',
|
|
),
|
|
]
|
|
for value, expected in cases:
|
|
self.assertEqual(
|
|
expected,
|
|
self.context.transform_legacy_condition(value)
|
|
)
|
|
|
|
def test_get_new_data(self):
|
|
self.assertEqual(
|
|
self.transaction.get_new_data('1'),
|
|
self.context.get_new_data('1')
|
|
)
|
|
|
|
def test_get_legacy_interpreter(self):
|
|
interpreter = self.context.get_legacy_interpreter('1')
|
|
self.assertTrue(interpreter('cluster:id == 1'))
|
|
self.assertTrue(interpreter("settings:common.attribute.value == '1'"))
|
|
|
|
def test_get_formatter_context(self):
|
|
self.assertEqual(
|
|
{
|
|
'CLUSTER_ID': 1,
|
|
'OPENSTACK_VERSION': 'liberty-9.0',
|
|
'MASTER_IP': settings.MASTER_IP,
|
|
'CN_HOSTNAME': 'localhost',
|
|
'SETTINGS': settings,
|
|
},
|
|
self.context.get_formatter_context('1')
|
|
)
|
|
|
|
|
|
class TestTaskSerializer(BaseUnitTest):
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
cls.transaction_context = TransactionContext({
|
|
'common': {
|
|
'cluster': {'id': 1},
|
|
'release': {'version': 'liberty-9.0'},
|
|
'openstack_version': 'liberty-9.0',
|
|
'public_ssl': {'hostname': 'localhost'},
|
|
},
|
|
'nodes': {
|
|
'1': {
|
|
'cluster': {'id': 1},
|
|
'attributes': {
|
|
'a_str': 'text1',
|
|
'a_int': 1
|
|
}
|
|
},
|
|
'2': {
|
|
'cluster': {'id': 2},
|
|
'attributes': {
|
|
'a_str': 'text2',
|
|
'a_int': 2
|
|
}
|
|
}
|
|
}
|
|
})
|
|
|
|
cls.context = task_serializer.Context(cls.transaction_context)
|
|
|
|
|
|
class TestDefaultTaskSerializer(TestTaskSerializer):
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super(TestDefaultTaskSerializer, cls).setUpClass()
|
|
cls.serializer_class = task_serializer.DefaultTaskSerializer
|
|
|
|
def check_condition(self, condition, expected):
|
|
task_template = {
|
|
'id': 'test',
|
|
'type': 'puppet',
|
|
'parameters': {},
|
|
'condition': condition
|
|
}
|
|
serializer = self.serializer_class(self.context, task_template)
|
|
for node_id, result in expected:
|
|
self.assertEqual(result, serializer.should_execute(
|
|
task_template, node_id
|
|
))
|
|
|
|
def test_should_execute_legacy_condition_for_settings(self):
|
|
self.check_condition(
|
|
"settings:common.attributes.a_str.value == 'text1'",
|
|
[('1', True), ('2', False)]
|
|
)
|
|
|
|
def test_should_execute_legacy_condition_for_cluster(self):
|
|
self.check_condition(
|
|
"cluster:id == 1",
|
|
[('1', True), ('2', False)]
|
|
)
|
|
|
|
def should_execute_returns_condition_if_it_is_bool(self):
|
|
self.check_condition(False, [('1', False), ('2', False)])
|
|
self.check_condition(True, [('1', True), ('2', True)])
|
|
|
|
def should_execute_returns_true_if_no_condition(self):
|
|
task_template = {
|
|
'id': 'test',
|
|
'type': 'puppet',
|
|
'parameters': {},
|
|
}
|
|
serializer = self.serializer_class(self.context, task_template)
|
|
self.assertTrue(serializer.should_execute('1'))
|
|
self.assertTrue(serializer.should_execute('2'))
|
|
|
|
def test_serialize_with_format(self):
|
|
task_template = {
|
|
'id': 'test',
|
|
'roles': ['controller'],
|
|
'condition': '1',
|
|
'type': 'upload_file',
|
|
'parameters': {
|
|
'data': {
|
|
'yaql_exp': '$.cluster',
|
|
},
|
|
'path': '/etc/{CLUSTER_ID}/astute.yaml'
|
|
},
|
|
'requires': ['deploy_start'],
|
|
'required_for': ['deploy_end'],
|
|
'cross_depends': [],
|
|
'cross_depended_by': [],
|
|
}
|
|
serializer = self.serializer_class(self.context, task_template)
|
|
serialized = serializer.serialize('1')
|
|
task_template['parameters']['data'] = \
|
|
self.context.get_new_data('1')['cluster']
|
|
task_template['parameters']['path'] = '/etc/1/astute.yaml'
|
|
task_template['parameters']['cwd'] = '/'
|
|
task_template['fail_on_error'] = True
|
|
del task_template['condition']
|
|
del task_template['roles']
|
|
self.assertEqual(task_template, serialized)
|
|
|
|
def test_serialize_does_not_fail_if_format_fail(self):
|
|
task_template = {
|
|
'id': 'test',
|
|
'type': 'upload_file',
|
|
'parameters': {
|
|
'cmd': "cat /etc/astute.yaml | awk '{ print $1 }'",
|
|
'cwd': '/tmp/'
|
|
},
|
|
'fail_on_error': False,
|
|
'required_for': None,
|
|
'requires': None,
|
|
'cross_depends': [],
|
|
'cross_depended_by': []
|
|
}
|
|
serializer = self.serializer_class(self.context, task_template)
|
|
serialized = serializer.serialize('1')
|
|
self.assertEqual(task_template, serialized)
|
|
|
|
def test_serialize_skipped_task(self):
|
|
task_template = {
|
|
'id': 'test',
|
|
'type': 'upload_file',
|
|
'condition': '0',
|
|
'parameters': {
|
|
'cmd': "cat /etc/astute.yaml | awk '{ print $1 }'",
|
|
'cwd': '/tmp/'
|
|
},
|
|
'fail_on_error': True,
|
|
'requires': ['deploy_start'],
|
|
'required_for': ['deploy_end'],
|
|
'cross_depends': [{'roles': '*', 'name': 'task1'}],
|
|
'cross_depended_by': [{'roles': '*', 'name': 'task1'}],
|
|
}
|
|
serializer = self.serializer_class(self.context, task_template)
|
|
serialized = serializer.serialize('1')
|
|
self.assertEqual(
|
|
{
|
|
'id': 'test',
|
|
'type': consts.ORCHESTRATOR_TASK_TYPES.skipped,
|
|
'fail_on_error': False,
|
|
'requires': ['deploy_start'],
|
|
'required_for': ['deploy_end'],
|
|
'cross_depends': [{'roles': '*', 'name': 'task1'}],
|
|
'cross_depended_by': [{'roles': '*', 'name': 'task1'}],
|
|
},
|
|
serialized
|
|
)
|
|
|
|
def test_serialize_context_components(self):
|
|
task_template = {
|
|
'id': 'test',
|
|
'roles': ['controller'],
|
|
'type': 'upload_file',
|
|
'parameters': {
|
|
'data': {
|
|
'yaql_exp': '$node',
|
|
},
|
|
'data2': {
|
|
'yaql_exp': '$common',
|
|
},
|
|
'path': '/etc/{CLUSTER_ID}/astute.yaml'
|
|
},
|
|
'requires': ['deploy_start'],
|
|
'required_for': ['deploy_end'],
|
|
'cross_depends': [],
|
|
'cross_depended_by': [],
|
|
}
|
|
serializer = self.serializer_class(self.context, task_template)
|
|
serialized = serializer.serialize('1')
|
|
self.assertEqual(serialized['parameters']['data'],
|
|
self.transaction_context.get_new_node_data('1'))
|
|
self.assertEqual(serialized['parameters']['data2'],
|
|
self.transaction_context.get_new_common_data())
|
|
|
|
|
|
class TestNoopTaskSerialzer(TestTaskSerializer):
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super(TestNoopTaskSerialzer, cls).setUpClass()
|
|
cls.serializer_class = task_serializer.NoopTaskSerializer
|
|
|
|
def test_serialize(self):
|
|
task_template = {
|
|
'id': 'test',
|
|
'type': 'skipped',
|
|
'parameters': {},
|
|
'fail_on_error': True,
|
|
'requires': {'yaql_exp': '["deploy_start"]'},
|
|
'required_for': ['deploy_end'],
|
|
}
|
|
serializer = self.serializer_class(self.context, task_template)
|
|
serialized = serializer.serialize('1')
|
|
self.assertEqual(
|
|
{
|
|
'id': 'test', 'type': 'skipped', 'fail_on_error': False,
|
|
'requires': ['deploy_start'], 'required_for': ['deploy_end']
|
|
},
|
|
serialized
|
|
)
|
|
|
|
|
|
class TestTasksSerializersFactory(BaseUnitTest):
|
|
factory_class = task_serializer.TasksSerializersFactory
|
|
|
|
def test_create_serializer_for_generic(self):
|
|
common_task_types = set(consts.ORCHESTRATOR_TASK_TYPES)
|
|
common_task_types.discard(consts.ORCHESTRATOR_TASK_TYPES.role)
|
|
common_task_types.discard(consts.ORCHESTRATOR_TASK_TYPES.skipped)
|
|
common_task_types.discard(consts.ORCHESTRATOR_TASK_TYPES.stage)
|
|
common_task_types.discard(consts.ORCHESTRATOR_TASK_TYPES.group)
|
|
|
|
factory = self.factory_class(TransactionContext({}))
|
|
for task_type in common_task_types:
|
|
task = {'id': 'test', 'type': task_type}
|
|
self.assertIsInstance(
|
|
factory.create_serializer(task),
|
|
task_serializer.DefaultTaskSerializer
|
|
)
|
|
|
|
def test_create_noop_serializer(self):
|
|
noop_task_types = [
|
|
consts.ORCHESTRATOR_TASK_TYPES.skipped,
|
|
consts.ORCHESTRATOR_TASK_TYPES.stage
|
|
]
|
|
factory = self.factory_class(TransactionContext({}))
|
|
for task_type in noop_task_types:
|
|
task = {'id': 'test', 'type': task_type}
|
|
self.assertIsInstance(
|
|
factory.create_serializer(task),
|
|
task_serializer.NoopTaskSerializer
|
|
)
|