Skip to content

Commit edbf79d

Browse files
committed
Fixes
* drop compression back as potentially causes errors * keepalive became int because used as keepalive packets period * fix typing and documentation in several places
1 parent 6dbec8c commit edbf79d

9 files changed

+64
-131
lines changed

doc/source/SSHClient.rst

Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ API: SSHClient and SSHAuth.
1010
1111
SSHClient helper.
1212

13-
.. py:method:: __init__(host, port=22, username=None, password=None, *, private_keys=None, auth=None, verbose=True, ssh_config=None, ssh_auth_map=None, sock=None, keepalive=True)
13+
.. py:method:: __init__(host, port=22, username=None, password=None, *, private_keys=None, auth=None, verbose=True, ssh_config=None, ssh_auth_map=None, sock=None, keepalive=1)
1414
1515
:param host: remote hostname
1616
:type host: ``str``
@@ -32,8 +32,8 @@ API: SSHClient and SSHAuth.
3232
:type ssh_auth_map: typing.Optional[typing.Union[typing.Dict[str, ssh_auth.SSHAuth], ssh_auth.SSHAuthMapping]]
3333
:param sock: socket for connection. Useful for ssh proxies support
3434
:type sock: typing.Optional[typing.Union[paramiko.ProxyCommand, paramiko.Channel, socket.socket]]
35-
:param keepalive: keepalive mode
36-
:type keepalive: bool
35+
:param keepalive: keepalive period
36+
:type keepalive: typing.Union[int, bool]
3737

3838
.. note:: auth has priority over username/password/private_keys
3939
.. note::
@@ -46,6 +46,7 @@ API: SSHClient and SSHAuth.
4646
.. versionchanged:: 6.0.0 added optional ssh_auth_map for ssh proxy cases with authentication on each step
4747
.. versionchanged:: 6.0.0 added optional sock for manual proxy chain handling
4848
.. versionchanged:: 6.0.0 keepalive exposed to constructor
49+
.. versionchanged:: 6.0.0 keepalive became int, now used in ssh transport as period of keepalive requests
4950
.. versionchanged:: 6.0.0 private_keys is deprecated
5051

5152
.. py:attribute:: log_mask_re
@@ -92,8 +93,8 @@ API: SSHClient and SSHAuth.
9293

9394
.. py:attribute:: keepalive_mode
9495
95-
``bool``
96-
Use keepalive mode for context manager. If `False` - close connection on exit from context manager.
96+
``typing.Union[int, bool]``
97+
Keepalive period for connection object. If `0` - close connection on exit from context manager.
9798

9899
.. py:method:: close()
99100
@@ -135,15 +136,16 @@ API: SSHClient and SSHAuth.
135136

136137
:param enforce: Enforce sudo enabled or disabled. By default: None
137138
:type enforce: ``typing.Optional[bool]``
138-
:rtype: ``typing.ContextManager``
139+
:rtype: ``typing.ContextManager[None]``
139140

140-
.. py:method:: keepalive(enforce=None)
141+
.. py:method:: keepalive(enforce=1)
141142
142143
Context manager getter for keepalive operation.
143144

144-
:param enforce: Enforce keepalive enabled or disabled. By default: True
145-
:type enforce: ``typing.bool``
146-
:rtype: ``typing.ContextManager``
145+
:param enforce: Enforce keepalive period.
146+
:type enforce: ``typing.Union[int, bool]``
147+
:return: context manager with selected keepalive state inside
148+
:rtype: ``typing.ContextManager[None]``
147149

148150
.. Note:: Enter and exit ssh context manager is produced as well.
149151
.. versionadded:: 1.2.1
@@ -245,7 +247,7 @@ API: SSHClient and SSHAuth.
245247
.. versionchanged:: 1.2.0 default timeout 1 hour
246248
.. versionchanged:: 3.2.0 Exception class can be substituted
247249

248-
.. py:method:: proxy_to(host, port=None, username=None, password=None, *, auth=None, verbose=True, ssh_config=None, ssh_auth_map=None, keepalive=True)
250+
.. py:method:: proxy_to(host, port=None, username=None, password=None, *, auth=None, verbose=True, ssh_config=None, ssh_auth_map=None, keepalive=1)
249251
250252
Start new SSH connection using current as proxy.
251253

