From e6406fa5a7a3ea4e521c911ec2eae77045f0848c Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Wed, 21 Mar 2012 20:00:48 -0400 Subject: [PATCH] Allow variable expressions to be stored as variables themselves, do some things to allow setup strings to more easily contain spaces without being mangled, which is neccessary because of the above. --- examples/playbooks/playbook4.yml | 32 +++++++++++++++++++++++++++----- lib/ansible/runner.py | 23 +++++++++++++++++------ library/setup | 8 +++++++- test/playbook1.events | 1 - 4 files changed, 51 insertions(+), 13 deletions(-) diff --git a/examples/playbooks/playbook4.yml b/examples/playbooks/playbook4.yml index 7fb6673cb0d..62cc0bc1cff 100644 --- a/examples/playbooks/playbook4.yml +++ b/examples/playbooks/playbook4.yml @@ -9,17 +9,39 @@ favcolor: "red" ssn: 8675309 +# Below we're going to define some expressions. +# +# Not only can we assign variables for reuse, but we can also assign conditional +# expressions. By keeping these in 'vars', the task section remains +# extraordinarily clean, and not littered with programming language +# constructs -- so it's easily skimmed by humans. +# +# Remember to quote any variables if they are not numbers! +# +# Interesting fact: aside from the $variables, these expressions are actually +# tiny bits of Python. They are evaluated in the context of each host, so different +# steps can be skipped on different hosts! They should evaluate to either True +# or False + + is_favcolor_blue: "'$favcolor' == 'blue'" +# is_centos: "'$facter_operatingsystem' == 'CentOS'" + +# NOTE: +# # facter and ohai variables can be used in only_if statements too -# ex: "$facter_operatingsystem == 'CentOS'", which bubble up automatically +# ex: "'$facter_operatingsystem' == 'CentOS'", which bubble up automatically # from the managed machines +# +# this example won't do that though, as you might not have facter or ohai, +# but you get the idea... tasks: - name: "do this if my favcolor is blue" action: shell /bin/false - only_if: "'$favcolor' == 'blue'" + only_if: '$is_favcolor_blue' - - name: "do this if my favcolor is red" - action: shell /bin/false - only_if: "'$favcolor' == 'red'" + - name: "do this if my favcolor is not blue" + action: shell /bin/true + only_if: 'not ($is_favcolor_blue)' diff --git a/lib/ansible/runner.py b/lib/ansible/runner.py index 0607bd39188..3e11700fdae 100755 --- a/lib/ansible/runner.py +++ b/lib/ansible/runner.py @@ -27,6 +27,7 @@ import random import traceback import tempfile import subprocess +import urllib import ansible.constants as C import ansible.connection @@ -295,16 +296,26 @@ class Runner(object): args = module_args if type(args) == list: - args = " ".join([ str(x) for x in module_args ]) - + if remote_module_path.endswith('setup'): + # briefly converting arguments to strings before file transfer + # causes some translation errors. This is a workaround only + # needed for the setup module + args = " ".join([ "\"%s\"" % str(x) for x in module_args ]) + else: + args = " ".join([ str(x) for x in module_args ]) + # by default the args to substitute in the action line are those from the setup cache inject_vars = self.setup_cache.get(conn.host,{}) # see if we really need to run this or not... - conditional = utils.template(self.conditionally_execute_if, inject_vars) - if not eval(conditional): - return utils.smjson(dict(skipped=True)) + # doubly templated so we can store a conditional expression in a variable! + conditional = utils.template( + utils.template(self.conditionally_execute_if, inject_vars), + inject_vars + ) + if not eval(conditional): + return [ utils.smjson(dict(skipped=True)), 'skipped' ] # if the host file was an external script, execute it with the hostname # as a first parameter to get the variables to use for the host @@ -333,7 +344,7 @@ class Runner(object): if not k.startswith('facter_') and not k.startswith('ohai_'): if str(v).find(" ") != -1: v = "\"%s\"" % v - args += " %s=%s" % (k, v) + args += " %s=%s" % (k, str(v).replace(" ","~~~")) # the metadata location for the setup module is transparently managed # since it's an 'internals' module, kind of a black box. See playbook diff --git a/library/setup b/library/setup index 6ab82f1a9d0..a2b90b7400d 100755 --- a/library/setup +++ b/library/setup @@ -33,12 +33,18 @@ except ImportError: if len(sys.argv) == 1: sys.exit(1) + argfile = sys.argv[1] if not os.path.exists(argfile): sys.exit(1) + input_data = shlex.split(open(argfile, 'r').read()) -new_options = dict([ x.split('=') for x in input_data ]) +# turn urlencoded k=v string (space delimited) to regular k=v directionary +splitted = [x.split('=',1) for x in input_data ] +splitted = [ (x[0], x[1].replace("~~~"," ")) for x in splitted ] +new_options = dict(splitted) + ansible_file = new_options.get('metadata', DEFAULT_ANSIBLE_SETUP) ansible_dir = os.path.dirname(ansible_file) diff --git a/test/playbook1.events b/test/playbook1.events index a4385a7d8fd..993b069d813 100644 --- a/test/playbook1.events +++ b/test/playbook1.events @@ -263,4 +263,3 @@ } } } -