api/machine checks: rename, add checkResult

This commit is contained in:
Johannes Kirschbauer
2025-07-07 13:01:18 +02:00
parent 19d86cc431
commit 6560738502
3 changed files with 25 additions and 17 deletions

View File

@@ -98,7 +98,7 @@ def find_reachable_host(deploy_info: DeployInfo) -> Remote | None:
return deploy_info.addrs[0] return deploy_info.addrs[0]
for addr in deploy_info.addrs: for addr in deploy_info.addrs:
if addr.is_ssh_reachable(): if addr.check_machine_ssh_reachable():
return addr return addr
return None return None

View File

@@ -12,7 +12,6 @@ from dataclasses import dataclass, field
from pathlib import Path from pathlib import Path
from shlex import quote from shlex import quote
from tempfile import TemporaryDirectory from tempfile import TemporaryDirectory
from typing import Literal
from clan_lib.api import API from clan_lib.api import API
from clan_lib.cmd import ClanCmdError, ClanCmdTimeoutError, CmdOut, RunOpts, run from clan_lib.cmd import ClanCmdError, ClanCmdTimeoutError, CmdOut, RunOpts, run
@@ -74,9 +73,9 @@ class Remote:
private_key=private_key if private_key is not None else self.private_key, private_key=private_key if private_key is not None else self.private_key,
password=password if password is not None else self.password, password=password if password is not None else self.password,
forward_agent=self.forward_agent, forward_agent=self.forward_agent,
host_key_check=host_key_check host_key_check=(
if host_key_check is not None host_key_check if host_key_check is not None else self.host_key_check
else self.host_key_check, ),
verbose_ssh=self.verbose_ssh, verbose_ssh=self.verbose_ssh,
ssh_options=self.ssh_options, ssh_options=self.ssh_options,
tor_socks=tor_socks if tor_socks is not None else self.tor_socks, tor_socks=tor_socks if tor_socks is not None else self.tor_socks,
@@ -425,8 +424,8 @@ class Remote:
self.check_sshpass_errorcode(res) self.check_sshpass_errorcode(res)
def is_ssh_reachable(self) -> bool: def check_machine_ssh_reachable(self) -> bool:
return is_ssh_reachable(self) return check_machine_ssh_reachable(self).ok
@dataclass(frozen=True) @dataclass(frozen=True)
@@ -435,10 +434,16 @@ class ConnectionOptions:
retries: int = 10 retries: int = 10
@dataclass
class CheckResult:
ok: bool
reason: str | None = None
@API.register @API.register
def can_ssh_login( def check_machine_ssh_login(
remote: Remote, opts: ConnectionOptions | None = None remote: Remote, opts: ConnectionOptions | None = None
) -> Literal["Online", "Offline"]: ) -> CheckResult:
if opts is None: if opts is None:
opts = ConnectionOptions() opts = ConnectionOptions()
@@ -449,7 +454,7 @@ def can_ssh_login(
["true"], ["true"],
RunOpts(timeout=opts.timeout, needs_user_terminal=True), RunOpts(timeout=opts.timeout, needs_user_terminal=True),
) )
return "Online" return CheckResult(True)
except ClanCmdTimeoutError: except ClanCmdTimeoutError:
pass pass
except ClanCmdError as e: except ClanCmdError as e:
@@ -458,11 +463,13 @@ def can_ssh_login(
else: else:
time.sleep(opts.timeout) time.sleep(opts.timeout)
return "Offline" return CheckResult(False, f"failed after {opts.retries} attempts")
@API.register @API.register
def is_ssh_reachable(remote: Remote, opts: ConnectionOptions | None = None) -> bool: def check_machine_ssh_reachable(
remote: Remote, opts: ConnectionOptions | None = None
) -> CheckResult:
if opts is None: if opts is None:
opts = ConnectionOptions() opts = ConnectionOptions()
@@ -472,10 +479,10 @@ def is_ssh_reachable(remote: Remote, opts: ConnectionOptions | None = None) -> b
sock.settimeout(opts.timeout) sock.settimeout(opts.timeout)
try: try:
sock.connect((remote.address, remote.port or 22)) sock.connect((remote.address, remote.port or 22))
return True return CheckResult(True)
except (TimeoutError, OSError): except (TimeoutError, OSError):
pass pass
else: else:
time.sleep(opts.timeout) time.sleep(opts.timeout)
return False return CheckResult(False, f"failed after {opts.retries} attempts")

View File

@@ -33,7 +33,7 @@ from clan_lib.nix_models.clan import (
) )
from clan_lib.nix_models.clan import InventoryMachineDeploy as MachineDeploy from clan_lib.nix_models.clan import InventoryMachineDeploy as MachineDeploy
from clan_lib.persist.util import set_value_by_path from clan_lib.persist.util import set_value_by_path
from clan_lib.ssh.remote import Remote, can_ssh_login from clan_lib.ssh.remote import Remote, check_machine_ssh_login
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@@ -190,8 +190,9 @@ def test_clan_create_api(
target_host = machine.target_host().override( target_host = machine.target_host().override(
private_key=private_key, host_key_check="none" private_key=private_key, host_key_check="none"
) )
result = can_ssh_login(target_host) assert check_machine_ssh_login(target_host).ok, (
assert result == "Online", f"Machine {machine.name} is not online" f"Machine {machine.name} is not online"
)
ssh_keys = [ ssh_keys = [
SSHKeyPair( SSHKeyPair(