You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用SaltStack Python客户端API实时输出Shell脚本执行日志并传至网页

问题

使用SaltStack Python客户端执行Shell脚本时,无法实时输出脚本的执行内容。现有代码如下,期望循环中持续打印日志,但实际无输出:

import salt.client

selector = "test"
client = salt.client.LocalClient()


def start_job(selector):
    return client.run_job(selector, "cmd.run", arg=["""for i in {1..50}; do echo "Log line $i at $(date '+%T')"; sleep 1; done"""])


def check_job(job):
    returned = client.get_cli_returns(job["jid"], job["minions"], timeout=30, verbose=True, show_jid=True)
    print(list(returned))
    for resp in returned:
        print(f"Response: {resp}")


if __name__ == "__main__":
    print("Immediate check -- fails")
    job = start_job(selector)
    print(job)
    check_job(job)

期望的实时输出效果:

Response: Log line 1 at 11:47:08
Response: Log line 2 at 11:47:09
Response: Log line 3 at 11:47:10
...

请问如何实现上述实时输出,或如何通过Python客户端API将命令执行过程传递到网页?


解决方案

方法1:利用Salt事件总线实时捕获输出

SaltStack的事件系统可以监听任务的实时输出,修改代码订阅对应任务的事件即可:

import salt.client
import salt.utils.event

selector = "test"
client = salt.client.LocalClient()


def start_job(selector):
    # 启动任务时开启stream参数,触发实时输出事件
    return client.run_job(
        selector,
        "cmd.run",
        arg=["""for i in {1..50}; do echo "Log line $i at $(date '+%T')"; sleep 1; done"""],
        kwargs={"stream": True}
    )


def listen_for_job_output(jid, minion_id):
    # 创建事件监听器,监听master端的任务输出事件
    event = salt.utils.event.get_event(
        "master",
        sock_dir="/var/run/salt/master",  # 根据实际master配置调整路径
        transport="tcp",
        opts=client.opts
    )

    while True:
        raw_event = event.get_event(full=True)
        if not raw_event:
            continue
        
        # 筛选当前任务的输出事件
        if (raw_event["tag"] == f"salt/job/{jid}/ret/{minion_id}" and 
            "data" in raw_event and "return" in raw_event["data"]):
            output_line = raw_event["data"]["return"]
            print(f"Response: {output_line}")
            # 检测任务结束(可根据脚本逻辑调整判断条件)
            if "Log line 50" in output_line:
                break


if __name__ == "__main__":
    job = start_job(selector)
    jid = job["jid"]
    minion_id = job["minions"][0]
    print(f"Started job with JID: {jid}")
    listen_for_job_output(jid, minion_id)

方法2:轮询任务进度获取实时输出

如果无法使用事件总线,可通过定期轮询任务状态来获取输出:

import salt.client
import time

selector = "test"
client = salt.client.LocalClient()


def start_job(selector):
    return client.run_job(
        selector,
        "cmd.run",
        arg=["""for i in {1..50}; do echo "Log line $i at $(date '+%T')"; sleep 1; done"""],
        kwargs={"stream": True, "output_loglevel": "trace"}
    )


def poll_job_output(jid, minion_id):
    seen_lines = set()
    while True:
        # 获取任务当前的输出内容
        job_result = client.get_job(jid)
        if minion_id not in job_result["return"]:
            time.sleep(1)
            continue
        
        output = job_result["return"][minion_id]
        # 分割输出为行,过滤已打印过的内容
        lines = output.split("\n")
        for line in lines:
            if line and line not in seen_lines:
                print(f"Response: {line}")
                seen_lines.add(line)
                if "Log line 50" in line:
                    return
        time.sleep(1)


if __name__ == "__main__":
    job = start_job(selector)
    jid = job["jid"]
    minion_id = job["minions"][0]
    print(f"Started job with JID: {jid}")
    poll_job_output(jid, minion_id)

传递到网页的实现思路

若要将实时输出传递到网页,可结合Web框架的WebSocket功能:

  • 在后端启动Salt任务并通过上述方法捕获实时输出
  • 利用WebSocket将每一行输出推送给前端页面
  • 前端页面接收WebSocket消息后,实时更新页面显示内容

核心逻辑是把实时捕获输出的代码集成到Web框架的异步任务或线程中,通过WebSocket实现前后端的实时通信。


内容的提问来源于stack exchange,提问作者Tony

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 02:50:10