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:
James Cammarata 2015-01-12 16:04:56 -06:00
commit 2aeb79f45f
24 changed files with 189 additions and 155 deletions

View file

@ -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):

View file

@ -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)

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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:

View file

@ -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

View file

@ -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

View file

@ -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:

View file

@ -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