Skip to content

Commit

Permalink
Allow to disable printing expected runtime errors in SSHAuth (#49)
Browse files Browse the repository at this point in the history
When users on a node are not prepared yet, it is expected
to get wrong authorization exception like this:

ssh_client.py:147 -- Connection using stored authentication info failed!
Traceback (most recent call last):
...
BadAuthenticationType: ('Bad authentication type', [u'publickey']) (allowed_types=[u'publickey'])

- new parameter to SSHClient 'verbose' allows to disable printing
  such errors during waiting for ssh access.
  • Loading branch information
dis-xcom authored and penguinolog committed Jun 16, 2018
1 parent 344b810 commit 332273a
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 5 deletions.
2 changes: 2 additions & 0 deletions doc/source/SSHClient.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ API: SSHClient and SSHAuth.
:type private_keys: ``typing.Optional[typing.Iterable[paramiko.RSAKey]]``
:param auth: credentials for connection
:type auth: typing.Optional[SSHAuth]
:param verbose: show additional error/warning messages
:type verbose: bool

.. note:: auth has priority over username/password/private_keys

Expand Down
12 changes: 9 additions & 3 deletions exec_helpers/_ssh_client_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ def __call__(
password=None, # type: typing.Optional[str]
private_keys=None, # type: typing.Optional[typing.Iterable[paramiko.RSAKey]]
auth=None, # type: typing.Optional[ssh_auth.SSHAuth]
verbose=True, # type: bool
): # type: (...) -> SSHClientBase
"""Main memorize method: check for cached instance and return it.
Expand All @@ -125,6 +126,7 @@ def __call__(
:type password: str
:type private_keys: list
:type auth: ssh_auth.SSHAuth
:type verbose: bool
:rtype: SSHClient
"""
if (host, port) in cls.__cache:
Expand Down Expand Up @@ -160,7 +162,7 @@ def __call__(
).__call__(
host=host, port=port,
username=username, password=password, private_keys=private_keys,
auth=auth)
auth=auth, verbose=verbose)
cls.__cache[(ssh.hostname, ssh.port)] = ssh
return ssh

Expand Down Expand Up @@ -195,7 +197,7 @@ class SSHClientBase(six.with_metaclass(_MemorizedSSH, _api.ExecHelper)):

__slots__ = (
'__hostname', '__port', '__auth', '__ssh', '__sftp',
'__sudo_mode', '__keepalive_mode',
'__sudo_mode', '__keepalive_mode', '__verbose',
)

class __get_sudo(object):
Expand Down Expand Up @@ -279,6 +281,7 @@ def __init__(
password=None, # type: typing.Optional[str]
private_keys=None, # type: typing.Optional[typing.Iterable[paramiko.RSAKey]]
auth=None, # type: typing.Optional[ssh_auth.SSHAuth]
verbose=True, # type: bool
): # type: (...) -> None
"""SSHClient helper.
Expand All @@ -294,6 +297,8 @@ def __init__(
:type private_keys: typing.Optional[typing.Iterable[paramiko.RSAKey]]
:param auth: credentials for connection
:type auth: typing.Optional[ssh_auth.SSHAuth]
:param verbose: show additional error/warning messages
:type verbose: bool
.. note:: auth has priority over username/password/private_keys
"""
Expand All @@ -310,6 +315,7 @@ def __init__(

self.__sudo_mode = False
self.__keepalive_mode = True
self.__verbose = verbose

self.__ssh = paramiko.SSHClient()
self.__ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
Expand Down Expand Up @@ -400,7 +406,7 @@ def __connect(self):
self.auth.connect(
client=self.__ssh,
hostname=self.hostname, port=self.port,
log=True)
log=self.__verbose)

def __connect_sftp(self):
"""SFTP connection opener."""
Expand Down
6 changes: 4 additions & 2 deletions exec_helpers/_ssh_client_base.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ class _MemorizedSSH(type):
username: typing.Optional[str]=...,
password: typing.Optional[str]=...,
private_keys: typing.Optional[typing.Iterable[paramiko.RSAKey]]=...,
auth: typing.Optional[ssh_auth.SSHAuth]=...
auth: typing.Optional[ssh_auth.SSHAuth]=...,
verbose: bool=...
) -> SSHClientBase: ...

@classmethod
Expand All @@ -37,7 +38,8 @@ class SSHClientBase(_api.ExecHelper, metaclass=_MemorizedSSH):
username: typing.Optional[str]=...,
password: typing.Optional[str]=...,
private_keys: typing.Optional[typing.Iterable[paramiko.RSAKey]]=...,
auth: typing.Optional[ssh_auth.SSHAuth]=...
auth: typing.Optional[ssh_auth.SSHAuth]=...,
verbose: bool=...
) -> None: ...

@property
Expand Down
18 changes: 18 additions & 0 deletions test/test_ssh_client_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -759,3 +759,21 @@ def test_025_init_memorize_reconnect(self, execute, client, policy, logger):
exec_helpers.SSHClient(host=host)
client.assert_called_once()
policy.assert_called_once()

@mock.patch('time.sleep', autospec=True)
def test_026_init_auth_impossible_key_no_verbose(
self, sleep, client, policy, logger):
connect = mock.Mock(side_effect=paramiko.AuthenticationException)

_ssh = mock.Mock()
_ssh.attach_mock(connect, 'connect')
client.return_value = _ssh

with self.assertRaises(paramiko.AuthenticationException):
exec_helpers.SSHClient(
host=host,
auth=exec_helpers.SSHAuth(key=gen_private_keys(1).pop()),
verbose=False
)

logger.assert_not_called()

0 comments on commit 332273a

Please sign in to comment.