2013-02-16 20:32:01 +00:00
|
|
|
# (c) 2012-2013, Michael DeHaan <michael.dehaan@gmail.com>
|
2012-02-24 04:28:58 +00:00
|
|
|
#
|
2012-02-29 00:08:09 +00:00
|
|
|
# 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 <http://www.gnu.org/licenses/>.
|
2012-03-03 03:03:03 +00:00
|
|
|
|
2012-02-24 04:28:58 +00:00
|
|
|
import multiprocessing
|
2012-02-27 05:43:02 +00:00
|
|
|
import signal
|
2012-02-24 04:28:58 +00:00
|
|
|
import os
|
2012-04-10 23:27:19 +00:00
|
|
|
import pwd
|
2012-02-28 05:45:37 +00:00
|
|
|
import Queue
|
2012-03-03 17:25:56 +00:00
|
|
|
import random
|
2012-03-14 00:59:05 +00:00
|
|
|
import traceback
|
2012-03-14 04:34:00 +00:00
|
|
|
import tempfile
|
2012-04-26 18:34:49 +00:00
|
|
|
import time
|
2012-07-15 14:57:22 +00:00
|
|
|
import collections
|
2012-08-09 01:19:20 +00:00
|
|
|
import socket
|
2012-09-27 03:50:54 +00:00
|
|
|
import base64
|
2012-10-11 11:43:56 +00:00
|
|
|
import sys
|
2012-11-27 20:58:32 +00:00
|
|
|
import shlex
|
2013-02-17 20:01:49 +00:00
|
|
|
import pipes
|
2012-03-13 03:11:54 +00:00
|
|
|
|
2012-08-07 00:07:02 +00:00
|
|
|
import ansible.constants as C
|
2012-04-13 12:39:54 +00:00
|
|
|
import ansible.inventory
|
2012-03-18 21:04:07 +00:00
|
|
|
from ansible import utils
|
2012-03-18 21:16:12 +00:00
|
|
|
from ansible import errors
|
2012-07-18 02:33:36 +00:00
|
|
|
from ansible import module_common
|
2012-06-01 01:44:30 +00:00
|
|
|
import poller
|
|
|
|
import connection
|
2012-08-18 13:30:33 +00:00
|
|
|
from return_data import ReturnData
|
2012-08-10 04:25:13 +00:00
|
|
|
from ansible.callbacks import DefaultRunnerCallbacks, vv
|
2012-08-07 00:07:02 +00:00
|
|
|
|
2012-03-29 00:32:04 +00:00
|
|
|
HAS_ATFORK=True
|
|
|
|
try:
|
|
|
|
from Crypto.Random import atfork
|
|
|
|
except ImportError:
|
|
|
|
HAS_ATFORK=False
|
2012-03-14 23:57:56 +00:00
|
|
|
|
2012-10-23 12:04:32 +00:00
|
|
|
multiprocessing_runner = None
|
2012-09-18 12:41:27 +00:00
|
|
|
|
2012-03-03 03:03:03 +00:00
|
|
|
################################################
|
2012-02-24 04:28:58 +00:00
|
|
|
|
2012-10-26 22:11:38 +00:00
|
|
|
def _executor_hook(job_queue, result_queue):
|
|
|
|
|
2012-10-23 12:19:15 +00:00
|
|
|
# attempt workaround of https://github.com/newsapps/beeswithmachineguns/issues/17
|
|
|
|
# this function also not present in CentOS 6
|
|
|
|
if HAS_ATFORK:
|
|
|
|
atfork()
|
2012-10-26 22:11:38 +00:00
|
|
|
|
2012-10-24 11:40:31 +00:00
|
|
|
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
2012-10-26 22:11:38 +00:00
|
|
|
while not job_queue.empty():
|
|
|
|
try:
|
|
|
|
host = job_queue.get(block=False)
|
|
|
|
result_queue.put(multiprocessing_runner._executor(host))
|
|
|
|
except Queue.Empty:
|
|
|
|
pass
|
|
|
|
except:
|
|
|
|
traceback.print_exc()
|
2012-10-23 12:19:15 +00:00
|
|
|
|
2012-10-08 18:26:58 +00:00
|
|
|
class HostVars(dict):
|
2012-10-13 00:07:05 +00:00
|
|
|
''' A special view of setup_cache that adds values from the inventory when needed. '''
|
|
|
|
|
2012-10-08 18:26:58 +00:00
|
|
|
def __init__(self, setup_cache, inventory):
|
|
|
|
self.setup_cache = setup_cache
|
|
|
|
self.inventory = inventory
|
|
|
|
self.lookup = {}
|
|
|
|
|
|
|
|
self.update(setup_cache)
|
|
|
|
|
|
|
|
def __getitem__(self, host):
|
|
|
|
if not host in self.lookup:
|
2012-10-13 00:07:05 +00:00
|
|
|
result = self.inventory.get_variables(host)
|
|
|
|
result.update(self.setup_cache.get(host, {}))
|
|
|
|
self.lookup[host] = result
|
|
|
|
return self.lookup[host]
|
2012-10-08 18:26:58 +00:00
|
|
|
|
2013-02-07 17:40:41 +00:00
|
|
|
def __contains__(self, host):
|
|
|
|
return host in self.lookup or host in self.setup_cache or self.inventory.get_host(host)
|
|
|
|
|
2012-02-24 04:28:58 +00:00
|
|
|
class Runner(object):
|
2012-07-15 14:22:15 +00:00
|
|
|
''' core API interface to ansible '''
|
2012-02-24 04:28:58 +00:00
|
|
|
|
2012-07-21 20:51:31 +00:00
|
|
|
# see bin/ansible for how this is used...
|
2012-03-22 03:39:09 +00:00
|
|
|
|
2012-08-07 00:07:02 +00:00
|
|
|
def __init__(self,
|
2012-07-21 20:51:31 +00:00
|
|
|
host_list=C.DEFAULT_HOST_LIST, # ex: /etc/ansible/hosts, legacy usage
|
2012-11-18 17:37:30 +00:00
|
|
|
module_path=None, # ex: /usr/share/ansible
|
2012-07-21 20:51:31 +00:00
|
|
|
module_name=C.DEFAULT_MODULE_NAME, # ex: copy
|
|
|
|
module_args=C.DEFAULT_MODULE_ARGS, # ex: "src=/tmp/a dest=/tmp/b"
|
|
|
|
forks=C.DEFAULT_FORKS, # parallelism level
|
2012-07-24 00:06:18 +00:00
|
|
|
timeout=C.DEFAULT_TIMEOUT, # SSH timeout
|
2012-07-21 20:51:31 +00:00
|
|
|
pattern=C.DEFAULT_PATTERN, # which hosts? ex: 'all', 'acme.example.org'
|
|
|
|
remote_user=C.DEFAULT_REMOTE_USER, # ex: 'username'
|
|
|
|
remote_pass=C.DEFAULT_REMOTE_PASS, # ex: 'password123' or None if using key
|
2012-10-08 23:03:37 +00:00
|
|
|
remote_port=None, # if SSH on different ports
|
2012-08-07 00:07:02 +00:00
|
|
|
private_key_file=C.DEFAULT_PRIVATE_KEY_FILE, # if not using keys/passwords
|
2012-07-21 20:51:31 +00:00
|
|
|
sudo_pass=C.DEFAULT_SUDO_PASS, # ex: 'password123' or None
|
|
|
|
background=0, # async poll every X seconds, else 0 for non-async
|
|
|
|
basedir=None, # directory of playbook, if applicable
|
|
|
|
setup_cache=None, # used to share fact data w/ other tasks
|
|
|
|
transport=C.DEFAULT_TRANSPORT, # 'ssh', 'paramiko', 'local'
|
|
|
|
conditional='True', # run only if this fact expression evals to true
|
|
|
|
callbacks=None, # used for output
|
|
|
|
sudo=False, # whether to run sudo or not
|
|
|
|
sudo_user=C.DEFAULT_SUDO_USER, # ex: 'root'
|
2012-08-07 00:07:02 +00:00
|
|
|
module_vars=None, # a playbooks internals thing
|
2012-07-21 20:51:31 +00:00
|
|
|
is_playbook=False, # running from playbook or not?
|
2012-08-11 18:05:24 +00:00
|
|
|
inventory=None, # reference to Inventory object
|
2013-02-04 00:46:25 +00:00
|
|
|
subset=None, # subset pattern
|
2013-02-08 03:51:33 +00:00
|
|
|
check=False, # don't make any changes, just try to probe for potential changes
|
2013-02-10 18:05:58 +00:00
|
|
|
diff=False, # whether to show diffs for template files that change
|
2013-02-17 20:01:49 +00:00
|
|
|
environment=None, # environment variables (as dict) to use inside the command
|
|
|
|
complex_args=None # structured data in addition to module_args, must be a dict
|
2012-07-21 20:51:31 +00:00
|
|
|
):
|
|
|
|
|
2013-02-17 20:01:49 +00:00
|
|
|
if not complex_args:
|
|
|
|
complex_args = {}
|
|
|
|
|
2012-07-21 20:51:31 +00:00
|
|
|
# storage & defaults
|
2013-02-04 00:46:25 +00:00
|
|
|
self.check = check
|
2013-02-08 03:51:33 +00:00
|
|
|
self.diff = diff
|
2012-07-21 20:51:31 +00:00
|
|
|
self.setup_cache = utils.default(setup_cache, lambda: collections.defaultdict(dict))
|
|
|
|
self.basedir = utils.default(basedir, lambda: os.getcwd())
|
2012-08-09 01:13:31 +00:00
|
|
|
self.callbacks = utils.default(callbacks, lambda: DefaultRunnerCallbacks())
|
2012-07-21 20:51:31 +00:00
|
|
|
self.generated_jid = str(random.randint(0, 999999999999))
|
|
|
|
self.transport = transport
|
|
|
|
self.inventory = utils.default(inventory, lambda: ansible.inventory.Inventory(host_list))
|
2012-08-11 18:05:24 +00:00
|
|
|
|
2012-07-21 20:51:31 +00:00
|
|
|
self.module_vars = utils.default(module_vars, lambda: {})
|
2012-07-15 15:54:39 +00:00
|
|
|
self.sudo_user = sudo_user
|
|
|
|
self.connector = connection.Connection(self)
|
|
|
|
self.conditional = conditional
|
|
|
|
self.module_name = module_name
|
|
|
|
self.forks = int(forks)
|
|
|
|
self.pattern = pattern
|
|
|
|
self.module_args = module_args
|
|
|
|
self.timeout = timeout
|
|
|
|
self.remote_user = remote_user
|
|
|
|
self.remote_pass = remote_pass
|
|
|
|
self.remote_port = remote_port
|
2012-05-14 20:14:38 +00:00
|
|
|
self.private_key_file = private_key_file
|
2012-07-15 15:54:39 +00:00
|
|
|
self.background = background
|
|
|
|
self.sudo = sudo
|
|
|
|
self.sudo_pass = sudo_pass
|
|
|
|
self.is_playbook = is_playbook
|
2013-02-10 18:05:58 +00:00
|
|
|
self.environment = environment
|
2013-02-17 20:01:49 +00:00
|
|
|
self.complex_args = complex_args
|
2012-03-31 02:47:58 +00:00
|
|
|
|
2012-07-21 20:51:31 +00:00
|
|
|
# misc housekeeping
|
2012-08-11 18:05:24 +00:00
|
|
|
if subset and self.inventory._subset is None:
|
|
|
|
# don't override subset when passed from playbook
|
|
|
|
self.inventory.subset(subset)
|
|
|
|
|
2012-07-21 20:51:31 +00:00
|
|
|
if self.transport == 'local':
|
|
|
|
self.remote_user = pwd.getpwuid(os.geteuid())[0]
|
2012-08-07 00:07:02 +00:00
|
|
|
|
2012-11-18 17:37:30 +00:00
|
|
|
if module_path is not None:
|
|
|
|
for i in module_path.split(os.pathsep):
|
|
|
|
utils.plugins.module_finder.add_directory(i)
|
|
|
|
|
2012-11-18 17:36:37 +00:00
|
|
|
utils.plugins.push_basedir(self.basedir)
|
|
|
|
|
2012-07-21 20:51:31 +00:00
|
|
|
# ensure we are using unique tmp paths
|
2012-03-03 17:25:56 +00:00
|
|
|
random.seed()
|
2013-02-17 20:01:49 +00:00
|
|
|
|
|
|
|
# *****************************************************
|
|
|
|
|
|
|
|
def _complex_args_hack(self, complex_args, module_args):
|
|
|
|
"""
|
|
|
|
ansible-playbook both allows specifying key=value string arguments and complex arguments
|
|
|
|
however not all modules use our python common module system and cannot
|
|
|
|
access these. An example might be a Bash module. This hack allows users to still pass "args"
|
|
|
|
as a hash of simple scalars to those arguments and is short term. We could technically
|
|
|
|
just feed JSON to the module, but that makes it hard on Bash consumers. The way this is implemented
|
|
|
|
it does mean values in 'args' have LOWER priority than those on the key=value line, allowing
|
|
|
|
args to provide yet another way to have pluggable defaults.
|
|
|
|
"""
|
|
|
|
if complex_args is None:
|
|
|
|
return module_args
|
|
|
|
if type(complex_args) != dict:
|
|
|
|
raise errors.AnsibleError("complex arguments are not a dictionary: %s" % complex_args)
|
|
|
|
for (k,v) in complex_args.iteritems():
|
|
|
|
if isinstance(v, basestring):
|
|
|
|
module_args = "%s=%s %s" % (k, pipes.quote(v), module_args)
|
|
|
|
return module_args
|
2012-03-22 03:39:09 +00:00
|
|
|
|
2012-03-14 00:59:05 +00:00
|
|
|
# *****************************************************
|
|
|
|
|
2012-04-19 02:19:25 +00:00
|
|
|
def _transfer_str(self, conn, tmp, name, data):
|
|
|
|
''' transfer string to remote file '''
|
2012-03-20 23:55:04 +00:00
|
|
|
|
2012-04-19 02:19:25 +00:00
|
|
|
if type(data) == dict:
|
2012-07-15 14:12:49 +00:00
|
|
|
data = utils.jsonify(data)
|
2012-03-31 02:28:10 +00:00
|
|
|
|
2012-04-19 02:19:25 +00:00
|
|
|
afd, afile = tempfile.mkstemp()
|
|
|
|
afo = os.fdopen(afd, 'w')
|
2012-10-13 00:07:05 +00:00
|
|
|
try:
|
2013-03-09 03:27:45 +00:00
|
|
|
if not isinstance(data, unicode):
|
|
|
|
#ensure the data is valid UTF-8
|
|
|
|
data.decode('utf-8')
|
|
|
|
else:
|
|
|
|
data = data.encode('utf-8')
|
|
|
|
afo.write(data)
|
2012-10-13 00:07:05 +00:00
|
|
|
except:
|
|
|
|
raise errors.AnsibleError("failure encoding into utf-8")
|
2012-04-19 02:19:25 +00:00
|
|
|
afo.flush()
|
|
|
|
afo.close()
|
2012-03-31 02:28:10 +00:00
|
|
|
|
2012-04-19 02:19:25 +00:00
|
|
|
remote = os.path.join(tmp, name)
|
2012-07-15 16:29:53 +00:00
|
|
|
try:
|
|
|
|
conn.put_file(afile, remote)
|
|
|
|
finally:
|
|
|
|
os.unlink(afile)
|
2012-04-19 02:19:25 +00:00
|
|
|
return remote
|
2012-03-06 03:23:56 +00:00
|
|
|
|
2012-03-22 03:39:09 +00:00
|
|
|
# *****************************************************
|
2012-07-14 23:18:33 +00:00
|
|
|
|
2013-02-10 18:05:58 +00:00
|
|
|
def _compute_environment_string(self, inject=None):
|
|
|
|
''' what environment variables to use when running the command? '''
|
|
|
|
|
|
|
|
if not self.environment:
|
|
|
|
return ""
|
|
|
|
enviro = utils.template(self.basedir, self.environment, inject)
|
|
|
|
if type(enviro) != dict:
|
2013-02-25 21:31:49 +00:00
|
|
|
raise errors.AnsibleError("environment must be a dictionary, received %s" % enviro)
|
2013-02-10 18:05:58 +00:00
|
|
|
result = ""
|
|
|
|
for (k,v) in enviro.iteritems():
|
2013-02-25 21:31:49 +00:00
|
|
|
result = "%s=%s %s" % (k, pipes.quote(str(v)), result)
|
2013-02-10 18:05:58 +00:00
|
|
|
return result
|
|
|
|
|
|
|
|
# *****************************************************
|
|
|
|
|
2012-08-07 00:07:02 +00:00
|
|
|
def _execute_module(self, conn, tmp, module_name, args,
|
2013-02-17 20:01:49 +00:00
|
|
|
async_jid=None, async_module=None, async_limit=None, inject=None, persist_files=False, complex_args=None):
|
2012-07-14 23:18:33 +00:00
|
|
|
|
2012-03-22 03:39:09 +00:00
|
|
|
''' runs a module that has already been transferred '''
|
|
|
|
|
2012-09-27 03:50:54 +00:00
|
|
|
# hack to support fireball mode
|
|
|
|
if module_name == 'fireball':
|
2012-10-08 23:03:37 +00:00
|
|
|
args = "%s password=%s" % (args, base64.b64encode(str(utils.key_for_hostname(conn.host))))
|
|
|
|
if 'port' not in args:
|
|
|
|
args += " port=%s" % C.ZEROMQ_PORT
|
2012-09-27 03:50:54 +00:00
|
|
|
|
2013-02-17 20:01:49 +00:00
|
|
|
(remote_module_path, is_new_style, shebang) = self._copy_module(conn, tmp, module_name, args, inject, complex_args)
|
2012-10-21 02:51:36 +00:00
|
|
|
|
2013-02-10 18:05:58 +00:00
|
|
|
environment_string = self._compute_environment_string(inject)
|
|
|
|
|
2012-10-21 02:51:36 +00:00
|
|
|
cmd_mod = ""
|
2012-07-28 12:48:05 +00:00
|
|
|
if self.sudo and self.sudo_user != 'root':
|
|
|
|
# deal with possible umask issues once sudo'ed to other user
|
2012-10-24 15:30:49 +00:00
|
|
|
cmd_chmod = "chmod a+r %s" % remote_module_path
|
|
|
|
self._low_level_exec_command(conn, cmd_chmod, tmp, sudoable=False)
|
2012-04-11 00:58:40 +00:00
|
|
|
|
2012-07-23 23:14:37 +00:00
|
|
|
cmd = ""
|
|
|
|
if not is_new_style:
|
2013-02-04 00:46:25 +00:00
|
|
|
if 'CHECKMODE=True' in args:
|
|
|
|
# if module isn't using AnsibleModuleCommon infrastructure we can't be certain it knows how to
|
|
|
|
# do --check mode, so to be safe we will not run it.
|
|
|
|
return ReturnData(conn=conn, result=dict(skippped=True, msg="cannot run check mode against old-style modules"))
|
|
|
|
|
2012-09-17 12:02:30 +00:00
|
|
|
args = utils.template(self.basedir, args, inject)
|
2012-07-23 23:14:37 +00:00
|
|
|
argsfile = self._transfer_str(conn, tmp, 'arguments', args)
|
|
|
|
if async_jid is None:
|
|
|
|
cmd = "%s %s" % (remote_module_path, argsfile)
|
|
|
|
else:
|
|
|
|
cmd = " ".join([str(x) for x in [remote_module_path, async_jid, async_limit, async_module, argsfile]])
|
2012-03-14 23:57:56 +00:00
|
|
|
else:
|
2012-07-23 23:14:37 +00:00
|
|
|
if async_jid is None:
|
|
|
|
cmd = "%s" % (remote_module_path)
|
|
|
|
else:
|
|
|
|
cmd = " ".join([str(x) for x in [remote_module_path, async_jid, async_limit, async_module]])
|
2012-04-02 17:29:12 +00:00
|
|
|
|
2012-10-21 02:51:36 +00:00
|
|
|
if not shebang:
|
|
|
|
raise errors.AnsibleError("module is missing interpreter line")
|
|
|
|
|
2013-02-10 18:05:58 +00:00
|
|
|
cmd = " ".join([environment_string, shebang.replace("#!",""), cmd])
|
2013-02-08 03:51:33 +00:00
|
|
|
if tmp.find("tmp") != -1 and C.DEFAULT_KEEP_REMOTE_FILES != '1' and not persist_files:
|
2012-10-23 12:04:32 +00:00
|
|
|
cmd = cmd + "; rm -rf %s >/dev/null 2>&1" % tmp
|
2012-05-25 22:44:29 +00:00
|
|
|
res = self._low_level_exec_command(conn, cmd, tmp, sudoable=True)
|
2013-02-07 22:43:33 +00:00
|
|
|
data = utils.parse_json(res['stdout'])
|
|
|
|
if 'parsed' in data and data['parsed'] == False:
|
|
|
|
data['msg'] += res['stderr']
|
|
|
|
return ReturnData(conn=conn, result=data)
|
2012-03-22 03:39:09 +00:00
|
|
|
|
|
|
|
# *****************************************************
|
|
|
|
|
2012-02-25 22:16:23 +00:00
|
|
|
def _executor(self, host):
|
2012-07-15 14:57:22 +00:00
|
|
|
''' handler for multiprocessing library '''
|
|
|
|
|
2012-03-25 23:05:27 +00:00
|
|
|
try:
|
2012-05-25 22:44:29 +00:00
|
|
|
exec_rc = self._executor_internal(host)
|
|
|
|
if type(exec_rc) != ReturnData:
|
|
|
|
raise Exception("unexpected return type: %s" % type(exec_rc))
|
2012-07-20 12:56:33 +00:00
|
|
|
# redundant, right?
|
2012-07-20 15:15:57 +00:00
|
|
|
if not exec_rc.comm_ok:
|
|
|
|
self.callbacks.on_unreachable(host, exec_rc.result)
|
2012-05-25 22:44:29 +00:00
|
|
|
return exec_rc
|
2012-03-25 23:05:27 +00:00
|
|
|
except errors.AnsibleError, ae:
|
|
|
|
msg = str(ae)
|
|
|
|
self.callbacks.on_unreachable(host, msg)
|
2012-05-25 22:44:29 +00:00
|
|
|
return ReturnData(host=host, comm_ok=False, result=dict(failed=True, msg=msg))
|
2012-03-25 23:05:27 +00:00
|
|
|
except Exception:
|
|
|
|
msg = traceback.format_exc()
|
|
|
|
self.callbacks.on_unreachable(host, msg)
|
2012-05-25 22:44:29 +00:00
|
|
|
return ReturnData(host=host, comm_ok=False, result=dict(failed=True, msg=msg))
|
2012-03-25 23:05:27 +00:00
|
|
|
|
2012-07-15 14:22:15 +00:00
|
|
|
# *****************************************************
|
|
|
|
|
2012-03-25 23:05:27 +00:00
|
|
|
def _executor_internal(self, host):
|
2012-07-15 16:51:56 +00:00
|
|
|
''' executes any module one or more times '''
|
|
|
|
|
2012-07-18 00:08:45 +00:00
|
|
|
host_variables = self.inventory.get_variables(host)
|
2013-02-22 16:11:08 +00:00
|
|
|
host_connection = host_variables.get('ansible_connection', self.transport)
|
|
|
|
if host_connection in [ 'paramiko', 'ssh' ]:
|
2012-10-08 23:03:37 +00:00
|
|
|
port = host_variables.get('ansible_ssh_port', self.remote_port)
|
|
|
|
if port is None:
|
2012-10-19 00:14:15 +00:00
|
|
|
port = C.DEFAULT_REMOTE_PORT
|
2012-10-08 23:03:37 +00:00
|
|
|
else:
|
|
|
|
# fireball, local, etc
|
|
|
|
port = self.remote_port
|
2012-07-21 20:23:00 +00:00
|
|
|
|
2012-08-18 23:49:49 +00:00
|
|
|
inject = {}
|
2012-07-18 00:08:45 +00:00
|
|
|
inject.update(host_variables)
|
|
|
|
inject.update(self.module_vars)
|
2012-08-18 23:49:49 +00:00
|
|
|
inject.update(self.setup_cache[host])
|
2012-10-08 18:26:58 +00:00
|
|
|
inject['hostvars'] = HostVars(self.setup_cache, self.inventory)
|
2012-09-07 22:07:52 +00:00
|
|
|
inject['group_names'] = host_variables.get('group_names', [])
|
|
|
|
inject['groups'] = self.inventory.groups_list()
|
2013-03-19 17:04:57 +00:00
|
|
|
inject['vars'] = self.module_vars
|
|
|
|
inject['environment'] = self.environment
|
2013-03-26 02:32:01 +00:00
|
|
|
if self.inventory.basedir() is not None:
|
|
|
|
inject['inventory_dir'] = self.inventory.basedir()
|
2012-07-20 12:29:44 +00:00
|
|
|
|
2012-10-25 13:10:33 +00:00
|
|
|
# allow with_foo to work in playbooks...
|
2012-10-31 15:37:26 +00:00
|
|
|
items = None
|
2012-10-13 00:07:05 +00:00
|
|
|
items_plugin = self.module_vars.get('items_lookup_plugin', None)
|
2012-11-01 23:41:50 +00:00
|
|
|
if items_plugin is not None and items_plugin in utils.plugins.lookup_loader:
|
2012-10-13 00:07:05 +00:00
|
|
|
items_terms = self.module_vars.get('items_lookup_terms', '')
|
2013-02-02 11:29:28 +00:00
|
|
|
items_terms = utils.template(self.basedir, items_terms, inject)
|
2012-11-01 23:41:50 +00:00
|
|
|
items = utils.plugins.lookup_loader.get(items_plugin, runner=self, basedir=self.basedir).run(items_terms, inject=inject)
|
2012-10-31 15:37:26 +00:00
|
|
|
if type(items) != list:
|
|
|
|
raise errors.AnsibleError("lookup plugins have to return a list: %r" % items)
|
2012-10-13 00:07:05 +00:00
|
|
|
|
2013-03-01 23:32:32 +00:00
|
|
|
if len(items) and utils.is_list_of_strings(items) and self.module_name in [ 'apt', 'yum' ]:
|
2012-10-31 15:37:26 +00:00
|
|
|
# hack for apt and soon yum, with_items maps back into a single module call
|
|
|
|
inject['item'] = ",".join(items)
|
|
|
|
items = None
|
2012-08-07 00:07:02 +00:00
|
|
|
|
2012-08-18 12:46:51 +00:00
|
|
|
# logic to decide how to run things depends on whether with_items is used
|
|
|
|
|
2012-10-31 15:37:26 +00:00
|
|
|
if items is None:
|
2013-03-07 10:05:17 +00:00
|
|
|
return self._executor_internal_inner(host, self.module_name, self.module_args, inject, port, complex_args=self.complex_args)
|
2012-10-31 15:37:26 +00:00
|
|
|
elif len(items) > 0:
|
2012-07-15 16:51:56 +00:00
|
|
|
# executing using with_items, so make multiple calls
|
|
|
|
# TODO: refactor
|
|
|
|
aggregrate = {}
|
|
|
|
all_comm_ok = True
|
|
|
|
all_changed = False
|
|
|
|
all_failed = False
|
|
|
|
results = []
|
|
|
|
for x in items:
|
2012-07-18 00:08:45 +00:00
|
|
|
inject['item'] = x
|
2013-03-07 10:05:17 +00:00
|
|
|
result = self._executor_internal_inner(host, self.module_name, self.module_args, inject, port, complex_args=self.complex_args)
|
2012-07-15 16:51:56 +00:00
|
|
|
results.append(result.result)
|
|
|
|
if result.comm_ok == False:
|
|
|
|
all_comm_ok = False
|
2012-10-13 00:07:05 +00:00
|
|
|
all_failed = True
|
2012-07-15 16:51:56 +00:00
|
|
|
break
|
|
|
|
for x in results:
|
|
|
|
if x.get('changed') == True:
|
|
|
|
all_changed = True
|
|
|
|
if (x.get('failed') == True) or (('rc' in x) and (x['rc'] != 0)):
|
|
|
|
all_failed = True
|
|
|
|
break
|
2012-10-13 00:07:05 +00:00
|
|
|
msg = 'All items completed'
|
2012-07-15 16:51:56 +00:00
|
|
|
if all_failed:
|
|
|
|
msg = "One or more items failed."
|
|
|
|
rd_result = dict(failed=all_failed, changed=all_changed, results=results, msg=msg)
|
|
|
|
if not all_failed:
|
|
|
|
del rd_result['failed']
|
|
|
|
return ReturnData(host=host, comm_ok=all_comm_ok, result=rd_result)
|
2012-10-31 15:37:26 +00:00
|
|
|
else:
|
|
|
|
self.callbacks.on_skipped(host, None)
|
|
|
|
return ReturnData(host=host, comm_ok=True, result=dict(skipped=True))
|
2012-07-15 16:51:56 +00:00
|
|
|
|
|
|
|
# *****************************************************
|
|
|
|
|
2013-03-07 10:05:17 +00:00
|
|
|
def _executor_internal_inner(self, host, module_name, module_args, inject, port, is_chained=False, complex_args=None):
|
2012-07-15 16:51:56 +00:00
|
|
|
''' decides how to invoke a module '''
|
2012-03-20 02:42:31 +00:00
|
|
|
|
2013-02-17 20:01:49 +00:00
|
|
|
|
2012-07-22 15:08:16 +00:00
|
|
|
# allow module args to work as a dictionary
|
|
|
|
# though it is usually a string
|
|
|
|
new_args = ""
|
2012-09-21 08:42:27 +00:00
|
|
|
if type(module_args) == dict:
|
|
|
|
for (k,v) in module_args.iteritems():
|
2012-07-22 15:08:16 +00:00
|
|
|
new_args = new_args + "%s='%s' " % (k,v)
|
2012-09-21 08:42:27 +00:00
|
|
|
module_args = new_args
|
2012-07-22 15:08:16 +00:00
|
|
|
|
2012-11-28 15:22:35 +00:00
|
|
|
module_name = utils.template(self.basedir, module_name, inject)
|
2013-02-08 19:55:55 +00:00
|
|
|
module_args = utils.template(self.basedir, module_args, inject)
|
2013-03-07 10:05:17 +00:00
|
|
|
complex_args = utils.template(self.basedir, complex_args, inject)
|
2012-11-28 15:22:35 +00:00
|
|
|
|
|
|
|
if module_name in utils.plugins.action_loader:
|
|
|
|
if self.background != 0:
|
|
|
|
raise errors.AnsibleError("async mode is not supported with the %s module" % module_name)
|
|
|
|
handler = utils.plugins.action_loader.get(module_name, self)
|
|
|
|
elif self.background == 0:
|
|
|
|
handler = utils.plugins.action_loader.get('normal', self)
|
|
|
|
else:
|
|
|
|
handler = utils.plugins.action_loader.get('async', self)
|
|
|
|
|
2013-02-08 19:55:55 +00:00
|
|
|
conditional = utils.template(self.basedir, self.conditional, inject, expand_lists=False)
|
2013-03-26 01:16:18 +00:00
|
|
|
if not utils.check_conditional(conditional):
|
2012-07-15 14:12:49 +00:00
|
|
|
result = utils.jsonify(dict(skipped=True))
|
2012-07-23 23:36:26 +00:00
|
|
|
self.callbacks.on_skipped(host, inject.get('item',None))
|
2012-06-11 13:06:23 +00:00
|
|
|
return ReturnData(host=host, result=result)
|
|
|
|
|
2012-04-17 01:52:15 +00:00
|
|
|
conn = None
|
2012-10-17 23:17:01 +00:00
|
|
|
actual_host = inject.get('ansible_ssh_host', host)
|
2012-10-18 02:50:17 +00:00
|
|
|
actual_port = port
|
2013-02-10 22:22:18 +00:00
|
|
|
actual_user = inject.get('ansible_ssh_user', self.remote_user)
|
|
|
|
actual_pass = inject.get('ansible_ssh_pass', self.remote_pass)
|
2013-02-22 16:11:08 +00:00
|
|
|
actual_transport = inject.get('ansible_connection', self.transport)
|
|
|
|
if actual_transport in [ 'paramiko', 'ssh' ]:
|
2012-10-18 02:50:17 +00:00
|
|
|
actual_port = inject.get('ansible_ssh_port', port)
|
2012-10-17 23:17:01 +00:00
|
|
|
|
|
|
|
# the delegated host may have different SSH port configured, etc
|
|
|
|
# and we need to transfer those, and only those, variables
|
|
|
|
delegate_to = inject.get('delegate_to', None)
|
|
|
|
if delegate_to is not None:
|
|
|
|
delegate_to = utils.template(self.basedir, delegate_to, inject)
|
2012-10-25 11:59:24 +00:00
|
|
|
inject = inject.copy()
|
|
|
|
interpreters = []
|
|
|
|
for i in inject:
|
|
|
|
if i.startswith("ansible_") and i.endswith("_interpreter"):
|
|
|
|
interpreters.append(i)
|
|
|
|
for i in interpreters:
|
|
|
|
del inject[i]
|
2012-11-01 10:08:24 +00:00
|
|
|
port = C.DEFAULT_REMOTE_PORT
|
2012-10-18 19:48:10 +00:00
|
|
|
try:
|
|
|
|
delegate_info = inject['hostvars'][delegate_to]
|
|
|
|
actual_host = delegate_info.get('ansible_ssh_host', delegate_to)
|
|
|
|
actual_port = delegate_info.get('ansible_ssh_port', port)
|
2013-02-10 22:22:18 +00:00
|
|
|
actual_user = delegate_info.get('ansible_ssh_user', actual_user)
|
|
|
|
actual_pass = delegate_info.get('ansible_ssh_pass', actual_pass)
|
2013-02-23 17:28:42 +00:00
|
|
|
actual_transport = delegate_info.get('ansible_connection', self.transport)
|
2012-10-25 11:59:24 +00:00
|
|
|
for i in delegate_info:
|
|
|
|
if i.startswith("ansible_") and i.endswith("_interpreter"):
|
|
|
|
inject[i] = delegate_info[i]
|
2012-10-18 19:48:10 +00:00
|
|
|
except errors.AnsibleError:
|
|
|
|
actual_host = delegate_to
|
2012-11-01 10:08:24 +00:00
|
|
|
actual_port = port
|
2012-10-17 00:57:37 +00:00
|
|
|
|
2013-02-10 22:22:18 +00:00
|
|
|
actual_user = utils.template(self.basedir, actual_user, inject)
|
|
|
|
actual_pass = utils.template(self.basedir, actual_pass, inject)
|
|
|
|
|
2012-10-17 23:17:01 +00:00
|
|
|
try:
|
2012-10-19 14:18:38 +00:00
|
|
|
if actual_port is not None:
|
|
|
|
actual_port = int(actual_port)
|
2012-11-20 18:04:37 +00:00
|
|
|
except ValueError, e:
|
|
|
|
result = dict(failed=True, msg="FAILED: Configured port \"%s\" is not a valid port, expected integer" % actual_port)
|
|
|
|
return ReturnData(host=host, comm_ok=False, result=result)
|
|
|
|
|
|
|
|
try:
|
2013-02-22 16:11:08 +00:00
|
|
|
conn = self.connector.connect(actual_host, actual_port, actual_user, actual_pass, actual_transport)
|
2012-10-21 18:11:27 +00:00
|
|
|
if delegate_to or host != actual_host:
|
2012-10-17 23:17:01 +00:00
|
|
|
conn.delegate = host
|
2012-10-17 00:57:37 +00:00
|
|
|
|
2012-10-19 00:14:15 +00:00
|
|
|
|
2012-04-17 01:52:15 +00:00
|
|
|
except errors.AnsibleConnectionFailed, e:
|
2012-05-25 22:44:29 +00:00
|
|
|
result = dict(failed=True, msg="FAILED: %s" % str(e))
|
|
|
|
return ReturnData(host=host, comm_ok=False, result=result)
|
2012-04-17 01:52:15 +00:00
|
|
|
|
2012-08-10 04:45:00 +00:00
|
|
|
tmp = ''
|
2012-11-28 15:22:35 +00:00
|
|
|
# all modules get a tempdir, action plugins get one unless they have NEEDS_TMPPATH set to False
|
|
|
|
if getattr(handler, 'NEEDS_TMPPATH', True):
|
2012-08-10 04:45:00 +00:00
|
|
|
tmp = self._make_tmp_path(conn)
|
2013-02-17 20:01:49 +00:00
|
|
|
|
2013-03-07 10:05:17 +00:00
|
|
|
result = handler.run(conn, tmp, module_name, module_args, inject, complex_args)
|
2012-03-11 22:40:35 +00:00
|
|
|
|
2012-03-03 03:38:55 +00:00
|
|
|
conn.close()
|
2012-03-25 23:05:27 +00:00
|
|
|
|
2012-05-25 22:44:29 +00:00
|
|
|
if not result.comm_ok:
|
|
|
|
# connection or parsing errors...
|
2012-06-11 13:07:37 +00:00
|
|
|
self.callbacks.on_unreachable(host, result.result)
|
2012-03-25 23:05:27 +00:00
|
|
|
else:
|
2012-05-25 22:44:29 +00:00
|
|
|
data = result.result
|
2012-07-20 12:29:44 +00:00
|
|
|
if 'item' in inject:
|
|
|
|
result.result['item'] = inject['item']
|
2012-08-21 00:41:28 +00:00
|
|
|
|
|
|
|
result.result['invocation'] = dict(
|
2012-09-21 08:42:27 +00:00
|
|
|
module_args=module_args,
|
|
|
|
module_name=module_name
|
2012-08-21 00:41:28 +00:00
|
|
|
)
|
|
|
|
|
2012-07-24 02:12:26 +00:00
|
|
|
if is_chained:
|
|
|
|
# no callbacks
|
|
|
|
return result
|
|
|
|
if 'skipped' in data:
|
2012-08-18 12:46:51 +00:00
|
|
|
self.callbacks.on_skipped(host)
|
2012-07-24 02:12:26 +00:00
|
|
|
elif not result.is_successful():
|
2012-08-01 19:17:16 +00:00
|
|
|
ignore_errors = self.module_vars.get('ignore_errors', False)
|
2012-08-18 12:46:51 +00:00
|
|
|
self.callbacks.on_failed(host, data, ignore_errors)
|
2012-07-24 02:12:26 +00:00
|
|
|
else:
|
2013-02-10 04:24:03 +00:00
|
|
|
if self.diff:
|
2013-02-25 22:32:52 +00:00
|
|
|
self.callbacks.on_file_diff(conn.host, result.diff)
|
2012-08-18 12:46:51 +00:00
|
|
|
self.callbacks.on_ok(host, data)
|
2012-03-03 03:38:55 +00:00
|
|
|
return result
|
|
|
|
|
2012-03-14 00:59:05 +00:00
|
|
|
# *****************************************************
|
|
|
|
|
2012-12-23 18:17:07 +00:00
|
|
|
def _low_level_exec_command(self, conn, cmd, tmp, sudoable=False, executable=None):
|
2012-02-27 01:29:27 +00:00
|
|
|
''' execute a command string over SSH, return the output '''
|
2012-07-15 14:57:22 +00:00
|
|
|
|
2013-01-08 16:45:37 +00:00
|
|
|
if executable is None:
|
2012-12-23 18:17:07 +00:00
|
|
|
executable = '/bin/sh'
|
|
|
|
|
2012-05-04 00:11:21 +00:00
|
|
|
sudo_user = self.sudo_user
|
2012-12-23 18:17:07 +00:00
|
|
|
rc, stdin, stdout, stderr = conn.exec_command(cmd, tmp, sudo_user, sudoable=sudoable, executable=executable)
|
2012-05-25 22:44:29 +00:00
|
|
|
|
2012-09-27 03:50:54 +00:00
|
|
|
if type(stdout) not in [ str, unicode ]:
|
2012-12-23 00:44:00 +00:00
|
|
|
out = ''.join(stdout.readlines())
|
2012-03-29 02:51:16 +00:00
|
|
|
else:
|
2012-07-31 20:59:45 +00:00
|
|
|
out = stdout
|
2012-08-07 00:07:02 +00:00
|
|
|
|
2012-09-27 03:50:54 +00:00
|
|
|
if type(stderr) not in [ str, unicode ]:
|
2012-12-23 00:44:00 +00:00
|
|
|
err = ''.join(stderr.readlines())
|
2012-07-31 20:59:45 +00:00
|
|
|
else:
|
|
|
|
err = stderr
|
|
|
|
|
2013-02-09 23:49:54 +00:00
|
|
|
if rc is not None:
|
2013-02-18 00:40:38 +00:00
|
|
|
return dict(rc=rc, stdout=out, stderr=err)
|
2012-12-23 00:44:00 +00:00
|
|
|
else:
|
2013-02-18 00:40:38 +00:00
|
|
|
return dict(stdout=out, stderr=err)
|
2012-04-27 05:25:38 +00:00
|
|
|
|
2012-03-14 00:59:05 +00:00
|
|
|
# *****************************************************
|
|
|
|
|
2012-07-07 12:45:06 +00:00
|
|
|
def _remote_md5(self, conn, tmp, path):
|
2012-08-07 00:07:02 +00:00
|
|
|
''' takes a remote md5sum without requiring python, and returns 0 if no file '''
|
|
|
|
|
2013-03-11 21:19:43 +00:00
|
|
|
path = pipes.quote(path)
|
2013-02-12 21:39:05 +00:00
|
|
|
test = "rc=0; [ -r \"%s\" ] || rc=2; [ -f \"%s\" ] || rc=1; [ -d \"%s\" ] && rc=3" % (path, path, path)
|
2012-07-07 12:45:06 +00:00
|
|
|
md5s = [
|
2012-11-12 20:37:00 +00:00
|
|
|
"(/usr/bin/md5sum %s 2>/dev/null)" % path, # Linux
|
|
|
|
"(/sbin/md5sum -q %s 2>/dev/null)" % path, # ?
|
|
|
|
"(/usr/bin/digest -a md5 %s 2>/dev/null)" % path, # Solaris 10+
|
|
|
|
"(/sbin/md5 -q %s 2>/dev/null)" % path, # Freebsd
|
|
|
|
"(/usr/bin/md5 -n %s 2>/dev/null)" % path, # Netbsd
|
|
|
|
"(/bin/md5 -q %s 2>/dev/null)" % path # Openbsd
|
2012-07-07 12:45:06 +00:00
|
|
|
]
|
2012-08-07 00:07:02 +00:00
|
|
|
|
2012-07-07 12:45:06 +00:00
|
|
|
cmd = " || ".join(md5s)
|
2012-07-17 08:14:16 +00:00
|
|
|
cmd = "%s; %s || (echo \"${rc} %s\")" % (test, cmd, path)
|
2012-08-17 02:45:43 +00:00
|
|
|
data = self._low_level_exec_command(conn, cmd, tmp, sudoable=False)
|
2012-12-23 00:44:00 +00:00
|
|
|
data2 = utils.last_non_blank_line(data['stdout'])
|
2012-10-11 11:43:56 +00:00
|
|
|
try:
|
|
|
|
return data2.split()[0]
|
2012-10-11 11:56:01 +00:00
|
|
|
except IndexError:
|
2012-10-11 11:43:56 +00:00
|
|
|
sys.stderr.write("warning: md5sum command failed unusually, please report this to the list so it can be fixed\n")
|
|
|
|
sys.stderr.write("command: %s\n" % md5s)
|
|
|
|
sys.stderr.write("----\n")
|
|
|
|
sys.stderr.write("output: %s\n" % data)
|
|
|
|
sys.stderr.write("----\n")
|
|
|
|
# this will signal that it changed and allow things to keep going
|
|
|
|
return "INVALIDMD5SUM"
|
2012-07-07 12:45:06 +00:00
|
|
|
|
|
|
|
# *****************************************************
|
|
|
|
|
2012-06-06 12:47:47 +00:00
|
|
|
def _make_tmp_path(self, conn):
|
|
|
|
''' make and return a temporary path on a remote box '''
|
2012-03-14 00:59:05 +00:00
|
|
|
|
2012-06-06 12:47:47 +00:00
|
|
|
basefile = 'ansible-%s-%s' % (time.time(), random.randint(0, 2**48))
|
|
|
|
basetmp = os.path.join(C.DEFAULT_REMOTE_TMP, basefile)
|
2013-03-06 22:32:12 +00:00
|
|
|
if self.sudo and self.sudo_user != 'root' and basetmp.startswith('$HOME'):
|
2012-07-28 00:53:50 +00:00
|
|
|
basetmp = os.path.join('/tmp', basefile)
|
2012-06-06 12:47:47 +00:00
|
|
|
|
|
|
|
cmd = 'mkdir -p %s' % basetmp
|
2012-04-21 15:38:39 +00:00
|
|
|
if self.remote_user != 'root':
|
2012-08-08 05:40:22 +00:00
|
|
|
cmd += ' && chmod a+rx %s' % basetmp
|
2012-06-06 12:47:47 +00:00
|
|
|
cmd += ' && echo %s' % basetmp
|
2012-04-21 15:38:39 +00:00
|
|
|
|
2012-05-25 22:44:29 +00:00
|
|
|
result = self._low_level_exec_command(conn, cmd, None, sudoable=False)
|
2012-12-23 00:44:00 +00:00
|
|
|
rc = utils.last_non_blank_line(result['stdout']).strip() + '/'
|
2012-09-27 03:50:54 +00:00
|
|
|
return rc
|
|
|
|
|
2012-02-27 22:52:37 +00:00
|
|
|
|
2012-03-14 00:59:05 +00:00
|
|
|
# *****************************************************
|
|
|
|
|
2013-02-17 20:01:49 +00:00
|
|
|
def _copy_module(self, conn, tmp, module_name, module_args, inject, complex_args=None):
|
2012-02-27 01:29:27 +00:00
|
|
|
''' transfer a module over SFTP, does not run it '''
|
2012-03-14 00:59:05 +00:00
|
|
|
|
2013-02-17 20:01:49 +00:00
|
|
|
# FIXME if complex args is none, set to {}
|
|
|
|
|
2012-09-21 08:42:27 +00:00
|
|
|
if module_name.startswith("/"):
|
|
|
|
raise errors.AnsibleFileNotFound("%s is not a module" % module_name)
|
2012-06-06 20:42:29 +00:00
|
|
|
|
|
|
|
# Search module path(s) for named module.
|
2012-11-18 17:37:30 +00:00
|
|
|
in_path = utils.plugins.module_finder.find_plugin(module_name)
|
|
|
|
if in_path is None:
|
|
|
|
raise errors.AnsibleFileNotFound("module %s not found in %s" % (module_name, utils.plugins.module_finder.print_paths()))
|
2012-03-13 00:53:10 +00:00
|
|
|
|
2012-09-21 08:42:27 +00:00
|
|
|
out_path = os.path.join(tmp, module_name)
|
2012-06-04 08:15:12 +00:00
|
|
|
|
2012-07-18 02:33:36 +00:00
|
|
|
module_data = ""
|
2012-07-23 23:14:37 +00:00
|
|
|
is_new_style=False
|
2012-08-15 00:35:01 +00:00
|
|
|
|
2012-07-18 02:33:36 +00:00
|
|
|
with open(in_path) as f:
|
|
|
|
module_data = f.read()
|
2012-07-23 23:14:37 +00:00
|
|
|
if module_common.REPLACER in module_data:
|
|
|
|
is_new_style=True
|
2013-02-17 20:01:49 +00:00
|
|
|
|
|
|
|
complex_args_json = utils.jsonify(complex_args)
|
2012-09-21 08:42:27 +00:00
|
|
|
encoded_args = "\"\"\"%s\"\"\"" % module_args.replace("\"","\\\"")
|
2012-11-27 08:07:13 +00:00
|
|
|
encoded_lang = "\"\"\"%s\"\"\"" % C.DEFAULT_MODULE_LANG
|
2013-02-27 18:43:27 +00:00
|
|
|
encoded_complex = "\"\"\"%s\"\"\"" % complex_args_json.replace("\\", "\\\\")
|
2013-02-17 20:01:49 +00:00
|
|
|
|
|
|
|
module_data = module_data.replace(module_common.REPLACER, module_common.MODULE_COMMON)
|
|
|
|
module_data = module_data.replace(module_common.REPLACER_ARGS, encoded_args)
|
2012-11-27 08:07:13 +00:00
|
|
|
module_data = module_data.replace(module_common.REPLACER_LANG, encoded_lang)
|
2013-02-17 20:01:49 +00:00
|
|
|
module_data = module_data.replace(module_common.REPLACER_COMPLEX, encoded_complex)
|
|
|
|
|
2012-11-10 06:13:00 +00:00
|
|
|
if is_new_style:
|
|
|
|
facility = C.DEFAULT_SYSLOG_FACILITY
|
|
|
|
if 'ansible_syslog_facility' in inject:
|
|
|
|
facility = inject['ansible_syslog_facility']
|
|
|
|
module_data = module_data.replace('syslog.LOG_USER', "syslog.%s" % facility)
|
2012-08-07 00:07:02 +00:00
|
|
|
|
2012-10-21 02:51:36 +00:00
|
|
|
lines = module_data.split("\n")
|
|
|
|
shebang = None
|
|
|
|
if lines[0].startswith("#!"):
|
|
|
|
shebang = lines[0]
|
2012-11-27 20:58:32 +00:00
|
|
|
args = shlex.split(str(shebang[2:]))
|
|
|
|
interpreter = args[0]
|
|
|
|
interpreter_config = 'ansible_%s_interpreter' % os.path.basename(interpreter)
|
2012-11-27 16:18:57 +00:00
|
|
|
|
|
|
|
if interpreter_config in inject:
|
2012-11-27 20:58:32 +00:00
|
|
|
lines[0] = shebang = "#!%s %s" % (inject[interpreter_config], " ".join(args[1:]))
|
2012-11-27 16:18:57 +00:00
|
|
|
module_data = "\n".join(lines)
|
|
|
|
|
|
|
|
self._transfer_str(conn, tmp, module_name, module_data)
|
2012-10-21 02:51:36 +00:00
|
|
|
|
|
|
|
return (out_path, is_new_style, shebang)
|
2012-02-25 22:16:23 +00:00
|
|
|
|
2012-03-14 00:59:05 +00:00
|
|
|
# *****************************************************
|
|
|
|
|
2012-10-26 22:11:38 +00:00
|
|
|
|
2012-03-22 03:39:09 +00:00
|
|
|
def _parallel_exec(self, hosts):
|
|
|
|
''' handles mulitprocessing when more than 1 fork is required '''
|
2012-04-04 14:27:24 +00:00
|
|
|
|
2012-10-26 22:11:38 +00:00
|
|
|
manager = multiprocessing.Manager()
|
|
|
|
job_queue = manager.Queue()
|
|
|
|
for host in hosts:
|
|
|
|
job_queue.put(host)
|
|
|
|
result_queue = manager.Queue()
|
|
|
|
|
|
|
|
workers = []
|
|
|
|
for i in range(self.forks):
|
|
|
|
prc = multiprocessing.Process(target=_executor_hook,
|
|
|
|
args=(job_queue, result_queue))
|
|
|
|
prc.start()
|
|
|
|
workers.append(prc)
|
2012-10-23 12:19:15 +00:00
|
|
|
|
2012-04-04 00:20:55 +00:00
|
|
|
try:
|
2012-10-26 22:11:38 +00:00
|
|
|
for worker in workers:
|
|
|
|
worker.join()
|
2012-04-04 00:20:55 +00:00
|
|
|
except KeyboardInterrupt:
|
2012-10-26 22:11:38 +00:00
|
|
|
for worker in workers:
|
|
|
|
worker.terminate()
|
|
|
|
worker.join()
|
|
|
|
|
|
|
|
results = []
|
|
|
|
try:
|
|
|
|
while not result_queue.empty():
|
|
|
|
results.append(result_queue.get(block=False))
|
|
|
|
except socket.error:
|
|
|
|
raise errors.AnsibleError("<interrupted>")
|
|
|
|
return results
|
2012-03-22 03:39:09 +00:00
|
|
|
|
|
|
|
# *****************************************************
|
|
|
|
|
|
|
|
def _partition_results(self, results):
|
2013-03-06 18:41:19 +00:00
|
|
|
''' separate results by ones we contacted & ones we didn't '''
|
2012-03-22 03:39:09 +00:00
|
|
|
|
2012-03-25 23:05:27 +00:00
|
|
|
if results is None:
|
|
|
|
return None
|
2012-07-15 14:57:22 +00:00
|
|
|
results2 = dict(contacted={}, dark={})
|
2012-03-25 23:05:27 +00:00
|
|
|
|
2012-03-22 03:39:09 +00:00
|
|
|
for result in results:
|
2012-05-25 22:44:29 +00:00
|
|
|
host = result.host
|
|
|
|
if host is None:
|
|
|
|
raise Exception("internal error, host not set")
|
|
|
|
if result.communicated_ok():
|
|
|
|
results2["contacted"][host] = result.result
|
2012-03-22 03:39:09 +00:00
|
|
|
else:
|
2012-05-25 22:44:29 +00:00
|
|
|
results2["dark"][host] = result.result
|
2012-03-22 03:39:09 +00:00
|
|
|
|
|
|
|
# hosts which were contacted but never got a chance to return
|
2012-04-13 12:39:54 +00:00
|
|
|
for host in self.inventory.list_hosts(self.pattern):
|
2012-03-22 03:39:09 +00:00
|
|
|
if not (host in results2['dark'] or host in results2['contacted']):
|
2012-02-27 05:43:02 +00:00
|
|
|
results2["dark"][host] = {}
|
2012-02-25 22:16:23 +00:00
|
|
|
return results2
|
2012-02-24 04:28:58 +00:00
|
|
|
|
2012-03-22 03:39:09 +00:00
|
|
|
# *****************************************************
|
|
|
|
|
|
|
|
def run(self):
|
|
|
|
''' xfer & run module on all matched hosts '''
|
2012-08-07 00:07:02 +00:00
|
|
|
|
2012-03-22 03:39:09 +00:00
|
|
|
# find hosts that match the pattern
|
2012-04-13 12:39:54 +00:00
|
|
|
hosts = self.inventory.list_hosts(self.pattern)
|
2012-03-22 03:39:09 +00:00
|
|
|
if len(hosts) == 0:
|
2012-04-12 01:05:46 +00:00
|
|
|
self.callbacks.on_no_hosts()
|
2012-03-22 03:39:09 +00:00
|
|
|
return dict(contacted={}, dark={})
|
2012-08-07 00:07:02 +00:00
|
|
|
|
2012-10-23 12:04:32 +00:00
|
|
|
global multiprocessing_runner
|
|
|
|
multiprocessing_runner = self
|
2012-03-25 23:05:27 +00:00
|
|
|
results = None
|
2012-09-22 06:07:49 +00:00
|
|
|
|
|
|
|
# Check if this is an action plugin. Some of them are designed
|
|
|
|
# to be ran once per group of hosts. Example module: pause,
|
|
|
|
# run once per hostgroup, rather than pausing once per each
|
|
|
|
# host.
|
2012-11-01 23:41:50 +00:00
|
|
|
p = utils.plugins.action_loader.get(self.module_name, self)
|
2013-02-17 20:01:49 +00:00
|
|
|
|
2012-09-22 06:07:49 +00:00
|
|
|
if p and getattr(p, 'BYPASS_HOST_LOOP', None):
|
2013-02-17 20:01:49 +00:00
|
|
|
|
2012-09-22 06:07:49 +00:00
|
|
|
# Expose the current hostgroup to the bypassing plugins
|
|
|
|
self.host_set = hosts
|
|
|
|
# We aren't iterating over all the hosts in this
|
|
|
|
# group. So, just pick the first host in our group to
|
|
|
|
# construct the conn object with.
|
2012-10-29 08:45:44 +00:00
|
|
|
result_data = self._executor(hosts[0]).result
|
2012-09-22 06:07:49 +00:00
|
|
|
# Create a ResultData item for each host in this group
|
|
|
|
# using the returned result. If we didn't do this we would
|
|
|
|
# get false reports of dark hosts.
|
2012-10-23 12:04:32 +00:00
|
|
|
results = [ ReturnData(host=h, result=result_data, comm_ok=True) \
|
2012-09-22 06:07:49 +00:00
|
|
|
for h in hosts ]
|
|
|
|
del self.host_set
|
2013-02-17 20:01:49 +00:00
|
|
|
|
2012-09-22 06:07:49 +00:00
|
|
|
elif self.forks > 1:
|
2012-10-24 11:46:24 +00:00
|
|
|
try:
|
|
|
|
results = self._parallel_exec(hosts)
|
|
|
|
except IOError, ie:
|
|
|
|
print ie.errno
|
|
|
|
if ie.errno == 32:
|
|
|
|
# broken pipe from Ctrl+C
|
|
|
|
raise errors.AnsibleError("interupted")
|
|
|
|
raise
|
2012-03-22 03:39:09 +00:00
|
|
|
else:
|
2012-10-23 12:04:32 +00:00
|
|
|
results = [ self._executor(h) for h in hosts ]
|
2012-03-22 03:39:09 +00:00
|
|
|
return self._partition_results(results)
|
|
|
|
|
2012-07-15 14:22:15 +00:00
|
|
|
# *****************************************************
|
|
|
|
|
2012-05-25 23:18:02 +00:00
|
|
|
def run_async(self, time_limit):
|
2012-04-26 18:34:49 +00:00
|
|
|
''' Run this module asynchronously and return a poller. '''
|
2012-07-15 14:57:22 +00:00
|
|
|
|
2012-04-26 18:34:49 +00:00
|
|
|
self.background = time_limit
|
|
|
|
results = self.run()
|
2012-05-25 23:18:02 +00:00
|
|
|
return results, poller.AsyncPoller(results, self)
|