refactor: unify SSH machines as services
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
"""Remote machine service definition.
|
||||
|
||||
An ``remote_machine`` instance is an SSH endpoint that can run reusable saved tasks.
|
||||
Tasks themselves stay in the global saved-task registry; the instance only owns
|
||||
transport (host/port/user/key). Every run is recorded in ``service_task_runs``
|
||||
and shown as history on the instance's service page.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from media_library_viewer_api.integrations.base import (
|
||||
SecretField,
|
||||
ServiceConfigBase,
|
||||
ServiceDefinition,
|
||||
TestResult,
|
||||
WidgetConfigBase,
|
||||
translate_connection_error,
|
||||
widget_kind,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from media_library_viewer_api.services.settings_store import SettingsStore
|
||||
|
||||
|
||||
def test_connection(
|
||||
config: dict[str, Any],
|
||||
secrets: dict[str, str],
|
||||
store: SettingsStore,
|
||||
) -> TestResult:
|
||||
"""Build an SSH client via build_ssh_client and attempt .connect().
|
||||
|
||||
Reuses the same error-translation patterns as test_machine_ssh (banner,
|
||||
auth failed). Known-host recording is preserved.
|
||||
"""
|
||||
from media_library_viewer_api.services.task_runner import build_ssh_client
|
||||
from media_library_viewer_api.widgets.sources import ServiceRecord
|
||||
|
||||
host = str(config.get("host") or "").strip()
|
||||
port = int(config.get("port") or 22)
|
||||
try:
|
||||
service = ServiceRecord(
|
||||
id="",
|
||||
service_type="remote_machine",
|
||||
name="test",
|
||||
config=config,
|
||||
secrets=secrets,
|
||||
enabled=True,
|
||||
)
|
||||
client = build_ssh_client(store, service)
|
||||
try:
|
||||
client.connect()
|
||||
except Exception as exc:
|
||||
lowered = str(exc).lower()
|
||||
if "protocol banner" in lowered:
|
||||
return TestResult(
|
||||
ok=False,
|
||||
detail=f"SSH banner not received from {host}:{port}; confirm the SSH service is running.",
|
||||
)
|
||||
if "no authentication methods available" in lowered or "authentication failed" in lowered:
|
||||
return TestResult(
|
||||
ok=False,
|
||||
detail=f"SSH authentication failed for {host}:{port}; check the SSH key, passphrase, or username.",
|
||||
)
|
||||
return translate_connection_error(exc, context=f"SSH {host}:{port}")
|
||||
finally:
|
||||
client.close()
|
||||
return TestResult(
|
||||
ok=True,
|
||||
detail=f"SSH connection succeeded for {host}:{port}.",
|
||||
evidence=f"Connected to {host}:{port}",
|
||||
)
|
||||
except ValueError as exc:
|
||||
return TestResult(ok=False, detail=str(exc))
|
||||
except Exception as exc:
|
||||
return translate_connection_error(exc, context=f"SSH {host}:{port}")
|
||||
|
||||
|
||||
class RemoteMachineConfig(ServiceConfigBase):
|
||||
"""Non-secret Remote machine config.
|
||||
|
||||
The SSH key itself lives in the saved SSH-key registry and is referenced by
|
||||
``ssh_key_id``. An optional ``passphrase`` is stored as a secret.
|
||||
"""
|
||||
|
||||
host: str
|
||||
port: int = 22
|
||||
username: str = ""
|
||||
ssh_key_id: str = ""
|
||||
timeout_seconds: int = 30
|
||||
|
||||
|
||||
class RemoteMachineTaskOutputWidgetConfig(WidgetConfigBase):
|
||||
"""Output of a saved task run on this instance."""
|
||||
|
||||
task_id: str
|
||||
# service_id is implicit (the widget's service); allow overriding per-widget.
|
||||
service_id: str | None = None
|
||||
|
||||
|
||||
DEFINITION = ServiceDefinition(
|
||||
service_type="remote_machine",
|
||||
name="Remote machine",
|
||||
description="SSH transport for files and reusable actions.",
|
||||
config_model=RemoteMachineConfig,
|
||||
secret_fields=[
|
||||
SecretField(key="passphrase", label="Key passphrase", helper="Optional"),
|
||||
SecretField(key="password", label="SSH password", helper="Optional"),
|
||||
],
|
||||
widget_kinds=[
|
||||
widget_kind(
|
||||
kind="task_output",
|
||||
name="Task output",
|
||||
description="Output of a saved task run.",
|
||||
model_cls=RemoteMachineTaskOutputWidgetConfig,
|
||||
default_config={"task_id": ""},
|
||||
refresh_interval_ms=0,
|
||||
),
|
||||
],
|
||||
test_callable=test_connection,
|
||||
)
|
||||
Reference in New Issue
Block a user