mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-08-02 04:04:23 -07:00
Starting work on getting integration tests working on v2
This is incomplete work, and requires some minor tweeks to the integration tests which are not included in this commit.
This commit is contained in:
parent
d7f67ea62b
commit
2aeb79f45f
24 changed files with 189 additions and 155 deletions
|
@ -397,7 +397,7 @@ class ActionBase:
|
|||
debug("done with _execute_module (%s, %s)" % (module_name, module_args))
|
||||
return data
|
||||
|
||||
def _low_level_execute_command(self, cmd, tmp, executable=None, sudoable=False, in_data=None):
|
||||
def _low_level_execute_command(self, cmd, tmp, executable=None, sudoable=True, in_data=None):
|
||||
'''
|
||||
This is the function which executes the low level shell command, which
|
||||
may be commands to create/remove directories for temporary files, or to
|
||||
|
@ -413,8 +413,19 @@ class ActionBase:
|
|||
if executable is None:
|
||||
executable = C.DEFAULT_EXECUTABLE
|
||||
|
||||
prompt = None
|
||||
success_key = None
|
||||
|
||||
if sudoable:
|
||||
if self._connection_info.su and self._connection_info.su_user:
|
||||
cmd, prompt, success_key = self._connection_info.make_su_cmd(executable, cmd)
|
||||
elif self._connection_info.sudo and self._connection_info.sudo_user:
|
||||
# FIXME: hard-coded sudo_exe here
|
||||
cmd, prompt, success_key = self._connection_info.make_sudo_cmd('/usr/bin/sudo', executable, cmd)
|
||||
|
||||
debug("executing the command through the connection")
|
||||
rc, stdin, stdout, stderr = self._connection.exec_command(cmd, tmp, executable=executable, in_data=in_data, sudoable=sudoable)
|
||||
#rc, stdin, stdout, stderr = self._connection.exec_command(cmd, tmp, executable=executable, in_data=in_data, sudoable=sudoable)
|
||||
rc, stdin, stdout, stderr = self._connection.exec_command(cmd, tmp, executable=executable, in_data=in_data)
|
||||
debug("command execution done")
|
||||
|
||||
if not isinstance(stdout, basestring):
|
||||
|
|
|
@ -24,8 +24,8 @@ import tempfile
|
|||
import base64
|
||||
import re
|
||||
|
||||
|
||||
from ansible.plugins.action import ActionBase
|
||||
from ansible.utils.boolean import boolean
|
||||
from ansible.utils.hashing import checksum_s
|
||||
|
||||
class ActionModule(ActionBase):
|
||||
|
@ -78,21 +78,16 @@ class ActionModule(ActionBase):
|
|||
src = self._task.args.get('src', None)
|
||||
dest = self._task.args.get('dest', None)
|
||||
delimiter = self._task.args.get('delimiter', None)
|
||||
# FIXME: boolean needs to be moved out of utils
|
||||
#remote_src = utils.boolean(options.get('remote_src', 'yes'))
|
||||
remote_src = self._task.args.get('remote_src', 'yes')
|
||||
regexp = self._task.args.get('regexp', None)
|
||||
|
||||
if src is None or dest is None:
|
||||
return dict(failed=True, msg="src and dest are required")
|
||||
|
||||
# FIXME: this should be boolean, hard-coded to yes for testing
|
||||
if remote_src == 'yes':
|
||||
if boolean(remote_src):
|
||||
return self._execute_module(tmp=tmp)
|
||||
# FIXME: we don't do inject anymore, so not sure where the original
|
||||
# file stuff is going to end up at this time
|
||||
#elif '_original_file' in inject:
|
||||
# src = utils.path_dwim_relative(inject['_original_file'], 'files', src, self.runner.basedir)
|
||||
elif self._task._role is not None:
|
||||
src = self._loader.path_dwim_relative(self._task._role._role_path, 'files', src)
|
||||
else:
|
||||
# the source is local, so expand it here
|
||||
src = os.path.expanduser(src)
|
||||
|
|
|
@ -111,16 +111,10 @@ class ActionModule(ActionBase):
|
|||
# return ReturnData(conn=conn, result=results)
|
||||
###############################################################################################
|
||||
else:
|
||||
# FIXME: templating needs to be worked out still
|
||||
#source = template.template(self.runner.basedir, source, inject)
|
||||
# FIXME: original_file stuff needs to be reworked - most likely
|
||||
# simply checking to see if the task has a role and using
|
||||
# using the role path as the dwim target and basedir would work
|
||||
#if '_original_file' in inject:
|
||||
# source = utils.path_dwim_relative(inject['_original_file'], 'files', source, self.runner.basedir)
|
||||
#else:
|
||||
# source = utils.path_dwim(self.runner.basedir, source)
|
||||
source = self._loader.path_dwim(source)
|
||||
if self._task._role is not None:
|
||||
source = self._loader.path_dwim_relative(self._task._role._role_path, 'files', source)
|
||||
else:
|
||||
source = self._loader.path_dwim(source)
|
||||
|
||||
# A list of source file tuples (full_path, relative_path) which will try to copy to the destination
|
||||
source_files = []
|
||||
|
@ -129,7 +123,7 @@ class ActionModule(ActionBase):
|
|||
if os.path.isdir(source):
|
||||
# Get the amount of spaces to remove to get the relative path.
|
||||
if source_trailing_slash:
|
||||
sz = len(source) + 1
|
||||
sz = len(source)
|
||||
else:
|
||||
sz = len(source.rsplit('/', 1)[0]) + 1
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ class ActionModule(ActionBase):
|
|||
source = self._task.args.get('_raw_params')
|
||||
|
||||
if self._task._role:
|
||||
source = self._loader.path_dwim_relative(self._task._role.get('_role_path',''), 'vars', source)
|
||||
source = self._loader.path_dwim_relative(self._task._role._role_path, 'vars', source)
|
||||
else:
|
||||
source = self._loader.path_dwim(source)
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ class ActionModule(ActionBase):
|
|||
# look up the files and use the first one we find as src
|
||||
#if 'first_available_file' in task_vars:
|
||||
# found = False
|
||||
# for fn in self.runner.module_vars.get('first_available_file'):
|
||||
# for fn in task_vars.get('first_available_file'):
|
||||
# fn_orig = fn
|
||||
# fnt = template.template(self.runner.basedir, fn, task_vars)
|
||||
# fnd = utils.path_dwim(self.runner.basedir, fnt)
|
||||
|
@ -59,14 +59,13 @@ class ActionModule(ActionBase):
|
|||
# result = dict(failed=True, msg="could not find src in first_available_file list")
|
||||
# return ReturnData(conn=conn, comm_ok=False, result=result)
|
||||
#else:
|
||||
# source = template.template(self.runner.basedir, source, task_vars)
|
||||
#
|
||||
# if '_original_file' in task_vars:
|
||||
# source = utils.path_dwim_relative(task_vars['_original_file'], 'templates', source, self.runner.basedir)
|
||||
# else:
|
||||
# source = utils.path_dwim(self.runner.basedir, source)
|
||||
if 1:
|
||||
if self._task._role is not None:
|
||||
source = self._loader.path_dwim_relative(self._task._role._role_path, 'templates', source)
|
||||
else:
|
||||
source = self._loader.path_dwim(source)
|
||||
##################################################################################################
|
||||
source = self._loader.path_dwim(source)
|
||||
# END FIXME
|
||||
##################################################################################################
|
||||
|
||||
# Expand any user home dir specification
|
||||
|
|
|
@ -39,12 +39,12 @@ class Connection(ConnectionBase):
|
|||
''' connect to the local host; nothing to do here '''
|
||||
return self
|
||||
|
||||
def exec_command(self, cmd, tmp_path, sudo_user=None, sudoable=False, executable='/bin/sh', in_data=None, su=None, su_user=None):
|
||||
def exec_command(self, cmd, tmp_path, executable='/bin/sh', in_data=None):
|
||||
''' run a command on the local host '''
|
||||
|
||||
debug("in local.exec_command()")
|
||||
# su requires to be run from a terminal, and therefore isn't supported here (yet?)
|
||||
if su or su_user:
|
||||
if self._connection_info.su:
|
||||
raise AnsibleError("Internal Error: this module does not support running commands via su")
|
||||
|
||||
if in_data:
|
||||
|
|
|
@ -87,6 +87,7 @@ class Connection(ConnectionBase):
|
|||
|
||||
if self._connection_info.port is not None:
|
||||
self._common_args += ["-o", "Port=%d" % (self._connection_info.port)]
|
||||
# FIXME: need to get this from connection info
|
||||
#if self.private_key_file is not None:
|
||||
# self._common_args += ["-o", "IdentityFile=\"%s\"" % os.path.expanduser(self.private_key_file)]
|
||||
#elif self.runner.private_key_file is not None:
|
||||
|
@ -256,7 +257,7 @@ class Connection(ConnectionBase):
|
|||
self._display.vvv("EXEC previous known host file not found for %s" % host)
|
||||
return True
|
||||
|
||||
def exec_command(self, cmd, tmp_path, executable='/bin/sh', in_data=None, sudoable=False):
|
||||
def exec_command(self, cmd, tmp_path, executable='/bin/sh', in_data=None):
|
||||
''' run a command on the remote host '''
|
||||
|
||||
ssh_cmd = self._password_cmd()
|
||||
|
@ -266,15 +267,14 @@ class Connection(ConnectionBase):
|
|||
# inside a tty automatically invokes the python interactive-mode but the modules are not
|
||||
# compatible with the interactive-mode ("unexpected indent" mainly because of empty lines)
|
||||
ssh_cmd += ["-tt"]
|
||||
# FIXME: verbosity needs to move, most likely into connection info or
|
||||
# whatever other context we pass around instead of runner objects
|
||||
#if utils.VERBOSITY > 3:
|
||||
# ssh_cmd += ["-vvv"]
|
||||
#else:
|
||||
# ssh_cmd += ["-q"]
|
||||
ssh_cmd += ["-q"]
|
||||
if self._connection_info.verbosity > 3:
|
||||
ssh_cmd += ["-vvv"]
|
||||
else:
|
||||
ssh_cmd += ["-q"]
|
||||
ssh_cmd += self._common_args
|
||||
|
||||
# FIXME: ipv6 stuff needs to be figured out. It's in the connection info, however
|
||||
# not sure if it's all working yet so this remains commented out
|
||||
#if self._ipv6:
|
||||
# ssh_cmd += ['-6']
|
||||
ssh_cmd += [self._host.ipv4_address]
|
||||
|
@ -436,6 +436,9 @@ class Connection(ConnectionBase):
|
|||
|
||||
# FIXME: make a function, used in all 3 methods EXEC/PUT/FETCH
|
||||
host = self._host.ipv4_address
|
||||
|
||||
# FIXME: ipv6 stuff needs to be figured out. It's in the connection info, however
|
||||
# not sure if it's all working yet so this remains commented out
|
||||
#if self._ipv6:
|
||||
# host = '[%s]' % host
|
||||
|
||||
|
@ -463,6 +466,9 @@ class Connection(ConnectionBase):
|
|||
|
||||
# FIXME: make a function, used in all 3 methods EXEC/PUT/FETCH
|
||||
host = self._host.ipv4_address
|
||||
|
||||
# FIXME: ipv6 stuff needs to be figured out. It's in the connection info, however
|
||||
# not sure if it's all working yet so this remains commented out
|
||||
#if self._ipv6:
|
||||
# host = '[%s]' % self._host
|
||||
|
||||
|
|
|
@ -60,12 +60,26 @@ class StrategyBase:
|
|||
# outstanding tasks still in queue
|
||||
self._blocked_hosts = dict()
|
||||
|
||||
def run(self, iterator, connection_info):
|
||||
def run(self, iterator, connection_info, result=True):
|
||||
# save the counts on failed/unreachable hosts, as the cleanup/handler
|
||||
# methods will clear that information during their runs
|
||||
num_failed = len(self._tqm._failed_hosts)
|
||||
num_unreachable = len(self._tqm._unreachable_hosts)
|
||||
|
||||
debug("running the cleanup portion of the play")
|
||||
result = self.cleanup(iterator, connection_info)
|
||||
result &= self.cleanup(iterator, connection_info)
|
||||
debug("running handlers")
|
||||
result &= self.run_handlers(iterator, connection_info)
|
||||
return result
|
||||
|
||||
if not result:
|
||||
if num_unreachable > 0:
|
||||
return 3
|
||||
elif num_failed > 0:
|
||||
return 2
|
||||
else:
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
def get_hosts_remaining(self, play):
|
||||
return [host for host in self._inventory.get_hosts(play.hosts) if host.name not in self._tqm._failed_hosts and host.get_name() not in self._tqm._unreachable_hosts]
|
||||
|
@ -73,37 +87,10 @@ class StrategyBase:
|
|||
def get_failed_hosts(self):
|
||||
return [host for host in self._inventory.get_hosts() if host.name in self._tqm._failed_hosts]
|
||||
|
||||
def _queue_task(self, play, host, task, connection_info):
|
||||
def _queue_task(self, host, task, task_vars, connection_info):
|
||||
''' handles queueing the task up to be sent to a worker '''
|
||||
|
||||
debug("entering _queue_task() for %s/%s/%s" % (play, host, task))
|
||||
# copy the task, to make sure we have a clean version, since the
|
||||
# post-validation step will alter attribute values but this Task object
|
||||
# is shared across all hosts in the play
|
||||
debug("copying task")
|
||||
new_task = task.copy()
|
||||
debug("done copying task")
|
||||
|
||||
# squash variables down to a single dictionary using the variable manager and
|
||||
# call post_validate() on the task, which will finalize the attribute values
|
||||
debug("getting variables")
|
||||
try:
|
||||
task_vars = self._variable_manager.get_vars(loader=self._loader, play=play, host=host, task=new_task)
|
||||
except EOFError:
|
||||
# usually happens if the program is aborted, and the proxied object
|
||||
# queue is cut off from the call, so we just ignore this and exit
|
||||
return
|
||||
debug("done getting variables")
|
||||
|
||||
debug("running post_validate() on the task")
|
||||
if new_task.loop:
|
||||
# if the task has a lookup loop specified, we do not error out
|
||||
# on undefined variables yet, as fields may use {{item}} or some
|
||||
# variant, which won't be defined until execution time
|
||||
new_task.post_validate(task_vars, fail_on_undefined=False)
|
||||
else:
|
||||
new_task.post_validate(task_vars)
|
||||
debug("done running post_validate() on the task")
|
||||
debug("entering _queue_task() for %s/%s" % (host, task))
|
||||
|
||||
# and then queue the new task
|
||||
debug("%s - putting task (%s) in queue" % (host, task))
|
||||
|
@ -116,12 +103,12 @@ class StrategyBase:
|
|||
self._cur_worker = 0
|
||||
|
||||
self._pending_results += 1
|
||||
main_q.put((host, new_task, self._loader.get_basedir(), task_vars, connection_info), block=False)
|
||||
main_q.put((host, task, self._loader.get_basedir(), task_vars, connection_info), block=False)
|
||||
except (EOFError, IOError, AssertionError), e:
|
||||
# most likely an abort
|
||||
debug("got an error while queuing: %s" % e)
|
||||
return
|
||||
debug("exiting _queue_task() for %s/%s/%s" % (play, host, task))
|
||||
debug("exiting _queue_task() for %s/%s" % (host, task))
|
||||
|
||||
def _process_pending_results(self):
|
||||
'''
|
||||
|
@ -140,7 +127,8 @@ class StrategyBase:
|
|||
host = task_result._host
|
||||
task = task_result._task
|
||||
if result[0] == 'host_task_failed':
|
||||
self._tqm._failed_hosts[host.get_name()] = True
|
||||
if not task.ignore_errors:
|
||||
self._tqm._failed_hosts[host.get_name()] = True
|
||||
self._callback.runner_on_failed(task, task_result)
|
||||
elif result[0] == 'host_unreachable':
|
||||
self._tqm._unreachable_hosts[host.get_name()] = True
|
||||
|
|
|
@ -43,7 +43,7 @@ class StrategyModule(StrategyBase):
|
|||
last_host = 0
|
||||
|
||||
work_to_do = True
|
||||
while work_to_do:
|
||||
while work_to_do and not self._tqm._terminated:
|
||||
|
||||
hosts_left = self.get_hosts_remaining()
|
||||
if len(hosts_left) == 0:
|
||||
|
|
|
@ -36,7 +36,7 @@ class StrategyModule(StrategyBase):
|
|||
|
||||
# iteratate over each task, while there is one left to run
|
||||
work_to_do = True
|
||||
while work_to_do:
|
||||
while work_to_do and not self._tqm._terminated:
|
||||
|
||||
try:
|
||||
debug("getting the remaining hosts for this loop")
|
||||
|
@ -52,7 +52,30 @@ class StrategyModule(StrategyBase):
|
|||
callback_sent = False
|
||||
work_to_do = False
|
||||
for host in hosts_left:
|
||||
task = iterator.get_next_task_for_host(host)
|
||||
while True:
|
||||
task = iterator.get_next_task_for_host(host)
|
||||
if not task:
|
||||
break
|
||||
|
||||
debug("getting variables")
|
||||
task_vars = self._variable_manager.get_vars(loader=self._loader, play=iterator._play, host=host, task=task)
|
||||
debug("done getting variables")
|
||||
|
||||
# check to see if this task should be skipped, due to it being a member of a
|
||||
# role which has already run (and whether that role allows duplicate execution)
|
||||
if task._role and task._role.has_run():
|
||||
# If there is no metadata, the default behavior is to not allow duplicates,
|
||||
# if there is metadata, check to see if the allow_duplicates flag was set to true
|
||||
if task._role._metadata is None or task._role._metadata and not task._role._metadata.allow_duplicates:
|
||||
debug("'%s' skipped because role has already run" % task)
|
||||
continue
|
||||
|
||||
if not task.evaluate_tags(connection_info.only_tags, connection_info.skip_tags, task_vars):
|
||||
debug("'%s' failed tag evaluation" % task)
|
||||
continue
|
||||
|
||||
break
|
||||
|
||||
if not task:
|
||||
continue
|
||||
|
||||
|
@ -61,24 +84,21 @@ class StrategyModule(StrategyBase):
|
|||
self._callback.playbook_on_task_start(task.get_name(), False)
|
||||
callback_sent = True
|
||||
|
||||
host_name = host.get_name()
|
||||
if 1: #host_name not in self._tqm._failed_hosts and host_name not in self._tqm._unreachable_hosts:
|
||||
self._blocked_hosts[host_name] = True
|
||||
self._queue_task(iterator._play, host, task, connection_info)
|
||||
self._blocked_hosts[host.get_name()] = True
|
||||
self._queue_task(host, task, task_vars, connection_info)
|
||||
|
||||
self._process_pending_results()
|
||||
|
||||
debug("done queuing things up, now waiting for results queue to drain")
|
||||
self._wait_on_pending_results()
|
||||
debug("results queue empty")
|
||||
except IOError, e:
|
||||
debug("got IOError: %s" % e)
|
||||
except (IOError, EOFError), e:
|
||||
debug("got IOError/EOFError in task loop: %s" % e)
|
||||
# most likely an abort, return failed
|
||||
return 1
|
||||
|
||||
# run the base class run() method, which executes the cleanup function
|
||||
# and runs any outstanding handlers which have been triggered
|
||||
|
||||
result &= super(StrategyModule, self).run(iterator, connection_info)
|
||||
return super(StrategyModule, self).run(iterator, connection_info, result)
|
||||
|
||||
return result
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue