2014-01-04 18:31:44 +00:00
# (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com>
2012-05-26 04:37:34 +00:00
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#############################################
2013-04-25 01:59:47 +00:00
from ansible . utils . template import template
2012-05-26 04:37:34 +00:00
from ansible import utils
from ansible import errors
from ansible . playbook . task import Task
2013-09-27 17:03:28 +00:00
import ansible . constants as C
2013-07-20 21:25:46 +00:00
import pipes
2012-05-26 04:37:34 +00:00
import shlex
import os
2013-10-08 22:12:18 +00:00
import sys
2014-03-26 17:29:34 +00:00
import uuid
2012-05-26 04:37:34 +00:00
class Play ( object ) :
2012-08-07 00:07:02 +00:00
__slots__ = [
2013-08-29 22:21:28 +00:00
' hosts ' , ' name ' , ' vars ' , ' default_vars ' , ' vars_prompt ' , ' vars_files ' ,
2013-09-03 02:54:02 +00:00
' handlers ' , ' remote_user ' , ' remote_port ' , ' included_roles ' , ' accelerate ' ,
2013-11-04 22:20:03 +00:00
' accelerate_port ' , ' accelerate_ipv6 ' , ' sudo ' , ' sudo_user ' , ' transport ' , ' playbook ' ,
2012-09-10 02:43:27 +00:00
' tags ' , ' gather_facts ' , ' serial ' , ' _ds ' , ' _handlers ' , ' _tasks ' ,
2014-02-11 17:03:11 +00:00
' basedir ' , ' any_errors_fatal ' , ' roles ' , ' max_fail_pct ' , ' _play_hosts ' , ' su ' , ' su_user ' , ' vault_password '
2012-05-26 04:37:34 +00:00
]
2012-07-17 23:09:36 +00:00
# to catch typos and so forth -- these are userland names
# and don't line up 1:1 with how they are stored
VALID_KEYS = [
' hosts ' , ' name ' , ' vars ' , ' vars_prompt ' , ' vars_files ' ,
2013-11-04 22:20:03 +00:00
' tasks ' , ' handlers ' , ' remote_user ' , ' user ' , ' port ' , ' include ' , ' accelerate ' , ' accelerate_port ' , ' accelerate_ipv6 ' ,
2013-03-28 06:17:01 +00:00
' sudo ' , ' sudo_user ' , ' connection ' , ' tags ' , ' gather_facts ' , ' serial ' ,
2014-01-21 01:19:03 +00:00
' any_errors_fatal ' , ' roles ' , ' pre_tasks ' , ' post_tasks ' , ' max_fail_percentage ' ,
2014-02-11 17:03:11 +00:00
' su ' , ' su_user ' , ' vault_password '
2012-07-17 23:09:36 +00:00
]
2012-05-26 04:37:34 +00:00
# *************************************************
2014-03-07 03:11:57 +00:00
def __init__ ( self , playbook , ds , basedir , vault_password = None ) :
2012-05-26 05:20:53 +00:00
''' constructor loads from a play datastructure '''
2012-07-17 23:09:36 +00:00
for x in ds . keys ( ) :
2012-08-11 16:20:16 +00:00
if not x in Play . VALID_KEYS :
raise errors . AnsibleError ( " %s is not a legal parameter in an Ansible Playbook " % x )
2012-07-17 23:09:36 +00:00
2013-04-06 14:09:16 +00:00
# allow all playbook keys to be set by --extra-vars
self . vars = ds . get ( ' vars ' , { } )
self . vars_prompt = ds . get ( ' vars_prompt ' , { } )
self . playbook = playbook
self . vars = self . _get_vars ( )
2013-04-06 14:45:09 +00:00
self . basedir = basedir
2013-04-06 16:13:04 +00:00
self . roles = ds . get ( ' roles ' , None )
2013-04-27 16:29:32 +00:00
self . tags = ds . get ( ' tags ' , None )
2014-02-11 17:03:11 +00:00
self . vault_password = vault_password
2013-04-27 16:29:32 +00:00
if self . tags is None :
self . tags = [ ]
elif type ( self . tags ) in [ str , unicode ] :
self . tags = self . tags . split ( " , " )
elif type ( self . tags ) != list :
self . tags = [ ]
2013-04-06 16:13:04 +00:00
2013-08-30 05:32:20 +00:00
# We first load the vars files from the datastructure
# so we have the default variables to pass into the roles
2013-08-29 22:21:28 +00:00
self . vars_files = ds . get ( ' vars_files ' , [ ] )
2013-10-18 15:19:16 +00:00
if not isinstance ( self . vars_files , list ) :
raise errors . AnsibleError ( ' vars_files must be a list ' )
2013-08-30 05:32:20 +00:00
self . _update_vars_files_for_host ( None )
2013-04-06 16:13:04 +00:00
2013-08-30 05:32:20 +00:00
# now we load the roles into the datastructure
2014-03-07 03:11:57 +00:00
self . included_roles = [ ]
2013-08-30 05:32:20 +00:00
ds = self . _load_roles ( self . roles , ds )
# and finally re-process the vars files as they may have
# been updated by the included roles
self . vars_files = ds . get ( ' vars_files ' , [ ] )
2013-10-18 15:19:16 +00:00
if not isinstance ( self . vars_files , list ) :
raise errors . AnsibleError ( ' vars_files must be a list ' )
2014-02-11 17:03:11 +00:00
2013-04-06 14:45:09 +00:00
self . _update_vars_files_for_host ( None )
2013-04-06 16:13:04 +00:00
2014-03-31 20:31:10 +00:00
# apply any extra_vars specified on the command line now
if type ( self . playbook . extra_vars ) == dict :
self . vars = utils . combine_vars ( self . vars , self . playbook . extra_vars )
2013-04-13 01:47:34 +00:00
# template everything to be efficient, but do not pre-mature template
# tasks/handlers as they may have inventory scope overrides
_tasks = ds . pop ( ' tasks ' , [ ] )
_handlers = ds . pop ( ' handlers ' , [ ] )
2013-06-03 22:19:11 +00:00
ds = template ( basedir , ds , self . vars )
2013-04-13 01:47:34 +00:00
ds [ ' tasks ' ] = _tasks
ds [ ' handlers ' ] = _handlers
2013-04-08 07:13:23 +00:00
2013-06-03 22:19:11 +00:00
self . _ds = ds
2012-07-11 23:51:26 +00:00
2012-05-31 20:06:21 +00:00
hosts = ds . get ( ' hosts ' )
if hosts is None :
raise errors . AnsibleError ( ' hosts declaration is required ' )
elif isinstance ( hosts , list ) :
hosts = ' ; ' . join ( hosts )
2013-04-06 14:09:16 +00:00
self . serial = int ( ds . get ( ' serial ' , 0 ) )
self . hosts = hosts
self . name = ds . get ( ' name ' , self . hosts )
self . _tasks = ds . get ( ' tasks ' , [ ] )
self . _handlers = ds . get ( ' handlers ' , [ ] )
2013-09-07 20:34:22 +00:00
self . remote_user = ds . get ( ' remote_user ' , ds . get ( ' user ' , self . playbook . remote_user ) )
2013-04-06 14:09:16 +00:00
self . remote_port = ds . get ( ' port ' , self . playbook . remote_port )
self . sudo = ds . get ( ' sudo ' , self . playbook . sudo )
self . sudo_user = ds . get ( ' sudo_user ' , self . playbook . sudo_user )
self . transport = ds . get ( ' connection ' , self . playbook . transport )
self . remote_port = self . remote_port
2013-09-09 15:20:41 +00:00
self . any_errors_fatal = utils . boolean ( ds . get ( ' any_errors_fatal ' , ' false ' ) )
2013-09-03 19:20:56 +00:00
self . accelerate = utils . boolean ( ds . get ( ' accelerate ' , ' false ' ) )
2013-08-27 20:25:54 +00:00
self . accelerate_port = ds . get ( ' accelerate_port ' , None )
2013-11-04 22:20:03 +00:00
self . accelerate_ipv6 = ds . get ( ' accelerate_ipv6 ' , False )
2013-07-26 18:05:00 +00:00
self . max_fail_pct = int ( ds . get ( ' max_fail_percentage ' , 100 ) )
2014-01-21 01:19:03 +00:00
self . su = ds . get ( ' su ' , self . playbook . su )
self . su_user = ds . get ( ' su_user ' , self . playbook . su_user )
2012-07-14 17:23:14 +00:00
2014-03-26 17:01:15 +00:00
# gather_facts is not a simple boolean, as None means that a 'smart'
# fact gathering mode will be used, so we need to be careful here as
# calling utils.boolean(None) returns False
self . gather_facts = ds . get ( ' gather_facts ' , None )
if self . gather_facts :
self . gather_facts = utils . boolean ( self . gather_facts )
2014-01-21 20:20:33 +00:00
# Fail out if user specifies a sudo param with a su param in a given play
if ( ds . get ( ' sudo ' ) or ds . get ( ' sudo_user ' ) ) and ( ds . get ( ' su ' ) or ds . get ( ' su_user ' ) ) :
raise errors . AnsibleError ( ' sudo params ( " sudo " , " sudo_user " ) and su params '
' ( " su " , " su_user " ) cannot be used together ' )
2013-03-26 02:32:01 +00:00
load_vars = { }
2014-03-12 02:13:29 +00:00
load_vars [ ' role_names ' ] = ds . get ( ' role_names ' , [ ] )
2013-10-25 05:53:52 +00:00
load_vars [ ' playbook_dir ' ] = self . basedir
2013-03-26 02:32:01 +00:00
if self . playbook . inventory . basedir ( ) is not None :
2013-04-10 20:37:49 +00:00
load_vars [ ' inventory_dir ' ] = self . playbook . inventory . basedir ( )
2013-04-06 16:13:04 +00:00
2013-03-26 02:32:01 +00:00
self . _tasks = self . _load_tasks ( self . _ds . get ( ' tasks ' , [ ] ) , load_vars )
self . _handlers = self . _load_tasks ( self . _ds . get ( ' handlers ' , [ ] ) , load_vars )
2012-05-26 04:37:34 +00:00
2014-03-20 15:55:46 +00:00
# apply any missing tags to role tasks
self . _late_merge_role_tags ( )
2012-07-12 04:58:48 +00:00
2012-05-26 04:37:34 +00:00
if self . sudo_user != ' root ' :
self . sudo = True
2013-06-03 22:19:11 +00:00
2014-01-07 02:16:43 +00:00
# place holder for the discovered hosts to be used in this play
self . _play_hosts = None
2012-05-26 04:37:34 +00:00
# *************************************************
Adding support for role dependencies.
Fixes #3686
Dependencies are enabled by adding a new directory/file named
meta/main.yml to the role. The format of the dependencies are:
dependencies:
- { role: foo, x: 1, y: 2 }
- { role: bar, x: 3, y: 4 }
...
Dependencies inherit variables as they are seen at the time of the
dependency inclusion. For example, if foo(x=1, y=2) has a dependency
on bar(x=3,z=4), then bar will have variables (x=3,y=2,z=4).
Different roles can have dependencies on the same role, and this
variable inheritence allows for the reuse of generic roles quite easily.
For example:
Role 'car' has the following dependencies:
dependencies:
- { role: wheel, n: 1 }
- { role: wheel, n: 2 }
- { role: wheel, n: 3 }
- { role: wheel, n: 4 }
Role 'wheel' has the following dependencies:
dependencies:
- { role: tire }
- { role: brake }
The role 'car' is then used as follows:
- { role: car, type: honda }
And tasks/main.yml in each role simply contains the following:
- name: {{ type }} whatever {{ n }}
command: echo ''
TASK: [honda tire 1]
TASK: [honda brake 1]
TASK: [honda wheel 1]
TASK: [honda tire 2]
TASK: [honda brake 2]
TASK: [honda wheel 2]
TASK: [honda tire 3]
TASK: [honda brake 3]
TASK: [honda wheel 3]
TASK: [honda tire 4]
TASK: [honda brake 4]
TASK: [honda wheel 4]
TASK: [I'm a honda] <- (this is in roles/car/tasks/main.yml)
2013-08-15 05:29:22 +00:00
def _get_role_path ( self , role ) :
"""
Returns the path on disk to the directory containing
the role directories like tasks , templates , etc . Also
returns any variables that were included with the role
"""
orig_path = template ( self . basedir , role , self . vars )
role_vars = { }
if type ( orig_path ) == dict :
# what, not a path?
role_name = orig_path . get ( ' role ' , None )
if role_name is None :
raise errors . AnsibleError ( " expected a role name in dictionary: %s " % orig_path )
role_vars = orig_path
orig_path = role_name
2013-09-27 17:03:28 +00:00
role_path = None
Adding support for role dependencies.
Fixes #3686
Dependencies are enabled by adding a new directory/file named
meta/main.yml to the role. The format of the dependencies are:
dependencies:
- { role: foo, x: 1, y: 2 }
- { role: bar, x: 3, y: 4 }
...
Dependencies inherit variables as they are seen at the time of the
dependency inclusion. For example, if foo(x=1, y=2) has a dependency
on bar(x=3,z=4), then bar will have variables (x=3,y=2,z=4).
Different roles can have dependencies on the same role, and this
variable inheritence allows for the reuse of generic roles quite easily.
For example:
Role 'car' has the following dependencies:
dependencies:
- { role: wheel, n: 1 }
- { role: wheel, n: 2 }
- { role: wheel, n: 3 }
- { role: wheel, n: 4 }
Role 'wheel' has the following dependencies:
dependencies:
- { role: tire }
- { role: brake }
The role 'car' is then used as follows:
- { role: car, type: honda }
And tasks/main.yml in each role simply contains the following:
- name: {{ type }} whatever {{ n }}
command: echo ''
TASK: [honda tire 1]
TASK: [honda brake 1]
TASK: [honda wheel 1]
TASK: [honda tire 2]
TASK: [honda brake 2]
TASK: [honda wheel 2]
TASK: [honda tire 3]
TASK: [honda brake 3]
TASK: [honda wheel 3]
TASK: [honda tire 4]
TASK: [honda brake 4]
TASK: [honda wheel 4]
TASK: [I'm a honda] <- (this is in roles/car/tasks/main.yml)
2013-08-15 05:29:22 +00:00
2013-09-27 17:03:28 +00:00
possible_paths = [
utils . path_dwim ( self . basedir , os . path . join ( ' roles ' , orig_path ) ) ,
utils . path_dwim ( self . basedir , orig_path )
]
if C . DEFAULT_ROLES_PATH :
search_locations = C . DEFAULT_ROLES_PATH . split ( os . pathsep )
for loc in search_locations :
2013-12-19 04:31:03 +00:00
loc = os . path . expanduser ( loc )
2013-09-27 17:03:28 +00:00
possible_paths . append ( utils . path_dwim ( loc , orig_path ) )
for path_option in possible_paths :
if os . path . isdir ( path_option ) :
role_path = path_option
break
if role_path is None :
raise errors . AnsibleError ( " cannot find role in %s " % " or " . join ( possible_paths ) )
return ( role_path , role_vars )
Adding support for role dependencies.
Fixes #3686
Dependencies are enabled by adding a new directory/file named
meta/main.yml to the role. The format of the dependencies are:
dependencies:
- { role: foo, x: 1, y: 2 }
- { role: bar, x: 3, y: 4 }
...
Dependencies inherit variables as they are seen at the time of the
dependency inclusion. For example, if foo(x=1, y=2) has a dependency
on bar(x=3,z=4), then bar will have variables (x=3,y=2,z=4).
Different roles can have dependencies on the same role, and this
variable inheritence allows for the reuse of generic roles quite easily.
For example:
Role 'car' has the following dependencies:
dependencies:
- { role: wheel, n: 1 }
- { role: wheel, n: 2 }
- { role: wheel, n: 3 }
- { role: wheel, n: 4 }
Role 'wheel' has the following dependencies:
dependencies:
- { role: tire }
- { role: brake }
The role 'car' is then used as follows:
- { role: car, type: honda }
And tasks/main.yml in each role simply contains the following:
- name: {{ type }} whatever {{ n }}
command: echo ''
TASK: [honda tire 1]
TASK: [honda brake 1]
TASK: [honda wheel 1]
TASK: [honda tire 2]
TASK: [honda brake 2]
TASK: [honda wheel 2]
TASK: [honda tire 3]
TASK: [honda brake 3]
TASK: [honda wheel 3]
TASK: [honda tire 4]
TASK: [honda brake 4]
TASK: [honda wheel 4]
TASK: [I'm a honda] <- (this is in roles/car/tasks/main.yml)
2013-08-15 05:29:22 +00:00
2013-08-16 01:53:23 +00:00
def _build_role_dependencies ( self , roles , dep_stack , passed_vars = { } , level = 0 ) :
Adding support for role dependencies.
Fixes #3686
Dependencies are enabled by adding a new directory/file named
meta/main.yml to the role. The format of the dependencies are:
dependencies:
- { role: foo, x: 1, y: 2 }
- { role: bar, x: 3, y: 4 }
...
Dependencies inherit variables as they are seen at the time of the
dependency inclusion. For example, if foo(x=1, y=2) has a dependency
on bar(x=3,z=4), then bar will have variables (x=3,y=2,z=4).
Different roles can have dependencies on the same role, and this
variable inheritence allows for the reuse of generic roles quite easily.
For example:
Role 'car' has the following dependencies:
dependencies:
- { role: wheel, n: 1 }
- { role: wheel, n: 2 }
- { role: wheel, n: 3 }
- { role: wheel, n: 4 }
Role 'wheel' has the following dependencies:
dependencies:
- { role: tire }
- { role: brake }
The role 'car' is then used as follows:
- { role: car, type: honda }
And tasks/main.yml in each role simply contains the following:
- name: {{ type }} whatever {{ n }}
command: echo ''
TASK: [honda tire 1]
TASK: [honda brake 1]
TASK: [honda wheel 1]
TASK: [honda tire 2]
TASK: [honda brake 2]
TASK: [honda wheel 2]
TASK: [honda tire 3]
TASK: [honda brake 3]
TASK: [honda wheel 3]
TASK: [honda tire 4]
TASK: [honda brake 4]
TASK: [honda wheel 4]
TASK: [I'm a honda] <- (this is in roles/car/tasks/main.yml)
2013-08-15 05:29:22 +00:00
# this number is arbitrary, but it seems sane
if level > 20 :
raise errors . AnsibleError ( " too many levels of recursion while resolving role dependencies " )
for role in roles :
2013-08-15 05:50:08 +00:00
role_path , role_vars = self . _get_role_path ( role )
2013-09-09 20:14:03 +00:00
role_vars = utils . combine_vars ( passed_vars , role_vars )
2013-08-30 05:32:20 +00:00
vars = self . _resolve_main ( utils . path_dwim ( self . basedir , os . path . join ( role_path , ' vars ' ) ) )
vars_data = { }
if os . path . isfile ( vars ) :
2014-02-19 16:01:21 +00:00
vars_data = utils . parse_yaml_from_file ( vars , vault_password = self . vault_password )
2013-08-30 05:32:20 +00:00
if vars_data :
2013-12-12 08:23:51 +00:00
if not isinstance ( vars_data , dict ) :
raise errors . AnsibleError ( " vars from ' %s ' are not a dict " % vars )
2013-08-30 05:32:20 +00:00
role_vars = utils . combine_vars ( vars_data , role_vars )
2013-09-01 15:52:42 +00:00
defaults = self . _resolve_main ( utils . path_dwim ( self . basedir , os . path . join ( role_path , ' defaults ' ) ) )
2013-09-03 02:35:47 +00:00
defaults_data = { }
2013-09-01 15:52:42 +00:00
if os . path . isfile ( defaults ) :
2014-02-19 16:01:21 +00:00
defaults_data = utils . parse_yaml_from_file ( defaults , vault_password = self . vault_password )
Adding support for role dependencies.
Fixes #3686
Dependencies are enabled by adding a new directory/file named
meta/main.yml to the role. The format of the dependencies are:
dependencies:
- { role: foo, x: 1, y: 2 }
- { role: bar, x: 3, y: 4 }
...
Dependencies inherit variables as they are seen at the time of the
dependency inclusion. For example, if foo(x=1, y=2) has a dependency
on bar(x=3,z=4), then bar will have variables (x=3,y=2,z=4).
Different roles can have dependencies on the same role, and this
variable inheritence allows for the reuse of generic roles quite easily.
For example:
Role 'car' has the following dependencies:
dependencies:
- { role: wheel, n: 1 }
- { role: wheel, n: 2 }
- { role: wheel, n: 3 }
- { role: wheel, n: 4 }
Role 'wheel' has the following dependencies:
dependencies:
- { role: tire }
- { role: brake }
The role 'car' is then used as follows:
- { role: car, type: honda }
And tasks/main.yml in each role simply contains the following:
- name: {{ type }} whatever {{ n }}
command: echo ''
TASK: [honda tire 1]
TASK: [honda brake 1]
TASK: [honda wheel 1]
TASK: [honda tire 2]
TASK: [honda brake 2]
TASK: [honda wheel 2]
TASK: [honda tire 3]
TASK: [honda brake 3]
TASK: [honda wheel 3]
TASK: [honda tire 4]
TASK: [honda brake 4]
TASK: [honda wheel 4]
TASK: [I'm a honda] <- (this is in roles/car/tasks/main.yml)
2013-08-15 05:29:22 +00:00
# the meta directory contains the yaml that should
# hold the list of dependencies (if any)
2013-08-15 05:50:08 +00:00
meta = self . _resolve_main ( utils . path_dwim ( self . basedir , os . path . join ( role_path , ' meta ' ) ) )
Adding support for role dependencies.
Fixes #3686
Dependencies are enabled by adding a new directory/file named
meta/main.yml to the role. The format of the dependencies are:
dependencies:
- { role: foo, x: 1, y: 2 }
- { role: bar, x: 3, y: 4 }
...
Dependencies inherit variables as they are seen at the time of the
dependency inclusion. For example, if foo(x=1, y=2) has a dependency
on bar(x=3,z=4), then bar will have variables (x=3,y=2,z=4).
Different roles can have dependencies on the same role, and this
variable inheritence allows for the reuse of generic roles quite easily.
For example:
Role 'car' has the following dependencies:
dependencies:
- { role: wheel, n: 1 }
- { role: wheel, n: 2 }
- { role: wheel, n: 3 }
- { role: wheel, n: 4 }
Role 'wheel' has the following dependencies:
dependencies:
- { role: tire }
- { role: brake }
The role 'car' is then used as follows:
- { role: car, type: honda }
And tasks/main.yml in each role simply contains the following:
- name: {{ type }} whatever {{ n }}
command: echo ''
TASK: [honda tire 1]
TASK: [honda brake 1]
TASK: [honda wheel 1]
TASK: [honda tire 2]
TASK: [honda brake 2]
TASK: [honda wheel 2]
TASK: [honda tire 3]
TASK: [honda brake 3]
TASK: [honda wheel 3]
TASK: [honda tire 4]
TASK: [honda brake 4]
TASK: [honda wheel 4]
TASK: [I'm a honda] <- (this is in roles/car/tasks/main.yml)
2013-08-15 05:29:22 +00:00
if os . path . isfile ( meta ) :
2014-02-19 16:01:21 +00:00
data = utils . parse_yaml_from_file ( meta , vault_password = self . vault_password )
Adding support for role dependencies.
Fixes #3686
Dependencies are enabled by adding a new directory/file named
meta/main.yml to the role. The format of the dependencies are:
dependencies:
- { role: foo, x: 1, y: 2 }
- { role: bar, x: 3, y: 4 }
...
Dependencies inherit variables as they are seen at the time of the
dependency inclusion. For example, if foo(x=1, y=2) has a dependency
on bar(x=3,z=4), then bar will have variables (x=3,y=2,z=4).
Different roles can have dependencies on the same role, and this
variable inheritence allows for the reuse of generic roles quite easily.
For example:
Role 'car' has the following dependencies:
dependencies:
- { role: wheel, n: 1 }
- { role: wheel, n: 2 }
- { role: wheel, n: 3 }
- { role: wheel, n: 4 }
Role 'wheel' has the following dependencies:
dependencies:
- { role: tire }
- { role: brake }
The role 'car' is then used as follows:
- { role: car, type: honda }
And tasks/main.yml in each role simply contains the following:
- name: {{ type }} whatever {{ n }}
command: echo ''
TASK: [honda tire 1]
TASK: [honda brake 1]
TASK: [honda wheel 1]
TASK: [honda tire 2]
TASK: [honda brake 2]
TASK: [honda wheel 2]
TASK: [honda tire 3]
TASK: [honda brake 3]
TASK: [honda wheel 3]
TASK: [honda tire 4]
TASK: [honda brake 4]
TASK: [honda wheel 4]
TASK: [I'm a honda] <- (this is in roles/car/tasks/main.yml)
2013-08-15 05:29:22 +00:00
if data :
dependencies = data . get ( ' dependencies ' , [ ] )
2013-12-19 19:19:14 +00:00
if dependencies is None :
dependencies = [ ]
Adding support for role dependencies.
Fixes #3686
Dependencies are enabled by adding a new directory/file named
meta/main.yml to the role. The format of the dependencies are:
dependencies:
- { role: foo, x: 1, y: 2 }
- { role: bar, x: 3, y: 4 }
...
Dependencies inherit variables as they are seen at the time of the
dependency inclusion. For example, if foo(x=1, y=2) has a dependency
on bar(x=3,z=4), then bar will have variables (x=3,y=2,z=4).
Different roles can have dependencies on the same role, and this
variable inheritence allows for the reuse of generic roles quite easily.
For example:
Role 'car' has the following dependencies:
dependencies:
- { role: wheel, n: 1 }
- { role: wheel, n: 2 }
- { role: wheel, n: 3 }
- { role: wheel, n: 4 }
Role 'wheel' has the following dependencies:
dependencies:
- { role: tire }
- { role: brake }
The role 'car' is then used as follows:
- { role: car, type: honda }
And tasks/main.yml in each role simply contains the following:
- name: {{ type }} whatever {{ n }}
command: echo ''
TASK: [honda tire 1]
TASK: [honda brake 1]
TASK: [honda wheel 1]
TASK: [honda tire 2]
TASK: [honda brake 2]
TASK: [honda wheel 2]
TASK: [honda tire 3]
TASK: [honda brake 3]
TASK: [honda wheel 3]
TASK: [honda tire 4]
TASK: [honda brake 4]
TASK: [honda wheel 4]
TASK: [I'm a honda] <- (this is in roles/car/tasks/main.yml)
2013-08-15 05:29:22 +00:00
for dep in dependencies :
2013-09-04 16:21:49 +00:00
allow_dupes = False
Adding support for role dependencies.
Fixes #3686
Dependencies are enabled by adding a new directory/file named
meta/main.yml to the role. The format of the dependencies are:
dependencies:
- { role: foo, x: 1, y: 2 }
- { role: bar, x: 3, y: 4 }
...
Dependencies inherit variables as they are seen at the time of the
dependency inclusion. For example, if foo(x=1, y=2) has a dependency
on bar(x=3,z=4), then bar will have variables (x=3,y=2,z=4).
Different roles can have dependencies on the same role, and this
variable inheritence allows for the reuse of generic roles quite easily.
For example:
Role 'car' has the following dependencies:
dependencies:
- { role: wheel, n: 1 }
- { role: wheel, n: 2 }
- { role: wheel, n: 3 }
- { role: wheel, n: 4 }
Role 'wheel' has the following dependencies:
dependencies:
- { role: tire }
- { role: brake }
The role 'car' is then used as follows:
- { role: car, type: honda }
And tasks/main.yml in each role simply contains the following:
- name: {{ type }} whatever {{ n }}
command: echo ''
TASK: [honda tire 1]
TASK: [honda brake 1]
TASK: [honda wheel 1]
TASK: [honda tire 2]
TASK: [honda brake 2]
TASK: [honda wheel 2]
TASK: [honda tire 3]
TASK: [honda brake 3]
TASK: [honda wheel 3]
TASK: [honda tire 4]
TASK: [honda brake 4]
TASK: [honda wheel 4]
TASK: [I'm a honda] <- (this is in roles/car/tasks/main.yml)
2013-08-15 05:29:22 +00:00
( dep_path , dep_vars ) = self . _get_role_path ( dep )
2013-08-30 16:35:35 +00:00
meta = self . _resolve_main ( utils . path_dwim ( self . basedir , os . path . join ( dep_path , ' meta ' ) ) )
if os . path . isfile ( meta ) :
2014-02-19 16:01:21 +00:00
meta_data = utils . parse_yaml_from_file ( meta , vault_password = self . vault_password )
2013-08-30 16:35:35 +00:00
if meta_data :
allow_dupes = utils . boolean ( meta_data . get ( ' allow_duplicates ' , ' ' ) )
2013-09-04 14:28:51 +00:00
2014-03-05 20:25:24 +00:00
# if any tags were specified as role/dep variables, merge
2014-03-18 04:00:50 +00:00
# them into the current dep_vars so they're passed on to any
2014-03-05 20:25:24 +00:00
# further dependencies too, and so we only have one place
2014-03-18 04:00:50 +00:00
# (dep_vars) to look for tags going forward
2014-03-05 20:25:24 +00:00
def __merge_tags ( var_obj ) :
2014-03-18 04:00:50 +00:00
old_tags = dep_vars . get ( ' tags ' , [ ] )
2014-03-18 17:30:54 +00:00
if isinstance ( old_tags , basestring ) :
old_tags = [ old_tags , ]
2014-03-17 16:28:01 +00:00
if isinstance ( var_obj , dict ) :
new_tags = var_obj . get ( ' tags ' , [ ] )
if isinstance ( new_tags , basestring ) :
new_tags = [ new_tags , ]
else :
new_tags = [ ]
2014-03-06 13:43:24 +00:00
return list ( set ( old_tags ) . union ( set ( new_tags ) ) )
2014-03-05 20:25:24 +00:00
2014-03-18 04:00:50 +00:00
dep_vars [ ' tags ' ] = __merge_tags ( role_vars )
dep_vars [ ' tags ' ] = __merge_tags ( passed_vars )
2014-03-05 20:25:24 +00:00
2013-10-08 20:12:36 +00:00
# if tags are set from this role, merge them
# into the tags list for the dependent role
if " tags " in passed_vars :
for included_role_dep in dep_stack :
included_dep_name = included_role_dep [ 0 ]
included_dep_vars = included_role_dep [ 2 ]
if included_dep_name == dep :
if " tags " in included_dep_vars :
2014-03-06 13:28:36 +00:00
included_dep_vars [ " tags " ] = list ( set ( included_dep_vars [ " tags " ] ) . union ( set ( passed_vars [ " tags " ] ) ) )
2013-10-08 20:12:36 +00:00
else :
2014-03-03 12:47:57 +00:00
included_dep_vars [ " tags " ] = passed_vars [ " tags " ] [ : ]
2013-09-04 14:28:51 +00:00
2013-08-30 05:32:20 +00:00
dep_vars = utils . combine_vars ( passed_vars , dep_vars )
dep_vars = utils . combine_vars ( role_vars , dep_vars )
2013-08-16 01:53:23 +00:00
vars = self . _resolve_main ( utils . path_dwim ( self . basedir , os . path . join ( dep_path , ' vars ' ) ) )
vars_data = { }
if os . path . isfile ( vars ) :
2014-02-19 16:01:21 +00:00
vars_data = utils . parse_yaml_from_file ( vars , vault_password = self . vault_password )
2013-08-30 05:32:20 +00:00
if vars_data :
dep_vars = utils . combine_vars ( vars_data , dep_vars )
2013-09-01 15:52:42 +00:00
defaults = self . _resolve_main ( utils . path_dwim ( self . basedir , os . path . join ( dep_path , ' defaults ' ) ) )
2013-09-03 02:35:47 +00:00
dep_defaults_data = { }
2013-09-01 15:52:42 +00:00
if os . path . isfile ( defaults ) :
2014-02-19 16:01:21 +00:00
dep_defaults_data = utils . parse_yaml_from_file ( defaults , vault_password = self . vault_password )
Adding support for role dependencies.
Fixes #3686
Dependencies are enabled by adding a new directory/file named
meta/main.yml to the role. The format of the dependencies are:
dependencies:
- { role: foo, x: 1, y: 2 }
- { role: bar, x: 3, y: 4 }
...
Dependencies inherit variables as they are seen at the time of the
dependency inclusion. For example, if foo(x=1, y=2) has a dependency
on bar(x=3,z=4), then bar will have variables (x=3,y=2,z=4).
Different roles can have dependencies on the same role, and this
variable inheritence allows for the reuse of generic roles quite easily.
For example:
Role 'car' has the following dependencies:
dependencies:
- { role: wheel, n: 1 }
- { role: wheel, n: 2 }
- { role: wheel, n: 3 }
- { role: wheel, n: 4 }
Role 'wheel' has the following dependencies:
dependencies:
- { role: tire }
- { role: brake }
The role 'car' is then used as follows:
- { role: car, type: honda }
And tasks/main.yml in each role simply contains the following:
- name: {{ type }} whatever {{ n }}
command: echo ''
TASK: [honda tire 1]
TASK: [honda brake 1]
TASK: [honda wheel 1]
TASK: [honda tire 2]
TASK: [honda brake 2]
TASK: [honda wheel 2]
TASK: [honda tire 3]
TASK: [honda brake 3]
TASK: [honda wheel 3]
TASK: [honda tire 4]
TASK: [honda brake 4]
TASK: [honda wheel 4]
TASK: [I'm a honda] <- (this is in roles/car/tasks/main.yml)
2013-08-15 05:29:22 +00:00
if ' role ' in dep_vars :
del dep_vars [ ' role ' ]
2013-10-08 20:12:36 +00:00
if not allow_dupes :
if dep in self . included_roles :
# skip back to the top, since we don't want to
# do anything else with this role
continue
else :
self . included_roles . append ( dep )
2014-05-14 06:45:35 +00:00
def _merge_conditional ( cur_conditionals , new_conditionals ) :
if isinstance ( new_conditionals , ( basestring , bool ) ) :
cur_conditionals . append ( new_conditionals )
elif isinstance ( new_conditionals , list ) :
cur_conditionals . extend ( new_conditionals )
2013-11-05 19:38:21 +00:00
# pass along conditionals from roles to dep roles
2014-05-14 06:45:35 +00:00
passed_when = passed_vars . get ( ' when ' )
role_when = role_vars . get ( ' when ' )
dep_when = dep_vars . get ( ' when ' )
tmpcond = [ ]
_merge_conditional ( tmpcond , passed_when )
_merge_conditional ( tmpcond , role_when )
_merge_conditional ( tmpcond , dep_when )
if len ( tmpcond ) > 0 :
dep_vars [ ' when ' ] = tmpcond
2013-11-05 19:38:21 +00:00
2013-08-16 01:53:23 +00:00
self . _build_role_dependencies ( [ dep ] , dep_stack , passed_vars = dep_vars , level = level + 1 )
2013-09-03 02:35:47 +00:00
dep_stack . append ( [ dep , dep_path , dep_vars , dep_defaults_data ] )
2013-10-08 20:12:36 +00:00
2013-08-17 05:06:24 +00:00
# only add the current role when we're at the top level,
# otherwise we'll end up in a recursive loop
if level == 0 :
2013-09-04 14:28:51 +00:00
self . included_roles . append ( role )
2013-09-03 02:35:47 +00:00
dep_stack . append ( [ role , role_path , role_vars , defaults_data ] )
Adding support for role dependencies.
Fixes #3686
Dependencies are enabled by adding a new directory/file named
meta/main.yml to the role. The format of the dependencies are:
dependencies:
- { role: foo, x: 1, y: 2 }
- { role: bar, x: 3, y: 4 }
...
Dependencies inherit variables as they are seen at the time of the
dependency inclusion. For example, if foo(x=1, y=2) has a dependency
on bar(x=3,z=4), then bar will have variables (x=3,y=2,z=4).
Different roles can have dependencies on the same role, and this
variable inheritence allows for the reuse of generic roles quite easily.
For example:
Role 'car' has the following dependencies:
dependencies:
- { role: wheel, n: 1 }
- { role: wheel, n: 2 }
- { role: wheel, n: 3 }
- { role: wheel, n: 4 }
Role 'wheel' has the following dependencies:
dependencies:
- { role: tire }
- { role: brake }
The role 'car' is then used as follows:
- { role: car, type: honda }
And tasks/main.yml in each role simply contains the following:
- name: {{ type }} whatever {{ n }}
command: echo ''
TASK: [honda tire 1]
TASK: [honda brake 1]
TASK: [honda wheel 1]
TASK: [honda tire 2]
TASK: [honda brake 2]
TASK: [honda wheel 2]
TASK: [honda tire 3]
TASK: [honda brake 3]
TASK: [honda wheel 3]
TASK: [honda tire 4]
TASK: [honda brake 4]
TASK: [honda wheel 4]
TASK: [I'm a honda] <- (this is in roles/car/tasks/main.yml)
2013-08-15 05:29:22 +00:00
return dep_stack
2013-04-06 16:13:04 +00:00
2013-08-20 09:11:39 +00:00
def _load_role_defaults ( self , defaults_files ) :
# process default variables
default_vars = { }
for filename in defaults_files :
if os . path . exists ( filename ) :
2014-02-19 16:01:21 +00:00
new_default_vars = utils . parse_yaml_from_file ( filename , vault_password = self . vault_password )
2013-08-20 09:11:39 +00:00
if new_default_vars :
if type ( new_default_vars ) != dict :
2013-10-19 18:01:20 +00:00
raise errors . AnsibleError ( " %s must be stored as dictionary/hash: %s " % ( filename , type ( new_default_vars ) ) )
2013-08-20 09:11:39 +00:00
default_vars = utils . combine_vars ( default_vars , new_default_vars )
return default_vars
Adding support for role dependencies.
Fixes #3686
Dependencies are enabled by adding a new directory/file named
meta/main.yml to the role. The format of the dependencies are:
dependencies:
- { role: foo, x: 1, y: 2 }
- { role: bar, x: 3, y: 4 }
...
Dependencies inherit variables as they are seen at the time of the
dependency inclusion. For example, if foo(x=1, y=2) has a dependency
on bar(x=3,z=4), then bar will have variables (x=3,y=2,z=4).
Different roles can have dependencies on the same role, and this
variable inheritence allows for the reuse of generic roles quite easily.
For example:
Role 'car' has the following dependencies:
dependencies:
- { role: wheel, n: 1 }
- { role: wheel, n: 2 }
- { role: wheel, n: 3 }
- { role: wheel, n: 4 }
Role 'wheel' has the following dependencies:
dependencies:
- { role: tire }
- { role: brake }
The role 'car' is then used as follows:
- { role: car, type: honda }
And tasks/main.yml in each role simply contains the following:
- name: {{ type }} whatever {{ n }}
command: echo ''
TASK: [honda tire 1]
TASK: [honda brake 1]
TASK: [honda wheel 1]
TASK: [honda tire 2]
TASK: [honda brake 2]
TASK: [honda wheel 2]
TASK: [honda tire 3]
TASK: [honda brake 3]
TASK: [honda wheel 3]
TASK: [honda tire 4]
TASK: [honda brake 4]
TASK: [honda wheel 4]
TASK: [I'm a honda] <- (this is in roles/car/tasks/main.yml)
2013-08-15 05:29:22 +00:00
def _load_roles ( self , roles , ds ) :
2013-04-06 16:13:04 +00:00
# a role is a name that auto-includes the following if they exist
# <rolename>/tasks/main.yml
# <rolename>/handlers/main.yml
# <rolename>/vars/main.yml
2013-04-27 11:02:38 +00:00
# <rolename>/library
# and it auto-extends tasks/handlers/vars_files/module paths as appropriate if found
2013-04-06 16:13:04 +00:00
if roles is None :
2013-04-20 22:03:03 +00:00
roles = [ ]
2013-04-06 16:13:04 +00:00
if type ( roles ) != list :
raise errors . AnsibleError ( " value of ' roles: ' must be a list " )
new_tasks = [ ]
new_handlers = [ ]
new_vars_files = [ ]
2013-08-20 09:11:39 +00:00
defaults_files = [ ]
2013-04-06 16:51:17 +00:00
2013-04-20 20:19:01 +00:00
pre_tasks = ds . get ( ' pre_tasks ' , None )
if type ( pre_tasks ) != list :
pre_tasks = [ ]
for x in pre_tasks :
2013-04-20 20:07:06 +00:00
new_tasks . append ( x )
2013-04-25 02:28:06 +00:00
# flush handlers after pre_tasks
2013-04-20 22:03:03 +00:00
new_tasks . append ( dict ( meta = ' flush_handlers ' ) )
2013-04-20 20:07:06 +00:00
Adding support for role dependencies.
Fixes #3686
Dependencies are enabled by adding a new directory/file named
meta/main.yml to the role. The format of the dependencies are:
dependencies:
- { role: foo, x: 1, y: 2 }
- { role: bar, x: 3, y: 4 }
...
Dependencies inherit variables as they are seen at the time of the
dependency inclusion. For example, if foo(x=1, y=2) has a dependency
on bar(x=3,z=4), then bar will have variables (x=3,y=2,z=4).
Different roles can have dependencies on the same role, and this
variable inheritence allows for the reuse of generic roles quite easily.
For example:
Role 'car' has the following dependencies:
dependencies:
- { role: wheel, n: 1 }
- { role: wheel, n: 2 }
- { role: wheel, n: 3 }
- { role: wheel, n: 4 }
Role 'wheel' has the following dependencies:
dependencies:
- { role: tire }
- { role: brake }
The role 'car' is then used as follows:
- { role: car, type: honda }
And tasks/main.yml in each role simply contains the following:
- name: {{ type }} whatever {{ n }}
command: echo ''
TASK: [honda tire 1]
TASK: [honda brake 1]
TASK: [honda wheel 1]
TASK: [honda tire 2]
TASK: [honda brake 2]
TASK: [honda wheel 2]
TASK: [honda tire 3]
TASK: [honda brake 3]
TASK: [honda wheel 3]
TASK: [honda tire 4]
TASK: [honda brake 4]
TASK: [honda wheel 4]
TASK: [I'm a honda] <- (this is in roles/car/tasks/main.yml)
2013-08-15 05:29:22 +00:00
roles = self . _build_role_dependencies ( roles , [ ] , self . vars )
2014-03-26 17:29:34 +00:00
# give each role a uuid
for idx , val in enumerate ( roles ) :
this_uuid = str ( uuid . uuid4 ( ) )
roles [ idx ] [ - 2 ] [ ' role_uuid ' ] = this_uuid
2014-03-12 02:13:29 +00:00
role_names = [ ]
2013-04-06 16:51:17 +00:00
2013-09-03 02:35:47 +00:00
for ( role , role_path , role_vars , default_vars ) in roles :
2013-07-18 22:32:56 +00:00
# special vars must be extracted from the dict to the included tasks
special_keys = [ " sudo " , " sudo_user " , " when " , " with_items " ]
special_vars = { }
for k in special_keys :
Adding support for role dependencies.
Fixes #3686
Dependencies are enabled by adding a new directory/file named
meta/main.yml to the role. The format of the dependencies are:
dependencies:
- { role: foo, x: 1, y: 2 }
- { role: bar, x: 3, y: 4 }
...
Dependencies inherit variables as they are seen at the time of the
dependency inclusion. For example, if foo(x=1, y=2) has a dependency
on bar(x=3,z=4), then bar will have variables (x=3,y=2,z=4).
Different roles can have dependencies on the same role, and this
variable inheritence allows for the reuse of generic roles quite easily.
For example:
Role 'car' has the following dependencies:
dependencies:
- { role: wheel, n: 1 }
- { role: wheel, n: 2 }
- { role: wheel, n: 3 }
- { role: wheel, n: 4 }
Role 'wheel' has the following dependencies:
dependencies:
- { role: tire }
- { role: brake }
The role 'car' is then used as follows:
- { role: car, type: honda }
And tasks/main.yml in each role simply contains the following:
- name: {{ type }} whatever {{ n }}
command: echo ''
TASK: [honda tire 1]
TASK: [honda brake 1]
TASK: [honda wheel 1]
TASK: [honda tire 2]
TASK: [honda brake 2]
TASK: [honda wheel 2]
TASK: [honda tire 3]
TASK: [honda brake 3]
TASK: [honda wheel 3]
TASK: [honda tire 4]
TASK: [honda brake 4]
TASK: [honda wheel 4]
TASK: [I'm a honda] <- (this is in roles/car/tasks/main.yml)
2013-08-15 05:29:22 +00:00
if k in role_vars :
special_vars [ k ] = role_vars [ k ]
2013-09-01 15:52:42 +00:00
task_basepath = utils . path_dwim ( self . basedir , os . path . join ( role_path , ' tasks ' ) )
handler_basepath = utils . path_dwim ( self . basedir , os . path . join ( role_path , ' handlers ' ) )
vars_basepath = utils . path_dwim ( self . basedir , os . path . join ( role_path , ' vars ' ) )
2013-09-19 04:29:37 +00:00
meta_basepath = utils . path_dwim ( self . basedir , os . path . join ( role_path , ' meta ' ) )
2013-09-01 15:52:42 +00:00
defaults_basepath = utils . path_dwim ( self . basedir , os . path . join ( role_path , ' defaults ' ) )
Adding support for role dependencies.
Fixes #3686
Dependencies are enabled by adding a new directory/file named
meta/main.yml to the role. The format of the dependencies are:
dependencies:
- { role: foo, x: 1, y: 2 }
- { role: bar, x: 3, y: 4 }
...
Dependencies inherit variables as they are seen at the time of the
dependency inclusion. For example, if foo(x=1, y=2) has a dependency
on bar(x=3,z=4), then bar will have variables (x=3,y=2,z=4).
Different roles can have dependencies on the same role, and this
variable inheritence allows for the reuse of generic roles quite easily.
For example:
Role 'car' has the following dependencies:
dependencies:
- { role: wheel, n: 1 }
- { role: wheel, n: 2 }
- { role: wheel, n: 3 }
- { role: wheel, n: 4 }
Role 'wheel' has the following dependencies:
dependencies:
- { role: tire }
- { role: brake }
The role 'car' is then used as follows:
- { role: car, type: honda }
And tasks/main.yml in each role simply contains the following:
- name: {{ type }} whatever {{ n }}
command: echo ''
TASK: [honda tire 1]
TASK: [honda brake 1]
TASK: [honda wheel 1]
TASK: [honda tire 2]
TASK: [honda brake 2]
TASK: [honda wheel 2]
TASK: [honda tire 3]
TASK: [honda brake 3]
TASK: [honda wheel 3]
TASK: [honda tire 4]
TASK: [honda brake 4]
TASK: [honda wheel 4]
TASK: [I'm a honda] <- (this is in roles/car/tasks/main.yml)
2013-08-15 05:29:22 +00:00
2013-06-21 04:03:17 +00:00
task = self . _resolve_main ( task_basepath )
handler = self . _resolve_main ( handler_basepath )
vars_file = self . _resolve_main ( vars_basepath )
2013-09-19 04:29:37 +00:00
meta_file = self . _resolve_main ( meta_basepath )
2013-08-20 09:11:39 +00:00
defaults_file = self . _resolve_main ( defaults_basepath )
2013-08-15 05:50:08 +00:00
library = utils . path_dwim ( self . basedir , os . path . join ( role_path , ' library ' ) )
Adding support for role dependencies.
Fixes #3686
Dependencies are enabled by adding a new directory/file named
meta/main.yml to the role. The format of the dependencies are:
dependencies:
- { role: foo, x: 1, y: 2 }
- { role: bar, x: 3, y: 4 }
...
Dependencies inherit variables as they are seen at the time of the
dependency inclusion. For example, if foo(x=1, y=2) has a dependency
on bar(x=3,z=4), then bar will have variables (x=3,y=2,z=4).
Different roles can have dependencies on the same role, and this
variable inheritence allows for the reuse of generic roles quite easily.
For example:
Role 'car' has the following dependencies:
dependencies:
- { role: wheel, n: 1 }
- { role: wheel, n: 2 }
- { role: wheel, n: 3 }
- { role: wheel, n: 4 }
Role 'wheel' has the following dependencies:
dependencies:
- { role: tire }
- { role: brake }
The role 'car' is then used as follows:
- { role: car, type: honda }
And tasks/main.yml in each role simply contains the following:
- name: {{ type }} whatever {{ n }}
command: echo ''
TASK: [honda tire 1]
TASK: [honda brake 1]
TASK: [honda wheel 1]
TASK: [honda tire 2]
TASK: [honda brake 2]
TASK: [honda wheel 2]
TASK: [honda tire 3]
TASK: [honda brake 3]
TASK: [honda wheel 3]
TASK: [honda tire 4]
TASK: [honda brake 4]
TASK: [honda wheel 4]
TASK: [I'm a honda] <- (this is in roles/car/tasks/main.yml)
2013-08-15 05:29:22 +00:00
2013-09-19 04:29:37 +00:00
missing = lambda f : not os . path . isfile ( f )
2013-10-19 18:01:20 +00:00
if missing ( task ) and missing ( handler ) and missing ( vars_file ) and missing ( defaults_file ) and missing ( meta_file ) and missing ( library ) :
raise errors . AnsibleError ( " found role at %s , but cannot find %s or %s or %s or %s or %s or %s " % ( role_path , task , handler , vars_file , defaults_file , meta_file , library ) )
2013-09-20 20:46:34 +00:00
if isinstance ( role , dict ) :
role_name = role [ ' role ' ]
else :
role_name = role
2014-03-12 02:13:29 +00:00
role_names . append ( role_name )
2013-04-06 16:13:04 +00:00
if os . path . isfile ( task ) :
2013-09-20 17:10:24 +00:00
nt = dict ( include = pipes . quote ( task ) , vars = role_vars , default_vars = default_vars , role_name = role_name )
2013-07-18 22:32:56 +00:00
for k in special_keys :
if k in special_vars :
nt [ k ] = special_vars [ k ]
2013-04-12 23:02:56 +00:00
new_tasks . append ( nt )
2013-04-06 16:13:04 +00:00
if os . path . isfile ( handler ) :
2013-09-20 20:46:34 +00:00
nt = dict ( include = pipes . quote ( handler ) , vars = role_vars , role_name = role_name )
2013-07-18 22:32:56 +00:00
for k in special_keys :
if k in special_vars :
nt [ k ] = special_vars [ k ]
2013-04-12 23:02:56 +00:00
new_handlers . append ( nt )
2013-04-06 16:13:04 +00:00
if os . path . isfile ( vars_file ) :
new_vars_files . append ( vars_file )
2013-08-20 09:11:39 +00:00
if os . path . isfile ( defaults_file ) :
defaults_files . append ( defaults_file )
2013-04-27 11:02:38 +00:00
if os . path . isdir ( library ) :
utils . plugins . module_finder . add_directory ( library )
2013-04-06 16:13:04 +00:00
Adding support for role dependencies.
Fixes #3686
Dependencies are enabled by adding a new directory/file named
meta/main.yml to the role. The format of the dependencies are:
dependencies:
- { role: foo, x: 1, y: 2 }
- { role: bar, x: 3, y: 4 }
...
Dependencies inherit variables as they are seen at the time of the
dependency inclusion. For example, if foo(x=1, y=2) has a dependency
on bar(x=3,z=4), then bar will have variables (x=3,y=2,z=4).
Different roles can have dependencies on the same role, and this
variable inheritence allows for the reuse of generic roles quite easily.
For example:
Role 'car' has the following dependencies:
dependencies:
- { role: wheel, n: 1 }
- { role: wheel, n: 2 }
- { role: wheel, n: 3 }
- { role: wheel, n: 4 }
Role 'wheel' has the following dependencies:
dependencies:
- { role: tire }
- { role: brake }
The role 'car' is then used as follows:
- { role: car, type: honda }
And tasks/main.yml in each role simply contains the following:
- name: {{ type }} whatever {{ n }}
command: echo ''
TASK: [honda tire 1]
TASK: [honda brake 1]
TASK: [honda wheel 1]
TASK: [honda tire 2]
TASK: [honda brake 2]
TASK: [honda wheel 2]
TASK: [honda tire 3]
TASK: [honda brake 3]
TASK: [honda wheel 3]
TASK: [honda tire 4]
TASK: [honda brake 4]
TASK: [honda wheel 4]
TASK: [I'm a honda] <- (this is in roles/car/tasks/main.yml)
2013-08-15 05:29:22 +00:00
tasks = ds . get ( ' tasks ' , None )
2013-04-20 20:19:01 +00:00
post_tasks = ds . get ( ' post_tasks ' , None )
Adding support for role dependencies.
Fixes #3686
Dependencies are enabled by adding a new directory/file named
meta/main.yml to the role. The format of the dependencies are:
dependencies:
- { role: foo, x: 1, y: 2 }
- { role: bar, x: 3, y: 4 }
...
Dependencies inherit variables as they are seen at the time of the
dependency inclusion. For example, if foo(x=1, y=2) has a dependency
on bar(x=3,z=4), then bar will have variables (x=3,y=2,z=4).
Different roles can have dependencies on the same role, and this
variable inheritence allows for the reuse of generic roles quite easily.
For example:
Role 'car' has the following dependencies:
dependencies:
- { role: wheel, n: 1 }
- { role: wheel, n: 2 }
- { role: wheel, n: 3 }
- { role: wheel, n: 4 }
Role 'wheel' has the following dependencies:
dependencies:
- { role: tire }
- { role: brake }
The role 'car' is then used as follows:
- { role: car, type: honda }
And tasks/main.yml in each role simply contains the following:
- name: {{ type }} whatever {{ n }}
command: echo ''
TASK: [honda tire 1]
TASK: [honda brake 1]
TASK: [honda wheel 1]
TASK: [honda tire 2]
TASK: [honda brake 2]
TASK: [honda wheel 2]
TASK: [honda tire 3]
TASK: [honda brake 3]
TASK: [honda wheel 3]
TASK: [honda tire 4]
TASK: [honda brake 4]
TASK: [honda wheel 4]
TASK: [I'm a honda] <- (this is in roles/car/tasks/main.yml)
2013-08-15 05:29:22 +00:00
handlers = ds . get ( ' handlers ' , None )
2013-04-06 16:13:04 +00:00
vars_files = ds . get ( ' vars_files ' , None )
2013-04-20 20:07:06 +00:00
2013-04-06 16:13:04 +00:00
if type ( tasks ) != list :
tasks = [ ]
if type ( handlers ) != list :
handlers = [ ]
if type ( vars_files ) != list :
vars_files = [ ]
2013-04-20 20:19:01 +00:00
if type ( post_tasks ) != list :
post_tasks = [ ]
2013-04-20 20:07:06 +00:00
2013-04-06 22:36:25 +00:00
new_tasks . extend ( tasks )
2013-04-25 02:28:06 +00:00
# flush handlers after tasks + role tasks
2013-04-20 22:03:03 +00:00
new_tasks . append ( dict ( meta = ' flush_handlers ' ) )
2013-04-20 20:19:01 +00:00
new_tasks . extend ( post_tasks )
2013-04-25 02:28:06 +00:00
# flush handlers after post tasks
2013-04-20 22:03:03 +00:00
new_tasks . append ( dict ( meta = ' flush_handlers ' ) )
Adding support for role dependencies.
Fixes #3686
Dependencies are enabled by adding a new directory/file named
meta/main.yml to the role. The format of the dependencies are:
dependencies:
- { role: foo, x: 1, y: 2 }
- { role: bar, x: 3, y: 4 }
...
Dependencies inherit variables as they are seen at the time of the
dependency inclusion. For example, if foo(x=1, y=2) has a dependency
on bar(x=3,z=4), then bar will have variables (x=3,y=2,z=4).
Different roles can have dependencies on the same role, and this
variable inheritence allows for the reuse of generic roles quite easily.
For example:
Role 'car' has the following dependencies:
dependencies:
- { role: wheel, n: 1 }
- { role: wheel, n: 2 }
- { role: wheel, n: 3 }
- { role: wheel, n: 4 }
Role 'wheel' has the following dependencies:
dependencies:
- { role: tire }
- { role: brake }
The role 'car' is then used as follows:
- { role: car, type: honda }
And tasks/main.yml in each role simply contains the following:
- name: {{ type }} whatever {{ n }}
command: echo ''
TASK: [honda tire 1]
TASK: [honda brake 1]
TASK: [honda wheel 1]
TASK: [honda tire 2]
TASK: [honda brake 2]
TASK: [honda wheel 2]
TASK: [honda tire 3]
TASK: [honda brake 3]
TASK: [honda wheel 3]
TASK: [honda tire 4]
TASK: [honda brake 4]
TASK: [honda wheel 4]
TASK: [I'm a honda] <- (this is in roles/car/tasks/main.yml)
2013-08-15 05:29:22 +00:00
2013-04-06 22:36:25 +00:00
new_handlers . extend ( handlers )
new_vars_files . extend ( vars_files )
Adding support for role dependencies.
Fixes #3686
Dependencies are enabled by adding a new directory/file named
meta/main.yml to the role. The format of the dependencies are:
dependencies:
- { role: foo, x: 1, y: 2 }
- { role: bar, x: 3, y: 4 }
...
Dependencies inherit variables as they are seen at the time of the
dependency inclusion. For example, if foo(x=1, y=2) has a dependency
on bar(x=3,z=4), then bar will have variables (x=3,y=2,z=4).
Different roles can have dependencies on the same role, and this
variable inheritence allows for the reuse of generic roles quite easily.
For example:
Role 'car' has the following dependencies:
dependencies:
- { role: wheel, n: 1 }
- { role: wheel, n: 2 }
- { role: wheel, n: 3 }
- { role: wheel, n: 4 }
Role 'wheel' has the following dependencies:
dependencies:
- { role: tire }
- { role: brake }
The role 'car' is then used as follows:
- { role: car, type: honda }
And tasks/main.yml in each role simply contains the following:
- name: {{ type }} whatever {{ n }}
command: echo ''
TASK: [honda tire 1]
TASK: [honda brake 1]
TASK: [honda wheel 1]
TASK: [honda tire 2]
TASK: [honda brake 2]
TASK: [honda wheel 2]
TASK: [honda tire 3]
TASK: [honda brake 3]
TASK: [honda wheel 3]
TASK: [honda tire 4]
TASK: [honda brake 4]
TASK: [honda wheel 4]
TASK: [I'm a honda] <- (this is in roles/car/tasks/main.yml)
2013-08-15 05:29:22 +00:00
2013-04-06 22:36:25 +00:00
ds [ ' tasks ' ] = new_tasks
ds [ ' handlers ' ] = new_handlers
ds [ ' vars_files ' ] = new_vars_files
2014-03-12 02:13:29 +00:00
ds [ ' role_names ' ] = role_names
2013-04-20 22:03:03 +00:00
2013-08-29 22:21:28 +00:00
self . default_vars = self . _load_role_defaults ( defaults_files )
2013-08-20 09:11:39 +00:00
2013-04-06 16:13:04 +00:00
return ds
# *************************************************
2013-06-21 04:03:17 +00:00
def _resolve_main ( self , basepath ) :
''' flexibly handle variations in main filenames '''
# these filenames are acceptable:
mains = (
os . path . join ( basepath , ' main ' ) ,
os . path . join ( basepath , ' main.yml ' ) ,
os . path . join ( basepath , ' main.yaml ' ) ,
2014-03-07 02:47:49 +00:00
os . path . join ( basepath , ' main.json ' ) ,
2013-06-21 04:03:17 +00:00
)
if sum ( [ os . path . isfile ( x ) for x in mains ] ) > 1 :
raise errors . AnsibleError ( " found multiple main files at %s , only one allowed " % ( basepath ) )
else :
for m in mains :
if os . path . isfile ( m ) :
return m # exactly one main file
return mains [ 0 ] # zero mains (we still need to return something)
# *************************************************
2014-01-21 01:19:03 +00:00
def _load_tasks ( self , tasks , vars = None , default_vars = None , sudo_vars = None ,
additional_conditions = None , original_file = None , role_name = None ) :
2012-05-26 04:37:34 +00:00
''' handle task and handler include statements '''
results = [ ]
2013-01-27 15:40:29 +00:00
if tasks is None :
# support empty handler files, and the like.
tasks = [ ]
2013-10-12 21:39:28 +00:00
if additional_conditions is None :
additional_conditions = [ ]
if vars is None :
vars = { }
if default_vars is None :
default_vars = { }
if sudo_vars is None :
sudo_vars = { }
old_conditions = list ( additional_conditions )
2013-01-27 15:40:29 +00:00
2012-06-09 19:42:45 +00:00
for x in tasks :
2013-10-12 21:39:28 +00:00
# prevent assigning the same conditions to each task on an include
included_additional_conditions = list ( old_conditions )
2013-03-14 12:06:55 +00:00
if not isinstance ( x , dict ) :
2014-04-23 11:13:46 +00:00
raise errors . AnsibleError ( " expecting dict; got: %s , error in %s " % ( x , original_file ) )
2013-04-20 22:03:03 +00:00
2013-07-18 22:32:56 +00:00
# evaluate sudo vars for current and child tasks
included_sudo_vars = { }
for k in [ " sudo " , " sudo_user " ] :
if k in x :
included_sudo_vars [ k ] = x [ k ]
elif k in sudo_vars :
included_sudo_vars [ k ] = sudo_vars [ k ]
x [ k ] = sudo_vars [ k ]
2013-04-20 22:03:03 +00:00
if ' meta ' in x :
if x [ ' meta ' ] == ' flush_handlers ' :
2014-01-21 01:19:03 +00:00
results . append ( Task ( self , x ) )
2013-04-20 22:03:03 +00:00
continue
2013-06-03 22:19:11 +00:00
2013-01-03 22:01:04 +00:00
task_vars = self . vars . copy ( )
task_vars . update ( vars )
2013-04-06 16:13:04 +00:00
if original_file :
task_vars [ ' _original_file ' ] = original_file
2012-05-26 04:37:34 +00:00
if ' include ' in x :
2013-04-25 01:59:47 +00:00
tokens = shlex . split ( str ( x [ ' include ' ] ) )
2012-10-25 13:13:34 +00:00
items = [ ' ' ]
2013-01-03 23:15:13 +00:00
included_additional_conditions = list ( additional_conditions )
2013-09-16 21:12:36 +00:00
include_vars = { }
2012-10-25 13:13:34 +00:00
for k in x :
2013-01-03 23:15:13 +00:00
if k . startswith ( " with_ " ) :
2014-02-23 16:06:46 +00:00
if original_file :
offender = " (in %s ) " % original_file
else :
offender = " "
utils . deprecated ( " include + with_items is a removed deprecated feature " + offender , " 1.5 " , removed = True )
2013-01-03 23:15:13 +00:00
elif k . startswith ( " when_ " ) :
2014-01-03 18:46:31 +00:00
utils . deprecated ( " \" when_<criteria>: \" is a removed deprecated feature, use the simplified ' when: ' conditional directly " , None , removed = True )
2013-04-06 22:43:39 +00:00
elif k == ' when ' :
2013-11-05 19:38:21 +00:00
if type ( x [ k ] ) is str :
2014-01-04 00:13:21 +00:00
included_additional_conditions . insert ( 0 , x [ k ] )
2013-11-05 19:38:21 +00:00
elif type ( x [ k ] ) is list :
for i in x [ k ] :
2014-01-04 00:13:21 +00:00
included_additional_conditions . insert ( 0 , i )
2014-01-31 22:09:10 +00:00
elif k in ( " include " , " vars " , " default_vars " , " sudo " , " sudo_user " , " role_name " , " no_log " ) :
2013-09-16 21:12:36 +00:00
continue
2013-01-03 23:15:13 +00:00
else :
2013-09-16 21:12:36 +00:00
include_vars [ k ] = x [ k ]
2012-10-25 13:13:34 +00:00
2013-09-16 18:16:06 +00:00
default_vars = x . get ( ' default_vars ' , { } )
if not default_vars :
default_vars = self . default_vars
else :
default_vars = utils . combine_vars ( self . default_vars , default_vars )
2013-09-16 21:12:36 +00:00
# append the vars defined with the include (from above)
# as well as the old-style 'vars' element. The old-style
# vars are given higher precedence here (just in case)
task_vars = utils . combine_vars ( task_vars , include_vars )
2013-01-03 22:36:42 +00:00
if ' vars ' in x :
2013-06-03 22:19:11 +00:00
task_vars = utils . combine_vars ( task_vars , x [ ' vars ' ] )
2013-09-16 21:12:36 +00:00
2014-01-04 00:13:21 +00:00
if ' when ' in x :
2014-05-14 06:45:35 +00:00
if isinstance ( x [ ' when ' ] , ( basestring , bool ) ) :
included_additional_conditions . append ( x [ ' when ' ] )
elif isinstance ( x [ ' when ' ] , list ) :
included_additional_conditions . extend ( x [ ' when ' ] )
2013-01-03 22:36:42 +00:00
2013-09-20 01:44:52 +00:00
new_role = None
if ' role_name ' in x :
new_role = x [ ' role_name ' ]
2012-09-30 11:40:52 +00:00
for item in items :
mv = task_vars . copy ( )
mv [ ' item ' ] = item
for t in tokens [ 1 : ] :
( k , v ) = t . split ( " = " , 1 )
2013-04-23 02:03:39 +00:00
mv [ k ] = template ( self . basedir , v , mv )
2013-04-06 22:32:36 +00:00
dirname = self . basedir
if original_file :
2013-06-03 22:19:11 +00:00
dirname = os . path . dirname ( original_file )
2013-04-23 02:03:39 +00:00
include_file = template ( dirname , tokens [ 0 ] , mv )
2013-04-06 22:32:36 +00:00
include_filename = utils . path_dwim ( dirname , include_file )
2014-02-11 17:03:11 +00:00
data = utils . parse_yaml_from_file ( include_filename , vault_password = self . vault_password )
2013-10-01 15:52:39 +00:00
if ' role_name ' in x and data is not None :
2014-04-02 21:13:22 +00:00
for y in data :
if isinstance ( y , dict ) and ' include ' in y :
y [ ' role_name ' ] = new_role
2013-10-12 21:39:28 +00:00
loaded = self . _load_tasks ( data , mv , default_vars , included_sudo_vars , list ( included_additional_conditions ) , original_file = include_filename , role_name = new_role )
results + = loaded
2012-05-26 04:37:34 +00:00
elif type ( x ) == dict :
2014-01-21 01:19:03 +00:00
task = Task (
self , x ,
module_vars = task_vars ,
default_vars = default_vars ,
additional_conditions = list ( additional_conditions ) ,
role_name = role_name
)
2013-10-12 21:39:28 +00:00
results . append ( task )
2012-06-09 19:42:45 +00:00
else :
raise Exception ( " unexpected task type " )
2012-08-22 02:08:08 +00:00
2012-07-12 04:58:48 +00:00
for x in results :
if self . tags is not None :
x . tags . extend ( self . tags )
2012-05-26 04:37:34 +00:00
return results
# *************************************************
2013-10-08 20:12:36 +00:00
def _is_valid_tag ( self , tag_list ) :
"""
Check to see if the list of tags passed in is in the list of tags
we only want ( playbook . only_tags ) , or if it is not in the list of
tags we don ' t want (playbook.skip_tags).
"""
matched_skip_tags = set ( tag_list ) & set ( self . playbook . skip_tags )
matched_only_tags = set ( tag_list ) & set ( self . playbook . only_tags )
if len ( matched_skip_tags ) > 0 or ( self . playbook . only_tags != [ ' all ' ] and len ( matched_only_tags ) == 0 ) :
return False
return True
# *************************************************
2012-05-26 04:37:34 +00:00
def tasks ( self ) :
2012-05-26 05:20:53 +00:00
''' return task objects for this play '''
2012-08-07 00:07:02 +00:00
return self . _tasks
2012-05-26 04:37:34 +00:00
2012-05-26 05:20:53 +00:00
def handlers ( self ) :
2012-08-26 03:10:43 +00:00
''' return handler objects for this play '''
2012-05-26 05:20:53 +00:00
return self . _handlers
2012-05-26 04:37:34 +00:00
# *************************************************
2012-09-10 02:43:27 +00:00
def _get_vars ( self ) :
2012-05-26 05:20:53 +00:00
''' load the vars section from a play, accounting for all sorts of variable features
including loading from yaml files , prompting , and conditional includes of the first
file found in a list . '''
2012-05-26 04:37:34 +00:00
if self . vars is None :
self . vars = { }
if type ( self . vars ) not in [ dict , list ] :
raise errors . AnsibleError ( " ' vars ' section must contain only key/value pairs " )
2012-11-26 20:16:13 +00:00
vars = { }
2012-08-07 00:07:02 +00:00
2012-05-26 04:37:34 +00:00
# translate a list of vars into a dict
if type ( self . vars ) == list :
for item in self . vars :
2012-08-22 02:08:08 +00:00
if getattr ( item , ' items ' , None ) is None :
raise errors . AnsibleError ( " expecting a key-value pair in ' vars ' section " )
2012-05-26 04:37:34 +00:00
k , v = item . items ( ) [ 0 ]
vars [ k ] = v
else :
vars . update ( self . vars )
2012-07-24 14:43:29 +00:00
if type ( self . vars_prompt ) == list :
for var in self . vars_prompt :
2012-07-27 23:00:29 +00:00
if not ' name ' in var :
raise errors . AnsibleError ( " ' vars_prompt ' item is missing ' name: ' " )
2012-08-09 14:56:40 +00:00
2012-07-27 23:00:29 +00:00
vname = var [ ' name ' ]
2012-09-27 23:32:09 +00:00
prompt = var . get ( " prompt " , vname )
2013-01-18 11:00:57 +00:00
default = var . get ( " default " , None )
2012-07-24 14:43:29 +00:00
private = var . get ( " private " , True )
2012-08-09 14:56:40 +00:00
confirm = var . get ( " confirm " , False )
encrypt = var . get ( " encrypt " , None )
salt_size = var . get ( " salt_size " , None )
salt = var . get ( " salt " , None )
2012-11-26 20:24:49 +00:00
if vname not in self . playbook . extra_vars :
2013-02-18 00:40:38 +00:00
vars [ vname ] = self . playbook . callbacks . on_vars_prompt (
2013-01-19 00:34:47 +00:00
vname , private , prompt , encrypt , confirm , salt_size , salt , default
2013-01-18 11:00:57 +00:00
)
2012-08-09 14:56:40 +00:00
2012-07-24 14:43:29 +00:00
elif type ( self . vars_prompt ) == dict :
2012-07-27 23:00:29 +00:00
for ( vname , prompt ) in self . vars_prompt . iteritems ( ) :
2012-08-09 14:56:40 +00:00
prompt_msg = " %s : " % prompt
2012-11-26 20:24:49 +00:00
if vname not in self . playbook . extra_vars :
2013-01-18 11:00:57 +00:00
vars [ vname ] = self . playbook . callbacks . on_vars_prompt (
2013-01-19 00:34:47 +00:00
varname = vname , private = False , prompt = prompt_msg , default = None
2013-01-18 11:00:57 +00:00
)
2012-08-09 14:56:40 +00:00
2012-07-24 14:43:29 +00:00
else :
2012-07-27 23:00:29 +00:00
raise errors . AnsibleError ( " ' vars_prompt ' section is malformed, see docs " )
2012-05-26 04:37:34 +00:00
2014-04-03 00:46:51 +00:00
if type ( self . playbook . extra_vars ) == dict :
vars = utils . combine_vars ( vars , self . playbook . extra_vars )
2013-02-11 22:25:50 +00:00
return vars
2012-05-26 04:37:34 +00:00
# *************************************************
2014-02-11 17:03:11 +00:00
def update_vars_files ( self , hosts , vault_password = None ) :
2012-05-26 04:37:34 +00:00
''' calculate vars_files, which requires that setup runs first so ansible facts can be mixed in '''
2012-08-07 00:07:02 +00:00
2012-07-14 17:12:19 +00:00
# now loop through all the hosts...
2012-05-26 04:37:34 +00:00
for h in hosts :
2014-02-11 17:03:11 +00:00
self . _update_vars_files_for_host ( h , vault_password = vault_password )
2012-05-26 04:37:34 +00:00
# *************************************************
2012-08-26 03:10:43 +00:00
def compare_tags ( self , tags ) :
''' given a list of tags that the user has specified, return two lists:
matched_tags : tags were found within the current play and match those given
by the user
unmatched_tags : tags that were found within the current play but do not match
any provided by the user '''
2012-07-12 04:17:51 +00:00
2014-03-10 15:05:31 +00:00
# gather all the tags in all the tasks and handlers into one list
# FIXME: isn't this in self.tags already?
2012-08-26 03:10:43 +00:00
all_tags = [ ]
2012-07-12 00:30:30 +00:00
for task in self . _tasks :
2013-04-20 22:03:03 +00:00
if not task . meta :
all_tags . extend ( task . tags )
2014-03-10 15:05:31 +00:00
for handler in self . _handlers :
all_tags . extend ( handler . tags )
2012-07-26 00:45:51 +00:00
2012-08-26 03:10:43 +00:00
# compare the lists of tags using sets and return the matched and unmatched
all_tags_set = set ( all_tags )
tags_set = set ( tags )
matched_tags = all_tags_set & tags_set
unmatched_tags = all_tags_set - tags_set
2012-08-07 00:07:02 +00:00
2012-08-26 03:10:43 +00:00
return matched_tags , unmatched_tags
2012-07-12 00:30:30 +00:00
# *************************************************
2014-03-20 15:55:46 +00:00
def _late_merge_role_tags ( self ) :
# build a local dict of tags for roles
role_tags = { }
for task in self . _ds [ ' tasks ' ] :
if ' role_name ' in task :
2014-03-26 17:29:34 +00:00
this_role = task [ ' role_name ' ] + " - " + task [ ' vars ' ] [ ' role_uuid ' ]
2014-03-20 15:55:46 +00:00
if this_role not in role_tags :
role_tags [ this_role ] = [ ]
if ' tags ' in task [ ' vars ' ] :
if isinstance ( task [ ' vars ' ] [ ' tags ' ] , basestring ) :
2014-03-26 17:29:34 +00:00
role_tags [ this_role ] + = shlex . split ( task [ ' vars ' ] [ ' tags ' ] )
2014-03-20 15:55:46 +00:00
else :
2014-03-26 17:29:34 +00:00
role_tags [ this_role ] + = task [ ' vars ' ] [ ' tags ' ]
2014-03-20 15:55:46 +00:00
# apply each role's tags to it's tasks
for idx , val in enumerate ( self . _tasks ) :
2014-03-26 17:29:34 +00:00
if getattr ( val , ' role_name ' , None ) is not None :
this_role = val . role_name + " - " + val . module_vars [ ' role_uuid ' ]
2014-03-20 15:55:46 +00:00
if this_role in role_tags :
self . _tasks [ idx ] . tags = sorted ( set ( self . _tasks [ idx ] . tags + role_tags [ this_role ] ) )
# *************************************************
2012-07-14 17:12:19 +00:00
def _has_vars_in ( self , msg ) :
2014-01-23 15:02:17 +00:00
return " $ " in msg or " {{ " in msg
2012-07-14 17:12:19 +00:00
# *************************************************
2014-02-11 17:03:11 +00:00
def _update_vars_files_for_host ( self , host , vault_password = None ) :
2012-05-26 04:37:34 +00:00
2014-04-10 17:43:59 +00:00
def generate_filenames ( host , inject , filename ) :
""" Render the raw filename into 3 forms """
filename2 = template ( self . basedir , filename , self . vars )
filename3 = filename2
if host is not None :
filename3 = template ( self . basedir , filename2 , inject )
if self . _has_vars_in ( filename3 ) and host is not None :
# allow play scoped vars and host scoped vars to template the filepath
inject . update ( self . vars )
filename4 = template ( self . basedir , filename3 , inject )
filename4 = utils . path_dwim ( self . basedir , filename4 )
else :
filename4 = utils . path_dwim ( self . basedir , filename3 )
return filename2 , filename3 , filename4
def update_vars_cache ( host , inject , data , filename ) :
""" update a host ' s varscache with new var data """
data = utils . combine_vars ( inject , data )
2014-05-13 16:24:15 +00:00
self . playbook . VARS_CACHE [ host ] = utils . combine_vars ( self . playbook . VARS_CACHE . get ( host , { } ) , data )
2014-04-10 17:43:59 +00:00
self . playbook . callbacks . on_import_for_host ( host , filename4 )
def process_files ( filename , filename2 , filename3 , filename4 , host = None ) :
""" pseudo-algorithm for deciding where new vars should go """
data = utils . parse_yaml_from_file ( filename4 , vault_password = self . vault_password )
if data :
if type ( data ) != dict :
raise errors . AnsibleError ( " %s must be stored as a dictionary/hash " % filename4 )
if host is not None :
if self . _has_vars_in ( filename2 ) and not self . _has_vars_in ( filename3 ) :
# running a host specific pass and has host specific variables
# load into setup cache
update_vars_cache ( host , inject , data , filename4 )
elif self . _has_vars_in ( filename3 ) and not self . _has_vars_in ( filename4 ) :
# handle mixed scope variables in filepath
update_vars_cache ( host , inject , data , filename4 )
elif not self . _has_vars_in ( filename4 ) :
# found a non-host specific variable, load into vars and NOT
# the setup cache
if host is not None :
self . vars . update ( data )
else :
self . vars = utils . combine_vars ( self . vars , data )
# Enforce that vars_files is always a list
2012-07-14 17:12:19 +00:00
if type ( self . vars_files ) != list :
self . vars_files = [ self . vars_files ]
2012-08-07 00:07:02 +00:00
2014-04-10 17:43:59 +00:00
# Build an inject if this is a host run started by self.update_vars_files
2013-01-03 22:16:38 +00:00
if host is not None :
2013-01-22 15:56:04 +00:00
inject = { }
2014-02-11 17:03:11 +00:00
inject . update ( self . playbook . inventory . get_variables ( host , vault_password = vault_password ) )
2014-04-03 00:46:51 +00:00
inject . update ( self . playbook . SETUP_CACHE . get ( host , { } ) )
inject . update ( self . playbook . VARS_CACHE . get ( host , { } ) )
2014-04-10 17:43:59 +00:00
else :
inject = None
2013-01-03 22:16:38 +00:00
2012-05-26 04:37:34 +00:00
for filename in self . vars_files :
if type ( filename ) == list :
2014-04-10 17:43:59 +00:00
# loop over all filenames, loading the first one, and failing if none found
2012-05-26 04:37:34 +00:00
found = False
sequence = [ ]
for real_filename in filename :
2014-04-10 17:43:59 +00:00
filename2 , filename3 , filename4 = generate_filenames ( host , inject , real_filename )
2012-07-14 17:12:19 +00:00
sequence . append ( filename4 )
if os . path . exists ( filename4 ) :
2012-05-26 04:37:34 +00:00
found = True
2014-04-10 17:43:59 +00:00
process_files ( filename , filename2 , filename3 , filename4 , host = host )
2012-07-14 17:12:19 +00:00
elif host is not None :
self . playbook . callbacks . on_not_import_for_host ( host , filename4 )
2012-07-19 00:48:13 +00:00
if found :
2012-07-18 22:31:04 +00:00
break
2012-10-28 15:35:22 +00:00
if not found and host is not None :
2012-05-26 04:37:34 +00:00
raise errors . AnsibleError (
" %s : FATAL, no files matched for vars_files import sequence: %s " % ( host , sequence )
)
else :
2012-07-14 17:12:19 +00:00
# just one filename supplied, load it!
2014-04-10 17:43:59 +00:00
filename2 , filename3 , filename4 = generate_filenames ( host , inject , filename )
2012-07-14 23:18:33 +00:00
if self . _has_vars_in ( filename4 ) :
2012-10-11 21:52:01 +00:00
continue
2014-04-10 17:43:59 +00:00
process_files ( filename , filename2 , filename3 , filename4 , host = host )
2014-04-01 14:48:14 +00:00
# finally, update the VARS_CACHE for the host, if it is set
if host is not None :
self . playbook . VARS_CACHE [ host ] . update ( self . playbook . extra_vars )