diff --git a/lib/ansible/plugins/strategy/linear.py b/lib/ansible/plugins/strategy/linear.py index 6c27a13787..203e019a11 100644 --- a/lib/ansible/plugins/strategy/linear.py +++ b/lib/ansible/plugins/strategy/linear.py @@ -113,7 +113,7 @@ class StrategyModule(StrategyBase): if host_tasks_to_run: try: lowest_cur_block = min( - (s.cur_block for h, (s, t) in host_tasks_to_run + (iterator.get_active_state(s).cur_block for h, (s, t) in host_tasks_to_run if s.run_state != PlayIterator.ITERATING_COMPLETE)) except ValueError: lowest_cur_block = None @@ -125,6 +125,7 @@ class StrategyModule(StrategyBase): for (k, v) in host_tasks_to_run: (s, t) = v + s = iterator.get_active_state(s) if s.cur_block > lowest_cur_block: # Not the current block, ignore it continue @@ -158,6 +159,7 @@ class StrategyModule(StrategyBase): if host_state_task is None: continue (s, t) = host_state_task + s = iterator.get_active_state(s) if t is None: continue if s.run_state == cur_state and s.cur_block == cur_block: diff --git a/test/units/plugins/strategy/test_strategy_linear.py b/test/units/plugins/strategy/test_strategy_linear.py new file mode 100644 index 0000000000..af15bc0d20 --- /dev/null +++ b/test/units/plugins/strategy/test_strategy_linear.py @@ -0,0 +1,183 @@ +# Copyright (c) 2018 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + + +from ansible.compat.tests import unittest +from ansible.compat.tests.mock import patch, MagicMock + +from ansible.executor.play_iterator import PlayIterator +from ansible.playbook import Playbook +from ansible.playbook.play_context import PlayContext +from ansible.plugins.strategy.linear import StrategyModule +from ansible.executor.task_queue_manager import TaskQueueManager + +from units.mock.loader import DictDataLoader +from units.mock.path import mock_unfrackpath_noop + + +class TestStrategyLinear(unittest.TestCase): + + def setUp(self): + pass + + def tearDown(self): + pass + + @patch('ansible.playbook.role.definition.unfrackpath', mock_unfrackpath_noop) + def test_noop(self): + fake_loader = DictDataLoader({ + "test_play.yml": """ + - hosts: all + gather_facts: no + tasks: + - block: + - block: + - name: task1 + debug: msg='task1' + failed_when: inventory_hostname == 'host01' + + - name: task2 + debug: msg='task2' + + rescue: + - name: rescue1 + debug: msg='rescue1' + + - name: rescue2 + debug: msg='rescue2' + """, + }) + + mock_var_manager = MagicMock() + mock_var_manager._fact_cache = dict() + mock_var_manager.get_vars.return_value = dict() + + p = Playbook.load('test_play.yml', loader=fake_loader, variable_manager=mock_var_manager) + + hosts = [] + for i in range(0, 2): + host = MagicMock() + host.name = host.get_name.return_value = 'host%02d' % i + hosts.append(host) + + mock_var_manager._fact_cache['host00'] = dict() + + inventory = MagicMock() + inventory.get_hosts.return_value = hosts + inventory.filter_hosts.return_value = hosts + + play_context = PlayContext(play=p._entries[0]) + + itr = PlayIterator( + inventory=inventory, + play=p._entries[0], + play_context=play_context, + variable_manager=mock_var_manager, + all_vars=dict(), + ) + + mock_options = MagicMock() + mock_options.module_path = None + + tqm = TaskQueueManager( + inventory=inventory, + variable_manager=mock_var_manager, + loader=fake_loader, + options=mock_options, + passwords=None, + ) + tqm._initialize_processes(3) + strategy = StrategyModule(tqm) + + # implicit meta: flush_handlers + hosts_left = strategy.get_hosts_left(itr) + hosts_tasks = strategy._get_next_task_lockstep(hosts_left, itr) + host1_task = hosts_tasks[0][1] + host2_task = hosts_tasks[1][1] + self.assertIsNotNone(host1_task) + self.assertIsNotNone(host2_task) + self.assertEqual(host1_task.action, 'meta') + self.assertEqual(host2_task.action, 'meta') + + # debug: task1, debug: task1 + hosts_left = strategy.get_hosts_left(itr) + hosts_tasks = strategy._get_next_task_lockstep(hosts_left, itr) + host1_task = hosts_tasks[0][1] + host2_task = hosts_tasks[1][1] + self.assertIsNotNone(host1_task) + self.assertIsNotNone(host2_task) + self.assertEqual(host1_task.action, 'debug') + self.assertEqual(host2_task.action, 'debug') + self.assertEqual(host1_task.name, 'task1') + self.assertEqual(host2_task.name, 'task1') + + # mark the second host failed + itr.mark_host_failed(hosts[1]) + + # debug: task2, meta: noop + hosts_left = strategy.get_hosts_left(itr) + hosts_tasks = strategy._get_next_task_lockstep(hosts_left, itr) + host1_task = hosts_tasks[0][1] + host2_task = hosts_tasks[1][1] + self.assertIsNotNone(host1_task) + self.assertIsNotNone(host2_task) + self.assertEqual(host1_task.action, 'debug') + self.assertEqual(host2_task.action, 'meta') + self.assertEqual(host1_task.name, 'task2') + self.assertEqual(host2_task.name, '') + + # meta: noop, debug: rescue1 + hosts_left = strategy.get_hosts_left(itr) + hosts_tasks = strategy._get_next_task_lockstep(hosts_left, itr) + host1_task = hosts_tasks[0][1] + host2_task = hosts_tasks[1][1] + self.assertIsNotNone(host1_task) + self.assertIsNotNone(host2_task) + self.assertEqual(host1_task.action, 'meta') + self.assertEqual(host2_task.action, 'debug') + self.assertEqual(host1_task.name, '') + self.assertEqual(host2_task.name, 'rescue1') + + # meta: noop, debug: rescue2 + hosts_left = strategy.get_hosts_left(itr) + hosts_tasks = strategy._get_next_task_lockstep(hosts_left, itr) + host1_task = hosts_tasks[0][1] + host2_task = hosts_tasks[1][1] + self.assertIsNotNone(host1_task) + self.assertIsNotNone(host2_task) + self.assertEqual(host1_task.action, 'meta') + self.assertEqual(host2_task.action, 'debug') + self.assertEqual(host1_task.name, '') + self.assertEqual(host2_task.name, 'rescue2') + + # implicit meta: flush_handlers + hosts_left = strategy.get_hosts_left(itr) + hosts_tasks = strategy._get_next_task_lockstep(hosts_left, itr) + host1_task = hosts_tasks[0][1] + host2_task = hosts_tasks[1][1] + self.assertIsNotNone(host1_task) + self.assertIsNotNone(host2_task) + self.assertEqual(host1_task.action, 'meta') + self.assertEqual(host2_task.action, 'meta') + + # implicit meta: flush_handlers + hosts_left = strategy.get_hosts_left(itr) + hosts_tasks = strategy._get_next_task_lockstep(hosts_left, itr) + host1_task = hosts_tasks[0][1] + host2_task = hosts_tasks[1][1] + self.assertIsNotNone(host1_task) + self.assertIsNotNone(host2_task) + self.assertEqual(host1_task.action, 'meta') + self.assertEqual(host2_task.action, 'meta') + + # end of iteration + hosts_left = strategy.get_hosts_left(itr) + hosts_tasks = strategy._get_next_task_lockstep(hosts_left, itr) + host1_task = hosts_tasks[0][1] + host2_task = hosts_tasks[1][1] + self.assertIsNone(host1_task) + self.assertIsNone(host2_task)