Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions atest/get_connection.robot
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ Get Connection Host And Timeout Only
Should Be Equal ${rhost} ${HOST}
Should Be Equal As Integers ${timeout} 3

Get Connection Host And Scp Timeout Only
Open Connection ${HOST} scp_socket_timeout=10 seconds
${rhost} ${scp_socket_timeout} = Get Connection host=Yes scp_socket_timeout=True port=false
Should Be Equal ${rhost} ${HOST}
Should Be Equal As Integers ${scp_socket_timeout} 10

Get Connections
Open Connection ${HOST} prompt=>> escape_ansi=True
Open Connection ${HOST} alias=another
Expand Down
4 changes: 3 additions & 1 deletion atest/importing_with_args.robot
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ Importing Library With Arguments
[Setup] Open Connections
${conn}= Get Connections
Should Be Equal As Integers ${conn[0].timeout} 210
Should Be Equal As Integers ${conn[0].scp_socket_timeout} 10
Should Be Equal ${conn[0].prompt} >>
Should Be Equal ${conn[1].path_separator} \\
Should Be Equal As Integers ${conn[1].timeout} 60
Should Be Equal As Integers ${conn[1].scp_socket_timeout} 90
Should Be Equal ${conn[1].prompt} >>
Should Be Equal ${conn[1].path_separator} \\
[Teardown] Close All Connections
Expand All @@ -19,5 +21,5 @@ Importing Library With Arguments
*** Keywords ***
Open Connections
Open Connection localhost
Set Default Configuration timeout=1 minute
Set Default Configuration timeout=1 minute scp_socket_timeout=90 seconds
Open Connection localhost
28 changes: 16 additions & 12 deletions src/SSHLibrary/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ class SSHClientException(RuntimeError):
class _ClientConfiguration(Configuration):

def __init__(self, host, alias, port, timeout, newline, prompt, term_type,
width, height, path_separator, encoding, escape_ansi, encoding_errors):
width, height, path_separator, encoding, escape_ansi, encoding_errors,
scp_socket_timeout):
super(_ClientConfiguration, self).__init__(
index=IntegerEntry(None),
host=StringEntry(host),
Expand All @@ -93,7 +94,8 @@ def __init__(self, host, alias, port, timeout, newline, prompt, term_type,
path_separator=StringEntry(path_separator),
encoding=StringEntry(encoding),
escape_ansi=StringEntry(escape_ansi),
encoding_errors=StringEntry(encoding_errors)
encoding_errors=StringEntry(encoding_errors),
scp_socket_timeout=TimeEntry(scp_socket_timeout)
)


Expand All @@ -107,10 +109,12 @@ class SSHClient(object):

def __init__(self, host, alias=None, port=22, timeout=3, newline='LF',
prompt=None, term_type='vt100', width=80, height=24,
path_separator='/', encoding='utf8', escape_ansi=False, encoding_errors='strict'):
path_separator='/', encoding='utf8', escape_ansi=False, encoding_errors='strict',
scp_socket_timeout=10):
self.config = _ClientConfiguration(host, alias, port, timeout, newline,
prompt, term_type, width, height,
path_separator, encoding, escape_ansi, encoding_errors)
path_separator, encoding, escape_ansi, encoding_errors,
scp_socket_timeout)
self._sftp_client = None
self._scp_transfer_client = None
self._scp_all_client = None
Expand Down Expand Up @@ -250,7 +254,7 @@ def _read_login_output(self, delay):

