|
18 | 18 |
|
19 | 19 | from __future__ import annotations |
20 | 20 |
|
21 | | -# Standard Library |
22 | 21 | import concurrent.futures |
23 | 22 | import copy |
24 | 23 | import datetime |
|
31 | 30 | import typing |
32 | 31 | import warnings |
33 | 32 |
|
34 | | -# External Dependencies |
35 | 33 | import paramiko |
36 | 34 | import tenacity |
37 | 35 |
|
38 | | -# Package Implementation |
39 | 36 | from exec_helpers import api |
40 | 37 | from exec_helpers import constants |
41 | 38 | from exec_helpers import exceptions |
42 | 39 | from exec_helpers import exec_result |
43 | 40 | from exec_helpers import proc_enums |
44 | 41 | from exec_helpers import ssh_auth |
45 | 42 |
|
46 | | -# Local Implementation |
47 | 43 | from . import _helpers |
48 | 44 | from . import _log_templates |
49 | 45 | from . import _ssh_helpers |
50 | 46 |
|
51 | 47 | if typing.TYPE_CHECKING: |
52 | | - # Standard Library |
53 | 48 | import socket |
54 | 49 | from collections.abc import Iterable |
55 | 50 | from collections.abc import Sequence |
56 | 51 | from types import TracebackType |
57 | 52 |
|
58 | | - # External Dependencies |
59 | 53 | from typing_extensions import Self |
60 | 54 |
|
61 | | - # Package Implementation |
62 | 55 | from exec_helpers.api import CalledProcessErrorSubClassT |
63 | 56 | from exec_helpers.api import CommandT |
64 | 57 | from exec_helpers.api import ErrorInfoT |
|
68 | 61 | from exec_helpers.api import OptionalTimeoutT |
69 | 62 | from exec_helpers.proc_enums import ExitCodeT |
70 | 63 |
|
71 | | - # Local Implementation |
72 | 64 | from ._ssh_helpers import SSHConfigsDictT |
73 | 65 |
|
74 | 66 | __all__ = ("SSHClientBase", "SshExecuteAsyncResult", "SupportPathT") |
@@ -316,7 +308,7 @@ def __enter__(self) -> SshExecuteAsyncResult: |
316 | 308 | stderr = None |
317 | 309 | _stdin: paramiko.channel.ChannelFile |
318 | 310 | with chan.makefile("wb") as _stdin: |
319 | | - started = datetime.datetime.utcnow() |
| 311 | + started = datetime.datetime.now(tz=datetime.timezone.utc) |
320 | 312 | if self.__sudo_mode: |
321 | 313 | chan.exec_command(self.command) # nosec # Sanitize on caller side |
322 | 314 | if not stdout.channel.closed: |
@@ -868,7 +860,7 @@ def sudo_mode(self, mode: bool) -> None: |
868 | 860 | :param mode: sudo status: enabled | disabled |
869 | 861 | :type mode: bool |
870 | 862 | """ |
871 | | - self.__sudo_mode = bool(mode) |
| 863 | + self.__sudo_mode = mode |
872 | 864 |
|
873 | 865 | @property |
874 | 866 | def keepalive_period(self) -> int: |
@@ -957,122 +949,14 @@ def _prepare_command(self, cmd: str, chroot_path: str | None = None) -> str: |
957 | 949 | if not self.sudo_mode: |
958 | 950 | return super()._prepare_command(cmd=cmd, chroot_path=chroot_path) |
959 | 951 | quoted_command: str = shlex.quote(cmd) |
960 | | - if chroot_path is None and self._chroot_path is None: |
| 952 | + if chroot_path is self._chroot_path is None: |
961 | 953 | return f'sudo -S sh -c {shlex.quote(f"eval {quoted_command}")}' |
962 | 954 | if chroot_path is not None: |
963 | 955 | target_path: str = shlex.quote(chroot_path) |
964 | 956 | else: |
965 | 957 | target_path = shlex.quote(self._chroot_path) # type: ignore[arg-type] |
966 | 958 | return f'chroot {target_path} sudo sh -c {shlex.quote(f"eval {quoted_command}")}' |
967 | 959 |
|
968 | | - # noinspection PyMethodOverriding |
969 | | - def _execute_async( |
970 | | - self, |
971 | | - command: str, |
972 | | - *, |
973 | | - stdin: OptionalStdinT = None, |
974 | | - open_stdout: bool = True, |
975 | | - open_stderr: bool = True, |
976 | | - chroot_path: str | None = None, |
977 | | - get_pty: bool = False, |
978 | | - width: int = 80, |
979 | | - height: int = 24, |
980 | | - timeout: OptionalTimeoutT = None, |
981 | | - **kwargs: typing.Any, |
982 | | - ) -> SshExecuteAsyncResult: |
983 | | - """Execute command in async mode and return channel with IO objects. |
984 | | -
|
985 | | - :param command: Command for execution |
986 | | - :type command: str |
987 | | - :param stdin: pass STDIN text to the process |
988 | | - :type stdin: bytes | str | bytearray | None |
989 | | - :param open_stdout: open STDOUT stream for read |
990 | | - :type open_stdout: bool |
991 | | - :param open_stderr: open STDERR stream for read |
992 | | - :type open_stderr: bool |
993 | | - :param chroot_path: chroot path override |
994 | | - :type chroot_path: str | None |
995 | | - :param get_pty: Get PTY for connection |
996 | | - :type get_pty: bool |
997 | | - :param width: PTY width |
998 | | - :type width: int |
999 | | - :param height: PTY height |
1000 | | - :type height: int |
1001 | | - :param timeout: timeout before stop execution with TimeoutError (will be set on channel) |
1002 | | - :type timeout: int | float | None |
1003 | | - :param kwargs: additional parameters for call. |
1004 | | - :type kwargs: typing.Any |
1005 | | - :return: Tuple with control interface and file-like objects for STDIN/STDERR/STDOUT |
1006 | | - :rtype: typing.NamedTuple( |
1007 | | - 'SshExecuteAsyncResult', |
1008 | | - [ |
1009 | | - ('interface', paramiko.Channel), |
1010 | | - ('stdin', paramiko.ChannelFile), |
1011 | | - ('stderr', paramiko.ChannelFile | None), |
1012 | | - ('stdout', paramiko.ChannelFile | None), |
1013 | | - ("started", datetime.datetime), |
1014 | | - ] |
1015 | | - ) |
1016 | | -
|
1017 | | - .. versionchanged:: 1.2.0 open_stdout and open_stderr flags |
1018 | | - .. versionchanged:: 1.2.0 stdin data |
1019 | | - .. versionchanged:: 1.2.0 get_pty moved to `**kwargs` |
1020 | | - .. versionchanged:: 2.1.0 Use typed NamedTuple as result |
1021 | | - .. versionchanged:: 3.2.0 Expose pty options as optional keyword-only arguments |
1022 | | - .. versionchanged:: 4.1.0 support chroot |
1023 | | - """ |
1024 | | - warnings.warn("_execute_async is deprecated and will be removed soon", DeprecationWarning, stacklevel=2) |
1025 | | - chan: paramiko.Channel = self._ssh_transport.open_session() |
1026 | | - if timeout is not None: |
1027 | | - chan.settimeout(timeout) |
1028 | | - |
1029 | | - if get_pty: |
1030 | | - # Open PTY |
1031 | | - chan.get_pty(term="vt100", width=width, height=height, width_pixels=0, height_pixels=0) |
1032 | | - |
1033 | | - _stdin: paramiko.ChannelFile = chan.makefile("wb") # type: ignore[name-defined] |
1034 | | - stdout: paramiko.ChannelFile = chan.makefile("rb") # type: ignore[name-defined] |
1035 | | - if open_stderr: |
1036 | | - stderr: paramiko.ChannelFile | None = chan.makefile_stderr("rb") # type: ignore[name-defined] |
1037 | | - else: |
1038 | | - stderr = None |
1039 | | - |
1040 | | - cmd = f"{self._prepare_command(cmd=command, chroot_path=chroot_path)}\n" |
1041 | | - |
1042 | | - started = datetime.datetime.utcnow() |
1043 | | - if self.sudo_mode: |
1044 | | - chan.exec_command(cmd) # nosec # Sanitize on caller side |
1045 | | - if not stdout.channel.closed: |
1046 | | - # noinspection PyTypeChecker |
1047 | | - self.auth.enter_password(_stdin) |
1048 | | - _stdin.flush() |
1049 | | - else: |
1050 | | - chan.exec_command(cmd) # nosec # Sanitize on caller side |
1051 | | - |
1052 | | - if stdin is not None: |
1053 | | - if not _stdin.channel.closed: |
1054 | | - stdin_str: bytes = self._string_bytes_bytearray_as_bytes(stdin) |
1055 | | - |
1056 | | - _stdin.write(stdin_str) |
1057 | | - _stdin.flush() |
1058 | | - else: |
1059 | | - self.logger.warning("STDIN Send failed: closed channel") |
1060 | | - |
1061 | | - if open_stdout: |
1062 | | - res_stdout = stdout |
1063 | | - else: |
1064 | | - stdout.close() |
1065 | | - res_stdout = None |
1066 | | - |
1067 | | - # noinspection PyArgumentList |
1068 | | - return SshExecuteAsyncResult( |
1069 | | - interface=chan, |
1070 | | - stdin=_stdin, |
1071 | | - stderr=stderr, |
1072 | | - stdout=res_stdout, |
1073 | | - started=started, |
1074 | | - ) |
1075 | | - |
1076 | 960 | def _exec_command( # type: ignore[override] |
1077 | 961 | self, |
1078 | 962 | command: str, |
|
0 commit comments