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
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-01-20 21:10:11 +00:00
' basedir ' , ' any_errors_fatal ' , ' roles ' , ' max_fail_pct '
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-20 21:10:11 +00:00
' any_errors_fatal ' , ' roles ' , ' pre_tasks ' , ' post_tasks ' , ' max_fail_percentage '
2012-07-17 23:09:36 +00:00
]
2012-05-26 04:37:34 +00:00
# *************************************************
2012-09-10 02:43:27 +00:00
def __init__ ( self , playbook , ds , basedir ) :
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 )
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
2013-08-30 16:35:35 +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 ' )
2013-04-06 14:45:09 +00:00
self . _update_vars_files_for_host ( None )
2013-04-06 16:13:04 +00:00
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 )
2014-01-04 18:49:07 +00:00
self . gather_facts = ds . get ( ' gather_facts ' , True )
2013-04-06 14:09:16 +00:00
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 ) )
2012-07-14 17:23:14 +00:00
2013-03-26 02:32:01 +00:00
load_vars = { }
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
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
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 ) :
vars_data = utils . parse_yaml_from_file ( vars )
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 ) :
2013-09-03 02:35:47 +00:00
defaults_data = utils . parse_yaml_from_file ( 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
# 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 ) :
data = utils . parse_yaml_from_file ( meta )
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 ) :
meta_data = utils . parse_yaml_from_file ( meta )
if meta_data :
allow_dupes = utils . boolean ( meta_data . get ( ' allow_duplicates ' , ' ' ) )
2013-09-04 14:28:51 +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 :
included_dep_vars [ " tags " ] = list ( set ( included_dep_vars [ " tags " ] + passed_vars [ " tags " ] ) )
else :
included_dep_vars [ " tags " ] = passed_vars [ " tags " ] . copy ( )
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 ) :
vars_data = utils . parse_yaml_from_file ( vars )
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 ) :
2013-09-03 02:35:47 +00:00
dep_defaults_data = utils . parse_yaml_from_file ( 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
if ' role ' in dep_vars :
del dep_vars [ ' role ' ]
2013-10-08 20:12:36 +00:00
if " tags " in passed_vars :
if not self . _is_valid_tag ( passed_vars [ " tags " ] ) :
# one of the tags specified for this role was in the
# skip list, or we're limiting the tags and it didn't
# match one, so we just skip it completely
continue
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 )
2013-11-05 19:38:21 +00:00
# pass along conditionals from roles to dep roles
if type ( role ) is dict :
if ' when ' in passed_vars :
if ' when ' in dep_vars :
tmpcond = [ ]
if type ( passed_vars [ ' when ' ] ) is str :
tmpcond . append ( passed_vars [ ' when ' ] )
elif type ( passed_vars [ ' when ' ] ) is list :
tmpcond . join ( passed_vars [ ' when ' ] )
if type ( dep_vars [ ' when ' ] ) is str :
tmpcond . append ( dep_vars [ ' when ' ] )
elif type ( dep_vars [ ' when ' ] ) is list :
2013-11-05 19:44:27 +00:00
tmpcond + = dep_vars [ ' when ' ]
2013-11-05 19:38:21 +00:00
if len ( tmpcond ) > 0 :
dep_vars [ ' when ' ] = tmpcond
else :
dep_vars [ ' when ' ] = passed_vars [ ' when ' ]
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 ) :
new_default_vars = utils . parse_yaml_from_file ( filename )
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 )
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
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
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 ' ) ,
)
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-20 21:10:11 +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 ) :
raise errors . AnsibleError ( " expecting dict; got: %s " % x )
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-20 21:10:11 +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-01-03 18:46:31 +00:00
utils . deprecated ( " include + with_items is a removed deprecated feature " , " 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 )
elif k in ( " include " , " vars " , " default_vars " , " sudo " , " sudo_user " , " role_name " ) :
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 :
included_additional_conditions . append ( 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 )
data = utils . parse_yaml_from_file ( include_filename )
2013-10-01 15:52:39 +00:00
if ' role_name ' in x and data is not None :
2013-09-26 13:50:14 +00:00
for x in data :
if ' include ' in x :
x [ ' 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-20 21:10:11 +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
2013-02-11 22:25:50 +00:00
if type ( self . playbook . extra_vars ) == dict :
vars . update ( self . playbook . extra_vars )
return vars
2012-05-26 04:37:34 +00:00
# *************************************************
def update_vars_files ( self , hosts ) :
''' 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 :
2012-05-26 05:20:53 +00:00
self . _update_vars_files_for_host ( h )
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
2012-08-26 03:10:43 +00:00
# gather all the tags in all the tasks into one list
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 )
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
# *************************************************
2012-07-14 17:12:19 +00:00
def _has_vars_in ( self , msg ) :
return ( ( msg . find ( " $ " ) != - 1 ) or ( msg . find ( " {{ " ) != - 1 ) )
# *************************************************
2012-05-26 05:20:53 +00:00
def _update_vars_files_for_host ( self , host ) :
2012-05-26 04:37:34 +00:00
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
2013-01-03 22:16:38 +00:00
if host is not None :
2013-01-22 15:56:04 +00:00
inject = { }
2013-01-03 22:16:38 +00:00
inject . update ( self . playbook . inventory . get_variables ( host ) )
2013-01-22 15:56:04 +00:00
inject . update ( self . playbook . SETUP_CACHE [ host ] )
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 :
# loop over all filenames, loading the first one, and failing if # none found
found = False
sequence = [ ]
for real_filename in filename :
2013-04-23 02:03:39 +00:00
filename2 = template ( self . basedir , real_filename , self . vars )
2012-07-14 17:12:19 +00:00
filename3 = filename2
if host is not None :
2013-04-23 02:03:39 +00:00
filename3 = template ( self . basedir , filename2 , inject )
2012-09-10 02:43:27 +00:00
filename4 = utils . path_dwim ( self . basedir , filename3 )
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
2012-07-14 17:12:19 +00:00
data = utils . parse_yaml_from_file ( filename4 )
2012-08-01 23:51:52 +00:00
if type ( data ) != dict :
raise errors . AnsibleError ( " %s must be stored as a dictionary/hash " % filename4 )
2012-07-14 17:12:19 +00:00
if host is not None :
if self . _has_vars_in ( filename2 ) and not self . _has_vars_in ( filename3 ) :
# this filename has variables in it that were fact specific
# so it needs to be loaded into the per host SETUP_CACHE
self . playbook . SETUP_CACHE [ host ] . update ( data )
self . playbook . callbacks . on_import_for_host ( host , filename4 )
elif not self . _has_vars_in ( filename4 ) :
# found a non-host specific variable, load into vars and NOT
# the setup cache
self . vars . update ( data )
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!
2013-04-23 02:03:39 +00:00
filename2 = template ( self . basedir , filename , self . vars )
2012-07-14 17:12:19 +00:00
filename3 = filename2
if host is not None :
2013-04-23 02:03:39 +00:00
filename3 = template ( self . basedir , filename2 , inject )
2012-09-10 02:43:27 +00:00
filename4 = utils . path_dwim ( self . basedir , filename3 )
2012-07-14 23:18:33 +00:00
if self . _has_vars_in ( filename4 ) :
2012-10-11 21:52:01 +00:00
continue
2012-07-14 17:12:19 +00:00
new_vars = utils . parse_yaml_from_file ( filename4 )
2012-05-29 14:23:09 +00:00
if new_vars :
2012-07-14 16:39:06 +00:00
if type ( new_vars ) != dict :
2013-08-25 18:29:07 +00:00
raise errors . AnsibleError ( " %s must be stored as dictionary/hash: %s " % ( filename4 , type ( new_vars ) ) )
2012-07-14 17:12:19 +00:00
if host is not None and self . _has_vars_in ( filename2 ) and not self . _has_vars_in ( filename3 ) :
# running a host specific pass and has host specific variables
2012-08-07 00:07:02 +00:00
# load into setup cache
2013-03-09 23:30:18 +00:00
self . playbook . SETUP_CACHE [ host ] = utils . combine_vars (
self . playbook . SETUP_CACHE [ host ] , new_vars )
2013-02-03 22:26:02 +00:00
self . playbook . callbacks . on_import_for_host ( host , filename4 )
2012-07-14 17:12:19 +00:00
elif host is None :
2012-08-07 00:07:02 +00:00
# running a non-host specific pass and we can update the global vars instead
2013-03-09 23:30:18 +00:00
self . vars = utils . combine_vars ( self . vars , new_vars )