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/>.
2012-05-26 05:20:53 +00:00
from ansible import errors
2012-05-26 05:49:23 +00:00
from ansible import utils
2013-04-29 01:05:19 +00:00
import os
2013-04-11 16:15:13 +00:00
import ansible . utils . template as template
2013-10-11 22:37:39 +00:00
import sys
2012-08-19 20:52:55 +00:00
2012-05-26 04:37:34 +00:00
class Task ( object ) :
2012-08-07 00:07:02 +00:00
__slots__ = [
2014-01-04 00:13:21 +00:00
' name ' , ' meta ' , ' action ' , ' when ' , ' async_seconds ' , ' async_poll_interval ' ,
2013-08-29 22:21:28 +00:00
' notify ' , ' module_name ' , ' module_args ' , ' module_vars ' , ' default_vars ' ,
2013-09-20 20:46:34 +00:00
' play ' , ' notified_by ' , ' tags ' , ' register ' , ' role_name ' ,
2012-08-19 20:52:55 +00:00
' delegate_to ' , ' first_available_file ' , ' ignore_errors ' ,
2013-08-23 21:20:13 +00:00
' local_action ' , ' transport ' , ' sudo ' , ' remote_user ' , ' sudo_user ' , ' sudo_pass ' ,
2013-03-28 06:17:01 +00:00
' items_lookup_plugin ' , ' items_lookup_terms ' , ' environment ' , ' args ' ,
2014-01-21 01:19:03 +00:00
' any_errors_fatal ' , ' changed_when ' , ' failed_when ' , ' always_run ' , ' delay ' , ' retries ' , ' until ' ,
2014-01-31 22:09:10 +00:00
' su ' , ' su_user ' , ' su_pass ' , ' no_log ' ,
2012-05-26 04:37:34 +00:00
]
2012-07-17 23:09:36 +00:00
# to prevent typos and such
VALID_KEYS = [
2014-01-04 00:13:21 +00:00
' name ' , ' meta ' , ' action ' , ' when ' , ' async ' , ' poll ' , ' notify ' ,
2012-08-18 12:46:51 +00:00
' first_available_file ' , ' include ' , ' tags ' , ' register ' , ' ignore_errors ' ,
2013-08-23 21:20:13 +00:00
' delegate_to ' , ' local_action ' , ' transport ' , ' remote_user ' , ' sudo ' , ' sudo_user ' ,
2013-03-28 06:17:01 +00:00
' sudo_pass ' , ' when ' , ' connection ' , ' environment ' , ' args ' ,
2014-01-21 01:19:03 +00:00
' any_errors_fatal ' , ' changed_when ' , ' failed_when ' , ' always_run ' , ' delay ' , ' retries ' , ' until ' ,
2014-01-31 22:09:10 +00:00
' su ' , ' su_user ' , ' su_pass ' , ' no_log ' ,
2012-07-17 23:09:36 +00:00
]
2013-10-12 21:39:28 +00:00
2013-09-20 01:44:52 +00:00
def __init__ ( self , play , ds , module_vars = None , default_vars = None , additional_conditions = None , role_name = None ) :
2012-05-26 05:20:53 +00:00
''' constructor loads from a task or handler datastructure '''
2012-10-11 11:56:01 +00:00
2013-04-20 22:03:03 +00:00
# meta directives are used to tell things like ansible/playbook to run
# operations like handler execution. Meta tasks are not executed
# normally.
if ' meta ' in ds :
self . meta = ds [ ' meta ' ]
2013-04-21 21:11:48 +00:00
self . tags = [ ]
2013-04-20 22:03:03 +00:00
return
else :
self . meta = None
2013-04-29 01:05:19 +00:00
library = os . path . join ( play . basedir , ' library ' )
if os . path . exists ( library ) :
utils . plugins . module_finder . add_directory ( library )
2012-07-17 23:09:36 +00:00
for x in ds . keys ( ) :
2012-10-13 00:07:05 +00:00
# code to allow for saying "modulename: args" versus "action: modulename args"
2012-11-18 17:37:30 +00:00
if x in utils . plugins . module_finder :
2013-04-23 02:03:39 +00:00
2012-11-26 21:42:44 +00:00
if ' action ' in ds :
2014-04-20 18:28:26 +00:00
raise errors . AnsibleError ( " multiple actions specified in task: ' %s ' and ' %s ' " % ( x , ds . get ( ' name ' , ds [ ' action ' ] ) ) )
2013-02-28 11:09:29 +00:00
if isinstance ( ds [ x ] , dict ) :
if ' args ' in ds :
raise errors . AnsibleError ( " can ' t combine args: and a dict for %s : in task %s " % ( x , ds . get ( ' name ' , " %s : %s " % ( x , ds [ x ] ) ) ) )
ds [ ' args ' ] = ds [ x ]
ds [ x ] = ' '
elif ds [ x ] is None :
ds [ x ] = ' '
2013-02-06 15:41:34 +00:00
if not isinstance ( ds [ x ] , basestring ) :
raise errors . AnsibleError ( " action specified for task %s has invalid type %s " % ( ds . get ( ' name ' , " %s : %s " % ( x , ds [ x ] ) ) , type ( ds [ x ] ) ) )
2012-10-13 00:07:05 +00:00
ds [ ' action ' ] = x + " " + ds [ x ]
2012-10-09 04:21:13 +00:00
ds . pop ( x )
2012-10-13 00:07:05 +00:00
# code to allow "with_glob" and to reference a lookup plugin named glob
2012-10-25 13:10:33 +00:00
elif x . startswith ( " with_ " ) :
2013-10-11 23:04:26 +00:00
2013-10-19 18:24:12 +00:00
if isinstance ( ds [ x ] , basestring ) and ds [ x ] . lstrip ( ) . startswith ( " {{ " ) :
2014-03-09 05:19:28 +00:00
utils . warning ( " It is unnecessary to use ' {{ ' in loops, leave variables in loop expressions bare. " )
2013-10-11 23:04:26 +00:00
2012-10-13 00:07:05 +00:00
plugin_name = x . replace ( " with_ " , " " )
2012-11-01 23:41:50 +00:00
if plugin_name in utils . plugins . lookup_loader :
2012-10-13 00:07:05 +00:00
ds [ ' items_lookup_plugin ' ] = plugin_name
ds [ ' items_lookup_terms ' ] = ds [ x ]
ds . pop ( x )
else :
raise errors . AnsibleError ( " cannot find lookup plugin named %s for usage in with_ %s " % ( plugin_name , plugin_name ) )
2013-09-09 23:34:01 +00:00
elif x in [ ' changed_when ' , ' failed_when ' , ' when ' ] :
2013-10-19 18:24:12 +00:00
if isinstance ( ds [ x ] , basestring ) and ds [ x ] . lstrip ( ) . startswith ( " {{ " ) :
2014-03-09 05:19:28 +00:00
utils . warning ( " It is unnecessary to use ' {{ ' in conditionals, leave variables in loop expressions bare. " )
2012-10-27 21:55:35 +00:00
elif x . startswith ( " when_ " ) :
2014-01-28 16:38:52 +00:00
utils . deprecated ( " The ' when_ ' conditional has been removed. Switch to using the regular unified ' when ' statements as described on docs.ansible.com. " , " 1.5 " , removed = True )
2013-10-11 22:37:39 +00:00
2013-02-07 07:11:30 +00:00
if ' when ' in ds :
2013-10-19 17:02:57 +00:00
raise errors . AnsibleError ( " multiple when_* statements specified in task %s " % ( ds . get ( ' name ' , ds [ ' action ' ] ) ) )
2012-10-27 21:55:35 +00:00
when_name = x . replace ( " when_ " , " " )
ds [ ' when ' ] = " %s %s " % ( when_name , ds [ x ] )
ds . pop ( x )
2012-10-09 04:21:13 +00:00
elif not x in Task . VALID_KEYS :
2012-07-17 23:09:36 +00:00
raise errors . AnsibleError ( " %s is not a legal parameter in an Ansible task or handler " % x )
2013-08-29 22:21:28 +00:00
self . module_vars = module_vars
2013-09-01 15:52:42 +00:00
self . default_vars = default_vars
2013-08-29 22:21:28 +00:00
self . play = play
2012-07-15 13:17:04 +00:00
# load various attributes
2012-08-19 20:52:55 +00:00
self . name = ds . get ( ' name ' , None )
self . tags = [ ' all ' ]
self . register = ds . get ( ' register ' , None )
2012-10-27 20:46:33 +00:00
self . sudo = utils . boolean ( ds . get ( ' sudo ' , play . sudo ) )
2014-01-22 17:04:19 +00:00
self . su = utils . boolean ( ds . get ( ' su ' , play . su ) )
2013-02-10 18:05:58 +00:00
self . environment = ds . get ( ' environment ' , { } )
2013-09-20 20:46:34 +00:00
self . role_name = role_name
2014-01-31 22:09:10 +00:00
self . no_log = utils . boolean ( ds . get ( ' no_log ' , " false " ) )
2013-09-24 08:29:38 +00:00
#Code to allow do until feature in a Task
if ' until ' in ds :
if not ds . get ( ' register ' ) :
raise errors . AnsibleError ( " register keyword is mandatory when using do until feature " )
self . module_vars [ ' delay ' ] = ds . get ( ' delay ' , 5 )
self . module_vars [ ' retries ' ] = ds . get ( ' retries ' , 3 )
self . module_vars [ ' register ' ] = ds . get ( ' register ' , None )
2014-01-04 00:13:21 +00:00
self . until = ds . get ( ' until ' )
self . module_vars [ ' until ' ] = self . until
2012-10-27 20:46:33 +00:00
2013-02-17 20:01:49 +00:00
# rather than simple key=value args on the options line, these represent structured data and the values
# can be hashes and lists, not just scalars
self . args = ds . get ( ' args ' , { } )
2013-10-21 16:07:51 +00:00
# get remote_user for task, then play, then playbook
if ds . get ( ' remote_user ' ) is not None :
self . remote_user = ds . get ( ' remote_user ' )
elif ds . get ( ' remote_user ' , play . remote_user ) is not None :
self . remote_user = ds . get ( ' remote_user ' , play . remote_user )
else :
self . remote_user = ds . get ( ' remote_user ' , play . playbook . remote_user )
2013-08-23 21:20:13 +00:00
2014-01-21 01:19:03 +00:00
self . sudo_user = None
self . sudo_pass = None
self . su_user = None
self . su_pass = None
2012-10-27 20:46:33 +00:00
if self . sudo :
2013-06-14 13:55:38 +00:00
self . sudo_user = ds . get ( ' sudo_user ' , play . sudo_user )
2012-10-12 16:39:45 +00:00
self . sudo_pass = ds . get ( ' sudo_pass ' , play . playbook . sudo_pass )
2014-01-21 01:19:03 +00:00
elif self . su :
self . su_user = ds . get ( ' su_user ' , play . su_user )
self . su_pass = ds . get ( ' su_pass ' , play . playbook . su_pass )
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 ' ) or ds . get ( ' sudo_pass ' ) ) and \
( ds . get ( ' su ' ) or ds . get ( ' su_user ' ) or ds . get ( ' su_pass ' ) ) :
raise errors . AnsibleError ( ' sudo params ( " sudo " , " sudo_user " , " sudo_pass " ) '
' and su params " su " , " su_user " , " su_pass " ) '
' cannot be used together ' )
2012-08-19 20:52:55 +00:00
# Both are defined
if ( ' action ' in ds ) and ( ' local_action ' in ds ) :
raise errors . AnsibleError ( " the ' action ' and ' local_action ' attributes can not be used together " )
# Both are NOT defined
elif ( not ' action ' in ds ) and ( not ' local_action ' in ds ) :
2012-11-13 11:53:42 +00:00
raise errors . AnsibleError ( " ' action ' or ' local_action ' attribute missing in task \" %s \" " % ds . get ( ' name ' , ' <Unnamed> ' ) )
2012-08-19 20:52:55 +00:00
# Only one of them is defined
elif ' local_action ' in ds :
self . action = ds . get ( ' local_action ' , ' ' )
2012-09-17 18:28:14 +00:00
self . delegate_to = ' 127.0.0.1 '
2012-08-19 20:52:55 +00:00
else :
self . action = ds . get ( ' action ' , ' ' )
self . delegate_to = ds . get ( ' delegate_to ' , None )
2013-01-03 20:27:01 +00:00
self . transport = ds . get ( ' connection ' , ds . get ( ' transport ' , play . transport ) )
2012-07-11 23:51:26 +00:00
2013-02-28 13:12:36 +00:00
if isinstance ( self . action , dict ) :
if ' module ' not in self . action :
raise errors . AnsibleError ( " ' module ' attribute missing from action in task \" %s \" " % ds . get ( ' name ' , ' %s ' % self . action ) )
if self . args :
raise errors . AnsibleError ( " ' args ' cannot be combined with dict ' action ' in task \" %s \" " % ds . get ( ' name ' , ' %s ' % self . action ) )
self . args = self . action
self . action = self . args . pop ( ' module ' )
2012-09-25 19:47:17 +00:00
# delegate_to can use variables
if not ( self . delegate_to is None ) :
2012-10-31 00:42:07 +00:00
# delegate_to: localhost should use local transport
if self . delegate_to in [ ' 127.0.0.1 ' , ' localhost ' ] :
self . transport = ' local '
2012-09-25 19:47:17 +00:00
2012-07-15 13:17:04 +00:00
# notified by is used by Playbook code to flag which hosts
# need to run a notifier
2012-05-26 04:37:34 +00:00
self . notified_by = [ ]
2012-07-15 13:17:04 +00:00
# if no name is specified, use the action line as the name
2012-05-26 04:37:34 +00:00
if self . name is None :
self . name = self . action
2012-07-15 13:17:04 +00:00
# load various attributes
2012-10-27 21:55:35 +00:00
self . when = ds . get ( ' when ' , None )
2013-07-14 19:07:45 +00:00
self . changed_when = ds . get ( ' changed_when ' , None )
2013-09-09 23:34:01 +00:00
self . failed_when = ds . get ( ' failed_when ' , None )
2014-03-20 20:09:58 +00:00
self . async_seconds = ds . get ( ' async ' , 0 ) # not async by default
self . async_seconds = template . template_from_string ( play . basedir , self . async_seconds , self . module_vars )
self . async_seconds = int ( self . async_seconds )
self . async_poll_interval = ds . get ( ' poll ' , 10 ) # default poll = 10 seconds
self . async_poll_interval = template . template_from_string ( play . basedir , self . async_poll_interval , self . module_vars )
self . async_poll_interval = int ( self . async_poll_interval )
2012-05-26 04:37:34 +00:00
self . notify = ds . get ( ' notify ' , [ ] )
2012-07-15 13:17:04 +00:00
self . first_available_file = ds . get ( ' first_available_file ' , None )
2012-08-18 12:46:51 +00:00
2012-10-13 00:07:05 +00:00
self . items_lookup_plugin = ds . get ( ' items_lookup_plugin ' , None )
self . items_lookup_terms = ds . get ( ' items_lookup_terms ' , None )
2012-08-01 16:13:07 +00:00
self . ignore_errors = ds . get ( ' ignore_errors ' , False )
2013-03-28 06:17:01 +00:00
self . any_errors_fatal = ds . get ( ' any_errors_fatal ' , play . any_errors_fatal )
2012-08-07 00:07:02 +00:00
2013-08-20 21:09:44 +00:00
self . always_run = ds . get ( ' always_run ' , False )
2012-11-07 13:59:39 +00:00
# action should be a string
if not isinstance ( self . action , basestring ) :
raise errors . AnsibleError ( " action is of type ' %s ' and not a string in task. name: %s " % ( type ( self . action ) . __name__ , self . name ) )
2012-07-15 13:17:04 +00:00
# notify can be a string or a list, store as a list
2012-05-26 04:37:34 +00:00
if isinstance ( self . notify , basestring ) :
self . notify = [ self . notify ]
2012-07-15 13:17:04 +00:00
# split the action line into a module name + arguments
2012-05-26 04:37:34 +00:00
tokens = self . action . split ( None , 1 )
if len ( tokens ) < 1 :
2012-08-02 12:37:43 +00:00
raise errors . AnsibleError ( " invalid/missing action in task. name: %s " % self . name )
2012-05-26 04:37:34 +00:00
self . module_name = tokens [ 0 ]
self . module_args = ' '
if len ( tokens ) > 1 :
self . module_args = tokens [ 1 ]
2012-07-15 13:17:04 +00:00
import_tags = self . module_vars . get ( ' tags ' , [ ] )
2013-10-07 19:18:51 +00:00
if type ( import_tags ) in [ int , float ] :
import_tags = str ( import_tags )
elif type ( import_tags ) in [ str , unicode ] :
2012-07-15 13:17:04 +00:00
# allow the user to list comma delimited tags
import_tags = import_tags . split ( " , " )
2012-05-26 05:49:23 +00:00
2012-07-15 13:17:04 +00:00
# handle mutually incompatible options
2012-10-25 13:10:33 +00:00
incompatibles = [ x for x in [ self . first_available_file , self . items_lookup_plugin ] if x is not None ]
2012-10-13 00:07:05 +00:00
if len ( incompatibles ) > 1 :
2012-10-25 13:10:33 +00:00
raise errors . AnsibleError ( " with_(plugin), and first_available_file are mutually incompatible in a single task " )
2012-07-15 13:17:04 +00:00
# make first_available_file accessable to Runner code
if self . first_available_file :
self . module_vars [ ' first_available_file ' ] = self . first_available_file
2012-08-07 00:07:02 +00:00
2012-10-13 00:07:05 +00:00
if self . items_lookup_plugin is not None :
self . module_vars [ ' items_lookup_plugin ' ] = self . items_lookup_plugin
self . module_vars [ ' items_lookup_terms ' ] = self . items_lookup_terms
2012-08-18 12:46:51 +00:00
# allow runner to see delegate_to option
self . module_vars [ ' delegate_to ' ] = self . delegate_to
2013-08-20 21:09:44 +00:00
# make some task attributes accessible to Runner code
2012-08-01 16:13:07 +00:00
self . module_vars [ ' ignore_errors ' ] = self . ignore_errors
2013-07-14 19:07:45 +00:00
self . module_vars [ ' register ' ] = self . register
self . module_vars [ ' changed_when ' ] = self . changed_when
2013-09-09 23:34:01 +00:00
self . module_vars [ ' failed_when ' ] = self . failed_when
2013-08-20 21:09:44 +00:00
self . module_vars [ ' always_run ' ] = self . always_run
2012-08-01 16:13:07 +00:00
2012-07-15 16:29:53 +00:00
# tags allow certain parts of a playbook to be run without running the whole playbook
2012-07-11 23:51:26 +00:00
apply_tags = ds . get ( ' tags ' , None )
if apply_tags is not None :
if type ( apply_tags ) in [ str , unicode ] :
self . tags . append ( apply_tags )
2013-10-07 19:18:51 +00:00
elif type ( apply_tags ) in [ int , float ] :
self . tags . append ( str ( apply_tags ) )
2012-07-11 23:51:26 +00:00
elif type ( apply_tags ) == list :
self . tags . extend ( apply_tags )
2012-07-12 00:30:30 +00:00
self . tags . extend ( import_tags )
2012-08-07 00:07:02 +00:00
2013-01-03 23:15:13 +00:00
if additional_conditions :
2013-10-12 00:43:16 +00:00
new_conditions = additional_conditions
2014-01-04 00:13:21 +00:00
new_conditions . append ( self . when )
self . when = new_conditions