From 9cd492befea21be4df1865a36076dfaf1e446c74 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Wed, 18 Apr 2012 22:43:17 -0400 Subject: [PATCH] make all templating happen locally, so no jinja2 deps are ever required --- lib/ansible/runner.py | 91 ++++++++++++++------------------ library/template | 120 ++---------------------------------------- 2 files changed, 43 insertions(+), 168 deletions(-) diff --git a/lib/ansible/runner.py b/lib/ansible/runner.py index 883c18860d..0b6ede1004 100755 --- a/lib/ansible/runner.py +++ b/lib/ansible/runner.py @@ -28,6 +28,7 @@ import traceback import tempfile import subprocess import getpass +import base64 import ansible.constants as C import ansible.connection @@ -456,13 +457,7 @@ class Runner(object): if source is None or dest is None: return (host, True, dict(failed=True, msg="src and dest are required"), '') - if metadata is None: - if self.remote_user == 'root': - metadata = '/etc/ansible/setup' - else: - metadata = '~/.ansible/setup' - - # apply templating to source argument + # apply templating to source argument so vars can be used in the path inject = self.setup_cache.get(conn.host,{}) source = utils.template(source, inject) @@ -470,53 +465,45 @@ class Runner(object): if not self.is_playbook: - # templating remotely, since we don't have the benefit of SETUP_CACHE - # TODO: maybe just fetch the setup file to a tempfile - - # first copy the source template over - temppath = tmp + os.path.split(source)[-1] - conn.put_file(utils.path_dwim(self.basedir, source), temppath) - - # install the template module - template_module = self._transfer_module(conn, tmp, 'template') - - # transfer module vars - if self.module_vars: - vars = utils.bigjson(self.module_vars) - vars_path = self._transfer_str(conn, tmp, 'module_vars', vars) - vars_arg=" vars=%s"%(vars_path) - else: - vars_arg="" - - # run the template module - args = "src=%s dest=%s metadata=%s%s" % (temppath, dest, metadata, vars_arg) - (result1, err, executed) = self._execute_module(conn, tmp, template_module, args) - (host, ok, data, err) = self._return_from_module(conn, host, result1, err, executed) - - else: - # templating LOCALLY to avoid Jinja2 dependency on nodes inside playbook runs - # non-playbook path can be moved to use this IF it is willing to fetch - # the metadata file first - - # install the template module - copy_module = self._transfer_module(conn, tmp, 'copy') - - # playbooks can template locally to avoid the jinja2 dependency - source_data = file(utils.path_dwim(self.basedir, source)).read() - - resultant = '' - try: - resultant = utils.template(source_data, inject) - except Exception, e: - return (host, False, dict(failed=True, msg=str(e)), '') - xfered = self._transfer_str(conn, tmp, 'source', resultant) + # not running from a playbook so we have to fetch the remote + # setup file contents before proceeding... + if metadata is None: + if self.remote_user == 'root': + metadata = '/etc/ansible/setup' + else: + metadata = '~/.ansible/setup' - # run the COPY module - args = "src=%s dest=%s" % (xfered, dest) - (result1, err, executed) = self._execute_module(conn, tmp, copy_module, args) - (host, ok, data, err) = self._return_from_module(conn, host, result1, err, executed) - + # install the template module + slurp_module = self._transfer_module(conn, tmp, 'slurp') + # run the slurp module to get the metadata file + args = "src=%s" % metadata + (result1, err, executed) = self._execute_module(conn, tmp, slurp_module, args) + result1 = utils.json_loads(result1) + if not 'content' in result1 or result1.get('encoding','base64') != 'base64': + result1['failed'] = True + return self._return_from_module(conn, host, result1, err, executed) + content = base64.b64decode(result1['content']) + inject = utils.json_loads(content) + + # install the template module + copy_module = self._transfer_module(conn, tmp, 'copy') + + # template the source data locally + source_data = file(utils.path_dwim(self.basedir, source)).read() + resultant = '' + try: + resultant = utils.template(source_data, inject) + except Exception, e: + return (host, False, dict(failed=True, msg=str(e)), '') + xfered = self._transfer_str(conn, tmp, 'source', resultant) + + # run the COPY module + args = "src=%s dest=%s" % (xfered, dest) + (result1, err, executed) = self._execute_module(conn, tmp, copy_module, args) + (host, ok, data, err) = self._return_from_module(conn, host, result1, err, executed) + + # modify file attribs if needed if ok: return self._chain_file_module(conn, tmp, data, err, options, executed) else: diff --git a/library/template b/library/template index 31a2095e50..a290899c5c 100755 --- a/library/template +++ b/library/template @@ -17,120 +17,8 @@ # You should have received a copy of the GNU General Public License # along with Ansible. If not, see . -import sys -import os -import jinja2 -import shlex -try: - import json -except ImportError: - import simplejson as json - -environment = jinja2.Environment() - -# =========================================== -# convert arguments of form a=b c=d -# to a dictionary -# FIXME: make more idiomatic - -if len(sys.argv) == 1: - sys.exit(1) -argfile = sys.argv[1] -if not os.path.exists(argfile): - sys.exit(1) -items = shlex.split(open(argfile, 'r').read()) - -params = {} -for x in items: - (k, v) = x.split("=") - params[k] = v - -source = params['src'] -dest = params['dest'] -metadata = params.get('metadata', '/etc/ansible/setup') -metadata = os.path.expanduser(metadata) -module_vars = params.get('vars') - -# raise an error if there is no template metadata -if not os.path.exists(metadata): - print json.dumps({ - "failed" : 1, - "msg" : "Missing %s, did you run the setup module yet?" % metadata - }) - sys.exit(1) - -# raise an error if we can't parse the template metadata -#data = {} -try: - f = open(metadata) - data = json.loads(f.read()) - f.close() -except: - print json.dumps({ - "failed" : 1, - "msg" : "Failed to parse/load %s, rerun the setup module?" % metadata - }) - sys.exit(1) - -if module_vars: - try: - f = open(module_vars) - vars = json.loads(f.read()) - data.update(vars) - f.close() - except: - print json.dumps({ - "failed" : 1, - "msg" : "Failed to parse/load %s." % module_vars - }) - sys.exit(1) - -if not os.path.exists(source): - print json.dumps({ - "failed" : 1, - "msg" : "Source template could not be read: %s" % source - }) - sys.exit(1) - -source = file(source).read() - -if os.path.isdir(dest): - print json.dumps({ - "failed" : 1, - "msg" : "Destination is a directory" - }) - sys.exit(1) - -# record md5sum of original source file so we can report if it changed -changed = False -md5sum = None -if os.path.exists(dest): - md5sum = os.popen("md5sum %s" % dest).read().split()[0] - -try: - # call Jinja2 here and save the new template file - template = environment.from_string(source) - data_out = template.render(data) -except jinja2.TemplateError, e: - print json.dumps({ - "failed": True, - "msg" : e.message - }) - sys.exit(1) -f = open(dest, "w+") -f.write(data_out) -f.close() - -# record m5sum and return success and whether things have changed -md5sum2 = os.popen("md5sum %s" % dest).read().split()[0] - -if md5sum != md5sum2: - changed = True - -# mission accomplished -print json.dumps({ - "md5sum" : md5sum2, - "changed" : changed -}) - +# hey the Ansible template module isn't really a remote transferred +# module. All the magic happens in Runner.py making use of the +# copy module, and if not running from a playbook, also the 'slurp' +# module.