Commit 1d75a29e by Michael DeHaan

Allow variables coming in from the playbook and the API to be expressed as…

Allow variables coming in from the playbook and the API to be expressed as dictionaries throughout their full life cycle
such that nested data can be made available in templates and playbooks.
parent 5a4d4bc0
......@@ -436,24 +436,17 @@ class PlayBook(object):
else:
self.callbacks.on_setup_primary()
# first run the setup task on every node, which gets the variables
# written to the JSON file and will also bubble facts back up via
# magic in Runner()
push_var_str=''
for (k,v) in vars.iteritems():
push_var_str += "%s=\"%s\" " % (k,v)
host_list = [ h for h in self.host_list if not (h in self.stats.failures or h in self.stats.dark) ]
# push any variables down to the system
setup_results = ansible.runner.Runner(
pattern=pattern, groups=self.groups, module_name='setup',
module_args=push_var_str, host_list=host_list,
module_args=vars, host_list=host_list,
forks=self.forks, module_path=self.module_path,
timeout=self.timeout, remote_user=user,
remote_pass=self.remote_pass, remote_port=self.remote_port,
setup_cache=SETUP_CACHE,
callbacks=self.runner_callbacks, sudo=sudo,
callbacks=self.runner_callbacks, sudo=sudo, debug=self.debug,
).run()
self.stats.compute(setup_results, setup=True)
......
......@@ -113,8 +113,8 @@ class Runner(object):
self.basedir = basedir
self.sudo = sudo
if type(self.module_args) != str:
raise Exception("module_args must be a string: %s" % self.module_args)
if type(self.module_args) != str and type(self.module_args) != dict:
raise Exception("module_args must be a string or dict: %s" % self.module_args)
self._tmp_paths = {}
random.seed()
......@@ -271,6 +271,9 @@ class Runner(object):
def _transfer_str(self, conn, tmp, name, args_str):
''' transfer arguments as a single file to be fed to the module. '''
if type(args_str) == dict:
args_str = utils.smjson(args_str)
args_fd, args_file = tempfile.mkstemp()
args_fo = os.fdopen(args_fd, 'w')
args_fo.write(args_str)
......@@ -316,23 +319,43 @@ class Runner(object):
def _add_setup_vars(self, inject, args):
''' setup module variables need special handling '''
is_dict = False
if type(args) == dict:
is_dict = True
# TODO: keep this as a dict through the whole path to simplify this code
for (k,v) in inject.iteritems():
if not k.startswith('facter_') and not k.startswith('ohai_'):
if str(v).find(" ") != -1:
v = "\"%s\"" % v
args += " %s=%s" % (k, str(v).replace(" ","~~~"))
if not is_dict:
if str(v).find(" ") != -1:
v = "\"%s\"" % v
args += " %s=%s" % (k, str(v).replace(" ","~~~"))
else:
args[k]=v
return args
# *****************************************************
def _add_setup_metadata(self, args):
''' automatically determine where to store variables for the setup module '''
if args.find("metadata=") == -1:
if self.remote_user == 'root':
args = "%s metadata=/etc/ansible/setup" % args
else:
args = "%s metadata=/home/%s/.ansible/setup" % (args, self.remote_user)
is_dict = False
if type(args) == dict:
is_dict = True
# TODO: keep this as a dict through the whole path to simplify this code
if not is_dict:
if args.find("metadata=") == -1:
if self.remote_user == 'root':
args = "%s metadata=/etc/ansible/setup" % args
else:
args = "%s metadata=/home/%s/.ansible/setup" % (args, self.remote_user)
else:
if not 'metadata' in args:
if self.remote_user == 'root':
args['metadata'] = '/etc/ansible/setup'
else:
args['metadata'] = "/home/%s/.ansible/setup" % (self.remote_user)
return args
# *****************************************************
......@@ -352,9 +375,11 @@ class Runner(object):
args = self._add_setup_vars(inject, args)
args = self._add_setup_metadata(args)
if type(args) == dict:
args = utils.bigjson(args)
args = utils.template(args, inject)
module_name_tail = remote_module_path.split("/")[-1]
client_executed_str = "%s %s" % (module_name_tail, args.strip())
argsfile = self._transfer_str(conn, tmp, 'arguments', args)
if async_jid is None:
......@@ -368,6 +393,7 @@ class Runner(object):
res, err = self._exec_command(conn, cmd, tmp, sudoable=True)
client_executed_str = "%s %s" % (module_name_tail, args.strip())
return ( res, err, client_executed_str )
# *****************************************************
......
......@@ -23,6 +23,7 @@ import sys
import os
import shlex
import subprocess
import traceback
try:
import json
......@@ -34,18 +35,22 @@ 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())
# 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)
setup_options = open(argfile).read().strip()
try:
setup_options = json.loads(setup_options)
except:
list_options = shlex.split(setup_options)
setup_options = {}
for opt in list_options:
(k,v) = opt.split("=")
setup_options[k]=v
ansible_file = setup_options.get('metadata', DEFAULT_ANSIBLE_SETUP)
ansible_dir = os.path.dirname(ansible_file)
# create the config dir if it doesn't exist
......@@ -74,7 +79,7 @@ if os.path.exists("/usr/bin/facter"):
facter = False
if facter:
for (k,v) in facter_ds.items():
new_options["facter_%s" % k] = v
setup_options["facter_%s" % k] = v
# ditto for ohai, but just top level string keys
# because it contains a lot of nested stuff we can't use for
......@@ -93,13 +98,13 @@ if os.path.exists("/usr/bin/ohai"):
for (k,v) in ohai_ds.items():
if type(v) == str or type(v) == unicode:
k2 = "ohai_%s" % k
new_options[k2] = v
setup_options[k2] = v
# write the template/settings file using
# instructions from server
f = open(ansible_file, "w+")
reformat = json.dumps(new_options, sort_keys=True, indent=4)
reformat = json.dumps(setup_options, sort_keys=True, indent=4)
f.write(reformat)
f.close()
......@@ -108,9 +113,9 @@ md5sum2 = os.popen("md5sum %s" % ansible_file).read().split()[0]
if md5sum != md5sum2:
changed = True
new_options['written'] = ansible_file
new_options['changed'] = changed
new_options['md5sum'] = md5sum2
setup_options['written'] = ansible_file
setup_options['changed'] = changed
setup_options['md5sum'] = md5sum2
print json.dumps(new_options)
print json.dumps(setup_options)
......@@ -15,7 +15,7 @@
"answer": "Wuh, I think so, Brain, but if we didn't have ears, we'd look like weasels.",
"changed": true,
"metadata": "/etc/ansible/setup",
"port": "5150",
"port": 5150,
"written": "/etc/ansible/setup"
}
]
......@@ -44,7 +44,7 @@
"cow": "moo",
"duck": "quack",
"metadata": "/etc/ansible/setup",
"port": "5150",
"port": 5150,
"testing": "default",
"written": "/etc/ansible/setup"
}
......@@ -233,6 +233,21 @@
[
"127.0.0.2",
{
"started": 1
}
]
],
[
"async poll",
[
"127.0.0.2"
]
],
[
"ok",
[
"127.0.0.2",
{
"changed": true,
"cmd": "sleep 5 ",
"finished": 1,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment