Fixing many bugs in v2
* delegate_to rudimentary support (still needs much more work) * lots of other things
This commit is contained in:
parent
402a6d0533
commit
31dd75de59
17 changed files with 144 additions and 124 deletions
|
@ -43,9 +43,11 @@ class ConnectionInformation:
|
||||||
# various different auth escalation methods (becomes, etc.)
|
# various different auth escalation methods (becomes, etc.)
|
||||||
|
|
||||||
self.connection = C.DEFAULT_TRANSPORT
|
self.connection = C.DEFAULT_TRANSPORT
|
||||||
|
self.remote_addr = None
|
||||||
self.remote_user = 'root'
|
self.remote_user = 'root'
|
||||||
self.password = ''
|
self.password = ''
|
||||||
self.port = 22
|
self.port = 22
|
||||||
|
self.private_key_file = None
|
||||||
self.su = False
|
self.su = False
|
||||||
self.su_user = ''
|
self.su_user = ''
|
||||||
self.su_pass = ''
|
self.su_pass = ''
|
||||||
|
@ -65,6 +67,14 @@ class ConnectionInformation:
|
||||||
if options:
|
if options:
|
||||||
self.set_options(options)
|
self.set_options(options)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
value = "CONNECTION INFO:\n"
|
||||||
|
fields = self._get_fields()
|
||||||
|
fields.sort()
|
||||||
|
for field in fields:
|
||||||
|
value += "%20s : %s\n" % (field, getattr(self, field))
|
||||||
|
return value
|
||||||
|
|
||||||
def set_play(self, play):
|
def set_play(self, play):
|
||||||
'''
|
'''
|
||||||
Configures this connection information instance with data from
|
Configures this connection information instance with data from
|
||||||
|
@ -128,26 +138,6 @@ class ConnectionInformation:
|
||||||
when merging in data from task overrides.
|
when merging in data from task overrides.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
#self.connection = ci.connection
|
|
||||||
#self.remote_user = ci.remote_user
|
|
||||||
#self.password = ci.password
|
|
||||||
#self.port = ci.port
|
|
||||||
#self.su = ci.su
|
|
||||||
#self.su_user = ci.su_user
|
|
||||||
#self.su_pass = ci.su_pass
|
|
||||||
#self.sudo = ci.sudo
|
|
||||||
#self.sudo_user = ci.sudo_user
|
|
||||||
#self.sudo_pass = ci.sudo_pass
|
|
||||||
#self.verbosity = ci.verbosity
|
|
||||||
|
|
||||||
# other
|
|
||||||
#self.no_log = ci.no_log
|
|
||||||
#self.environment = ci.environment
|
|
||||||
|
|
||||||
# requested tags
|
|
||||||
#self.only_tags = ci.only_tags.copy()
|
|
||||||
#self.skip_tags = ci.skip_tags.copy()
|
|
||||||
|
|
||||||
for field in self._get_fields():
|
for field in self._get_fields():
|
||||||
value = getattr(ci, field, None)
|
value = getattr(ci, field, None)
|
||||||
if isinstance(value, dict):
|
if isinstance(value, dict):
|
||||||
|
|
|
@ -172,7 +172,7 @@ class TaskExecutor:
|
||||||
self._connection_info.post_validate(variables=variables, loader=self._loader)
|
self._connection_info.post_validate(variables=variables, loader=self._loader)
|
||||||
|
|
||||||
# get the connection and the handler for this execution
|
# get the connection and the handler for this execution
|
||||||
self._connection = self._get_connection()
|
self._connection = self._get_connection(variables)
|
||||||
self._handler = self._get_action_handler(connection=self._connection)
|
self._handler = self._get_action_handler(connection=self._connection)
|
||||||
|
|
||||||
# Evaluate the conditional (if any) for this task, which we do before running
|
# Evaluate the conditional (if any) for this task, which we do before running
|
||||||
|
@ -204,6 +204,7 @@ class TaskExecutor:
|
||||||
# with the registered variable value later on when testing conditions
|
# with the registered variable value later on when testing conditions
|
||||||
vars_copy = variables.copy()
|
vars_copy = variables.copy()
|
||||||
|
|
||||||
|
|
||||||
debug("starting attempt loop")
|
debug("starting attempt loop")
|
||||||
result = None
|
result = None
|
||||||
for attempt in range(retries):
|
for attempt in range(retries):
|
||||||
|
@ -301,7 +302,7 @@ class TaskExecutor:
|
||||||
else:
|
else:
|
||||||
return async_result
|
return async_result
|
||||||
|
|
||||||
def _get_connection(self):
|
def _get_connection(self, variables):
|
||||||
'''
|
'''
|
||||||
Reads the connection property for the host, and returns the
|
Reads the connection property for the host, and returns the
|
||||||
correct connection object from the list of connection plugins
|
correct connection object from the list of connection plugins
|
||||||
|
@ -310,13 +311,17 @@ class TaskExecutor:
|
||||||
# FIXME: delegate_to calculation should be done here
|
# FIXME: delegate_to calculation should be done here
|
||||||
# FIXME: calculation of connection params/auth stuff should be done here
|
# FIXME: calculation of connection params/auth stuff should be done here
|
||||||
|
|
||||||
|
self._connection_info.remote_addr = self._host.ipv4_address
|
||||||
|
if self._task.delegate_to is not None:
|
||||||
|
self._compute_delegate(variables)
|
||||||
|
|
||||||
# FIXME: add all port/connection type munging here (accelerated mode,
|
# FIXME: add all port/connection type munging here (accelerated mode,
|
||||||
# fixing up options for ssh, etc.)? and 'smart' conversion
|
# fixing up options for ssh, etc.)? and 'smart' conversion
|
||||||
conn_type = self._connection_info.connection
|
conn_type = self._connection_info.connection
|
||||||
if conn_type == 'smart':
|
if conn_type == 'smart':
|
||||||
conn_type = 'ssh'
|
conn_type = 'ssh'
|
||||||
|
|
||||||
connection = connection_loader.get(conn_type, self._host, self._connection_info)
|
connection = connection_loader.get(conn_type, self._connection_info)
|
||||||
if not connection:
|
if not connection:
|
||||||
raise AnsibleError("the connection plugin '%s' was not found" % conn_type)
|
raise AnsibleError("the connection plugin '%s' was not found" % conn_type)
|
||||||
|
|
||||||
|
@ -350,3 +355,37 @@ class TaskExecutor:
|
||||||
raise AnsibleError("the handler '%s' was not found" % handler_name)
|
raise AnsibleError("the handler '%s' was not found" % handler_name)
|
||||||
|
|
||||||
return handler
|
return handler
|
||||||
|
|
||||||
|
def _compute_delegate(self, variables):
|
||||||
|
|
||||||
|
# get the vars for the delegate by its name
|
||||||
|
try:
|
||||||
|
this_info = variables['hostvars'][self._task.delegate_to]
|
||||||
|
except:
|
||||||
|
# make sure the inject is empty for non-inventory hosts
|
||||||
|
this_info = {}
|
||||||
|
|
||||||
|
# get the real ssh_address for the delegate and allow ansible_ssh_host to be templated
|
||||||
|
#self._connection_info.remote_user = self._compute_delegate_user(self.delegate_to, delegate['inject'])
|
||||||
|
self._connection_info.remote_addr = this_info.get('ansible_ssh_host', self._task.delegate_to)
|
||||||
|
self._connection_info.port = this_info.get('ansible_ssh_port', self._connection_info.port)
|
||||||
|
self._connection_info.password = this_info.get('ansible_ssh_pass', self._connection_info.password)
|
||||||
|
self._connection_info.private_key_file = this_info.get('ansible_ssh_private_key_file', self._connection_info.private_key_file)
|
||||||
|
self._connection_info.connection = this_info.get('ansible_connection', self._connection_info.connection)
|
||||||
|
self._connection_info.sudo_pass = this_info.get('ansible_sudo_pass', self._connection_info.sudo_pass)
|
||||||
|
|
||||||
|
if self._connection_info.remote_addr in ('127.0.0.1', 'localhost'):
|
||||||
|
self._connection_info.connection = 'local'
|
||||||
|
|
||||||
|
# Last chance to get private_key_file from global variables.
|
||||||
|
# this is useful if delegated host is not defined in the inventory
|
||||||
|
#if delegate['private_key_file'] is None:
|
||||||
|
# delegate['private_key_file'] = remote_inject.get('ansible_ssh_private_key_file', None)
|
||||||
|
|
||||||
|
#if delegate['private_key_file'] is not None:
|
||||||
|
# delegate['private_key_file'] = os.path.expanduser(delegate['private_key_file'])
|
||||||
|
|
||||||
|
for i in this_info:
|
||||||
|
if i.startswith("ansible_") and i.endswith("_interpreter"):
|
||||||
|
variables[i] = this_info[i]
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 095f8681dbdfd2e9247446822e953287c9bca66c
|
Subproject commit 34784b7a617aa35d3b994c9f0795567afc6fb0b0
|
|
@ -219,7 +219,7 @@ class ModuleArgsParser:
|
||||||
thing = None
|
thing = None
|
||||||
|
|
||||||
action = None
|
action = None
|
||||||
delegate_to = None
|
delegate_to = self._task_ds.get('delegate_to', None)
|
||||||
args = dict()
|
args = dict()
|
||||||
|
|
||||||
|
|
||||||
|
@ -236,15 +236,12 @@ class ModuleArgsParser:
|
||||||
|
|
||||||
# action
|
# action
|
||||||
if 'action' in self._task_ds:
|
if 'action' in self._task_ds:
|
||||||
|
|
||||||
# an old school 'action' statement
|
# an old school 'action' statement
|
||||||
thing = self._task_ds['action']
|
thing = self._task_ds['action']
|
||||||
delegate_to = None
|
|
||||||
action, args = self._normalize_parameters(thing, additional_args=additional_args)
|
action, args = self._normalize_parameters(thing, additional_args=additional_args)
|
||||||
|
|
||||||
# local_action
|
# local_action
|
||||||
if 'local_action' in self._task_ds:
|
if 'local_action' in self._task_ds:
|
||||||
|
|
||||||
# local_action is similar but also implies a delegate_to
|
# local_action is similar but also implies a delegate_to
|
||||||
if action is not None:
|
if action is not None:
|
||||||
raise AnsibleParserError("action and local_action are mutually exclusive", obj=self._task_ds)
|
raise AnsibleParserError("action and local_action are mutually exclusive", obj=self._task_ds)
|
||||||
|
|
|
@ -66,6 +66,7 @@ class Conditional:
|
||||||
evaluation.
|
evaluation.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
original = conditional
|
||||||
if conditional is None or conditional == '':
|
if conditional is None or conditional == '':
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
|
@ -161,7 +161,9 @@ class ActionBase:
|
||||||
tmp_mode = 'a+rx'
|
tmp_mode = 'a+rx'
|
||||||
|
|
||||||
cmd = self._shell.mkdtemp(basefile, use_system_tmp, tmp_mode)
|
cmd = self._shell.mkdtemp(basefile, use_system_tmp, tmp_mode)
|
||||||
|
debug("executing _low_level_execute_command to create the tmp path")
|
||||||
result = self._low_level_execute_command(cmd, None, sudoable=False)
|
result = self._low_level_execute_command(cmd, None, sudoable=False)
|
||||||
|
debug("done with creation of tmp path")
|
||||||
|
|
||||||
# error handling on this seems a little aggressive?
|
# error handling on this seems a little aggressive?
|
||||||
if result['rc'] != 0:
|
if result['rc'] != 0:
|
||||||
|
@ -196,11 +198,13 @@ class ActionBase:
|
||||||
def _remove_tmp_path(self, tmp_path):
|
def _remove_tmp_path(self, tmp_path):
|
||||||
'''Remove a temporary path we created. '''
|
'''Remove a temporary path we created. '''
|
||||||
|
|
||||||
if "-tmp-" in tmp_path:
|
if tmp_path and "-tmp-" in tmp_path:
|
||||||
cmd = self._shell.remove(tmp_path, recurse=True)
|
cmd = self._shell.remove(tmp_path, recurse=True)
|
||||||
# If we have gotten here we have a working ssh configuration.
|
# If we have gotten here we have a working ssh configuration.
|
||||||
# If ssh breaks we could leave tmp directories out on the remote system.
|
# If ssh breaks we could leave tmp directories out on the remote system.
|
||||||
|
debug("calling _low_level_execute_command to remove the tmp path")
|
||||||
self._low_level_execute_command(cmd, None, sudoable=False)
|
self._low_level_execute_command(cmd, None, sudoable=False)
|
||||||
|
debug("done removing the tmp path")
|
||||||
|
|
||||||
def _transfer_data(self, remote_path, data):
|
def _transfer_data(self, remote_path, data):
|
||||||
'''
|
'''
|
||||||
|
@ -213,14 +217,16 @@ class ActionBase:
|
||||||
afd, afile = tempfile.mkstemp()
|
afd, afile = tempfile.mkstemp()
|
||||||
afo = os.fdopen(afd, 'w')
|
afo = os.fdopen(afd, 'w')
|
||||||
try:
|
try:
|
||||||
if not isinstance(data, unicode):
|
# FIXME: is this still necessary?
|
||||||
#ensure the data is valid UTF-8
|
#if not isinstance(data, unicode):
|
||||||
data = data.decode('utf-8')
|
# #ensure the data is valid UTF-8
|
||||||
else:
|
# data = data.decode('utf-8')
|
||||||
data = data.encode('utf-8')
|
#else:
|
||||||
|
# data = data.encode('utf-8')
|
||||||
afo.write(data)
|
afo.write(data)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
raise AnsibleError("failure encoding into utf-8: %s" % str(e))
|
#raise AnsibleError("failure encoding into utf-8: %s" % str(e))
|
||||||
|
raise AnsibleError("failure writing module data to temporary file for transfer: %s" % str(e))
|
||||||
|
|
||||||
afo.flush()
|
afo.flush()
|
||||||
afo.close()
|
afo.close()
|
||||||
|
@ -238,7 +244,10 @@ class ActionBase:
|
||||||
'''
|
'''
|
||||||
|
|
||||||
cmd = self._shell.chmod(mode, path)
|
cmd = self._shell.chmod(mode, path)
|
||||||
return self._low_level_execute_command(cmd, tmp, sudoable=sudoable)
|
debug("calling _low_level_execute_command to chmod the remote path")
|
||||||
|
res = self._low_level_execute_command(cmd, tmp, sudoable=sudoable)
|
||||||
|
debug("done with chmod call")
|
||||||
|
return res
|
||||||
|
|
||||||
def _remote_checksum(self, tmp, path):
|
def _remote_checksum(self, tmp, path):
|
||||||
'''
|
'''
|
||||||
|
@ -250,7 +259,9 @@ class ActionBase:
|
||||||
#python_interp = inject['hostvars'][inject['inventory_hostname']].get('ansible_python_interpreter', 'python')
|
#python_interp = inject['hostvars'][inject['inventory_hostname']].get('ansible_python_interpreter', 'python')
|
||||||
python_interp = 'python'
|
python_interp = 'python'
|
||||||
cmd = self._shell.checksum(path, python_interp)
|
cmd = self._shell.checksum(path, python_interp)
|
||||||
|
debug("calling _low_level_execute_command to get the remote checksum")
|
||||||
data = self._low_level_execute_command(cmd, tmp, sudoable=True)
|
data = self._low_level_execute_command(cmd, tmp, sudoable=True)
|
||||||
|
debug("done getting the remote checksum")
|
||||||
# FIXME: implement this function?
|
# FIXME: implement this function?
|
||||||
#data2 = utils.last_non_blank_line(data['stdout'])
|
#data2 = utils.last_non_blank_line(data['stdout'])
|
||||||
try:
|
try:
|
||||||
|
@ -286,7 +297,9 @@ class ActionBase:
|
||||||
expand_path = '~%s' % self._connection_info.su_user
|
expand_path = '~%s' % self._connection_info.su_user
|
||||||
|
|
||||||
cmd = self._shell.expand_user(expand_path)
|
cmd = self._shell.expand_user(expand_path)
|
||||||
|
debug("calling _low_level_execute_command to expand the remote user path")
|
||||||
data = self._low_level_execute_command(cmd, tmp, sudoable=False)
|
data = self._low_level_execute_command(cmd, tmp, sudoable=False)
|
||||||
|
debug("done expanding the remote user path")
|
||||||
#initial_fragment = utils.last_non_blank_line(data['stdout'])
|
#initial_fragment = utils.last_non_blank_line(data['stdout'])
|
||||||
initial_fragment = data['stdout'].strip().splitlines()[-1]
|
initial_fragment = data['stdout'].strip().splitlines()[-1]
|
||||||
|
|
||||||
|
@ -354,7 +367,9 @@ class ActionBase:
|
||||||
# FIXME: async stuff here?
|
# FIXME: async stuff here?
|
||||||
#if (module_style != 'new' or async_jid is not None or not self._connection._has_pipelining or not C.ANSIBLE_SSH_PIPELINING or C.DEFAULT_KEEP_REMOTE_FILES):
|
#if (module_style != 'new' or async_jid is not None or not self._connection._has_pipelining or not C.ANSIBLE_SSH_PIPELINING or C.DEFAULT_KEEP_REMOTE_FILES):
|
||||||
if remote_module_path:
|
if remote_module_path:
|
||||||
|
debug("transfering module to remote")
|
||||||
self._transfer_data(remote_module_path, module_data)
|
self._transfer_data(remote_module_path, module_data)
|
||||||
|
debug("done transfering module to remote")
|
||||||
|
|
||||||
environment_string = self._compute_environment_string()
|
environment_string = self._compute_environment_string()
|
||||||
|
|
||||||
|
@ -389,7 +404,9 @@ class ActionBase:
|
||||||
# specified in the play, not the sudo_user
|
# specified in the play, not the sudo_user
|
||||||
sudoable = False
|
sudoable = False
|
||||||
|
|
||||||
|
debug("calling _low_level_execute_command() for command %s" % cmd)
|
||||||
res = self._low_level_execute_command(cmd, tmp, sudoable=sudoable, in_data=in_data)
|
res = self._low_level_execute_command(cmd, tmp, sudoable=sudoable, in_data=in_data)
|
||||||
|
debug("_low_level_execute_command returned ok")
|
||||||
|
|
||||||
if tmp and "tmp" in tmp and not C.DEFAULT_KEEP_REMOTE_FILES and not persist_files and delete_remote_tmp:
|
if tmp and "tmp" in tmp and not C.DEFAULT_KEEP_REMOTE_FILES and not persist_files and delete_remote_tmp:
|
||||||
if (self._connection_info.sudo and self._connection_info.sudo_user != 'root') or (self._connection_info.su and self._connection_info.su_user != 'root'):
|
if (self._connection_info.sudo and self._connection_info.sudo_user != 'root') or (self._connection_info.su and self._connection_info.su_user != 'root'):
|
||||||
|
@ -446,7 +463,7 @@ class ActionBase:
|
||||||
# FIXME: hard-coded sudo_exe here
|
# FIXME: hard-coded sudo_exe here
|
||||||
cmd, prompt, success_key = self._connection_info.make_sudo_cmd('/usr/bin/sudo', executable, cmd)
|
cmd, prompt, success_key = self._connection_info.make_sudo_cmd('/usr/bin/sudo', executable, cmd)
|
||||||
|
|
||||||
debug("executing the command through the connection")
|
debug("executing the command %s through the connection" % cmd)
|
||||||
rc, stdin, stdout, stderr = self._connection.exec_command(cmd, tmp, executable=executable, in_data=in_data)
|
rc, stdin, stdout, stderr = self._connection.exec_command(cmd, tmp, executable=executable, in_data=in_data)
|
||||||
debug("command execution done")
|
debug("command execution done")
|
||||||
|
|
||||||
|
|
|
@ -251,12 +251,6 @@ class ActionModule(ActionBase):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# FIXME: checkmode and no_log stuff
|
|
||||||
#if self.runner.noop_on_check(inject):
|
|
||||||
# new_module_args['CHECKMODE'] = True
|
|
||||||
#if self.runner.no_log:
|
|
||||||
# new_module_args['NO_LOG'] = True
|
|
||||||
|
|
||||||
module_return = self._execute_module(module_name='copy', module_args=new_module_args, delete_remote_tmp=delete_remote_tmp)
|
module_return = self._execute_module(module_name='copy', module_args=new_module_args, delete_remote_tmp=delete_remote_tmp)
|
||||||
module_executed = True
|
module_executed = True
|
||||||
|
|
||||||
|
@ -279,11 +273,6 @@ class ActionModule(ActionBase):
|
||||||
original_basename=source_rel
|
original_basename=source_rel
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
# FIXME: checkmode and no_log stuff
|
|
||||||
#if self.runner.noop_on_check(inject):
|
|
||||||
# new_module_args['CHECKMODE'] = True
|
|
||||||
#if self.runner.no_log:
|
|
||||||
# new_module_args['NO_LOG'] = True
|
|
||||||
|
|
||||||
# Execute the file module.
|
# Execute the file module.
|
||||||
module_return = self._execute_module(module_name='file', module_args=new_module_args, delete_remote_tmp=delete_remote_tmp)
|
module_return = self._execute_module(module_name='file', module_args=new_module_args, delete_remote_tmp=delete_remote_tmp)
|
||||||
|
@ -296,18 +285,17 @@ class ActionModule(ActionBase):
|
||||||
if module_return.get('changed') == True:
|
if module_return.get('changed') == True:
|
||||||
changed = True
|
changed = True
|
||||||
|
|
||||||
|
# the file module returns the file path as 'path', but
|
||||||
|
# the copy module uses 'dest', so add it if it's not there
|
||||||
|
if 'path' in module_return and 'dest' not in module_return:
|
||||||
|
module_return['dest'] = module_return['path']
|
||||||
|
|
||||||
# Delete tmp path if we were recursive or if we did not execute a module.
|
# Delete tmp path if we were recursive or if we did not execute a module.
|
||||||
if (not C.DEFAULT_KEEP_REMOTE_FILES and not delete_remote_tmp) \
|
if (not C.DEFAULT_KEEP_REMOTE_FILES and not delete_remote_tmp) or (not C.DEFAULT_KEEP_REMOTE_FILES and delete_remote_tmp and not module_executed):
|
||||||
or (not C.DEFAULT_KEEP_REMOTE_FILES and delete_remote_tmp and not module_executed):
|
|
||||||
self._remove_tmp_path(tmp)
|
self._remove_tmp_path(tmp)
|
||||||
|
|
||||||
# the file module returns the file path as 'path', but
|
|
||||||
# the copy module uses 'dest', so add it if it's not there
|
|
||||||
if 'path' in module_return and 'dest' not in module_return:
|
|
||||||
module_return['dest'] = module_return['path']
|
|
||||||
|
|
||||||
# TODO: Support detailed status/diff for multiple files
|
# TODO: Support detailed status/diff for multiple files
|
||||||
if len(source_files) == 1:
|
if module_executed and len(source_files) == 1:
|
||||||
result = module_return
|
result = module_return
|
||||||
else:
|
else:
|
||||||
result = dict(dest=dest, src=source, changed=changed)
|
result = dict(dest=dest, src=source, changed=changed)
|
||||||
|
|
|
@ -92,7 +92,7 @@ class ActionModule(ActionBase):
|
||||||
dest = self._loader.path_dwim(dest)
|
dest = self._loader.path_dwim(dest)
|
||||||
else:
|
else:
|
||||||
# files are saved in dest dir, with a subdir for each host, then the filename
|
# files are saved in dest dir, with a subdir for each host, then the filename
|
||||||
dest = "%s/%s/%s" % (self._loader.path_dwim(dest), self._connection._host, source_local)
|
dest = "%s/%s/%s" % (self._loader.path_dwim(dest), self._connection_info.remote_addr, source_local)
|
||||||
|
|
||||||
dest = dest.replace("//","/")
|
dest = dest.replace("//","/")
|
||||||
|
|
||||||
|
|
|
@ -34,8 +34,7 @@ class ConnectionBase:
|
||||||
A base class for connections to contain common code.
|
A base class for connections to contain common code.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __init__(self, host, connection_info, *args, **kwargs):
|
def __init__(self, connection_info, *args, **kwargs):
|
||||||
self._host = host
|
|
||||||
self._connection_info = connection_info
|
self._connection_info = connection_info
|
||||||
self._has_pipelining = False
|
self._has_pipelining = False
|
||||||
self._display = Display(connection_info)
|
self._display = Display(connection_info)
|
||||||
|
|
|
@ -65,7 +65,7 @@ class Connection(ConnectionBase):
|
||||||
|
|
||||||
executable = executable.split()[0] if executable else None
|
executable = executable.split()[0] if executable else None
|
||||||
|
|
||||||
self._display.vvv("%s EXEC %s" % (self._host, local_cmd))
|
self._display.vvv("%s EXEC %s" % (self._connection_info.remote_addr, local_cmd))
|
||||||
# FIXME: cwd= needs to be set to the basedir of the playbook
|
# FIXME: cwd= needs to be set to the basedir of the playbook
|
||||||
debug("opening command with Popen()")
|
debug("opening command with Popen()")
|
||||||
p = subprocess.Popen(
|
p = subprocess.Popen(
|
||||||
|
@ -115,7 +115,7 @@ class Connection(ConnectionBase):
|
||||||
''' transfer a file from local to local '''
|
''' transfer a file from local to local '''
|
||||||
|
|
||||||
#vvv("PUT %s TO %s" % (in_path, out_path), host=self.host)
|
#vvv("PUT %s TO %s" % (in_path, out_path), host=self.host)
|
||||||
self._display.vvv("%s PUT %s TO %s" % (self._host, in_path, out_path))
|
self._display.vvv("%s PUT %s TO %s" % (self._connection_info.remote_addr, in_path, out_path))
|
||||||
if not os.path.exists(in_path):
|
if not os.path.exists(in_path):
|
||||||
#raise AnsibleFileNotFound("file or module does not exist: %s" % in_path)
|
#raise AnsibleFileNotFound("file or module does not exist: %s" % in_path)
|
||||||
raise AnsibleError("file or module does not exist: %s" % in_path)
|
raise AnsibleError("file or module does not exist: %s" % in_path)
|
||||||
|
@ -130,7 +130,7 @@ class Connection(ConnectionBase):
|
||||||
|
|
||||||
def fetch_file(self, in_path, out_path):
|
def fetch_file(self, in_path, out_path):
|
||||||
#vvv("FETCH %s TO %s" % (in_path, out_path), host=self.host)
|
#vvv("FETCH %s TO %s" % (in_path, out_path), host=self.host)
|
||||||
self._display.vvv("%s FETCH %s TO %s" % (self._host, in_path, out_path))
|
self._display.vvv("%s FETCH %s TO %s" % (self._connection_info.remote_addr, in_path, out_path))
|
||||||
''' fetch a file from local to local -- for copatibility '''
|
''' fetch a file from local to local -- for copatibility '''
|
||||||
self.put_file(in_path, out_path)
|
self.put_file(in_path, out_path)
|
||||||
|
|
||||||
|
|
|
@ -37,8 +37,8 @@ from ansible.plugins.connections import ConnectionBase
|
||||||
class Connection(ConnectionBase):
|
class Connection(ConnectionBase):
|
||||||
''' ssh based connections '''
|
''' ssh based connections '''
|
||||||
|
|
||||||
def __init__(self, host, connection_info, *args, **kwargs):
|
def __init__(self, connection_info, *args, **kwargs):
|
||||||
super(Connection, self).__init__(host, connection_info)
|
super(Connection, self).__init__(connection_info)
|
||||||
|
|
||||||
# SSH connection specific init stuff
|
# SSH connection specific init stuff
|
||||||
self.HASHED_KEY_MAGIC = "|1|"
|
self.HASHED_KEY_MAGIC = "|1|"
|
||||||
|
@ -57,7 +57,7 @@ class Connection(ConnectionBase):
|
||||||
def connect(self):
|
def connect(self):
|
||||||
''' connect to the remote host '''
|
''' connect to the remote host '''
|
||||||
|
|
||||||
self._display.vvv("ESTABLISH CONNECTION FOR USER: %s" % self._connection_info.remote_user, host=self._host)
|
self._display.vvv("ESTABLISH CONNECTION FOR USER: %s" % self._connection_info.remote_user, host=self._connection_info.remote_addr)
|
||||||
|
|
||||||
self._common_args = []
|
self._common_args = []
|
||||||
extra_args = C.ANSIBLE_SSH_ARGS
|
extra_args = C.ANSIBLE_SSH_ARGS
|
||||||
|
@ -277,7 +277,7 @@ class Connection(ConnectionBase):
|
||||||
# not sure if it's all working yet so this remains commented out
|
# not sure if it's all working yet so this remains commented out
|
||||||
#if self._ipv6:
|
#if self._ipv6:
|
||||||
# ssh_cmd += ['-6']
|
# ssh_cmd += ['-6']
|
||||||
ssh_cmd += [self._host.ipv4_address]
|
ssh_cmd += [self._connection_info.remote_addr]
|
||||||
|
|
||||||
if not (self._connection_info.sudo or self._connection_info.su):
|
if not (self._connection_info.sudo or self._connection_info.su):
|
||||||
prompt = None
|
prompt = None
|
||||||
|
@ -293,9 +293,9 @@ class Connection(ConnectionBase):
|
||||||
sudo_cmd, prompt, success_key = self._connection_info.make_sudo_cmd('/usr/bin/sudo', executable, cmd)
|
sudo_cmd, prompt, success_key = self._connection_info.make_sudo_cmd('/usr/bin/sudo', executable, cmd)
|
||||||
ssh_cmd.append(sudo_cmd)
|
ssh_cmd.append(sudo_cmd)
|
||||||
|
|
||||||
self._display.vvv("EXEC %s" % ' '.join(ssh_cmd), host=self._host)
|
self._display.vvv("EXEC %s" % ' '.join(ssh_cmd), host=self._connection_info.remote_addr)
|
||||||
|
|
||||||
not_in_host_file = self.not_in_host_file(self._host.get_name())
|
not_in_host_file = self.not_in_host_file(self._connection_info.remote_addr)
|
||||||
|
|
||||||
# FIXME: move the locations of these lock files, same as init above
|
# FIXME: move the locations of these lock files, same as init above
|
||||||
#if C.HOST_KEY_CHECKING and not_in_host_file:
|
#if C.HOST_KEY_CHECKING and not_in_host_file:
|
||||||
|
@ -309,44 +309,6 @@ class Connection(ConnectionBase):
|
||||||
|
|
||||||
self._send_password()
|
self._send_password()
|
||||||
|
|
||||||
no_prompt_out = ''
|
|
||||||
no_prompt_err = ''
|
|
||||||
# FIXME: su/sudo stuff
|
|
||||||
#if (self.runner.sudo and sudoable and self.runner.sudo_pass) or \
|
|
||||||
# (self.runner.su and su and self.runner.su_pass):
|
|
||||||
# # several cases are handled for sudo privileges with password
|
|
||||||
# # * NOPASSWD (tty & no-tty): detect success_key on stdout
|
|
||||||
# # * without NOPASSWD:
|
|
||||||
# # * detect prompt on stdout (tty)
|
|
||||||
# # * detect prompt on stderr (no-tty)
|
|
||||||
# fcntl.fcntl(p.stdout, fcntl.F_SETFL,
|
|
||||||
# fcntl.fcntl(p.stdout, fcntl.F_GETFL) | os.O_NONBLOCK)
|
|
||||||
# fcntl.fcntl(p.stderr, fcntl.F_SETFL,
|
|
||||||
# fcntl.fcntl(p.stderr, fcntl.F_GETFL) | os.O_NONBLOCK)
|
|
||||||
# sudo_output = ''
|
|
||||||
# sudo_errput = ''
|
|
||||||
#
|
|
||||||
# while True:
|
|
||||||
# if success_key in sudo_output or \
|
|
||||||
# (self.runner.sudo_pass and sudo_output.endswith(prompt)) or \
|
|
||||||
# (self.runner.su_pass and utils.su_prompts.check_su_prompt(sudo_output)):
|
|
||||||
# break
|
|
||||||
self._display.vvv("EXEC %s" % ' '.join(ssh_cmd), host=self._host)
|
|
||||||
|
|
||||||
not_in_host_file = self.not_in_host_file(self._host.get_name())
|
|
||||||
|
|
||||||
# FIXME: file locations
|
|
||||||
#if C.HOST_KEY_CHECKING and not_in_host_file:
|
|
||||||
# # lock around the initial SSH connectivity so the user prompt about whether to add
|
|
||||||
# # the host to known hosts is not intermingled with multiprocess output.
|
|
||||||
# fcntl.lockf(self.runner.process_lockfile, fcntl.LOCK_EX)
|
|
||||||
# fcntl.lockf(self.runner.output_lockfile, fcntl.LOCK_EX)
|
|
||||||
|
|
||||||
# create process
|
|
||||||
(p, stdin) = self._run(ssh_cmd, in_data)
|
|
||||||
|
|
||||||
self._send_password()
|
|
||||||
|
|
||||||
no_prompt_out = ''
|
no_prompt_out = ''
|
||||||
no_prompt_err = ''
|
no_prompt_err = ''
|
||||||
# FIXME: su/sudo stuff
|
# FIXME: su/sudo stuff
|
||||||
|
@ -429,13 +391,13 @@ class Connection(ConnectionBase):
|
||||||
|
|
||||||
def put_file(self, in_path, out_path):
|
def put_file(self, in_path, out_path):
|
||||||
''' transfer a file from local to remote '''
|
''' transfer a file from local to remote '''
|
||||||
self._display.vvv("PUT %s TO %s" % (in_path, out_path), host=self._host)
|
self._display.vvv("PUT %s TO %s" % (in_path, out_path), host=self._connection_info.remote_addr)
|
||||||
if not os.path.exists(in_path):
|
if not os.path.exists(in_path):
|
||||||
raise AnsibleFileNotFound("file or module does not exist: %s" % in_path)
|
raise AnsibleFileNotFound("file or module does not exist: %s" % in_path)
|
||||||
cmd = self._password_cmd()
|
cmd = self._password_cmd()
|
||||||
|
|
||||||
# FIXME: make a function, used in all 3 methods EXEC/PUT/FETCH
|
# FIXME: make a function, used in all 3 methods EXEC/PUT/FETCH
|
||||||
host = self._host.ipv4_address
|
host = self._connection_info.remote_addr
|
||||||
|
|
||||||
# FIXME: ipv6 stuff needs to be figured out. It's in the connection info, however
|
# 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
|
# not sure if it's all working yet so this remains commented out
|
||||||
|
@ -461,16 +423,16 @@ class Connection(ConnectionBase):
|
||||||
|
|
||||||
def fetch_file(self, in_path, out_path):
|
def fetch_file(self, in_path, out_path):
|
||||||
''' fetch a file from remote to local '''
|
''' fetch a file from remote to local '''
|
||||||
self._display.vvv("FETCH %s TO %s" % (in_path, out_path), host=self._host)
|
self._display.vvv("FETCH %s TO %s" % (in_path, out_path), host=self._connection_info.remote_addr)
|
||||||
cmd = self._password_cmd()
|
cmd = self._password_cmd()
|
||||||
|
|
||||||
# FIXME: make a function, used in all 3 methods EXEC/PUT/FETCH
|
# FIXME: make a function, used in all 3 methods EXEC/PUT/FETCH
|
||||||
host = self._host.ipv4_address
|
host = self._connection_info.remote_addr
|
||||||
|
|
||||||
# FIXME: ipv6 stuff needs to be figured out. It's in the connection info, however
|
# 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
|
# not sure if it's all working yet so this remains commented out
|
||||||
#if self._ipv6:
|
#if self._ipv6:
|
||||||
# host = '[%s]' % self._host
|
# host = '[%s]' % self._connection_info.remote_addr
|
||||||
|
|
||||||
if C.DEFAULT_SCP_IF_SSH:
|
if C.DEFAULT_SCP_IF_SSH:
|
||||||
cmd += ["scp"] + self._common_args
|
cmd += ["scp"] + self._common_args
|
||||||
|
|
|
@ -119,6 +119,9 @@
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
from jinja2.exceptions import UndefinedError
|
||||||
|
|
||||||
|
from ansible.errors import AnsibleUndefinedVariable
|
||||||
from ansible.plugins.lookup import LookupBase
|
from ansible.plugins.lookup import LookupBase
|
||||||
from ansible.template import Templar
|
from ansible.template import Templar
|
||||||
from ansible.utils.boolean import boolean
|
from ansible.utils.boolean import boolean
|
||||||
|
@ -172,7 +175,11 @@ class LookupModule(LookupBase):
|
||||||
templar = Templar(loader=self._loader, variables=variables)
|
templar = Templar(loader=self._loader, variables=variables)
|
||||||
roledir = variables.get('roledir')
|
roledir = variables.get('roledir')
|
||||||
for fn in total_search:
|
for fn in total_search:
|
||||||
fn = templar.template(fn)
|
try:
|
||||||
|
fn = templar.template(fn)
|
||||||
|
except (AnsibleUndefinedVariable, UndefinedError), e:
|
||||||
|
continue
|
||||||
|
|
||||||
if os.path.isabs(fn) and os.path.exists(fn):
|
if os.path.isabs(fn) and os.path.exists(fn):
|
||||||
return [fn]
|
return [fn]
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -85,8 +85,8 @@ class StrategyBase:
|
||||||
def get_hosts_remaining(self, play):
|
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]
|
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]
|
||||||
|
|
||||||
def get_failed_hosts(self):
|
def get_failed_hosts(self, play):
|
||||||
return [host for host in self._inventory.get_hosts() if host.name in self._tqm._failed_hosts]
|
return [host for host in self._inventory.get_hosts(play.hosts) if host.name in self._tqm._failed_hosts]
|
||||||
|
|
||||||
def _queue_task(self, host, task, task_vars, connection_info):
|
def _queue_task(self, host, task, task_vars, connection_info):
|
||||||
''' handles queueing the task up to be sent to a worker '''
|
''' handles queueing the task up to be sent to a worker '''
|
||||||
|
@ -129,6 +129,7 @@ class StrategyBase:
|
||||||
task = task_result._task
|
task = task_result._task
|
||||||
if result[0] == 'host_task_failed':
|
if result[0] == 'host_task_failed':
|
||||||
if not task.ignore_errors:
|
if not task.ignore_errors:
|
||||||
|
debug("marking %s as failed" % host.get_name())
|
||||||
self._tqm._failed_hosts[host.get_name()] = True
|
self._tqm._failed_hosts[host.get_name()] = True
|
||||||
self._callback.runner_on_failed(task, task_result)
|
self._callback.runner_on_failed(task, task_result)
|
||||||
elif result[0] == 'host_unreachable':
|
elif result[0] == 'host_unreachable':
|
||||||
|
@ -284,7 +285,7 @@ class StrategyBase:
|
||||||
result = True
|
result = True
|
||||||
|
|
||||||
debug("getting failed hosts")
|
debug("getting failed hosts")
|
||||||
failed_hosts = self.get_failed_hosts()
|
failed_hosts = self.get_failed_hosts(iterator._play)
|
||||||
if len(failed_hosts) == 0:
|
if len(failed_hosts) == 0:
|
||||||
debug("there are no failed hosts")
|
debug("there are no failed hosts")
|
||||||
return result
|
return result
|
||||||
|
@ -317,8 +318,9 @@ class StrategyBase:
|
||||||
# pop the task, mark the host blocked, and queue it
|
# pop the task, mark the host blocked, and queue it
|
||||||
self._blocked_hosts[host_name] = True
|
self._blocked_hosts[host_name] = True
|
||||||
task = iterator.get_next_task_for_host(host)
|
task = iterator.get_next_task_for_host(host)
|
||||||
|
task_vars = self._variable_manager.get_vars(loader=self._loader, play=iterator._play, host=host, task=task)
|
||||||
self._callback.playbook_on_cleanup_task_start(task.get_name())
|
self._callback.playbook_on_cleanup_task_start(task.get_name())
|
||||||
self._queue_task(iterator._play, host, task, connection_info)
|
self._queue_task(host, task, task_vars, connection_info)
|
||||||
|
|
||||||
self._process_pending_results()
|
self._process_pending_results()
|
||||||
|
|
||||||
|
@ -352,8 +354,8 @@ class StrategyBase:
|
||||||
self._callback.playbook_on_handler_task_start(handler_name)
|
self._callback.playbook_on_handler_task_start(handler_name)
|
||||||
for host in self._notified_handlers[handler_name]:
|
for host in self._notified_handlers[handler_name]:
|
||||||
if not handler.has_triggered(host):
|
if not handler.has_triggered(host):
|
||||||
temp_data = handler.serialize()
|
task_vars = self._variable_manager.get_vars(loader=self._loader, play=iterator._play, host=host, task=handler)
|
||||||
self._queue_task(iterator._play, host, handler, connection_info)
|
self._queue_task(host, handler, task_vars, connection_info)
|
||||||
handler.flag_for_host(host)
|
handler.flag_for_host(host)
|
||||||
|
|
||||||
self._process_pending_results()
|
self._process_pending_results()
|
||||||
|
|
|
@ -76,7 +76,7 @@ class Display:
|
||||||
if host is None:
|
if host is None:
|
||||||
self.display(msg, color='blue')
|
self.display(msg, color='blue')
|
||||||
else:
|
else:
|
||||||
self.display("<%s> %s" % (host.name, msg), color='blue')
|
self.display("<%s> %s" % (host, msg), color='blue')
|
||||||
|
|
||||||
def deprecated(self, msg, version, removed=False):
|
def deprecated(self, msg, version, removed=False):
|
||||||
''' used to print out a deprecation message.'''
|
''' used to print out a deprecation message.'''
|
||||||
|
|
|
@ -43,6 +43,8 @@ def secure_hash_s(data, hash_func=sha1):
|
||||||
|
|
||||||
digest = hash_func()
|
digest = hash_func()
|
||||||
try:
|
try:
|
||||||
|
if not isinstance(data, basestring):
|
||||||
|
data = "%s" % data
|
||||||
digest.update(data)
|
digest.update(data)
|
||||||
except UnicodeEncodeError:
|
except UnicodeEncodeError:
|
||||||
digest.update(data.encode('utf-8'))
|
digest.update(data.encode('utf-8'))
|
||||||
|
|
|
@ -39,14 +39,11 @@ def listify_lookup_plugin_terms(terms, variables, loader):
|
||||||
# with_items: {{ alist }}
|
# with_items: {{ alist }}
|
||||||
|
|
||||||
stripped = terms.strip()
|
stripped = terms.strip()
|
||||||
if not (stripped.startswith('{') or stripped.startswith('[')) and \
|
templar = Templar(loader=loader, variables=variables)
|
||||||
not stripped.startswith("/") and \
|
if not (stripped.startswith('{') or stripped.startswith('[')) and not stripped.startswith("/") and not stripped.startswith('set([') and not LOOKUP_REGEX.search(terms):
|
||||||
not stripped.startswith('set([') and \
|
|
||||||
not LOOKUP_REGEX.search(terms):
|
|
||||||
# if not already a list, get ready to evaluate with Jinja2
|
# if not already a list, get ready to evaluate with Jinja2
|
||||||
# not sure why the "/" is in above code :)
|
# not sure why the "/" is in above code :)
|
||||||
try:
|
try:
|
||||||
templar = Templar(loader=loader, variables=variables)
|
|
||||||
new_terms = templar.template("{{ %s }}" % terms)
|
new_terms = templar.template("{{ %s }}" % terms)
|
||||||
if isinstance(new_terms, basestring) and "{{" in new_terms:
|
if isinstance(new_terms, basestring) and "{{" in new_terms:
|
||||||
pass
|
pass
|
||||||
|
@ -54,6 +51,8 @@ def listify_lookup_plugin_terms(terms, variables, loader):
|
||||||
terms = new_terms
|
terms = new_terms
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
else:
|
||||||
|
terms = templar.template(terms)
|
||||||
|
|
||||||
if '{' in terms or '[' in terms:
|
if '{' in terms or '[' in terms:
|
||||||
# Jinja2 already evaluated a variable to a list.
|
# Jinja2 already evaluated a variable to a list.
|
||||||
|
|
17
v2/samples/test_block.yml
Normal file
17
v2/samples/test_block.yml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
- hosts: localhost
|
||||||
|
connection: local
|
||||||
|
gather_facts: no
|
||||||
|
tasks:
|
||||||
|
- block:
|
||||||
|
- command: /bin/false
|
||||||
|
- debug: msg="you shouldn't see me"
|
||||||
|
rescue:
|
||||||
|
- debug: msg="this is the rescue"
|
||||||
|
- command: /bin/false
|
||||||
|
- debug: msg="you shouldn't see this either"
|
||||||
|
always:
|
||||||
|
- debug: msg="this is the always block, it will always be seen"
|
||||||
|
when: foo|default('') != "some value"
|
||||||
|
tags:
|
||||||
|
- foo
|
||||||
|
- bar
|
Loading…
Reference in a new issue