From 4104478abd3a56e7bbb0fae0f1ee53540a56d2bb Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Sat, 20 Oct 2012 12:12:07 -0400 Subject: [PATCH] Added 'script', a module that can push and insantly run a remote script. Replaces the need to copy+command. This is a bit of a rough draft, some error handling still needs to be added. --- hacking/module_formatter.py | 8 +-- lib/ansible/runner/action_plugins/script.py | 67 +++++++++++++++++++++ lib/ansible/utils.py | 6 +- library/postgresql_user | 2 +- library/script | 23 +++++++ 5 files changed, 98 insertions(+), 8 deletions(-) create mode 100644 lib/ansible/runner/action_plugins/script.py create mode 100755 library/script diff --git a/hacking/module_formatter.py b/hacking/module_formatter.py index 43f84dcb2f3..8653d994a88 100755 --- a/hacking/module_formatter.py +++ b/hacking/module_formatter.py @@ -29,6 +29,7 @@ import optparse import time import datetime import subprocess +import traceback # modules that are ok that they do not have documentation strings BLACKLIST_MODULES = [ @@ -149,11 +150,8 @@ def get_docstring(filename, verbose=False): doc = yaml.load(child.value.s) except: - if verbose: - raise - else: - print "unable to parse %s" % filename - + traceback.print_exc() + print "unable to parse %s" % filename return doc diff --git a/lib/ansible/runner/action_plugins/script.py b/lib/ansible/runner/action_plugins/script.py new file mode 100644 index 00000000000..d58e09946d3 --- /dev/null +++ b/lib/ansible/runner/action_plugins/script.py @@ -0,0 +1,67 @@ +# (c) 2012, Michael DeHaan +# +# 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 . + +import os +import pwd +import traceback +import shlex + +import ansible.constants as C +from ansible import utils +from ansible import errors +from ansible import module_common +from ansible.runner.return_data import ReturnData + +class ActionModule(object): + + def __init__(self, runner): + self.runner = runner + + def run(self, conn, tmp, module_name, module_args, inject): + ''' handler for file transfer operations ''' + + # load up options + options = utils.parse_kv(module_args) + + tokens = shlex.split(module_args) + source = tokens[0] + # FIXME: error handling + args = " ".join(tokens[1:]) + source = utils.template(self.runner.basedir, source, inject) + source = utils.path_dwim(self.runner.basedir, source) + + exec_rc = None + + # transfer the file to a remote tmp location + source = source.replace('\x00','') # why does this happen here? + args = args.replace('\x00','') # why does this happen here? + tmp_src = os.path.join(tmp, os.path.basename(source)) + tmp_src = tmp_src.replace('\x00', '') + + conn.put_file(source, tmp_src) + + # fix file permissions when the copy is done as a different user + if self.runner.sudo and self.runner.sudo_user != 'root': + self.runner._low_level_exec_command(conn, "chmod a+r %s" % tmp_src, tmp) + + # make executable + self.runner._low_level_exec_command(conn, "chmod +x %s" % tmp_src, tmp) + + # run it through the command module + module_args = tmp_src + " " + args + " #USE_SHELL" + return self.runner._execute_module(conn, tmp, 'command', module_args, inject=inject) + diff --git a/lib/ansible/utils.py b/lib/ansible/utils.py index a41689873c9..ba377e5befc 100644 --- a/lib/ansible/utils.py +++ b/lib/ansible/utils.py @@ -184,6 +184,8 @@ def json_loads(data): def parse_json(raw_data): ''' this version for module return data only ''' + + orig_data = raw_data # ignore stuff like tcgetattr spewage or other warnings data = filter_leading_non_json_lines(raw_data) @@ -202,7 +204,7 @@ def parse_json(raw_data): for t in tokens: if t.find("=") == -1: - raise errors.AnsibleError("failed to parse: %s" % raw_data) + raise errors.AnsibleError("failed to parse: %s" % orig_data) (key,value) = t.split("=", 1) if key == 'changed' or 'failed': if value.lower() in [ 'true', '1' ]: @@ -213,7 +215,7 @@ def parse_json(raw_data): value = int(value) results[key] = value if len(results.keys()) == 0: - return { "failed" : True, "parsed" : False, "msg" : raw_data } + return { "failed" : True, "parsed" : False, "msg" : orig_data } return results _LISTRE = re.compile(r"(\w+)\[(\d+)\]") diff --git a/library/postgresql_user b/library/postgresql_user index c72ff54a6cd..14b2d679076 100755 --- a/library/postgresql_user +++ b/library/postgresql_user @@ -94,7 +94,7 @@ examples: - code: postgresql_user db=acme user=django password=ceec4eif7ya priv=CONNECT/products:ALL description: Create django user and grant access to database and products table - code: postgresql_user user=rails password=secret role_attr_flags=CREATEDB,NOSUPERUSER - - description: Create rails user, grant privilege to create other databases and demote rails from super user status + description: Create rails user, grant privilege to create other databases and demote rails from super user status - code: postgresql_user db=acme user=test priv=ALL/products:ALL state=absent fail_on_user=no description: Remove test user privileges from acme - code: postgresql_user db=test user=test priv=ALL state=absent diff --git a/library/script b/library/script new file mode 100755 index 00000000000..3a1614e8087 --- /dev/null +++ b/library/script @@ -0,0 +1,23 @@ + +DOCUMENTATION = """ +--- +module: script +short_description: Runs a local script on a remote node +description: + - The command module takes the script name followed by a list of space-delimited arguments. + - The given command will be processed through the shell environment. +options: + free_form: + description: + - the command module takes a free form command to run + required: true + default: null + aliases: [] +examples: + - description: "Example from Ansible Playbooks" + code: "script: /some/local/script.sh --some-arguments 1234" +notes: + - It is preferable to write Ansible modules than pushing scripts. Convert your script to an ansible module for bonus points! +author: Michael DeHaan +""" +