@@ -265,8 +267,8 @@ API: SSHClient and SSHAuth.
265267
:type ssh_config: typing.Union[str, paramiko.SSHConfig, typing.Dict[str, typing.Dict[str, typing.Union[str, int, bool, typing.List[str]]]], HostsSSHConfigs, None]
266268
:param ssh_auth_map: SSH authentication information mapped to host names. Useful for complex SSH Proxy cases.
267269
:type ssh_auth_map: typing.Optional[typing.Union[typing.Dict[str, ssh_auth.SSHAuth], ssh_auth.SSHAuthMapping]]
268-
:param keepalive: keepalive mode
269-
:type keepalive: bool
270+
:param keepalive: keepalive period
271+
:type keepalive: typing.Union[int, bool]
270272
:return: new ssh client instance using current as a proxy
271273
:rtype: SSHClientBase
272274

@@ -508,7 +510,7 @@ API: SSHClient and SSHAuth.
508510
:param tgt: Target
509511
:type tgt: file
510512

511-
.. py:method:: connect(client, hostname, port=22, log=True, *, sock=None, compress=False)
513+
.. py:method:: connect(client, hostname, port=22, log=True, *, sock=None)
512514
513515
Connect SSH client object using credentials.
514516

@@ -522,8 +524,6 @@ API: SSHClient and SSHAuth.
522524
:type log: ``bool``
523525
:param sock: socket for connection. Useful for ssh proxies support
524526
:type sock: ``typing.Optional[typing.Union[paramiko.ProxyCommand, paramiko.Channel, socket.socket]]``
525-
:param compress: use SSH compression
526-
:type compress: ``bool``
527527
:raises PasswordRequiredException: No password has been set, but required.
528528
:raises AuthenticationException: Authentication failed.
529529

@@ -611,7 +611,7 @@ API: SSHClient and SSHAuth.
611611
612612
Parsed SSH Config for creation connection.
613613

614-
.. py:method:: __init__(hostname, port=None, user=None, identityfile=None, proxycommand=None, proxyjump=None, *, controlpath=None, controlmaster=None, compression=None, )
614+
.. py:method:: __init__(hostname, port=None, user=None, identityfile=None, proxycommand=None, proxyjump=None, *, controlpath=None, controlmaster=None, )
615615
616616
SSH Config for creation connection.
617617

@@ -631,8 +631,6 @@ API: SSHClient and SSHAuth.
631631
:type controlpath: typing.Optional[str]
632632
:param controlmaster: re-use connection
633633
:type controlmaster: typing.Optional[typing.Union[str, bool]]
634-
:param compression: use ssh compression
635-
:type compression: typing.Optional[typing.Union[str, bool]]
636634
:raises ValueError: Invalid argument provided.
637635

638636
.. versionadded:: 6.0.0
@@ -697,8 +695,3 @@ API: SSHClient and SSHAuth.
697695
698696
``typing.Optional[bool]``
699697
Re-use connection.
700-
701-
.. py:attribute:: compression
702-
703-
``typing.Optional[bool]``
704-
Use ssh compression.

exec_helpers/_ssh_client_base.py

Lines changed: 36 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -124,24 +124,22 @@ class _KeepAliveContext:
124124

125125
__slots__ = ("__ssh", "__keepalive_status", "__enforce")
126126

127-
def __init__(self, ssh: "SSHClientBase", enforce: bool = True) -> None:
127+
def __init__(self, ssh: "SSHClientBase", enforce: int) -> None:
128128
"""Context manager for keepalive management.
129129
130130
:param ssh: connection instance
131131
:type ssh: SSHClientBase
132-
:param enforce: keepalive mode for context manager
133-
:type enforce: bool
134-
:param enforce: Keep connection alive after context manager exit
132+
:param enforce: keepalive period for context manager
133+
:type enforce: int
135134
"""
136135
self.__ssh: "SSHClientBase" = ssh
137-
self.__keepalive_status: bool = ssh.keepalive_mode
138-
self.__enforce: typing.Optional[bool] = enforce
136+
self.__keepalive_status: int = ssh.keepalive_mode
137+
self.__enforce: int = enforce
139138

140139
def __enter__(self) -> None:
141140
self.__ssh.__enter__()
142141
self.__keepalive_status = self.__ssh.keepalive_mode
143-
if self.__enforce is not None:
144-
self.__ssh.keepalive_mode = self.__enforce
142+
self.__ssh.keepalive_mode = self.__enforce
145143

