fuel-web/nailgun/nailgun/test/integration/test_transactions_manager.py

473 lines
16 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
from nailgun import consts
from nailgun import objects
from nailgun.rpc import receiver
from nailgun.transactions import manager
from nailgun.test import base
class TestTransactionManager(base.BaseIntegrationTest):
def setUp(self):
super(TestTransactionManager, self).setUp()
self.cluster = self.env.create(
cluster_kwargs={},
nodes_kwargs=[
{"status": consts.NODE_STATUSES.discover},
{"status": consts.NODE_STATUSES.discover, "online": False},
],
release_kwargs={
'version': 'mitaka-9.0',
'operating_system': consts.RELEASE_OS.ubuntu
})
self.graph = objects.DeploymentGraph.create_for_model(
{
'tasks': [
{
'id': 'test_task',
'type': consts.ORCHESTRATOR_TASK_TYPES.puppet,
'roles': ['/.*/']
},
],
'name': 'test_graph',
},
instance=self.cluster,
graph_type='test_graph')
self.manager = manager.TransactionsManager(self.cluster.id)
self.receiver = receiver.NailgunReceiver
self.expected_metadata = {
'fault_tolerance_groups': [],
'node_statuses_transitions': {
'successful': {'status': consts.NODE_STATUSES.ready},
'failed': {'status': consts.NODE_STATUSES.error},
'stopped': {'status': consts.NODE_STATUSES.stopped}}
}
def _success(self, transaction_uuid):
self.receiver.transaction_resp(
task_uuid=transaction_uuid,
nodes=[
{'uid': n.uid, 'status': consts.NODE_STATUSES.ready}
for n in self.cluster.nodes
],
progress=100,
status=consts.TASK_STATUSES.ready)
def _fail(self, transaction_uuid):
self.receiver.transaction_resp(
task_uuid=transaction_uuid,
nodes=[
{'uid': n.uid, 'status': consts.NODE_STATUSES.error}
for n in self.cluster.nodes
],
progress=100,
status=consts.TASK_STATUSES.error)
@mock.patch('nailgun.transactions.manager.rpc')
def test_execute_graph(self, rpc_mock):
task = self.manager.execute(graphs=[{"type": "test_graph"}])
rpc_mock.cast.assert_called_once_with(
'naily',
[{
'args': {
'tasks_metadata': self.expected_metadata,
'task_uuid': task.subtasks[0].uuid,
'tasks_graph': {
None: [],
self.cluster.nodes[0].uid: [
{
'id': 'test_task',
'type': 'puppet',
'fail_on_error': True,
'parameters': {'cwd': '/'}
},
]
},
'tasks_directory': {},
'dry_run': False,
},
'respond_to': 'transaction_resp',
'method': 'task_deploy',
'api_version': '1'
}])
self._success(task.subtasks[0].uuid)
self.assertEqual(task.status, consts.TASK_STATUSES.ready)
@mock.patch('nailgun.transactions.manager.rpc')
def test_execute_few_graphs(self, rpc_mock):
objects.DeploymentGraph.create_for_model(
{
'tasks': [
{
'id': 'super-mega-other-task',
'type': consts.ORCHESTRATOR_TASK_TYPES.puppet,
'roles': ['/.*/']
},
],
'name': 'test_graph_2',
},
instance=self.cluster,
graph_type='test_graph_2')
task = self.manager.execute(graphs=[
{"type": "test_graph"},
{"type": "test_graph_2"},
])
self.assertItemsEqual(
["test_graph", "test_graph_2"],
[sub.graph_type for sub in task.subtasks])
# Only a message for the first graph should be sent, because
# the second graph should be sent by RPC receiver once first
# one is completed.
rpc_mock.cast.assert_called_once_with(
'naily',
[{
'args': {
'tasks_metadata': self.expected_metadata,
'task_uuid': task.subtasks[0].uuid,
'tasks_graph': {
None: [],
self.cluster.nodes[0].uid: [
{
'id': 'test_task',
'type': 'puppet',
'fail_on_error': True,
'parameters': {'cwd': '/'}
},
]
},
'tasks_directory': {},
'dry_run': False,
},
'respond_to': 'transaction_resp',
'method': 'task_deploy',
'api_version': '1'
}])
# Consider we've got success from Astute.
self._success(task.subtasks[0].uuid)
# It's time to send the second graph to execution.
rpc_mock.cast.assert_called_with(
'naily',
[{
'args': {
'tasks_metadata': self.expected_metadata,
'task_uuid': task.subtasks[1].uuid,
'tasks_graph': {
None: [],
self.cluster.nodes[0].uid: [
{
'id': 'super-mega-other-task',
'type': 'puppet',
'fail_on_error': True,
'parameters': {'cwd': '/'}
},
]
},
'tasks_directory': {},
'dry_run': False,
},
'respond_to': 'transaction_resp',
'method': 'task_deploy',
'api_version': '1'
}])
# Consider we've got success from Astute.
self._success(task.subtasks[1].uuid)
# Ensure the top leve transaction is ready.
self.assertEqual(task.status, consts.TASK_STATUSES.ready)
@mock.patch('nailgun.transactions.manager.rpc')
def test_execute_few_graphs_first_fail(self, rpc_mock):
objects.DeploymentGraph.create_for_model(
{
'tasks': [
{
'id': 'super-mega-other-task',
'type': consts.ORCHESTRATOR_TASK_TYPES.puppet,
'roles': ['/.*/']
},
],
'name': 'test_graph_2',
},
instance=self.cluster,
graph_type='test_graph_2')
task = self.manager.execute(graphs=[
{"type": "test_graph"},
{"type": "test_graph_2"},
])
self.assertItemsEqual(
["test_graph", "test_graph_2"],
[sub.graph_type for sub in task.subtasks])
# Only a message for the first graph should be sent, because
# the second graph should be sent by RPC receiver once first
# one is completed.
rpc_mock.cast.assert_called_once_with(
'naily',
[{
'args': {
'tasks_metadata': self.expected_metadata,
'task_uuid': task.subtasks[0].uuid,
'tasks_graph': {
None: [],
self.cluster.nodes[0].uid: [
{
'id': 'test_task',
'type': 'puppet',
'fail_on_error': True,
'parameters': {'cwd': '/'}
},
]
},
'tasks_directory': {},
'dry_run': False,
},
'respond_to': 'transaction_resp',
'method': 'task_deploy',
'api_version': '1'
}])
self._fail(task.subtasks[0].uuid)
self.assertEqual(rpc_mock.cast.call_count, 1)
self.assertEqual(task.status, consts.TASK_STATUSES.error)
@mock.patch('nailgun.transactions.manager.rpc')
def test_execute_w_task(self, rpc_mock):
self.graph.tasks.append(objects.DeploymentGraphTask.create(
{
'id': 'test_task_2',
'type': consts.ORCHESTRATOR_TASK_TYPES.puppet,
'roles': ['/.*/']
}))
task = self.manager.execute(graphs=[
{
"type": "test_graph",
"tasks": ["test_task"],
}])
rpc_mock.cast.assert_called_once_with(
'naily',
[{
'args': {
'tasks_metadata': self.expected_metadata,
'task_uuid': task.subtasks[0].uuid,
'tasks_graph': {
None: [],
self.cluster.nodes[0].uid: mock.ANY,
},
'tasks_directory': {},
'dry_run': False,
},
'respond_to': 'transaction_resp',
'method': 'task_deploy',
'api_version': '1'
}])
tasks_graph = rpc_mock.cast.call_args[0][1][0]['args']['tasks_graph']
self.assertItemsEqual(tasks_graph[self.cluster.nodes[0].uid], [
{
'id': 'test_task',
'type': 'puppet',
'fail_on_error': True,
'parameters': {'cwd': '/'}
},
{
'id': 'test_task_2',
'type': 'skipped',
'fail_on_error': False,
}
])
self._success(task.subtasks[0].uuid)
self.assertEqual(task.status, consts.TASK_STATUSES.ready)
@mock.patch('nailgun.transactions.manager.rpc')
def test_execute_w_non_existing_task(self, rpc_mock):
task = self.manager.execute(graphs=[
{
"type": "test_graph",
"tasks": ["non_exist"],
}])
rpc_mock.cast.assert_called_once_with(
'naily',
[{
'args': {
'tasks_metadata': self.expected_metadata,
'task_uuid': task.subtasks[0].uuid,
'tasks_graph': {
None: [],
self.cluster.nodes[0].uid: [
{
'id': 'test_task',
'type': 'skipped',
'fail_on_error': False,
},
]
},
'tasks_directory': {},
'dry_run': False,
},
'respond_to': 'transaction_resp',
'method': 'task_deploy',
'api_version': '1'
}])
self._success(task.subtasks[0].uuid)
self.assertEqual(task.status, consts.TASK_STATUSES.ready)
@mock.patch('nailgun.transactions.manager.rpc')
def test_execute_dry_run(self, rpc_mock):
task = self.manager.execute(
graphs=[{"type": "test_graph"}], dry_run=True)
rpc_mock.cast.assert_called_once_with(
'naily',
[{
'args': {
'tasks_metadata': self.expected_metadata,
'task_uuid': task.subtasks[0].uuid,
'tasks_graph': {
None: [],
self.cluster.nodes[0].uid: [
{
'id': 'test_task',
'type': 'puppet',
'fail_on_error': True,
'parameters': {'cwd': '/'}
},
]
},
'tasks_directory': {},
'dry_run': True,
},
'respond_to': 'transaction_resp',
'method': 'task_deploy',
'api_version': '1'
}])
self._success(task.subtasks[0].uuid)
self.assertEqual(task.status, consts.TASK_STATUSES.ready)
@mock.patch('nailgun.transactions.manager.rpc')
def test_execute_on_one_node(self, rpc_mock):
node = self.env.create_node(
cluster_id=self.cluster.id, pending_roles=["compute"])
task = self.manager.execute(graphs=[
{
"type": "test_graph",
"nodes": [node.id],
}])
rpc_mock.cast.assert_called_once_with(
'naily',
[{
'args': {
'tasks_metadata': self.expected_metadata,
'task_uuid': task.subtasks[0].uuid,
'tasks_graph': {
None: [],
node.uid: [
{
'id': 'test_task',
'type': 'puppet',
'fail_on_error': True,
'parameters': {'cwd': '/'}
},
]
},
'tasks_directory': {},
'dry_run': False,
},
'respond_to': 'transaction_resp',
'method': 'task_deploy',
'api_version': '1'
}]
)
self._success(task.subtasks[0].uuid)
self.assertEqual(task.status, consts.TASK_STATUSES.ready)
@mock.patch('nailgun.transactions.manager.rpc')
def test_execute_with_node_filter(self, rpc_mock):
node = self.env.create_node(
cluster_id=self.cluster.id, pending_deletion=True,
roles=["compute"]
)
objects.DeploymentGraph.create_for_model(
{
'tasks': [
{
'id': 'delete_node',
'type': consts.ORCHESTRATOR_TASK_TYPES.puppet,
'roles': ['/.*/']
},
],
'name': 'deletion_graph',
'node_filter': '$.pending_deletion'
},
instance=self.cluster,
graph_type='deletion_graph',
)
task = self.manager.execute(graphs=[{"type": "deletion_graph"}])
self.assertNotEqual(consts.TASK_STATUSES.error, task.status)
rpc_mock.cast.assert_called_once_with(
'naily',
[{
'args': {
'tasks_metadata': self.expected_metadata,
'task_uuid': task.subtasks[0].uuid,
'tasks_graph': {
None: [],
node.uid: [
{
'id': 'delete_node',
'type': 'puppet',
'fail_on_error': True,
'parameters': {'cwd': '/'}
},
]
},
'tasks_directory': {},
'dry_run': False,
},
'respond_to': 'transaction_resp',
'method': 'task_deploy',
'api_version': '1'
}]
)
self._success(task.subtasks[0].uuid)
self.assertEqual(task.status, consts.TASK_STATUSES.ready)