ConnectorTypeSSH
class connector_types.connector_type_ssh.ConnectorTypeSSH
Connect to a remote host using SSH and execute a script.
Cancelling an active execution created by an SSH connector might not be possible if the third party system, that is running the script, doesn't respond. In this case Cloudomation Engine sends the signal to cancel the process on the third party system but as long as the script is running on said system, the execution status will be shown a running in Cloudomation. To cancel the execution you have to kill the process directly in the non-responsive system.
To run interactive commands e.g. sudo
use the mode execute_script
. Running interactive commands with run_commands
is not supported and might lead
to unexpected behavior like the connection not exiting properly.
Input Schema
-
schema_version
Type:
string
-
authentication
Type:
anyOf
Options: -
host
The remote hostname or IP address.
Type:
string
-
hostkey
The public key of host.
Type:
anyOf
Options: -
port
Type:
anyOf
Options: -
mode
Type:
anyOf
Options: -
connect_timeout
A timeout for connecting to a peer in seconds.
Type:
integer
Default:
30
-
script_timeout
The timeout for the script execution in seconds.
Type:
integer
Default:
60
-
remove_cr
Remove carriage-return (CR) characters from the report string.
Type:
boolean
Default:
True
-
remove_ansi_escapes
Remove ANSI escape sequences from the report string.
Type:
boolean
Default:
True
-
output_vars
A list of shell variables which will be returned in the output_value of the connection.
Type:
array
Items: -
copy_files
A list of paths to files which will be copied to Cloudomation Engine after the script ran.
Type:
array
Items: -
destination_location
The location to store the file in.
Type:
anyOf
Options: -
output_files
A list of paths to files which will be returned in the output_value of the connection.
Type:
array
Items: -
encoding
The encoding to use when binary data is returned by the server.
Type:
string
Default:
utf-8
Output Schema
Example
import flow_api
def handler(system: flow_api.System, this: flow_api.Execution, inputs: dict):
# Authenticate using private key
info_child = this.connect(
connector_type='SSH',
authentication={
'authentication_method': 'ssh_key',
'username': '...',
'ssh_key': '...',
}
# public accessible name or IP
host='...',
# key to check host identity.
# can be read with "$ ssh-keyscan -t rsa <my-ssh-server>"
hostkey='...',
mode={
'mode_name': 'execute_script',
'script': (
'''
HOSTNAME=$(hostname)
USERNAME=$(id -un)
CPU=$(uname -p)
#OUTPUT_VAR(HOSTNAME)
#OUTPUT_VAR(USERNAME)
#OUTPUT_VAR(CPU)
'''
),
},
)
outputs = info_child.get('output_value')
hostname = outputs['vars']['HOSTNAME']
username = outputs['vars']['USERNAME']
cpu = outputs['vars']['CPU']
this.log(f'info_child was running on {hostname} using {cpu} as {username}')
# Authenticate using password
uptime_child = this.connect(
connector_type='SSH',
authentication={
'authentication_method': 'username_password',
'username': '...',
'password': '...',
}
hostname='...',
hostkey='...',
port={
'port_mode': 'port_number',
'port_number': 2022, # non standard port
},
mode={
'mode_name': 'execute_script',
'temp_path': '/var/tmp',
'script': (
'''
UPTIME=$(uptime -s)
#OUTPUT_VAR(UPTIME)
'''
),
},
)
outputs = uptime_child.get('output_value')
uptime = outputs['vars']['UPTIME']
this.log(f'{hostname} is up since {uptime}')
return this.success('all done')
More
Output variables
There are two ways how to define "output variables":
- from the flow starting the connection, in the
output_vars
field of the input dictionary - from inside the connection, in the
script
field of the input dictionary
Output variables in output_vars
You can register shell variables as "output variables" in the output_vars
field of the input dictionary, e.g.:
child_execution = this.connect(
connector_type='SSH',
authentication={
'authentication_method': 'ssh_key',
'username': '...',
'ssh_key': '...',
}
# public accessible name or IP
host='...',
# key to check host identity.
hostkey='...',
mode={
'mode_name': 'execute_script',
'script': (
'''
VALUE=foo
'''
),
},
output_vars=['VALUE'],
)
assert child_execution.get('output_value')['vars']['VALUE'] == 'foo'
Output variables in script
You can register shell variables as "output variables" using
#OUTPUT_VAR(variable_name)
:
VARIABLE="some content"
#OUTPUT_VAR(VARIABLE)
The value of registered variables is available to the calling flow script
in the var
dictionary of the connection outputs:
outputs = connect(...).get('output_value')
variable = outputs['vars']['VARIABLE']
# `variable` contains "some content"
Output files
There are two ways how to define "output files":
- from the flow starting the connection, in the
output_files
field of the input dictionary - from inside the connection, in the
script
field of the input dictionary
Output files in output_files
You can register files as "output files" in the output_files
field of the input dictionary, e.g.:
child_execution = this.connect(
connector_type='SSH',
authentication={
'authentication_method': 'ssh_key',
'username': '...',
'ssh_key': '...',
}
# public accessible name or IP
host='...',
# key to check host identity.
hostkey='...',
mode={
'mode_name': 'execute_script',
'script': (
'''
echo -n "spam" > file.txt
'''
),
},
output_files=['file.txt'],
)
assert {'name': 'file.txt', 'content': 'spam'} in child_execution.get('output_value')['files']
Output files in script
You can register files as "output files" using
#OUTPUT_FILE(filename)
:
child_execution = this.connect(
connector_type='SSH',
authentication={
'authentication_method': 'ssh_key',
'username': '...',
'ssh_key': '...',
}
# public accessible name or IP
host='...',
# key to check host identity.
hostkey='...',
mode={
'mode_name': 'execute_script',
'script': (
'''
echo -n "egg" > file2.txt
#OUTPUT_FILE(file2.txt)
'''
),
},
)
assert {'name': 'file2.txt', 'content': 'egg'} in child_execution.get('output_value')['files']
Copy files
There are two ways how to define "copy files":
- from the flow starting the connection, in the
copy_files
field of the input dictionary - from inside the connection, in the
script
field of the input dictionary
Copy files in copy_files
You can register files as "copy files" in the copy_files
field of the input dictionary, e.g.:
child_execution = this.connect(
connector_type='SSH',
authentication={
'authentication_method': 'ssh_key',
'username': '...',
'ssh_key': '...',
}
# public accessible name or IP
host='...',
# key to check host identity.
hostkey='...',
mode={
'mode_name': 'execute_script',
'script': (
'''
echo -n "spam" > file.txt
'''
),
},
copy_files=['file.txt'],
)
assert {'name': 'file.txt'} in child_execution.get('output_value')['files']
assert system.file('file.txt').get_text_content() == 'spam'
Copy files in script
You can register files as "copy files" using
#COPY_FILE(filename)
:
child_execution = this.connect(
connector_type='SSH',
authentication={
'authentication_method': 'ssh_key',
'username': '...',
'ssh_key': '...',
}
# public accessible name or IP
host='...',
# key to check host identity.
hostkey='...',
mode={
'mode_name': 'execute_script',
'script': (
'''
echo -n "egg" > file2.txt
#COPY_FILE(file2.txt)
'''
),
},
name='output_var',
)
assert {'name': 'file2.txt'} in child_execution.get('output_value')['files']
assert system.file('file2.txt').get_text_content() == 'egg'
Source .profile or .bashrc on the remote.
By default no dotfiles are sourced by the connector. To source the .bashrc file
using the bash shell change the interpreter to something like /bin/bash -ie
to
tell bash to run in interactive mode. Similarly, to source the .bash_profile
file (refer to the bash documentation to find out which files will be sourced
and in what order) run bash as if it was invoked as a login shell by specifying
the -l
flag: /bin/bash -le
.
child_execution = this.connect(
connector_type='SSH',
authentication={
'authentication_method': 'ssh_key',
'username': '...',
'ssh_key': '...',
}
# public accessible name or IP
host='...',
# key to check host identity.
hostkey='...',
mode={
'mode_name': 'execute_script',
'interpreter': '/bin/bash -ie',
'script': (
'''
true
'''
),
},
)
Executing a script versus running commands interactively
With mode_name == 'execute_script'
Cloudomation Engine creates a script file
on the remote host and executes it. If the execution is interrupted in Cloudomation Engine
(for example when the Cloudomation host is restarted) the script on the remote
host continues running. After the restart Cloudomation Engine will continue to
wait for the script to end or detect if the script already ended in the meantime.
Using mode_name == 'run_commands'
Cloudomation Engine logs in "interactively"
to the remote host and "types" the script to the command prompt. This way no
write access to a temporary directory is required. If the execution is interrupted
in Cloudomation Engine the connection is closed. What happens with the processes
which are started by the script depends on how they were started. The processes
started directly by the script are usually cancelled too.
child_execution = this.connect(
connector_type='SSH',
authentication={
'authentication_method': 'ssh_key',
'username': '...',
'ssh_key': '...',
}
# public accessible name or IP
host='...',
# key to check host identity.
hostkey='...',
mode={
'mode_name': 'run_commands',
'script': (
'''
cp file.csv /mnt/file.csv
'''
),
},
)