def login_with_public_key(self, username, keyfile, password, allow_agent=False,
look_for_keys=False, delay=None, proxy_cmd=None,
jumphost_connection=None, read_config=False, keep_alive_interval='0 seconds',
jumphost_connection=None, read_config=False, keep_alive_interval='0 seconds',
disabled_algorithms=None):
"""Logs into the remote host using the public key authentication.

Expand Down Expand Up @@ -297,7 +301,7 @@ def login_with_public_key(self, username, keyfile, password, allow_agent=False,
self._login_with_public_key(username, keyfile, password,
allow_agent, look_for_keys,
proxy_cmd, jumphost_connection,
read_config, keep_alive_interval,
read_config, keep_alive_interval,
disabled_algorithms)
except SSHClientException:
self.client.close()
Expand Down Expand Up @@ -1009,10 +1013,10 @@ def _create_sftp_client(self):
return SFTPClient(self.client, self.config.encoding)

def _create_scp_transfer_client(self):
return SCPTransferClient(self.client, self.config.encoding)
return SCPTransferClient(self.client, self.config.encoding, self.config.scp_socket_timeout)

def _create_scp_all_client(self):
return SCPClient(self.client)
return SCPClient(self.client, self.config.scp_socket_timeout)

def _create_shell(self):
return Shell(self.client, self.config.term_type,
Expand Down Expand Up @@ -1584,8 +1588,8 @@ def _readlink(self, path):


class SCPClient(object):
def __init__(self, ssh_client):
self._scp_client = scp.SCPClient(ssh_client.get_transport())
def __init__(self, ssh_client, scp_socket_timeout):
self._scp_client = scp.SCPClient(ssh_client.get_transport(), socket_timeout=scp_socket_timeout)

def put_file(self, source, destination, scp_preserve_times, *args):
sources = self._get_put_file_sources(source)
Expand Down Expand Up @@ -1614,8 +1618,8 @@ def _get_put_file_sources(self, source):

class SCPTransferClient(SFTPClient):

def __init__(self, ssh_client, encoding):
self._scp_client = scp.SCPClient(ssh_client.get_transport())
def __init__(self, ssh_client, encoding, scp_socket_timeout):
self._scp_client = scp.SCPClient(ssh_client.get_transport(), socket_timeout=scp_socket_timeout)
super(SCPTransferClient, self).__init__(ssh_client, encoding)

def _put_file(self, source, destination, mode, newline, path_separator, scp_preserve_times=False):
Expand Down
64 changes: 46 additions & 18 deletions src/SSHLibrary/library.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
NewlineEntry,
StringEntry,
TimeEntry,

)
from .version import VERSION

Expand Down Expand Up @@ -163,6 +164,16 @@ class SSHLibrary:
Argument ``timeout`` is used by `Read Until` variants. The default value
is ``3 seconds``. See `time format` below for supported timeout syntax.

=== SCP Socket timeout ===
Argument ``scp_socket_timeout`` defines the timeout of the socket used for scp operations.
It exposes the 'socket_timeout' parameter of SCPClient.
(See SCPClient __init__() in https://github.com/jbardin/scp.py/blob/master/scp.py)
This allows to specify the timeout that the scp client should use in keywords
`Get File`, `Get Directory`, `Put File`, `Put Directory`, when the parameter ``scp``
is set to ``TRANSFER`` or ``ALL`.
The default is ``10 seconds``.
See `time format` below for supported scp_socket_timeout syntax.

=== Newline ===

Argument ``newline`` is the line break sequence used by `Write` keyword
Expand Down Expand Up @@ -449,7 +460,7 @@ class SSHLibrary:
DEFAULT_ENCODING = "UTF-8"
DEFAULT_ESCAPE_ANSI = False
DEFAULT_ENCODING_ERRORS = "strict"

DEFAULT_SCP_SOCKET_TIMEOUT = "10 seconds"
def __init__(
self,
timeout=DEFAULT_TIMEOUT,
Expand All @@ -463,6 +474,7 @@ def __init__(
encoding=DEFAULT_ENCODING,
escape_ansi=DEFAULT_ESCAPE_ANSI,
encoding_errors=DEFAULT_ENCODING_ERRORS,
scp_socket_timeout=DEFAULT_SCP_SOCKET_TIMEOUT
):
"""SSHLibrary allows some import time `configuration`.

Expand Down Expand Up @@ -502,6 +514,7 @@ def __init__(
encoding or self.DEFAULT_ENCODING,
escape_ansi or self.DEFAULT_ESCAPE_ANSI,
encoding_errors or self.DEFAULT_ENCODING_ERRORS,
scp_socket_timeout or self.DEFAULT_SCP_SOCKET_TIMEOUT
)
self._last_commands = dict()

Expand All @@ -523,6 +536,7 @@ def set_default_configuration(
encoding=None,
escape_ansi=None,
encoding_errors=None,
scp_socket_timeout=None
):
"""Update the default `configuration`.

Expand Down Expand Up @@ -566,6 +580,7 @@ def set_default_configuration(
encoding=encoding,
escape_ansi=escape_ansi,
encoding_errors=encoding_errors,
scp_socket_timeout=scp_socket_timeout
)

@keyword(tags=("configuration",))
Expand All @@ -581,6 +596,7 @@ def set_client_configuration(
encoding=None,
escape_ansi=None,
encoding_errors=None,
scp_socket_timeout=None
):
"""Update the `configuration` of the current connection.

Expand Down Expand Up @@ -621,6 +637,7 @@ def set_client_configuration(
encoding=encoding,
escape_ansi=escape_ansi,
encoding_errors=encoding_errors,
scp_socket_timeout=scp_socket_timeout
)

@keyword(tags=("configuration",))
Expand Down Expand Up @@ -661,6 +678,7 @@ def open_connection(
encoding=None,
escape_ansi=None,
encoding_errors=None,
scp_socket_timeout=None
):
"""Opens a new SSH connection to the given ``host`` and ``port``.

Expand Down Expand Up @@ -729,6 +747,7 @@ def open_connection(
encoding = encoding or self._config.encoding
escape_ansi = escape_ansi or self._config.escape_ansi
encoding_errors = encoding_errors or self._config.encoding_errors
scp_socket_timeout = scp_socket_timeout or self._config.scp_socket_timeout
client = SSHClient(
host,
alias,
Expand All @@ -743,6 +762,7 @@ def open_connection(
encoding,
escape_ansi,
encoding_errors,
scp_socket_timeout
)
connection_index = self._connections.register(client, alias)
client.config.update(index=connection_index)
Expand Down Expand Up @@ -833,6 +853,7 @@ def get_connection(
height=False,
encoding=False,
escape_ansi=False,
scp_socket_timeout=False
):
"""Returns information about the connection.

