mirror of
ssh://git.janware.com/janware/proj/jw-pkg
synced 2026-04-24 17:23:36 +02:00
lib.ec.SSHClient.__init__(): Add parameter caps
Add an optional caps ("capabilities") argument to the constructor of
SSHClient. It is meant to be used by derived classes in order to
declare that they don't want the base class to handle a default
behaviour for a certain capability, but that they want to implement
it themselves instead.
Also, give the _run_ssh() callbacks the necessary info as parameters,
so that the derived classes have the means to do so.
Signed-off-by: Jan Lindemann <jan@janware.com>
This commit is contained in:
parent
4393ca21fc
commit
3a84408436
3 changed files with 36 additions and 8 deletions
|
|
@ -3,6 +3,7 @@
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
import os, abc, sys
|
import os, abc, sys
|
||||||
|
from enum import Flag, auto
|
||||||
|
|
||||||
from ..util import pretty_cmd
|
from ..util import pretty_cmd
|
||||||
from ..log import *
|
from ..log import *
|
||||||
|
|
@ -11,8 +12,15 @@ from urllib.parse import urlparse
|
||||||
|
|
||||||
class SSHClient(ExecContext):
|
class SSHClient(ExecContext):
|
||||||
|
|
||||||
def __init__(self, uri: str, *args, **kwargs) -> None:
|
class Caps(Flag):
|
||||||
|
LogOutput = auto()
|
||||||
|
Interactive = auto()
|
||||||
|
Env = auto()
|
||||||
|
Wd = auto()
|
||||||
|
|
||||||
|
def __init__(self, uri: str, caps: Caps=Caps(0), *args, **kwargs) -> None:
|
||||||
super().__init__(uri=uri, *args, **kwargs)
|
super().__init__(uri=uri, *args, **kwargs)
|
||||||
|
self.__caps = caps
|
||||||
try:
|
try:
|
||||||
parsed = urlparse(uri)
|
parsed = urlparse(uri)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
@ -23,7 +31,15 @@ class SSHClient(ExecContext):
|
||||||
self.__username = parsed.username
|
self.__username = parsed.username
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
async def _run_ssh(self, cmd: list[str]) -> Result:
|
async def _run_ssh(
|
||||||
|
cmd: list[str],
|
||||||
|
wd: str|None,
|
||||||
|
verbose: bool,
|
||||||
|
cmd_input: str|None,
|
||||||
|
env: dict[str, str]|None,
|
||||||
|
interactive: bool,
|
||||||
|
log_prefix: str
|
||||||
|
) -> Result:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
async def _run(
|
async def _run(
|
||||||
|
|
@ -41,6 +57,8 @@ class SSHClient(ExecContext):
|
||||||
log(prio, log_prefix, *args)
|
log(prio, log_prefix, *args)
|
||||||
|
|
||||||
def __log_block(prio: int, title: str, block: str):
|
def __log_block(prio: int, title: str, block: str):
|
||||||
|
if self.__caps & self.Caps.LogOutput:
|
||||||
|
return
|
||||||
encoding = sys.stdout.encoding or 'utf-8'
|
encoding = sys.stdout.encoding or 'utf-8'
|
||||||
block = block.decode(encoding).strip()
|
block = block.decode(encoding).strip()
|
||||||
if not block:
|
if not block:
|
||||||
|
|
@ -51,21 +69,31 @@ class SSHClient(ExecContext):
|
||||||
__log(prio, '|', line)
|
__log(prio, '|', line)
|
||||||
__log(prio, f'`{delim}')
|
__log(prio, f'`{delim}')
|
||||||
|
|
||||||
if wd is not None:
|
if wd is not None and not self.__caps & self.Caps.Wd:
|
||||||
cmd = ['cd', wd, '&&', *cmd]
|
cmd = ['cd', wd, '&&', *cmd]
|
||||||
|
|
||||||
if interactive:
|
if interactive and not self.__caps & self.Caps.Interactive:
|
||||||
raise NotImplementedError('Interactive SSH is not yet implemented')
|
raise NotImplementedError('Interactive SSH is not yet implemented')
|
||||||
|
|
||||||
if env is not None:
|
if env is not None and not self.__caps & self.Caps.Env:
|
||||||
raise NotImplementedError('Passing an environment to SSH commands is not yet implemented')
|
raise NotImplementedError('Passing an environment to SSH commands is not yet implemented')
|
||||||
|
|
||||||
ret = await self._run_ssh(cmd, cmd_input=cmd_input)
|
ret = await self._run_ssh(
|
||||||
|
cmd=cmd,
|
||||||
|
wd=wd,
|
||||||
|
verbose=verbose,
|
||||||
|
cmd_input=cmd_input,
|
||||||
|
env=env,
|
||||||
|
interactive=interactive,
|
||||||
|
log_prefix=log_prefix
|
||||||
|
)
|
||||||
|
|
||||||
if verbose:
|
if verbose:
|
||||||
__log_block(NOTICE, 'stdout', ret.stdout)
|
__log_block(NOTICE, 'stdout', ret.stdout)
|
||||||
__log_block(NOTICE, 'stderr', ret.stderr)
|
__log_block(NOTICE, 'stderr', ret.stderr)
|
||||||
if ret.status != 0:
|
if ret.status != 0:
|
||||||
__log(WARNING, f'Exit code {ret.status}')
|
__log(WARNING, f'Exit code {ret.status}')
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
async def _sudo(self, cmd: list[str], mod_env: dict[str, str], opts: list[str], *args, **kwargs) -> Result:
|
async def _sudo(self, cmd: list[str], mod_env: dict[str, str], opts: list[str], *args, **kwargs) -> Result:
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ class Exec(Base): # export
|
||||||
self.__askpass_orig[key] = os.getenv(key)
|
self.__askpass_orig[key] = os.getenv(key)
|
||||||
os.environ[key] = val
|
os.environ[key] = val
|
||||||
|
|
||||||
async def _run_ssh(self, cmd: list[str], cmd_input: str|None) -> Result:
|
async def _run_ssh(self, cmd: list[str], cmd_input: str|None, *args, **kwargs) -> Result:
|
||||||
self.__init_askpass()
|
self.__init_askpass()
|
||||||
return await run_cmd(['ssh', self.hostname, shlex.join(cmd)], cmd_input=cmd_input)
|
return await run_cmd(['ssh', self.hostname, shlex.join(cmd)], cmd_input=cmd_input)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ class Paramiko(Base): # export
|
||||||
def __scp(self):
|
def __scp(self):
|
||||||
return SCPClient(self.__ssh.get_transport())
|
return SCPClient(self.__ssh.get_transport())
|
||||||
|
|
||||||
async def _run_ssh(self, cmd: list[str], cmd_input: str|None) -> Result:
|
async def _run_ssh(self, cmd: list[str], cmd_input: str|None, *args, **kwargs) -> Result:
|
||||||
try:
|
try:
|
||||||
stdin, stdout, stderr = self.__ssh.exec_command(shlex.join(cmd), timeout=self.__timeout)
|
stdin, stdout, stderr = self.__ssh.exec_command(shlex.join(cmd), timeout=self.__timeout)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue