фикс передачи логинов в задачи

This commit is contained in:
dv 2026-04-10 16:19:31 +03:00
parent 2a90c9b8dd
commit be1e98e18a
9 changed files with 24 additions and 18 deletions

View file

@ -7,7 +7,9 @@ and an emit() callback to write progress messages to the SSE stream.
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import Any, Awaitable, Callable from typing import Any, Awaitable, Callable, Optional
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.device import Device from app.models.device import Device
from app.models.object import Object from app.models.object import Object
@ -46,6 +48,7 @@ class BaseAction(ABC):
obj: Object, obj: Object,
params: dict, params: dict,
emit: Emitter, emit: Emitter,
db: Optional[AsyncSession] = None,
) -> ActionResult: ... ) -> ActionResult: ...
def is_compatible(self, category: str) -> bool: def is_compatible(self, category: str) -> bool:
@ -79,10 +82,11 @@ class CompositeAction(BaseAction):
obj: Object, obj: Object,
params: dict, params: dict,
emit: Emitter, emit: Emitter,
db: Optional[AsyncSession] = None,
) -> ActionResult: ) -> ActionResult:
action = self._routing.get(device.category) action = self._routing.get(device.category)
if action is None: if action is None:
msg = f"[{device.ip}] Нет обработчика для категории '{device.category}'" msg = f"[{device.ip}] Нет обработчика для категории '{device.category}'"
await emit(msg, "error") await emit(msg, "error")
return ActionResult(success=False, error=msg) return ActionResult(success=False, error=msg)
return await action.execute(device, obj, params, emit) return await action.execute(device, obj, params, emit, db=db)

View file

@ -19,12 +19,12 @@ class MikrotikGetConfigAction(BaseAction):
params_schema = [] params_schema = []
async def execute( async def execute(
self, device: Device, obj: Object, params: dict, emit: Emitter self, device: Device, obj: Object, params: dict, emit: Emitter, db=None
) -> ActionResult: ) -> ActionResult:
await emit(f"[{device.ip}] Running /export compact", "info") await emit(f"[{device.ip}] Running /export compact", "info")
result = await ssh_service.run_command( result = await ssh_service.run_command(
device, obj, "/export compact", timeout=60 device, obj, "/export compact", timeout=60, db=db
) )
if not result.success and not result.stdout: if not result.success and not result.stdout:

View file

@ -29,12 +29,12 @@ class MikrotikGetNTPAction(BaseAction):
params_schema = [] params_schema = []
async def execute( async def execute(
self, device: Device, obj: Object, params: dict, emit: Emitter self, device: Device, obj: Object, params: dict, emit: Emitter, db=None
) -> ActionResult: ) -> ActionResult:
await emit(f"[{device.ip}] /system ntp client print", "info") await emit(f"[{device.ip}] /system ntp client print", "info")
result = await ssh_service.run_command( result = await ssh_service.run_command(
device, obj, "/system ntp client print" device, obj, "/system ntp client print", db=db
) )
if result.error: if result.error:

View file

