From 9f6d98844e2742d89dcfd0e580ab5eebbb6dba25 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Fri, 30 Mar 2012 19:06:14 -0400 Subject: [PATCH] If the inventory file is EXECUTABLE, treat it as a file returning JSON, if called directly, return the host and groups list. If called with an argument of a host name, return the key=value data for that particular host. --- bin/ansible | 2 -- lib/ansible/runner.py | 77 +++++++++++++++++++++++++++++++++---------- 2 files changed, 60 insertions(+), 19 deletions(-) diff --git a/bin/ansible b/bin/ansible index fccd6adb43d..caaeb644d6d 100755 --- a/bin/ansible +++ b/bin/ansible @@ -200,8 +200,6 @@ if __name__ == '__main__': # Generic handler for ansible specific errors print "ERROR: %s" % str(e) sys.exit(1) - except Exception as e2: - print e2.__class__ else: cli.output(runner, results, options, args) diff --git a/lib/ansible/runner.py b/lib/ansible/runner.py index 69bd3693b93..8bdb9b1352e 100755 --- a/lib/ansible/runner.py +++ b/lib/ansible/runner.py @@ -29,6 +29,7 @@ import random import jinja2 import traceback import tempfile +import subprocess # FIXME: stop importing *, use as utils/errors from ansible.utils import * @@ -135,24 +136,41 @@ class Runner(object): if not os.path.exists(host_list): raise AnsibleFileNotFound("inventory file not found: %s" % host_list) - lines = file(host_list).read().split("\n") - groups = {} - groups['ungrouped'] = [] - group_name = 'ungrouped' results = [] - for item in lines: - item = item.lstrip().rstrip() - if item.startswith("#"): - # ignore commented out lines - continue - if item.startswith("["): - # looks like a group - group_name = item.replace("[","").replace("]","").lstrip().rstrip() - groups[group_name] = [] - else: - # looks like a regular host - groups[group_name].append(item) - results.append(item) + groups = { 'ungrouped' : [] } + if not os.access(host_list, os.X_OK): + # it's a regular file + lines = file(host_list).read().split("\n") + group_name = 'ungrouped' + results = [] + for item in lines: + item = item.lstrip().rstrip() + if item.startswith("#"): + # ignore commented out lines + continue + if item.startswith("["): + # looks like a group + group_name = item.replace("[","").replace("]","").lstrip().rstrip() + groups[group_name] = [] + else: + # looks like a regular host + groups[group_name].append(item) + results.append(item) + else: + host_list = os.path.abspath(host_list) + cls._external_variable_script = host_list + # it's a script -- expect a return of a JSON hash with group names keyed + # to lists of hosts + cmd = subprocess.Popen([host_list], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False) + out, err = cmd.communicate() + try: + groups = json.loads(out) + except: + raise AnsibleError("invalid JSON response from script: %s" % host_list) + for (groupname, hostlist) in groups.iteritems(): + for host in hostlist: + if host not in results: + results.append(host) return (results, groups) @@ -272,6 +290,31 @@ class Runner(object): args = " ".join(args) inject_vars = self.setup_cache.get(conn.host,{}) + # 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 + if Runner._external_variable_script is not None: + host = conn.host + cmd = subprocess.Popen([Runner._external_variable_script, host], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + shell=False + ) + out, err = cmd.communicate() + inject2 = {} + try: + inject2 = json.loads(out) + except: + raise AnsibleError("%s returned invalid result when called with hostname %s" % ( + Runner._external_variable_script, + host + )) + inject_vars.update(inject2) + + # store injected variables in the templates + if self.module_name == 'setup': + for (k,v) in inject2.iteritems(): + args = "%s %s=%s" % (args, k, v) + # the metadata location for the setup module is transparently managed # since it's an 'internals' module, kind of a black box. See playbook # other modules are not allowed to have this kind of handling