- 将 Windows/Linux service control 执行器从占位实现推进到可用 - 新增 service control 测试,覆盖 status/start/stop/restart 主路径 - 增强 edge-agent 启动脚本,优先使用包内私有 Python 运行时 - 增强 Windows/Linux 打包脚本,支持携带私有 Python 运行时 - 更新 edge-agent README 与当前进度总结 - 新增 dist 忽略规则,避免打包产物污染仓库
65 lines
3.5 KiB
Python
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,
|
|
}
|