Repository: ansible/ansible

Issue:
Keep track of roles applied across plays
We have a play of plays that we run against our entire infrastructure.  Each role has an implied dependency on the common role (it was created before role dependencies existed). As part of this play of plays, we run the common play first against all hosts with as many forks as possible. The play of plays then continues with a play for each role.

If we were to link the common role dependency to each role, ansible would run the common role again in each play.  If ansible remembered that the role had been applied previously throughout this play of plays, that would solve this problem.

An alternative is to not use role dependencies, but this means that we lose the ability to keep most handlers (among other things) in the common role and call them from the child roles.



Discussion:
@mpdehaan, @jimi-c, @tgerla - as discussed yesterday

Thanks!


Resolving patch:
diff --git a/lib/ansible/playbook/__init__.py b/lib/ansible/playbook/__init__.py
--- a/lib/ansible/playbook/__init__.py
+++ b/lib/ansible/playbook/__init__.py
@@ -240,13 +240,20 @@ def run(self):
         plays = []
         matched_tags_all = set()
         unmatched_tags_all = set()
+        included_roles = []
 
         # loop through all patterns and run them
         self.callbacks.on_start()
         for (play_ds, play_basedir) in zip(self.playbook, self.play_basedirs):
-            play = Play(self, play_ds, play_basedir, vault_password=self.vault_password)
+            play = Play(self, play_ds, play_basedir, included_roles=included_roles, vault_password=self.vault_password)
             assert play is not None
 
+            # add any new roles brought in by this play to the 
+            # global list of roles we're tracking
+            for role in play.included_roles:
+                if role not in included_roles:
+                    included_roles.append(role)
+
             matched_tags, unmatched_tags = play.compare_tags(self.only_tags)
             matched_tags_all = matched_tags_all | matched_tags
             unmatched_tags_all = unmatched_tags_all | unmatched_tags
diff --git a/lib/ansible/playbook/play.py b/lib/ansible/playbook/play.py
--- a/lib/ansible/playbook/play.py
+++ b/lib/ansible/playbook/play.py
@@ -49,7 +49,7 @@ class Play(object):
 
     # *************************************************
 
-    def __init__(self, playbook, ds, basedir, vault_password=None):
+    def __init__(self, playbook, ds, basedir, included_roles=[], vault_password=None):
         ''' constructor loads from a play datastructure '''
 
         for x in ds.keys():
@@ -81,7 +81,7 @@ def __init__(self, playbook, ds, basedir, vault_password=None):
         self._update_vars_files_for_host(None)
 
         # now we load the roles into the datastructure
-        self.included_roles = []
+        self.included_roles = included_roles
         ds = self._load_roles(self.roles, ds)
         
         # and finally re-process the vars files as they may have
@@ -227,6 +227,20 @@ def _build_role_dependencies(self, roles, dep_stack, passed_vars={}, level=0):
                             if meta_data:
                                 allow_dupes = utils.boolean(meta_data.get('allow_duplicates',''))
 
+                        # if any tags were specified as role/dep variables, merge
+                        # them into the passed_vars so they're passed on to any 
+                        # further dependencies too, and so we only have one place
+                        # (passed_vars) to look for tags going forward
+                        def __merge_tags(var_obj):
+                            old_tags = passed_vars.get('tags', [])
+                            new_tags = var_obj.get('tags', [])
+                            if isinstance(new_tags, basestring):
+                                new_tags = [new_tags, ]
+                            return list(set(old_tags + new_tags))
+
+                        passed_vars['tags'] = __merge_tags(role_vars)
+                        passed_vars['tags'] = __merge_tags(dep_vars)
+
                         # if tags are set from this role, merge them
                         # into the tags list for the dependent role
                         if "tags" in passed_vars:


Test patch: