From 9a6ae1d62a773285c23cc095f43d91ef86112f6e Mon Sep 17 00:00:00 2001
From: Christoph Dittmann <github@christoph-d.de>
Date: Wed, 2 Dec 2015 10:54:28 +0100
Subject: [PATCH 1/3] Let PlayIterator.add_tasks accept empty task lists

PlayIterator.add_tasks raised an error when trying to add an empty task
list.  This was the root cause of ansible issue #13370.
---
 lib/ansible/executor/play_iterator.py | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/lib/ansible/executor/play_iterator.py b/lib/ansible/executor/play_iterator.py
index fd59478ead9..23ba52edf93 100644
--- a/lib/ansible/executor/play_iterator.py
+++ b/lib/ansible/executor/play_iterator.py
@@ -399,6 +399,9 @@ class PlayIterator:
         if state.fail_state != self.FAILED_NONE:
             return state
 
+        if not task_list:
+            return state
+
         if state.run_state == self.ITERATING_TASKS:
             if state.tasks_child_state:
                 state.tasks_child_state = self._insert_tasks_into_state(state.tasks_child_state, task_list)

From c6e400fbeacdfc5f66a739ec2a2d61cdc9d00672 Mon Sep 17 00:00:00 2001
From: Christoph Dittmann <github@christoph-d.de>
Date: Wed, 2 Dec 2015 10:55:04 +0100
Subject: [PATCH 2/3] Fix issue #13370

all_blocks is referenced after the loop over included_files, so it needs
to be initialized before this loop, not inside.
---
 lib/ansible/plugins/strategy/free.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/ansible/plugins/strategy/free.py b/lib/ansible/plugins/strategy/free.py
index e83184891dc..c8e6053a183 100644
--- a/lib/ansible/plugins/strategy/free.py
+++ b/lib/ansible/plugins/strategy/free.py
@@ -145,6 +145,7 @@ class StrategyModule(StrategyBase):
                 return False
 
             if len(included_files) > 0:
+                all_blocks = dict((host, []) for host in hosts_left)
                 for included_file in included_files:
                     # included hosts get the task list while those excluded get an equal-length
                     # list of noop tasks, to make sure that they continue running in lock-step
@@ -157,7 +158,6 @@ class StrategyModule(StrategyBase):
                         continue
 
                     display.debug("generating all_blocks data")
-                    all_blocks = dict((host, []) for host in hosts_left)
                     display.debug("done generating all_blocks data")
                     for new_block in new_blocks:
                         task_vars = self._variable_manager.get_vars(loader=self._loader, play=iterator._play, task=included_file._task)

From 267199fdd5cc65a7e23397a3dec4c368e75c3366 Mon Sep 17 00:00:00 2001
From: Christoph Dittmann <github@christoph-d.de>
Date: Wed, 2 Dec 2015 10:56:10 +0100
Subject: [PATCH 3/3] Update debug messages and comments

The comment was taken literally from lib/plugins/strategy/linear.py and
makes no sense in free.py where we have no noop tasks.

Also update the debug messages.
---
 lib/ansible/plugins/strategy/free.py | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/lib/ansible/plugins/strategy/free.py b/lib/ansible/plugins/strategy/free.py
index c8e6053a183..11eeaa92494 100644
--- a/lib/ansible/plugins/strategy/free.py
+++ b/lib/ansible/plugins/strategy/free.py
@@ -147,8 +147,7 @@ class StrategyModule(StrategyBase):
             if len(included_files) > 0:
                 all_blocks = dict((host, []) for host in hosts_left)
                 for included_file in included_files:
-                    # included hosts get the task list while those excluded get an equal-length
-                    # list of noop tasks, to make sure that they continue running in lock-step
+                    display.debug("collecting new blocks for %s" % included_file)
                     try:
                         new_blocks = self._load_included_file(included_file, iterator=iterator)
                     except AnsibleError as e:
@@ -157,17 +156,18 @@ class StrategyModule(StrategyBase):
                         display.warning(str(e))
                         continue
 
-                    display.debug("generating all_blocks data")
-                    display.debug("done generating all_blocks data")
                     for new_block in new_blocks:
                         task_vars = self._variable_manager.get_vars(loader=self._loader, play=iterator._play, task=included_file._task)
                         final_block = new_block.filter_tagged_tasks(play_context, task_vars)
                         for host in hosts_left:
                             if host in included_file._hosts:
                                 all_blocks[host].append(final_block)
+                    display.debug("done collecting new blocks for %s" % included_file)
 
+                display.debug("adding all collected blocks from %d included file(s) to iterator" % len(included_files))
                 for host in hosts_left:
                     iterator.add_tasks(host, all_blocks[host])
+                display.debug("done adding collected blocks to iterator")
 
             # pause briefly so we don't spin lock
             time.sleep(0.05)