@ -23,7 +23,7 @@ class MikrotikSetNTPAction(BaseAction):
] ]
async def execute( async def execute(
self, device: Device, obj: Object, params: dict, emit: Emitter self, device: Device, obj: Object, params: dict, emit: Emitter, db=None
) -> ActionResult: ) -> ActionResult:
server = params.get("server", "").strip() server = params.get("server", "").strip()
if not server: if not server:
@ -37,7 +37,7 @@ class MikrotikSetNTPAction(BaseAction):
for cmd in commands: for cmd in commands:
await emit(f"[{device.ip}] {cmd}", "info") await emit(f"[{device.ip}] {cmd}", "info")
result = await ssh_service.run_command(device, obj, cmd) result = await ssh_service.run_command(device, obj, cmd, db=db)
if result.error: if result.error:
await emit(f"[{device.ip}] Error: {result.error}", "error") await emit(f"[{device.ip}] Error: {result.error}", "error")
return ActionResult( return ActionResult(
@ -49,7 +49,7 @@ class MikrotikSetNTPAction(BaseAction):
) )
# Verify # Verify
verify = await ssh_service.run_command(device, obj, "/system ntp client print") verify = await ssh_service.run_command(device, obj, "/system ntp client print", db=db)
await emit(f"[{device.ip}] Verify: {verify.stdout[:200]}", "info") await emit(f"[{device.ip}] Verify: {verify.stdout[:200]}", "info")
return ActionResult( return ActionResult(

View file

@ -26,10 +26,10 @@ class DebianSystemInfoAction(BaseAction):
compatible_categories = ["server", "raspberry", "embedded", "other"] compatible_categories = ["server", "raspberry", "embedded", "other"]
params_schema = [] params_schema = []
async def execute(self, device: Device, obj: Object, params: dict, emit: Emitter) -> ActionResult: async def execute(self, device: Device, obj: Object, params: dict, emit: Emitter, db=None) -> ActionResult:
await emit(f"[{device.ip}] Collecting system info...", "info") await emit(f"[{device.ip}] Collecting system info...", "info")
result = await ssh_service.run_command(device, obj, _CMD, timeout=30) result = await ssh_service.run_command(device, obj, _CMD, timeout=30, db=db)
if result.error: if result.error:
await emit(f"[{device.ip}] Error: {result.error}", "error") await emit(f"[{device.ip}] Error: {result.error}", "error")

View file

@ -26,14 +26,14 @@ class GetFileAction(BaseAction):
] ]
async def execute( async def execute(
self, device: Device, obj: Object, params: dict, emit: Emitter self, device: Device, obj: Object, params: dict, emit: Emitter, db=None
) -> ActionResult: ) -> ActionResult:
path = params.get("path", "").strip() path = params.get("path", "").strip()
if not path: if not path:
return ActionResult(success=False, error="No file path specified") return ActionResult(success=False, error="No file path specified")
await emit(f"[{device.ip}] Reading {path}", "info") await emit(f"[{device.ip}] Reading {path}", "info")
result = await ssh_service.run_command(device, obj, f"cat {path}") result = await ssh_service.run_command(device, obj, f"cat {path}", db=db)
if not result.success: if not result.success:
msg = result.error or result.stderr or f"exit code {result.exit_code}" msg = result.error or result.stderr or f"exit code {result.exit_code}"

View file

@ -10,9 +10,9 @@ class GetTimedatectlAction(BaseAction):
compatible_categories = ["raspberry", "server"] compatible_categories = ["raspberry", "server"]
params_schema = [] params_schema = []
async def execute(self, device: Device, obj: Object, params: dict, emit: Emitter) -> ActionResult: async def execute(self, device: Device, obj: Object, params: dict, emit: Emitter, db=None) -> ActionResult:
await emit(f"[{device.ip}] Running timedatectl...", "info") await emit(f"[{device.ip}] Running timedatectl...", "info")
result = await ssh_service.run_command(device, obj, "timedatectl show --no-pager 2>/dev/null || timedatectl") result = await ssh_service.run_command(device, obj, "timedatectl show --no-pager 2>/dev/null || timedatectl", db=db)
if result.error: if result.error:
await emit(f"[{device.ip}] Error: {result.error}", "error") await emit(f"[{device.ip}] Error: {result.error}", "error")

View file

@ -12,13 +12,13 @@ class SSHCommandAction(BaseAction):
{"name": "command", "type": "string", "label": "Command", "required": True}, {"name": "command", "type": "string", "label": "Command", "required": True},
] ]
async def execute(self, device: Device, obj: Object, params: dict, emit: Emitter) -> ActionResult: async def execute(self, device: Device, obj: Object, params: dict, emit: Emitter, db=None) -> ActionResult:
command = params.get("command", "").strip() command = params.get("command", "").strip()
if not command: if not command:
return ActionResult(success=False, error="No command specified") return ActionResult(success=False, error="No command specified")
await emit(f"[{device.ip}] $ {command}", "info") await emit(f"[{device.ip}] $ {command}", "info")
result = await ssh_service.run_command(device, obj, command) result = await ssh_service.run_command(device, obj, command, db=db)
if result.error: if result.error:
await emit(f"[{device.ip}] Error: {result.error}", "error") await emit(f"[{device.ip}] Error: {result.error}", "error")

View file

@ -134,7 +134,9 @@ async def _run_on_device(
result_id = tr.id result_id = tr.id
try: try:
action_result = await action.execute(device, obj, params, emit) async with AsyncSessionLocal() as action_db:
async with action_db.begin():
action_result = await action.execute(device, obj, params, emit, db=action_db)
except Exception as exc: except Exception as exc:
action_result_success = False action_result_success = False
action_stdout = "" action_stdout = ""