146144
def __exit__(self, exc_type: typing.Any, exc_val: typing.Any, exc_tb: typing.Any) -> None:
147145
# Exit before releasing!
@@ -189,7 +187,7 @@ def __init__(
189187
] = None,
190188
ssh_auth_map: typing.Optional[typing.Union[typing.Dict[str, ssh_auth.SSHAuth], ssh_auth.SSHAuthMapping]] = None,
191189
sock: typing.Optional[typing.Union[paramiko.ProxyCommand, paramiko.Channel, socket.socket]] = None,
192-
keepalive: bool = True,
190+
keepalive: typing.Union[int, bool] = 1,
193191
) -> None:
194192
"""Main SSH Client helper.
195193
@@ -220,8 +218,8 @@ def __init__(
220218
:type ssh_auth_map: typing.Optional[typing.Union[typing.Dict[str, ssh_auth.SSHAuth], ssh_auth.SSHAuthMapping]]
221219
:param sock: socket for connection. Useful for ssh proxies support
222220
:type sock: typing.Optional[typing.Union[paramiko.ProxyCommand, paramiko.Channel, socket.socket]]
223-
:param keepalive: keepalive mode
224-
:type keepalive: bool
221+
:param keepalive: keepalive period
222+
:type keepalive: typing.Union[int, bool]
225223
226224
.. note:: auth has priority over username/password/private_keys
227225
.. note::
@@ -234,6 +232,7 @@ def __init__(
234232
.. versionchanged:: 6.0.0 added optional ssh_auth_map for ssh proxy cases with authentication on each step
235233
.. versionchanged:: 6.0.0 added optional sock for manual proxy chain handling
236234
.. versionchanged:: 6.0.0 keepalive exposed to constructor
235+
.. versionchanged:: 6.0.0 keepalive became int, now used in ssh transport as period of keepalive requests
237236
.. versionchanged:: 6.0.0 private_keys is deprecated
238237
"""
239238
if private_keys is not None:
@@ -262,7 +261,7 @@ def __init__(
262261
self.__auth_mapping[self.hostname] = self.__auth_mapping[host]
263262

264263
self.__sudo_mode = False
265-
self.__keepalive_mode: bool = keepalive
264+
self.__keepalive_mode: int = int(keepalive)
266265
self.__verbose: bool = verbose
267266
self.__sock = sock
268267

@@ -402,12 +401,7 @@ def __connect(self) -> None:
402401
self.__ssh = paramiko.SSHClient()
403402
self.__ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
404403
self.auth.connect(
405-
client=self.__ssh,
406-
hostname=self.hostname,
407-
port=self.port,
408-
log=self.__verbose,
409-
sock=sock,
410-
compress=bool(self.ssh_config[self.hostname].compression),
404+
client=self.__ssh, hostname=self.hostname, port=self.port, log=self.__verbose, sock=sock,
411405
)
412406
else:
413407
self.__ssh = self.__get_client()
@@ -434,12 +428,9 @@ def __get_client(self) -> paramiko.SSHClient:
434428
hostname=config.hostname,
435429
port=config.port or 22,
436430
sock=paramiko.ProxyCommand(config.proxycommand),
437-
compress=bool(config.compression),
438431
)
439432
else:
440-
auth.connect(
441-
last_ssh_client, hostname=config.hostname, port=config.port or 22, compress=bool(config.compression)
442-
)
433+
auth.connect(last_ssh_client, hostname=config.hostname, port=config.port or 22)
443434

444435
for config, auth in self.__conn_chain[1:]: # start has another logic, so do it out of cycle
445436
ssh = paramiko.SSHClient()
@@ -449,9 +440,7 @@ def __get_client(self) -> paramiko.SSHClient:
449440
sock = last_ssh_client.get_transport().open_channel(
450441
kind="direct-tcpip", dest_addr=(config.hostname, config.port or 22), src_addr=(config.proxyjump, 0),
451442
)
452-
auth.connect(
453-
ssh, hostname=config.hostname, port=config.port or 22, sock=sock, compress=bool(config.compression)
454-
)
443+
auth.connect(ssh, hostname=config.hostname, port=config.port or 22, sock=sock)
455444
last_ssh_client = ssh
456445
continue
457446

@@ -541,23 +530,25 @@ def sudo_mode(self, mode: bool) -> None:
541530
self.__sudo_mode = bool(mode)
542531

