ansible template 文件中如果包含{{}} 等非ansible 变量处理

发布于:2025-05-30 ⋅ 阅读:(20) ⋅ 点赞:(0)

在 Ansible 模板中,如果你的 Python 脚本里有大量 {}、f""、或者其他 Jinja 会误解析的语法,就需要用 {% raw %}…{% endraw %} 把它们包起来,只在需要替换变量的那一行单独“放行”。例如:

{% raw %}
#!/usr/bin/env python3
import subprocess
import re
import socket
import time
from datetime import datetime
{% endraw %}

# 输出到的文件
PROM_FILE = "{{ node_exporter_textfile_dir }}/iotop_metrics.prom"

{% raw %}
# 单位换算
def parse_size(size_str):
    size_str = size_str.strip()
    if size_str.endswith("/s"):
        size_str = size_str[:-2].strip()
    m = re.match(r"([\d\.]+)\s*([BKMGTP]?)(B)?", size_str, re.I)
    if not m:
        return 0.0
    val = float(m.group(1))
    unit = m.group(2).upper()
    unit_map = {
        "": 1/1024, "B": 1/1024,
        "K": 1, "M": 1024, "G": 1024**2,
        "T": 1024**3, "P": 1024**4,
    }
    return val * unit_map.get(unit, 1)

# 用正则提取每列
iotop_line_re = re.compile(
    r"^\s*(\d+)\s+(\S+)\s+(\S+)\s+([\d\.]+\s+\S+/s)\s+([\d\.]+\s+\S+/s)\s+([\d\.]+\s+%)\s+([\d\.]+\s+%)\s+(.*)$"
)

# 运行 iotop 并解析
def parse_iotop():
    res = subprocess.run(
        ["sudo", "iotop", "-b", "-n", "1", "-P"],
        capture_output=True, text=True, check=True
    )
    lines = res.stdout.splitlines()

    total_read = total_write = 0.0
    for line in lines:
        if "Total DISK READ:" in line and "Total DISK WRITE:" in line:
            left, right = line.split("|", 1)
            total_read = parse_size(left.split("Total DISK READ:")[1].strip())
            total_write = parse_size(right.split("Total DISK WRITE:")[1].strip())
            break

    procs = []
    for line in lines:
        line = line.strip()
        m = iotop_line_re.match(line)
        if not m:
            continue
        pid, prio, user, d_read, d_write, swapin, io, cmd = m.groups()
        read_kb = parse_size(d_read)
        write_kb = parse_size(d_write)
        if read_kb == 0.0 and write_kb == 0.0:
            continue
        cmd = cmd.replace('"', '\\"')
        procs.append((pid, user, cmd, read_kb, write_kb))

    return total_read, total_write, procs

# 输出 Prometheus 格式指标
def write_metrics():
    hostname = socket.gethostname()
    now = datetime.utcnow().isoformat()
    total_read, total_write, procs = parse_iotop()

    lines = []
    lines.append(f"# Timestamp: {now}")

    # 指标定义
    lines.append("# HELP proc_io_read_kbps Process disk read in KB/s")
    lines.append("# TYPE proc_io_read_kbps gauge")
    lines.append("# HELP proc_io_write_kbps Process disk write in KB/s")
    lines.append("# TYPE proc_io_write_kbps gauge")
    lines.append("# HELP proc_io_total_read_kbps Total disk read in KB/s")
    lines.append("# TYPE proc_io_total_read_kbps gauge")
    lines.append("# HELP proc_io_total_write_kbps Total disk write in KB/s")
    lines.append("# TYPE proc_io_total_write_kbps gauge")

    # 总量
    lines.append(f'proc_io_total_read_kbps{{hostname="{hostname}"}} {total_read}')
    lines.append(f'proc_io_total_write_kbps{{hostname="{hostname}"}} {total_write}')

    # 每进程
    for pid, user, cmd, r, w in procs:
        labels = f'hostname="{hostname}",pid="{pid}",user="{user}",command="{cmd}"'
        lines.append(f'proc_io_read_kbps{{{labels}}} {r}')
        lines.append(f'proc_io_write_kbps{{{labels}}} {w}')

    # 写入指标文件
    with open(PROM_FILE, "w") as f:
        f.write("\n".join(lines) + "\n")

# 主循环(不启动 web,仅周期更新)
def main():
    while True:
        try:
            write_metrics()
        except Exception as e:
            print(f"[ERROR] {e}", flush=True)
        time.sleep(1)

if __name__ == "__main__":
    main()
{% endraw %}