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

如何用Ansible检测远程服务器uptime并统一发送CSV/HTML报告邮件

问题描述

我编写了Ansible脚本,用于检测inventory组「linux」内60余台远程服务器的uptime,并发送附带CSV与HTML文件的邮件。当前脚本可正常运行,但会为每台服务器单独发送一封邮件。我需要汇总所有服务器的主机名与uptime信息,发送单份包含CSV和HTML报告的邮件。以下是我的原脚本:

原main.yaml

---
- name: Generate Uptime HTML Report and Send Email
  hosts: linux
  gather_facts: true

  vars:

    email_subject: Uptime Report
    email_host: xyz
    email_from: xyz
    email_to: xyz
    email_body_template: templates/report.html.j2
    csv_path: /tmp
    csv_filename: report.csv
    headers: Hostname,Uptime_In_Days

  tasks:

    - name: Get Uptime
      command: uptime
      register: uptime_result

    - name: Extract Uptime in Days
      set_fact:
        uptime_days: "{{ uptime_result.stdout | regex_replace('^.*up ([0-9]+) day.*', '\\\\1') | int }}"
      when: uptime_result.rc == 0

    - name: Create CSV Data
      set_fact:
        csv_data:
          - Hostname: "{{ inventory_hostname }}"
            Uptime_In_Days: "{{ uptime_days }}"
      when: uptime_result.rc == 0

    - name: Transfer CSV Data File
      copy:
        content: "{{ csv_data | to_nice_json }}"
        dest: "{{ csv_path }}/{{ csv_filename }}"
        mode: '0644'

    - name: Send Email
      mail:
        host: "{{ email_host }}"
        from: "{{ email_from }}"
        to: "{{ email_to }}"
        subject: "[Ansible] {{ email_subject }}"
        body: "{{ lookup('template', email_body_template) }}"
        attach: "{{ csv_path }}/{{ csv_filename }}"
        subtype: html

原html.j2模板

<sub><table style="border: 1px solid black; border-collapse: collapse;">
<tr>
    {% for header in headers.split(",") %}
    <th style="border: 1px solid black; padding: 8px 16px;">{{ header }}</th>
    {% endfor %}
</tr>
{% for host in csv_data %}
<tr>
    {% for header in headers.split(",") %}
    <td style="border: 1px solid black; padding: 8px 16px;">{{ host[header] }}</td>
    {% endfor %}
</tr>
{% endfor %}
</table></sub>
解决方案

核心思路是把数据收集和汇总发送拆分成两个独立的Play:第一个Play在所有linux主机上收集uptime数据,第二个Play在localhost上汇总所有主机的数据,生成报告并发送单封邮件。

修改后的main.yaml

---
- name: Collect Uptime Data from Linux Hosts
  hosts: linux
  gather_facts: true
  vars:
    headers: Hostname,Uptime_In_Days

  tasks:
    - name: Get Uptime
      command: uptime
      register: uptime_result

    - name: Extract Uptime in Days
      set_fact:
        uptime_days: "{{ uptime_result.stdout | regex_replace('^.*up ([0-9]+) day.*', '\\\\1') | int }}"
      when: uptime_result.rc == 0

    # 将单台主机的数据存入主机变量,方便后续汇总
    - name: Store Host Uptime Data
      set_fact:
        host_uptime_data:
          Hostname: "{{ inventory_hostname }}"
          Uptime_In_Days: "{{ uptime_days | default('N/A') }}"
      when: uptime_result.rc == 0

    # 处理命令执行失败的情况
    - name: Store Failed Host Data
      set_fact:
        host_uptime_data:
          Hostname: "{{ inventory_hostname }}"
          Uptime_In_Days: "收集失败"
      when: uptime_result.rc != 0

- name: Generate Aggregated Report and Send Email
  hosts: localhost
  gather_facts: false
  vars:
    email_subject: Uptime Report
    email_host: xyz
    email_from: xyz
    email_to: xyz
    email_body_template: templates/report.html.j2
    csv_path: /tmp
    csv_filename: report.csv
    headers: Hostname,Uptime_In_Days

  tasks:
    # 从所有linux主机的变量中汇总数据
    - name: Aggregate All Host Uptime Data
      set_fact:
        aggregated_uptime_data: "{{ groups['linux'] | map('extract', hostvars, 'host_uptime_data') | list }}"

    # 生成CSV内容(直接用逗号分隔格式,而非JSON转CSV)
    - name: Generate CSV Content
      set_fact:
        csv_content: |
          {{ headers }}
          {% for item in aggregated_uptime_data %}
          {{ item.Hostname }},{{ item.Uptime_In_Days }}
          {% endfor %}

    # 将CSV内容写入文件
    - name: Write CSV File
      copy:
        content: "{{ csv_content }}"
        dest: "{{ csv_path }}/{{ csv_filename }}"
        mode: '0644'

    # 发送邮件,使用汇总后的数据渲染模板
    - name: Send Aggregated Email Report
      mail:
        host: "{{ email_host }}"
        from: "{{ email_from }}"
        to: "{{ email_to }}"
        subject: "[Ansible] {{ email_subject }}"
        body: "{{ lookup('template', email_body_template, vars={'csv_data': aggregated_uptime_data}) }}"
        attach: "{{ csv_path }}/{{ csv_filename }}"
        subtype: html

修改后的html.j2模板(无需大改,仅确保变量匹配)

<sub><table style="border: 1px solid black; border-collapse: collapse;">
<tr>
    {% for header in headers.split(",") %}
    <th style="border: 1px solid black; padding: 8px 16px;">{{ header }}</th>
    {% endfor %}
</tr>
{% for host in csv_data %}
<tr>
    {% for header in headers.split(",") %}
    <td style="border: 1px solid black; padding: 8px 16px;">{{ host[header] }}</td>
    {% endfor %}
</tr>
{% endfor %}
</table></sub>

关键修改说明

  1. 拆分Play:第一个Play负责在所有目标机上收集数据并存储到主机变量host_uptime_data,第二个Play在本地汇总所有主机的数据,避免每台机单独发邮件。
  2. 数据汇总:使用groups['linux'] | map('extract', hostvars, 'host_uptime_data')从所有linux组主机的变量中提取数据并汇总成列表。
  3. CSV生成优化:直接生成标准CSV格式(而非JSON转CSV),兼容性更好。
  4. 异常处理:新增命令执行失败时的逻辑,标记为"收集失败",避免报告缺失主机信息。
  5. 模板变量传递:在发送邮件时,通过vars={'csv_data': aggregated_uptime_data}将汇总数据传递给模板渲染HTML表格。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 17:40:57