@ -107,7 +107,7 @@ class Connection(ConnectionBase):
explanation of why they were added .
explanation of why they were added .
"""
"""
self . _command + = args
self . _command + = args
display . vvvvv ( ' SSH: ' + explanation + ' : ( %s ) ' % ' )( ' . join ( args ) , host = self . _play_context . remote_addr )
display . vvvvv ( ' SSH: ' + explanation + ' : ( %s ) ' % ' )( ' . join ( map ( to_unicode , args ) ) , host = self . _play_context . remote_addr )
def _build_command ( self , binary , * other_args ) :
def _build_command ( self , binary , * other_args ) :
'''
'''
@ -217,15 +217,14 @@ class Connection(ConnectionBase):
if not controlpath :
if not controlpath :
cpdir = unfrackpath ( ' $HOME/.ansible/cp ' )
cpdir = unfrackpath ( ' $HOME/.ansible/cp ' )
b_cpdir = to_bytes ( cpdir )
# The directory must exist and be writable.
# The directory must exist and be writable.
makedirs_safe ( cpdir, 0o700 )
makedirs_safe ( b_ cpdir, 0o700 )
if not os . access ( cpdir, os . W_OK ) :
if not os . access ( b_ cpdir, os . W_OK ) :
raise AnsibleError ( " Cannot write to ControlPath %s " % cpdir)
raise AnsibleError ( " Cannot write to ControlPath %s " % to_str( cpdir) )
args = ( " -o " , " ControlPath= {0} " . format (
args = ( " -o " , " ControlPath= " + C . ANSIBLE_SSH_CONTROL_PATH % dict ( directory = cpdir ) )
to_bytes ( C . ANSIBLE_SSH_CONTROL_PATH % dict ( directory = cpdir ) ) )
)
self . _add_args ( " found only ControlPersist; added ControlPath " , args )
self . _add_args ( " found only ControlPersist; added ControlPath " , args )
## Finally, we add any caller-supplied extras.
## Finally, we add any caller-supplied extras.
@ -233,7 +232,8 @@ class Connection(ConnectionBase):
if other_args :
if other_args :
self . _command + = other_args
self . _command + = other_args
return self . _command
cmd = [ to_bytes ( a ) for a in self . _command ]
return cmd
def _send_initial_data ( self , fh , in_data ) :
def _send_initial_data ( self , fh , in_data ) :
'''
'''
@ -245,7 +245,7 @@ class Connection(ConnectionBase):
display . debug ( ' Sending initial data ' )
display . debug ( ' Sending initial data ' )
try :
try :
fh . write ( in_data)
fh . write ( to_bytes( in_data) )
fh . close ( )
fh . close ( )
except ( OSError , IOError ) :
except ( OSError , IOError ) :
raise AnsibleConnectionFailure ( ' SSH Error: data could not be sent to the remote host. Make sure this host can be reached over ssh ' )
raise AnsibleConnectionFailure ( ' SSH Error: data could not be sent to the remote host. Make sure this host can be reached over ssh ' )
@ -263,7 +263,7 @@ class Connection(ConnectionBase):
# This is separate from _run() because we need to do the same thing for stdout
# This is separate from _run() because we need to do the same thing for stdout
# and stderr.
# and stderr.
def _examine_output ( self , source , state , chunk, sudoable ) :
def _examine_output ( self , source , state , b_ chunk, sudoable ) :
'''
'''
Takes a string , extracts complete lines from it , tests to see if they
Takes a string , extracts complete lines from it , tests to see if they
are a prompt , error message , etc . , and sets appropriate flags in self .
are a prompt , error message , etc . , and sets appropriate flags in self .
@ -274,46 +274,47 @@ class Connection(ConnectionBase):
'''
'''
output = [ ]
output = [ ]
for l in chunk . splitlines ( True ) :
for b_line in b_chunk . splitlines ( True ) :
display_line = to_unicode ( b_line , errors = ' replace ' ) . rstrip ( ' \r \n ' )
suppress_output = False
suppress_output = False
#display.debug("Examining line (source=%s, state=%s): '%s'" % (source, state, l.rstrip('\r\n') ))
#display.debug("Examining line (source=%s, state=%s): '%s'" % (source, state, display_line ))
if self . _play_context . prompt and self . check_password_prompt ( l) :
if self . _play_context . prompt and self . check_password_prompt ( b_ line ) :
display . debug ( " become_prompt: (source= %s , state= %s ): ' %s ' " % ( source , state , l. rstrip ( ' \r \n ' ) ) )
display . debug ( " become_prompt: (source= %s , state= %s ): ' %s ' " % ( source , state , display_line ) )
self . _flags [ ' become_prompt ' ] = True
self . _flags [ ' become_prompt ' ] = True
suppress_output = True
suppress_output = True
elif self . _play_context . success_key and self . check_become_success ( l) :
elif self . _play_context . success_key and self . check_become_success ( b_ line ) :
display . debug ( " become_success: (source= %s , state= %s ): ' %s ' " % ( source , state , l. rstrip ( ' \r \n ' ) ) )
display . debug ( " become_success: (source= %s , state= %s ): ' %s ' " % ( source , state , display_line ) )
self . _flags [ ' become_success ' ] = True
self . _flags [ ' become_success ' ] = True
suppress_output = True
suppress_output = True
elif sudoable and self . check_incorrect_password ( l) :
elif sudoable and self . check_incorrect_password ( b_ line ) :
display . debug ( " become_error: (source= %s , state= %s ): ' %s ' " % ( source , state , l. rstrip ( ' \r \n ' ) ) )
display . debug ( " become_error: (source= %s , state= %s ): ' %s ' " % ( source , state , display_line ) )
self . _flags [ ' become_error ' ] = True
self . _flags [ ' become_error ' ] = True
elif sudoable and self . check_missing_password ( l) :
elif sudoable and self . check_missing_password ( b_ line ) :
display . debug ( " become_nopasswd_error: (source= %s , state= %s ): ' %s ' " % ( source , state , l. rstrip ( ' \r \n ' ) ) )
display . debug ( " become_nopasswd_error: (source= %s , state= %s ): ' %s ' " % ( source , state , display_line ) )
self . _flags [ ' become_nopasswd_error ' ] = True
self . _flags [ ' become_nopasswd_error ' ] = True
if not suppress_output :
if not suppress_output :
output . append ( l)
output . append ( b_ line )
# The chunk we read was most likely a series of complete lines, but just
# The chunk we read was most likely a series of complete lines, but just
# in case the last line was incomplete (and not a prompt, which we would
# in case the last line was incomplete (and not a prompt, which we would
# have removed from the output), we retain it to be processed with the
# have removed from the output), we retain it to be processed with the
# next chunk.
# next chunk.
remainder = ' '
remainder = b ' '
if output and not output [ - 1 ] . endswith ( ' \n ' ) :
if output and not output [ - 1 ] . endswith ( b ' \n ' ) :
remainder = output [ - 1 ]
remainder = output [ - 1 ]
output = output [ : - 1 ]
output = output [ : - 1 ]
return ' ' . join ( output ) , remainder
return b ' ' . join ( output ) , remainder
def _run ( self , cmd , in_data , sudoable = True ) :
def _run ( self , cmd , in_data , sudoable = True ) :
'''
'''
Starts the command and communicates with it until it ends .
Starts the command and communicates with it until it ends .
'''
'''
display_cmd = map( to_unicode , map ( pipes . quote , cmd ) )
display_cmd = list( map ( pipes . quote , map ( to_unicode , cmd ) ) )
display . vvv ( u ' SSH: EXEC {0} ' . format ( u ' ' . join ( display_cmd ) ) , host = self . host )
display . vvv ( u ' SSH: EXEC {0} ' . format ( u ' ' . join ( display_cmd ) ) , host = self . host )
# Start the given command. If we don't need to pipeline data, we can try
# Start the given command. If we don't need to pipeline data, we can try
@ -333,7 +334,7 @@ class Connection(ConnectionBase):
# Make sure stdin is a proper pty to avoid tcgetattr errors
# Make sure stdin is a proper pty to avoid tcgetattr errors
master , slave = pty . openpty ( )
master , slave = pty . openpty ( )
p = subprocess . Popen ( cmd , stdin = slave , stdout = subprocess . PIPE , stderr = subprocess . PIPE )
p = subprocess . Popen ( cmd , stdin = slave , stdout = subprocess . PIPE , stderr = subprocess . PIPE )
stdin = os . fdopen ( master , ' w ' , 0 )
stdin = os . fdopen ( master , ' w b ' , 0 )
os . close ( slave )
os . close ( slave )
except ( OSError , IOError ) :
except ( OSError , IOError ) :
p = None
p = None
@ -348,7 +349,7 @@ class Connection(ConnectionBase):
if self . _play_context . password :
if self . _play_context . password :
os . close ( self . sshpass_pipe [ 0 ] )
os . close ( self . sshpass_pipe [ 0 ] )
try :
try :
os . write ( self . sshpass_pipe [ 1 ] , " {0} \n " . format ( to_bytes ( self . _play_context . password ) ) )
os . write ( self . sshpass_pipe [ 1 ] , to_bytes ( self . _play_context . password ) + b ' \n ' )
except OSError as e :
except OSError as e :
# Ignore broken pipe errors if the sshpass process has exited.
# Ignore broken pipe errors if the sshpass process has exited.
if e . errno != errno . EPIPE or p . poll ( ) is None :
if e . errno != errno . EPIPE or p . poll ( ) is None :
@ -375,12 +376,12 @@ class Connection(ConnectionBase):
# We're requesting escalation with a password, so we have to
# We're requesting escalation with a password, so we have to
# wait for a password prompt.
# wait for a password prompt.
state = states . index ( ' awaiting_prompt ' )
state = states . index ( ' awaiting_prompt ' )
display . debug ( ' Initial state: %s : %s ' % ( states [ state ] , self . _play_context . prompt ) )
display . debug ( u ' Initial state: %s : %s ' % ( states [ state ] , self . _play_context . prompt ) )
elif self . _play_context . become and self . _play_context . success_key :
elif self . _play_context . become and self . _play_context . success_key :
# We're requesting escalation without a password, so we have to
# We're requesting escalation without a password, so we have to
# detect success/failure before sending any initial data.
# detect success/failure before sending any initial data.
state = states . index ( ' awaiting_escalation ' )
state = states . index ( ' awaiting_escalation ' )
display . debug ( ' Initial state: %s : %s ' % ( states [ state ] , self . _play_context . success_key ) )
display . debug ( u ' Initial state: %s : %s ' % ( states [ state ] , self . _play_context . success_key ) )
# We store accumulated stdout and stderr output from the process here,
# We store accumulated stdout and stderr output from the process here,
# but strip any privilege escalation prompt/confirmation lines first.
# but strip any privilege escalation prompt/confirmation lines first.
@ -388,8 +389,8 @@ class Connection(ConnectionBase):
# an array, then checked and removed or copied to stdout or stderr. We
# an array, then checked and removed or copied to stdout or stderr. We
# set any flags based on examining the output in self._flags.
# set any flags based on examining the output in self._flags.
stdout = stderr = ' '
b_ stdout = b_ stderr = b ' '
tmp_stdout = tmp_stderr = ' '
b_ tmp_stdout = b_ tmp_stderr = b ' '
self . _flags = dict (
self . _flags = dict (
become_prompt = False , become_success = False ,
become_prompt = False , become_success = False ,
@ -423,43 +424,43 @@ class Connection(ConnectionBase):
if p . poll ( ) is not None :
if p . poll ( ) is not None :
break
break
self . _terminate_process ( p )
self . _terminate_process ( p )
raise AnsibleError ( ' Timeout ( %d s) waiting for privilege escalation prompt: %s ' % ( timeout , stdout) )
raise AnsibleError ( ' Timeout ( %d s) waiting for privilege escalation prompt: %s ' % ( timeout , to_str( b_ stdout) ) )
# Read whatever output is available on stdout and stderr, and stop
# Read whatever output is available on stdout and stderr, and stop
# listening to the pipe if it's been closed.
# listening to the pipe if it's been closed.
if p . stdout in rfd :
if p . stdout in rfd :
chunk = p . stdout . read ( )
b_ chunk = p . stdout . read ( )
if chunk == ' ' :
if b_ chunk == b ' ' :
rpipes . remove ( p . stdout )
rpipes . remove ( p . stdout )
tmp_stdout + = chunk
b_ tmp_stdout + = b_ chunk
display . debug ( " stdout chunk (state= %s ): \n >>> %s <<< \n " % ( state , chunk) )
display . debug ( " stdout chunk (state= %s ): \n >>> %s <<< \n " % ( state , to_unicode( b_ chunk, errors = ' replace ' ) ) )
if p . stderr in rfd :
if p . stderr in rfd :
chunk = p . stderr . read ( )
b_ chunk = p . stderr . read ( )
if chunk == ' ' :
if b_ chunk == b ' ' :
rpipes . remove ( p . stderr )
rpipes . remove ( p . stderr )
tmp_stderr + = chunk
b_ tmp_stderr + = b_ chunk
display . debug ( " stderr chunk (state= %s ): \n >>> %s <<< \n " % ( state , chunk) )
display . debug ( " stderr chunk (state= %s ): \n >>> %s <<< \n " % ( state , to_unicode( b_ chunk, errors = ' replace ' ) ) )
# We examine the output line-by-line until we have negotiated any
# We examine the output line-by-line until we have negotiated any
# privilege escalation prompt and subsequent success/error message.
# privilege escalation prompt and subsequent success/error message.
# Afterwards, we can accumulate output without looking at it.
# Afterwards, we can accumulate output without looking at it.
if state < states . index ( ' ready_to_send ' ) :
if state < states . index ( ' ready_to_send ' ) :
if tmp_stdout:
if b_ tmp_stdout:
output, unprocessed = self . _examine_output ( ' stdout ' , states [ state ] , tmp_stdout, sudoable )
b_ output, b_ unprocessed = self . _examine_output ( ' stdout ' , states [ state ] , b_ tmp_stdout, sudoable )
stdout + = output
b_ stdout + = b_ output
tmp_stdout = unprocessed
b_ tmp_stdout = b_ unprocessed
if tmp_stderr:
if b_ tmp_stderr:
output, unprocessed = self . _examine_output ( ' stderr ' , states [ state ] , tmp_stderr, sudoable )
b_ output, b_ unprocessed = self . _examine_output ( ' stderr ' , states [ state ] , b_ tmp_stderr, sudoable )
stderr + = output
b_ stderr + = b_ output
tmp_stderr = unprocessed
b_ tmp_stderr = b_ unprocessed
else :
else :
stdout + = tmp_stdout
b_ stdout + = b_ tmp_stdout
stderr + = tmp_stderr
b_ stderr + = b_ tmp_stderr
tmp_stdout = tmp_stderr = ' '
b_ tmp_stdout = b_ tmp_stderr = b ' '
# If we see a privilege escalation prompt, we send the password.
# If we see a privilege escalation prompt, we send the password.
# (If we're expecting a prompt but the escalation succeeds, we
# (If we're expecting a prompt but the escalation succeeds, we
@ -468,7 +469,7 @@ class Connection(ConnectionBase):
if states [ state ] == ' awaiting_prompt ' :
if states [ state ] == ' awaiting_prompt ' :
if self . _flags [ ' become_prompt ' ] :
if self . _flags [ ' become_prompt ' ] :
display . debug ( ' Sending become_pass in response to prompt ' )
display . debug ( ' Sending become_pass in response to prompt ' )
stdin . write ( ' {0} \n ' . format ( to_bytes ( self . _play_context . become_pass ) ) )
stdin . write ( to_bytes ( self . _play_context . become_pass ) + b ' \n ' )
self . _flags [ ' become_prompt ' ] = False
self . _flags [ ' become_prompt ' ] = False
state + = 1
state + = 1
elif self . _flags [ ' become_success ' ] :
elif self . _flags [ ' become_success ' ] :
@ -546,14 +547,14 @@ class Connection(ConnectionBase):
if cmd [ 0 ] == b " sshpass " and p . returncode == 6 :
if cmd [ 0 ] == b " sshpass " and p . returncode == 6 :
raise AnsibleError ( ' Using a SSH password instead of a key is not possible because Host Key checking is enabled and sshpass does not support this. Please add this host \' s fingerprint to your known_hosts file to manage this host. ' )
raise AnsibleError ( ' Using a SSH password instead of a key is not possible because Host Key checking is enabled and sshpass does not support this. Please add this host \' s fingerprint to your known_hosts file to manage this host. ' )
controlpersisterror = ' Bad configuration option: ControlPersist ' in stderr or ' unknown configuration option: ControlPersist ' in stderr
controlpersisterror = b ' Bad configuration option: ControlPersist ' in b_ stderr or b ' unknown configuration option: ControlPersist ' in b_ stderr
if p . returncode != 0 and controlpersisterror :
if p . returncode != 0 and controlpersisterror :
raise AnsibleError ( ' using -c ssh on certain older ssh versions may not support ControlPersist, set ANSIBLE_SSH_ARGS= " " (or ssh_args in [ssh_connection] section of the config file) before running again ' )
raise AnsibleError ( ' using -c ssh on certain older ssh versions may not support ControlPersist, set ANSIBLE_SSH_ARGS= " " (or ssh_args in [ssh_connection] section of the config file) before running again ' )
if p . returncode == 255 and in_data :
if p . returncode == 255 and in_data :
raise AnsibleConnectionFailure ( ' SSH Error: data could not be sent to the remote host. Make sure this host can be reached over ssh ' )
raise AnsibleConnectionFailure ( ' SSH Error: data could not be sent to the remote host. Make sure this host can be reached over ssh ' )
return ( p . returncode , stdout, stderr)
return ( p . returncode , b_ stdout, b_ stderr)
def _exec_command ( self , cmd , in_data = None , sudoable = True ) :
def _exec_command ( self , cmd , in_data = None , sudoable = True ) :
''' run a command on the remote host '''
''' run a command on the remote host '''