auto_agent/edge-agent/app/executors/windows_service_executor.py
2521690 591df2d18e feat: 增强 edge-agent 服务控制与便携打包能力
- 将 Windows/Linux service control 执行器从占位实现推进到可用
- 新增 service control 测试,覆盖 status/start/stop/restart 主路径
- 增强 edge-agent 启动脚本,优先使用包内私有 Python 运行时
- 增强 Windows/Linux 打包脚本,支持携带私有 Python 运行时
- 更新 edge-agent README 与当前进度总结
- 新增 dist 忽略规则,避免打包产物污染仓库
2026-04-09 11:26:42 +08:00

65 lines
3.5 KiB
Python

from __future__ import annotations
import subprocess
from typing import Any
class WindowsServiceExecutor:
def execute(self, params: dict[str, Any]) -> tuple[bool, str, dict[str, Any], dict[str, Any]]:
service_name = str(params["service_name"])
action = str(params.get("action", "status")).lower()
if action == "status":
return self._query_status(service_name, action)
if action == "start":
status_before = self._query_service_status(service_name)
if status_before == "RUNNING":
return True, "service already running", self._build_data(service_name, action, status_before), {}
self._run_command(["sc.exe", "start", service_name])
return self._query_status(service_name, action)
if action == "stop":
status_before = self._query_service_status(service_name)
if status_before == "STOPPED":
return True, "service already stopped", self._build_data(service_name, action, status_before), {}
self._run_command(["sc.exe", "stop", service_name])
return self._query_status(service_name, action)
if action == "restart":
stop_success, stop_message, stop_data, stop_evidence = self.execute({"service_name": service_name, "action": "stop"})
if not stop_success:
return stop_success, stop_message, stop_data, stop_evidence
start_success, start_message, start_data, start_evidence = self.execute({"service_name": service_name, "action": "start"})
start_data["previous_action"] = "stop"
start_evidence["stop"] = stop_evidence
return start_success, start_message, start_data, start_evidence
return False, f"unsupported action: {action}", self._build_data(service_name, action, None), {}
def _query_status(self, service_name: str, action: str) -> tuple[bool, str, dict[str, Any], dict[str, Any]]:
result = self._run_command(["sc.exe", "query", service_name], check=False)
service_status = self._parse_service_status(result.stdout + "\n" + result.stderr)
success = result.returncode == 0 and service_status not in {None, "NOT_FOUND"}
message = "service status queried" if success else (result.stderr.strip() or result.stdout.strip() or "service query failed")
return success, message, self._build_data(service_name, action, service_status), {"raw_output": (result.stdout + "\n" + result.stderr).strip()}
def _query_service_status(self, service_name: str) -> str | None:
result = self._run_command(["sc.exe", "query", service_name], check=False)
return self._parse_service_status(result.stdout + "\n" + result.stderr)
def _run_command(self, command: list[str], check: bool = True) -> subprocess.CompletedProcess[str]:
return subprocess.run(command, capture_output=True, text=True, check=check)
def _parse_service_status(self, text: str) -> str | None:
normalized = text.upper()
if "FAILED 1060" in normalized or "DOES NOT EXIST" in normalized:
return "NOT_FOUND"
for candidate in ("RUNNING", "STOPPED", "START_PENDING", "STOP_PENDING", "PAUSED"):
if candidate in normalized:
return candidate
return None
def _build_data(self, service_name: str, action: str, service_status: str | None) -> dict[str, Any]:
return {
"service_name": service_name,
"action": action,
"service_status": service_status,
}