英文:
Copy outputs from all hosts into a single txt file with Ansible
问题
以下是翻译好的内容:
以下代码似乎会覆盖之前的输出,因为我只得到最后一个主机的输出。
我期望所有输出都在一个文件中,就像屏幕上的标准输出一样。
```yaml
tasks:
- ios_command:
commands:
- sh run | i hostname
- show vlan brief | i active
register: output
- debug:
msg="{{ output.stdout_lines }}"
- copy:
content: "{{ output.stdout_lines }}"
dest: /etc/ansible/show01.txt
结果缺少了 s1、s2 和 s3:
[["hostname s4"],
["1 default active ",
"10 VLAN0010 active ",
"100 VLAN0100 active Gi0/0"]]
在Python中(使用nornir、netmiko或scrapli),很容易告诉copy
或open()
命令追加'a'
的结果,而不是覆盖'w'
,然后它将“添加”行到txt文件中,而不是覆盖它。
with open('output_june.txt', 'a') as file3:
如何在Ansible中实现这一点?
英文:
The following code seems to overwrite the previous output as I get only the last host's output in there.
I'm expecting all outputs together in one file. Just like the stdout on screen.
tasks:
- ios_command:
commands:
- sh run | i hostname
- show vlan brief | i active
register: output
- debug:
msg="{{ output.stdout_lines }}"
- copy:
content: "{{ output.stdout_lines }}"
dest: /etc/ansible/show01.txt
The result is missing s1, s2, and s3:
[["hostname s4"],
["1 default active ",
"10 VLAN0010 active ",
"100 VLAN0100 active Gi0/0"]]
In Python (with nornir, netmiko, or scrapli), it's easy to tell the copy or open() command to append 'a' the result, not to write 'w' it, then it will "add" lines into the txt file, not overwrite it.
with open('output_june.txt', 'a') as file3:
How to do this in Ansible?
答案1
得分: 1
将所有记录在一个单一任务中写入文件。这更有效率,可以避免并行写入同一文件时可能出现的竞争条件。例如,在下面的示例中,在变量out中注册命令的输出,并在单一debug中显示记录。
shell> cat pb.yml
- hosts: all
tasks:
- shell: "date; uname -smr"
register: out
- debug:
msg: |
{% for h in ansible_play_hosts %}
"{{ h }}" "{{ hostvars[h].out.stdout_lines|join('" "') }}"
{% endfor %}
run_once: true
结果是
shell> ansible-playbook pb.yml
PLAY [all] ************************************************************************************
TASK [shell] **********************************************************************************
changed: [s1]
changed: [s2]
TASK [debug] **********************************************************************************
ok: [s1] =>
msg: |-
"s1" "Sun Jun 25 12:46:55 UTC 2023" "FreeBSD 13.0-RELEASE-p13 amd64"
"s2" "Sun Jun 25 12:46:55 UTC 2023" "FreeBSD 13.0-RELEASE-p13 amd64"
将内容复制到文件中:
- copy:
dest: /tmp/show01.txt
content: |-
{% for h in ansible_play_hosts %}
"{{ h }}" "{{ hostvars[h].out.stdout_lines|join('" "') }}"
{% endfor %}
run_once: true
delegate_to: localhost
结果是
shell> cat /tmp/show01.txt
"s1" "Sun Jun 25 12:46:55 UTC 2023" "FreeBSD 13.0-RELEASE-p13 amd64"
"s2" "Sun Jun 25 12:46:55 UTC 2023" "FreeBSD 13.0-RELEASE-p13 amd64"
追加内容到文件中:
- lineinfile:
dest: /tmp/show01.txt
regexp: '^$'
line: |-
{% for h in ansible_play_hosts %}
"{{ h }}" "{{ hostvars[h].out.stdout_lines|join('" "') }}"
{% endfor %}
run_once: true
delegate_to: localhost
结果是
shell> cat /tmp/show01.txt
"s1" "Sun Jun 25 12:46:55 UTC 2023" "FreeBSD 13.0-RELEASE-p13 amd64"
"s2" "Sun Jun 25 12:46:55 UTC 2023" "FreeBSD 13.0-RELEASE-p13 amd64"
"s1" "Sun Jun 25 12:52:03 UTC 2023" "FreeBSD 13.0-RELEASE-p13 amd64"
"s2" "Sun Jun 25 12:52:03 UTC 2023" "FreeBSD 13.0-RELEASE-p13 amd64"
英文:
Write the content in single task
Write all records into the file in a single task. It's more efficient and you'll avoid the potential race conditions by parallel writing to the same file. For example, in the play below, register the output of the commands in the variable out and display the records in the single debug
shell> cat pb.yml
- hosts: all
tasks:
- shell: "date; uname -smr"
register: out
- debug:
msg: |
{% for h in ansible_play_hosts %}
"{{ h }}" "{{ hostvars[h].out.stdout_lines|join('" "') }}"
{% endfor %}
run_once: true
gives
shell> ansible-playbook pb.yml
PLAY [all] ************************************************************************************
TASK [shell] **********************************************************************************
changed: [s1]
changed: [s2]
TASK [debug] **********************************************************************************
ok: [s1] =>
msg: |-
"s1" "Sun Jun 25 12:46:55 UTC 2023" "FreeBSD 13.0-RELEASE-p13 amd64"
"s2" "Sun Jun 25 12:46:55 UTC 2023" "FreeBSD 13.0-RELEASE-p13 amd64"
Copy the content to a file
You can copy the content to a file. For example,
- copy:
dest: /tmp/show01.txt
content: |-
{% for h in ansible_play_hosts %}
"{{ h }}" "{{ hostvars[h].out.stdout_lines|join('" "') }}"
{% endfor %}
run_once: true
delegate_to: localhost
gives
shell> cat /tmp/show01.txt
"s1" "Sun Jun 25 12:46:55 UTC 2023" "FreeBSD 13.0-RELEASE-p13 amd64"
"s2" "Sun Jun 25 12:46:55 UTC 2023" "FreeBSD 13.0-RELEASE-p13 amd64"
Append the content to a file
If you repeat it, the above copy task will overwrite the content of the file. If you want to append the content, use lineinfile. For example,
- lineinfile:
dest: /tmp/show01.txt
regexp: '^$'
line: |-
{% for h in ansible_play_hosts %}
"{{ h }}" "{{ hostvars[h].out.stdout_lines|join('" "') }}"
{% endfor %}
run_once: true
delegate_to: localhost
gives
shell> cat /tmp/show01.txt
"s1" "Sun Jun 25 12:46:55 UTC 2023" "FreeBSD 13.0-RELEASE-p13 amd64"
"s2" "Sun Jun 25 12:46:55 UTC 2023" "FreeBSD 13.0-RELEASE-p13 amd64"
"s1" "Sun Jun 25 12:52:03 UTC 2023" "FreeBSD 13.0-RELEASE-p13 amd64"
"s2" "Sun Jun 25 12:52:03 UTC 2023" "FreeBSD 13.0-RELEASE-p13 amd64"
<hr>
<sup>
Example of a complete playbook for testing
- hosts: all
vars:
content: |-
{% for h in ansible_play_hosts %}
"{{ h }}" "{{ hostvars[h].out.stdout_lines|join('" "') }}"
{% endfor %}
tasks:
- shell: "date; uname -smr"
register: out
- block:
- debug:
msg: "{{ content }}"
- copy:
dest: /tmp/show01.txt
content: "{{ content }}"
when: not append|d(false)|bool
- lineinfile:
dest: /tmp/show01.txt
regexp: '^$'
line: "{{ content }}"
when: append|d(false)|bool
run_once: true
delegate_to: localhost
</sup>
<hr>
Q: What "|-"
does in "content: |-"
?
A: The dash -
after the literal block |
asks to strip the final line break. See Block Chomping Indicator. The module copy doesn't care and always strips the final break if present, but the lineinfile module obeys the format of line. When you remove the dash -
content: |
{% for h in ansible_play_hosts %}
"{{ h }}" "{{ hostvars[h].out.stdout_lines|join('" "') }}"
{% endfor %}
You will get by lineinfile (note the last empty line)
shell> cat /tmp/show01.txt
"s1" "Wed Jun 28 02:06:32 UTC 2023" "FreeBSD 13.0-RELEASE-p13 amd64"
"s2" "Wed Jun 28 02:06:32 UTC 2023" "FreeBSD 13.0-RELEASE-p13 amd64"
However, the code is robust enough to deal with it. The parameter regexp: '^$'
looks for an empty line to be replaced (always the last line if present). If the regular expression is not matched (you strip the final beak), the line will be added to the file in keeping with insertbefore or insertafter settings. The default is insertafter EOF
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论