如何使用 ansible 和 jinja2 为多个主机生成 html 报告
how to generate html report for multiple hosts using ansible and jinja2
我的剧本
- name: create HTML report
template:
src: report.j2
dest: /cktreport.html
delegate_to: localhost
run_once: true
报告.j2
<!DOCTYPE html>
<html>
<body>
<table>
<thead>
<tr>
<th>HOSTNAME</th>
<th>PORT</th>
<th>CKTID</th>
</tr>
</thead>
<tbody>
{% for item in output.parsed %}
<tr>
{% if 'CID' in item.DESCRIP %}
<td>{{inventory_hostname}}</td>
<td>{{item.PORT}}</td>
<td>{{item.DESCRIP}}</td>
{%elif 'ckid' in item.DESCRIP %}
<td>{{inventory_hostname}}</td>
<td>{{item.PORT}}</td>
<td>{{item.DESCRIP}}</td>
{% endif %}
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>
'output.parsed' 有以下信息
TASK [debug] *******************************************************************
ok: [host-1] => {
"msg": [
{
"DESCRIP": "CID: xxxx",
"PORT": "Gi0/0/0",
"PROTOCOL": "up",
"STATUS": "up"
},
{
"DESCRIP": "",
"PORT": "Gi0/0/1",
"PROTOCOL": "up",
"STATUS": "up"
},
{
"DESCRIP": "",
"PORT": "Gi0/0/2",
"PROTOCOL": "down",
"STATUS": "down"
},
{
"DESCRIP": "ckid: XXXX",
"PORT": "Gi0/0/3",
"PROTOCOL": "up",
"STATUS": "up"
}
]
}
ok: [host-2] => {
"msg": [
{
"DESCRIP": "CID: xxxx",
"PORT": "Gi0/0/1",
"PROTOCOL": "up",
"STATUS": "up"
},
{
"DESCRIP": "",
"PORT": "Gi0/0/6",
"PROTOCOL": "up",
"STATUS": "up"
},
{
"DESCRIP": "",
"PORT": "Gi0/0/7",
"PROTOCOL": "down",
"STATUS": "down"
},
{
"DESCRIP": "ckid: XXXX",
"PORT": "Gi0/0/8",
"PROTOCOL": "up",
"STATUS": "up"
}
]
}
我遇到的问题是 - 当我 运行 playbook 时,只为 host-1 生成 cktreport.html,
我有多个主机,如何生成 single/one cktreport.html 以便其中包含所有主机信息。
我知道我需要修改 jinja2 模板,但不确定要使用什么逻辑。
我会理所当然地认为你的剧本针对的是魔术组 all
。如果不是这种情况,只需将下面的模板替换为正确的组即可。
如果您正在做一些更复杂的事情(针对多个组,使用高级主机模式,在命令行上使用限制...)看看 special variables 比如 ansible_play_hosts
这将更适应。
在大多数情况下,在自然播放主机循环之外直接在主机列表上循环是一种不好的做法...除非有充分的理由和这个。在您的模板中,您需要遍历所有主机以包含在报告中,然后循环处理您之前收集的已解析信息。我相信以下模板应该可以满足您的要求(未经测试)。
备注:
- 因为
h
实际上包含了inventory_hostname
,为了简洁我直接用了。但如果您愿意,可以用 hostvars[h].inventory_hostname
替换。结果将完全相同。
- 您的示例模板无效(关闭
endfor
没有 opennig for
挂起)。我在下面修复了它。
- 我保留了你的
if
节,但你有几个选项可以在这里 DRY 并同时检查两个条件(使用 match
test 或其他正则表达式解决方案...)以输出之后的同一块html。如果你愿意,我会让你自己探索。
<!DOCTYPE html>
<html>
<body>
<table>
<thead>
<tr>
<th>HOSTNAME</th>
<th>PORT</th>
<th>CKTID</th>
</tr>
</thead>
<tbody>
{% for h in groups['all'] %}
{% for item in hostvars[h].output.parsed %}
<tr>
{% if 'CID' in item.DESCRIP %}
<td>{{ h }}</td>
<td>{{ item.PORT }}</td>
<td>{{ item.DESCRIP }}</td>
{%elif 'ckid' in item.DESCRIP %}
<td>{{ h }}</td>
<td>{{ item.PORT }}</td>
<td>{{ item.DESCRIP }}</td>
{% endif %}
</tr>
{% endfor %}
{% endfor %}
</tbody>
</table>
</body>
</html>
我的剧本
- name: create HTML report
template:
src: report.j2
dest: /cktreport.html
delegate_to: localhost
run_once: true
报告.j2
<!DOCTYPE html>
<html>
<body>
<table>
<thead>
<tr>
<th>HOSTNAME</th>
<th>PORT</th>
<th>CKTID</th>
</tr>
</thead>
<tbody>
{% for item in output.parsed %}
<tr>
{% if 'CID' in item.DESCRIP %}
<td>{{inventory_hostname}}</td>
<td>{{item.PORT}}</td>
<td>{{item.DESCRIP}}</td>
{%elif 'ckid' in item.DESCRIP %}
<td>{{inventory_hostname}}</td>
<td>{{item.PORT}}</td>
<td>{{item.DESCRIP}}</td>
{% endif %}
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>
'output.parsed' 有以下信息
TASK [debug] *******************************************************************
ok: [host-1] => {
"msg": [
{
"DESCRIP": "CID: xxxx",
"PORT": "Gi0/0/0",
"PROTOCOL": "up",
"STATUS": "up"
},
{
"DESCRIP": "",
"PORT": "Gi0/0/1",
"PROTOCOL": "up",
"STATUS": "up"
},
{
"DESCRIP": "",
"PORT": "Gi0/0/2",
"PROTOCOL": "down",
"STATUS": "down"
},
{
"DESCRIP": "ckid: XXXX",
"PORT": "Gi0/0/3",
"PROTOCOL": "up",
"STATUS": "up"
}
]
}
ok: [host-2] => {
"msg": [
{
"DESCRIP": "CID: xxxx",
"PORT": "Gi0/0/1",
"PROTOCOL": "up",
"STATUS": "up"
},
{
"DESCRIP": "",
"PORT": "Gi0/0/6",
"PROTOCOL": "up",
"STATUS": "up"
},
{
"DESCRIP": "",
"PORT": "Gi0/0/7",
"PROTOCOL": "down",
"STATUS": "down"
},
{
"DESCRIP": "ckid: XXXX",
"PORT": "Gi0/0/8",
"PROTOCOL": "up",
"STATUS": "up"
}
]
}
我遇到的问题是 - 当我 运行 playbook 时,只为 host-1 生成 cktreport.html, 我有多个主机,如何生成 single/one cktreport.html 以便其中包含所有主机信息。
我知道我需要修改 jinja2 模板,但不确定要使用什么逻辑。
我会理所当然地认为你的剧本针对的是魔术组 all
。如果不是这种情况,只需将下面的模板替换为正确的组即可。
如果您正在做一些更复杂的事情(针对多个组,使用高级主机模式,在命令行上使用限制...)看看 special variables 比如 ansible_play_hosts
这将更适应。
在大多数情况下,在自然播放主机循环之外直接在主机列表上循环是一种不好的做法...除非有充分的理由和这个。在您的模板中,您需要遍历所有主机以包含在报告中,然后循环处理您之前收集的已解析信息。我相信以下模板应该可以满足您的要求(未经测试)。
备注:
- 因为
h
实际上包含了inventory_hostname
,为了简洁我直接用了。但如果您愿意,可以用hostvars[h].inventory_hostname
替换。结果将完全相同。 - 您的示例模板无效(关闭
endfor
没有 opennigfor
挂起)。我在下面修复了它。 - 我保留了你的
if
节,但你有几个选项可以在这里 DRY 并同时检查两个条件(使用match
test 或其他正则表达式解决方案...)以输出之后的同一块html。如果你愿意,我会让你自己探索。
<!DOCTYPE html>
<html>
<body>
<table>
<thead>
<tr>
<th>HOSTNAME</th>
<th>PORT</th>
<th>CKTID</th>
</tr>
</thead>
<tbody>
{% for h in groups['all'] %}
{% for item in hostvars[h].output.parsed %}
<tr>
{% if 'CID' in item.DESCRIP %}
<td>{{ h }}</td>
<td>{{ item.PORT }}</td>
<td>{{ item.DESCRIP }}</td>
{%elif 'ckid' in item.DESCRIP %}
<td>{{ h }}</td>
<td>{{ item.PORT }}</td>
<td>{{ item.DESCRIP }}</td>
{% endif %}
</tr>
{% endfor %}
{% endfor %}
</tbody>
</table>
</body>
</html>