如何用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>
关键修改说明
- 拆分Play:第一个Play负责在所有目标机上收集数据并存储到主机变量
host_uptime_data,第二个Play在本地汇总所有主机的数据,避免每台机单独发邮件。 - 数据汇总:使用
groups['linux'] | map('extract', hostvars, 'host_uptime_data')从所有linux组主机的变量中提取数据并汇总成列表。 - CSV生成优化:直接生成标准CSV格式(而非JSON转CSV),兼容性更好。
- 异常处理:新增命令执行失败时的逻辑,标记为"收集失败",避免报告缺失主机信息。
- 模板变量传递:在发送邮件时,通过
vars={'csv_data': aggregated_uptime_data}将汇总数据传递给模板渲染HTML表格。
内容的提问来源于stack exchange,提问作者sab
相关产品推荐
相关产品推荐