Expand All @@ -845,19 +866,20 @@ def get_connection(

This keyword returns an object that has the following attributes:

| = Name = | = Type = | = Explanation = |
| index | integer | Number of the connection. Numbering starts from ``1``. |
| host | string | Destination hostname. |
| alias | string | An optional alias given when creating the connection. |
| port | integer | Destination port. |
| timeout | string | `Timeout` length in textual representation. |
| newline | string | The line break sequence used by `Write` keyword. See `newline`. |
| prompt | string | `Prompt` character sequence for `Read Until Prompt`. |
| term_type | string | Type of the virtual terminal. See `terminal settings`. |
| width | integer | Width of the virtual terminal. See `terminal settings`. |
| height | integer | Height of the virtual terminal. See `terminal settings`. |
| path_separator | string | The `path separator` used on the remote host. |
| encoding | string | The `encoding` used for inputs and outputs. |
| = Name = | = Type = | = Explanation = |
| index | integer | Number of the connection. Numbering starts from ``1``. |
| host | string | Destination hostname. |
| alias | string | An optional alias given when creating the connection. |
| port | integer | Destination port. |
| timeout | string | `Timeout` length in textual representation. |
| newline | string | The line break sequence used by `Write` keyword. See `newline`. |
| prompt | string | `Prompt` character sequence for `Read Until Prompt`. |
| term_type | string | Type of the virtual terminal. See `terminal settings`. |
| width | integer | Width of the virtual terminal. See `terminal settings`. |
| height | integer | Height of the virtual terminal. See `terminal settings`. |
| path_separator | string | The `path separator` used on the remote host. |
| encoding | string | The `encoding` used for inputs and outputs. |
| scp_socket_timeout | string | `SCP Socket timeout` length in textual representation. |

If there is no connection, an object having ``index`` and ``host``
as ``None`` is returned, rest of its attributes having their values
Expand Down Expand Up @@ -937,6 +959,7 @@ def get_connection(
height,
encoding,
escape_ansi,
scp_socket_timeout
)
)
if not return_values:
Expand Down Expand Up @@ -985,6 +1008,7 @@ def _get_config_values(
height,
encoding,
escape_ansi,
scp_socket_timeout
):
if is_truthy(index):
yield config.index
Expand All @@ -1010,6 +1034,8 @@ def _get_config_values(
yield config.encoding
if is_truthy(escape_ansi):
yield config.escape_ansi
if is_truthy(scp_socket_timeout):
yield config.scp_socket_timeout

@keyword(tags=("connection",))
def get_connections(self):
Expand Down Expand Up @@ -1085,8 +1111,8 @@ def login(
``keep_alive_interval`` is new in SSHLibrary 3.7.0.

``disabled_algorithms`` is a list of algorithms that should be disabled.
For example, if you need to disable diffie-hellman-group16-sha512 key exchange
(perhaps because your code talks to a server which implements it differently from Paramiko),
For example, if you need to disable diffie-hellman-group16-sha512 key exchange
(perhaps because your code talks to a server which implements it differently from Paramiko),
specify disabled_algorithms={"kex": ["diffie-hellman-group16-sha512"]}

Example that logs in and returns the output:
Expand Down Expand Up @@ -1213,8 +1239,8 @@ def login_with_public_key(
``keep_alive_interval`` is new in SSHLibrary 3.7.0.

``disabled_algorithms`` is a list of algorithms that should be disabled.
For example, if you need to disable diffie-hellman-group16-sha512 key exchange
(perhaps because your code talks to a server which implements it differently from Paramiko),
For example, if you need to disable diffie-hellman-group16-sha512 key exchange
(perhaps because your code talks to a server which implements it differently from Paramiko),
specify disabled_algorithms={"kex": ["diffie-hellman-group16-sha512"]}

Example login with disabled algorithms:
Expand Down Expand Up @@ -2273,6 +2299,7 @@ def __init__(
encoding,
escape_ansi,
encoding_errors,
scp_socket_timeout
):
super(_DefaultConfiguration, self).__init__(
timeout=TimeEntry(timeout),
Expand All @@ -2286,4 +2313,5 @@ def __init__(
encoding=StringEntry(encoding),
escape_ansi=StringEntry(escape_ansi),
encoding_errors=StringEntry(encoding_errors),
scp_socket_timeout = TimeEntry(scp_socket_timeout)
)