543532
@property
544-
def keepalive_mode(self) -> bool:
545-
"""Persistent keepalive mode for connection object.
533+
def keepalive_mode(self) -> int:
534+
"""Keepalive period for connection object.
546535
547-
:rtype: bool
536+
:rtype: int
537+
If 0 - close connection on exit from context manager.
548538
"""
549539
return self.__keepalive_mode
550540

551541
@keepalive_mode.setter
552-
def keepalive_mode(self, mode: bool) -> None:
553-
"""Persistent keepalive mode change for connection object.
542+
def keepalive_mode(self, period: typing.Union[int, bool]) -> None:
543+
"""Keepalive period change for connection object.
554544
555-
:param mode: keepalive mode enable/disable
556-
:type mode: bool
545+
:param period: keepalive period change
546+
:type period: typing.Union[int, bool]
547+
If 0 - close connection on exit from context manager.
557548
"""
558-
self.__keepalive_mode = bool(mode)
549+
self.__keepalive_mode = int(period)
559550
transport: paramiko.Transport = self.__ssh.get_transport()
560-
transport.set_keepalive(1 if mode else 0)
551+
transport.set_keepalive(int(period))
561552

562553
def reconnect(self) -> None:
563554
"""Reconnect SSH session."""
@@ -571,22 +562,22 @@ def sudo(self, enforce: typing.Optional[bool] = None) -> "typing.ContextManager[
571562
:param enforce: Enforce sudo enabled or disabled. By default: None
572563
:type enforce: typing.Optional[bool]
573564
:return: context manager with selected sudo state inside
574-
:rtype: typing.ContextManager
565+
:rtype: typing.ContextManager[None]
575566
"""
576567
return _SudoContext(ssh=self, enforce=enforce)
577568

578-
def keepalive(self, enforce: bool = True) -> "typing.ContextManager[None]":
579-
"""Call contextmanager with keepalive mode change.
569+
def keepalive(self, enforce: typing.Union[int, bool] = 1) -> "typing.ContextManager[None]":
570+
"""Call contextmanager with keepalive period change.
580571
581-
:param enforce: Enforce keepalive enabled or disabled.
582-
:type enforce: bool
572+
:param enforce: Enforce keepalive period.
573+
:type enforce: typing.Union[int, bool]
583574
:return: context manager with selected keepalive state inside
584-
:rtype: typing.ContextManager
575+
:rtype: typing.ContextManager[None]
585576
586577
.. Note:: Enter and exit ssh context manager is produced as well.
587578
.. versionadded:: 1.2.1
588579
"""
589-
return _KeepAliveContext(ssh=self, enforce=enforce)
580+
return _KeepAliveContext(ssh=self, enforce=int(enforce))
590581

591582
def _prepare_command(self, cmd: str, chroot_path: typing.Optional[str] = None) -> str:
592583
"""Prepare command: cower chroot and other cases.
@@ -811,7 +802,7 @@ def proxy_to(
811802
None,
812803
] = None,
813804
ssh_auth_map: typing.Optional[typing.Union[typing.Dict[str, ssh_auth.SSHAuth], ssh_auth.SSHAuthMapping]] = None,
814-
keepalive: bool = True,
805+
keepalive: typing.Union[int, bool] = 1,
815806
) -> "SSHClientBase":
816807
"""Start new SSH connection using current as proxy.
817808
@@ -838,8 +829,8 @@ def proxy_to(
838829
]
839830
:param ssh_auth_map: SSH authentication information mapped to host names. Useful for complex SSH Proxy cases.
840831
:type ssh_auth_map: typing.Optional[typing.Union[typing.Dict[str, ssh_auth.SSHAuth], ssh_auth.SSHAuthMapping]]
841-
:param keepalive: keepalive mode
842-
:type keepalive: bool
832+
:param keepalive: keepalive period
833+
:type keepalive: typing.Union[int, bool]
843834
:return: new ssh client instance using current as a proxy
844835
:rtype: SSHClientBase
845836
@@ -866,7 +857,7 @@ def proxy_to(
866857
ssh_config=ssh_config,
867858
sock=sock,
868859
ssh_auth_map=ssh_auth_map if ssh_auth_map is not None else self.__auth_mapping,
869-
keepalive=keepalive,
860+
keepalive=int(keepalive),
870861
)
871862

872863
def execute_through_host(

0 commit comments

Comments
 (0)