Skip to main content
Version: 10 - Vanillekipferl

ConnectorTypeSSH

class connector_types.connector_type_ssh.ConnectorTypeSSH

Connect to a remote host using SSH and execute a script.

note

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.

Input Schema

  • schema_version

    Type: string

  • authentication

    Type: anyOf

  • host

    The remote hostname or IP address.

    Type: string

  • hostkey

    The public key of host.

    Type: anyOf

  • port

    Type: anyOf

  • mode

    Type: anyOf

  • 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

  • copy_files

    A list of paths to files which will be copied to Cloudomation Engine after the script ran.

    Type: array

  • destination_location

    The location to store the file in.

    Type: anyOf

  • output_files

    A list of paths to files which will be returned in the output_value of the connection.

    Type: array

  • encoding

    The encoding to use when binary data is returned by the server.

    Type: string

    Default: utf-8

Output Schema

  • retcode

    Type: integer

  • report

    The outputs your script produced on the remote system.

    Type: string

  • handler_report

    Only set when the input use_shell is False.

  • waiter_report

    Only set when the input use_shell is False.

  • vars

    The content of all variables which were registered using #OUTPUT_VAR(variable) or set in the output_vars input.

    Type: object

    Additional Properties: {'element': 'form-string', 'type': 'string', 'default': ''}

  • files

    The names and content of the output files which were registered using #OUTPUT_FILE(path) or set in the output_files input as well as names of the copy files which were registered using #COPY_FILE(path) or set in the copy_files input.

    Type: array

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
'''
),
},
)