简体   繁体   English

ansible CSV header 附加 for 循环时的选项

[英]ansible CSV header options when appending with for loop

i'm trying to gather.network devices data using ansible and add to csv file which is working fine for me but then now i would like to have the headers to the match to whats been gathered.我正在尝试使用 ansible 收集网络设备数据并添加到 csv 文件中,该文件对我来说工作正常,但现在我想让标头与所收集的内容相匹配。

- name: Playbook to collect ntp,snmp_facts and put into csv file
  hosts: all
  connection: network_cli
  gather_facts: true
#  check_mode: yes
  vars:
    output_path: "./reports/"
    filename: "device_report_{{ date }}.csv"
    vendor: CISCO

  tasks:

    - name: CSV - Generate output filename
      set_fact: date="{{lookup('pipe','date +%Y%m%d')}}"
      run_once: true

    - name: CSV - Create file and set the header
      lineinfile:
        dest: "{{ output_path }}/{{ filename }}"
        **line:   hostname,ip_address,image,iostype,model,serialnum,system,version,ntp_server_1,ntp_server_2,vrf,snmp_server_1,snmp_server_2,snmp_server_3,snmp_server_4,snmp_server_5,snmp_server_6**
        create: true
        state: present

    - import_tasks: /path/playbooks/facts/ntp_facts/ntp_facts_get.yml
    # - import_tasks: /path/playbooks/facts/snmp_facts/snmp_facts_get_2960.yml
    # - import_tasks: /path/playbooks/facts/snmp_facts/snmp_facts_get_not_2960.yml
    - import_tasks: /path/playbooks/facts/snmp_facts/snmp_another_test.yml
    - import_tasks: /path/playbooks/facts/dns_facts/dns_facts_domain_name_get.yml

    - name: CSV - Getting all the data just before printing to csv
      set_fact:
        csv_tmp: >
          {{ ansible_net_hostname|default('N/A') }},
          {{ ansible_host|default('N/A') }},
          {{ ansible_net_image|default('N/A') }},
          {{ ansible_net_iostype|default('N/A') }},
          {{ ansible_net_model|default('N/A') }},
          {{ ansible_net_serialnum|default('N/A') }},
          {{ ansible_net_system|default('N/A') }},
          {{ ansible_net_version|default('N/A') }},
          {{ ntp_servers.gathered.servers[0].server|default('N/A') }},
          {{ ntp_servers.gathered.servers[1].server|default('N/A') }},
          {{ ntp_servers.gathered.servers[0].vrf|default('N/A') }}, 
          {% set snmp_list = [] %}
          {% for snmp_host in snmp_hosts %}
            {% set snmp_list = snmp_list.append(snmp_host.host ~ ',' ~ snmp_host.version) %}
          {% endfor %}
          {{ snmp_list|join(',') }},
          {{ domain_name[0]|default('N/A') }},

    - name: check whats up with this csv_tmp
      debug:
        var: csv_tmp
        
    - name: CSV - Write information into .csv file
      lineinfile:
        insertafter: EOF
        dest: "{{ output_path }}/{{ filename }}"
        line: "{{ csv_tmp }}"

    - name: CSV - Blank lines removal
      lineinfile:
        path: "./{{ output_path }}/{{ filename }}"
        state: absent
        regex: '^\s*$'

when appending for each device using csv_tmp i have the for loop for snmp当使用 csv_tmp 为每个设备附加时,我有 snmp 的 for 循环

          {% for snmp_host in snmp_hosts %}
            {% set snmp_list = snmp_list.append(snmp_host.host ~ ',' ~ snmp_host.version) %}
          {% endfor %}
          {{ snmp_list|join(',') }},

So i would not know how many snmp hosts are configured so i was thinking if there are some better ways to have this achieved or somehow have a dynamic option to generate header or have an option to make sure that each of value {{ ansible.net_hostname|default('N/A') }}, into certain specified values first and then remove any empty column.所以我不知道配置了多少 snmp 主机,所以我在想是否有更好的方法来实现这一点,或者有一个动态选项来生成 header 或者有一个选项来确保每个值{{ ansible.net_hostname|default('N/A') }},首先转换为特定的指定值,然后删除任何空列。

I have a bit of time constraint so reaching out here for help.我有一点时间限制,所以在这里寻求帮助。

Given the inventory for testing给定测试库存

shell> cat hosts
test_11 ansible_host=10.1.0.61
test_13 ansible_host=10.1.0.63

Create a dictionary first.首先创建字典。 Declare the below variables in vars .vars中声明以下变量。 For example, in group_vars例如,在group_vars

shell> cat group_vars/all/csv_content.yml 
csv_content_dict_str: |
  hostname: {{ ansible_net_hostname|d('N/A') }}
  ip_address: {{ ansible_host|d('N/A') }}
  image: {{ ansible_net_image|d('N/A') }}
  iostype: {{ ansible_net_iostype|d('N/A') }}
  model: {{ ansible_net_model|d('N/A') }}
  serialnum: {{ ansible_net_serialnum|d('N/A') }}
  system: {{ ansible_net_system|d('N/A') }}
  version: {{ ansible_net_version|d('N/A') }}
  ntp_server_1: {{ ntp_servers.gathered.servers[0].server|d('N/A') }}
  ntp_server_2: {{ ntp_servers.gathered.servers[1].server|d('N/A') }}
  vrf: {{ ntp_servers.gathered.servers[0].vrf|d('N/A') }}
  {% for snmp_host in snmp_hosts|d([]) %}
  snmp_server_{{ loop.index }}:  {{ snmp_host.host }}
  snmp_server_version_{{ loop.index }}: {{ snmp_host.version }}
  {% endfor %}
  domain_name: {{ domain_name[0]|d('N/A') }}
csv_content_dict: "{{ csv_content_dict_str|from_yaml }}"
csv_content: |
  {{ csv_content_dict.keys()|join(',') }}
  {{ csv_content_dict.values()|join(',') }}

Without any facts collected, this gives在没有收集任何事实的情况下,这给出了

ok: [test_11] => 
  csv_content_dict:
    domain_name: N/A
    hostname: N/A
    image: N/A
    iostype: N/A
    ip_address: 10.1.0.61
    model: N/A
    ntp_server_1: N/A
    ntp_server_2: N/A
    serialnum: N/A
    system: N/A
    version: N/A
    vrf: N/A
ok: [test_13] => 
  csv_content_dict:
    domain_name: N/A
    hostname: N/A
    image: N/A
    iostype: N/A
    ip_address: 10.1.0.63
    model: N/A
    ntp_server_1: N/A
    ntp_server_2: N/A
    serialnum: N/A
    system: N/A
    version: N/A
    vrf: N/A

Write the files写文件

    - copy:
        dest: "{{ output_path }}/{{ filename }}"
        content: "{{ csv_content }}"

gives

shell> ssh admin@test_11 cat /tmp/reports/device_report_2023-01-22.csv
hostname,ip_address,image,iostype,model,serialnum,system,version,ntp_server_1,ntp_server_2,vrf,domain_name
N/A,10.1.0.61,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A
shell> ssh admin@test_13 cat /tmp/reports/device_report_2023-01-22.csv
hostname,ip_address,image,iostype,model,serialnum,system,version,ntp_server_1,ntp_server_2,vrf,domain_name
N/A,10.1.0.63,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A

  • Example of a complete playbook to write the report编写报告的完整剧本示例
shell> cat write_report.yml - hosts: all vars: output_path: /tmp/reports filename: "device_report_{{ date }}.csv" tasks: - set_fact: date: "{{ '%Y-%m-%d'|strftime }}" run_once: true - file: state: directory path: "{{ output_path }}" - block: - debug: var: csv_content_dict - debug: msg: | {{ csv_content }} when: debug|d(false)|bool - copy: dest: "{{ output_path }}/{{ filename }}" content: "{{ csv_content }}"
  • Example of a complete playbook to read the report阅读报告的完整剧本示例
shell> cat host_vars/test_11/test_network_facts.yml 
snmp_hosts:
  - host: snmp1.example.com
    version: SNMPv3
  - host: snmp2.example.com
    version: SNMPv3

Create, or collect facts.创建或收集事实。 For example, create host_vars for testing例如,创建host_vars用于测试

shell> cat host_vars/test_11/test.network_facts.yml snmp_hosts: - host: snmp1.example.com version: SNMPv3 - host: snmp2.example.com version: SNMPv3
  • Write the report to test_11将报告写入test_11
 shell> ansible-playbook write_report.yml -l test_11 PLAY [all] *********************************************************************************** TASK [Gathering Facts] *********************************************************************** ok: [test_11] TASK [set_fact] ****************************************************************************** ok: [test_11] TASK [file] ********************************************************************************** ok: [test_11] TASK [debug] ********************************************************************************* skipping: [test_11] TASK [debug] ********************************************************************************* skipping: [test_11] TASK [copy] ********************************************************************************** changed: [test_11] PLAY RECAP *********************************************************************************** test_11: ok=4 changed=1 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0
  • Take a look at the file看一下文件
shell> ssh admin@test_11 cat /tmp/reports/device_report_2023-01-22.csv hostname,ip_address,image,iostype,model,serialnum,system,version,ntp_server_1,ntp_server_2,vrf,snmp_server_1,snmp_server_version_1,snmp_server_2,snmp_server_version_2,domain_name N/A,10.1.0.61,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,snmp1.example.com,SNMPv3,snmp2.example.com,SNMPv3,N/A
  • Read the file from test_11test_11读取文件
shell> ansible-playbook read_report.yml -l test_11 PLAY [all] *********************************************************************************** TASK [Gathering Facts] *********************************************************************** ok: [test_11] TASK [set_fact] ****************************************************************************** ok: [test_11] TASK [community.general.read_csv] ************************************************************ ok: [test_11] TASK [debug] ********************************************************************************* ok: [test_11] => report.list: - domain_name: N/A hostname: N/A image: N/A iostype: N/A ip_address: 10.1.0.61 model: N/A ntp_server_1: N/A ntp_server_2: N/A serialnum: N/A snmp_server_1: snmp1.example.com snmp_server_2: snmp2.example.com snmp_server_version_1: SNMPv3 snmp_server_version_2: SNMPv3 system: N/A version: N/A vrf: N/A PLAY RECAP *********************************************************************************** test_11: ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Q: "It's overwriting every time since some hosts have 5 SNMP configured and some only 2."问: “每次都覆盖,因为有些主机配置了 5 个 SNMP,而有些主机只配置了 2 个。”

A: This can't happen if you write reports into separate files at the remote hosts.答:如果您将报告写入远程主机上的单独文件,则不会发生这种情况。 Add hours-minutes-seconds %H-%M-%S to the name of the CSV file在CSV文件名中加入时分秒%H-%M-%S

 - set_fact: date: "{{ '%Y-%m-%d-%H-%M-%S'|strftime }}" run_once: true

Then, a new file will be created each time you run the playbook.然后,每次运行剧本时都会创建一个新文件。 For example,例如,

shell> ssh admin@test_13 ls -la /tmp/reports
total 34
drwxr-xr-x   2 root  wheel    4 Jan 23 03:30 .
drwxrwxrwt  10 root  wheel   17 Jan 23 03:30 ..
-rw-r--r--   1 root  wheel  161 Jan 22 08:30 device_report_2023-01-22.csv
-rw-r--r--   1 root  wheel  161 Jan 23 03:30 device_report_2023-01-23-04-30-00.csv

will create two files将创建两个文件

shell> ssh admin@test_11 ls -la /tmp/reports total 34 drwxr-xr-x 2 root wheel 4 Jan 23 03:30. drwxrwxrwt 13 root wheel 27 Jan 23 03:30.. -rw-r--r-- 1 root wheel 283 Jan 22 08:57 device_report_2023-01-22.csv -rw-r--r-- 1 root wheel 283 Jan 23 03:30 device_report_2023-01-23-04-30-00.csv
    - file:
        state: directory
        path: "{{ output_path }}/{{ inventory_hostname }}"
      delegate_to: localhost

The next option is to write all files to the controller (localhost).下一个选项是将所有文件写入 controller(本地主机)。 For example, create the directories例如,创建目录

 - file: state: directory path: "{{ output_path }}/{{ inventory_hostname }}" delegate_to: localhost

and write the files并写入文件

 - copy: dest: "{{ output_path }}/{{ inventory_hostname }}/{{ filename }}" content: "{{ csv_content }}" delegate_to: localhost

Then, each time you run the playbook new files will be created at the controller然后,每次运行剧本时,都会在 controller 处创建新文件

shell> tree /tmp/reports/
/tmp/reports/
├── test_11
│   ├── device_report_2023-01-23-04-49-27.csv
│   └── device_report_2023-01-23-05-32-40.csv
└── test_13
    ├── device_report_2023-01-23-04-49-27.csv
    └── device_report_2023-01-23-05-32-40.csv

2 directories, 4 files

You can easily read the reports from the files at the controller. For example, give the CSV files您可以轻松地从 controller 的文件中读取报告。例如,给 CSV 文件

shell> tree /tmp/reports/ /tmp/reports/ ├── test_11 │   ├── device_report_2023-01-23-04-49-27.csv │   └── device_report_2023-01-23-05-32-40.csv └── test_13 ├── device_report_2023-01-23-04-49-27.csv └── device_report_2023-01-23-05-32-40.csv 2 directories, 4 files

Read the files读取文件

 - community.general.read_csv: path: "{{ item.src }}" register: out with_community.general.filetree: "{{ output_path }}" when: item.state == 'file' loop_control: label: "{{ item.path }}"

Declare the below variables声明以下变量

  reports_lists:
    test_11:
    - 2023-01-23-05-32-40
    - 2023-01-23-04-49-27
    test_13:
    - 2023-01-23-05-32-40
    - 2023-01-23-04-49-27

give

 reports: test_11: 2023-01-23-04-49-27: - domain_name: N/A hostname: N/A image: N/A iostype: N/A ip_address: 10.1.0.61 model: N/A ntp_server_1: N/A ntp_server_2: N/A serialnum: N/A snmp_server_1: snmp1.example.com snmp_server_2: snmp2.example.com snmp_server_version_1: SNMPv3 snmp_server_version_2: SNMPv3 system: N/A version: N/A vrf: N/A 2023-01-23-05-32-40: - domain_name: N/A hostname: N/A image: N/A iostype: N/A ip_address: 10.1.0.61 model: N/A ntp_server_1: N/A ntp_server_2: N/A serialnum: N/A snmp_server_1: snmp1.example.com snmp_server_2: snmp2.example.com snmp_server_version_1: SNMPv3 snmp_server_version_2: SNMPv3 system: N/A version: N/A vrf: N/A test_13: 2023-01-23-04-49-27: - domain_name: N/A hostname: N/A image: N/A iostype: N/A ip_address: 10.1.0.63 model: N/A ntp_server_1: N/A ntp_server_2: N/A serialnum: N/A system: N/A version: N/A vrf: N/A 2023-01-23-05-32-40: - domain_name: N/A hostname: N/A image: N/A iostype: N/A ip_address: 10.1.0.63 model: N/A ntp_server_1: N/A ntp_server_2: N/A serialnum: N/A system: N/A version: N/A vrf: N/A
 reports_lists: test_11: - 2023-01-23-05-32-40 - 2023-01-23-04-49-27 test_13: - 2023-01-23-05-32-40 - 2023-01-23-04-49-27

Example of a complete playbook to read the reports at the controller阅读 controller 报告的完整剧本示例

shell> cat read_report.yml - hosts: localhost vars: output_path: /tmp/reports reports_str: | {% for result in out.results|selectattr('list', 'defined') %} {% set _keys = result.item.path|split('/') %} {% set host = _keys|first %} {% set report = _keys|last|regex_replace('^device_report_(.*)\.csv', '\\1') %} - {{ host }}: {{ report }}: {{ result.list }} {% endfor %} reports: "{{ reports_str|from_yaml|combine(recursive=true) }}" reports_lists: "{{ dict(reports|dict2items|json_query('[].[key, value.keys(@)]')) }}" tasks: - debug: msg: "{{ item.src }}" with_community.general.filetree: "{{ output_path }}" when: - debug|d(false)|bool - item.state == 'file' loop_control: label: "{{ item.path }}" - community.general.read_csv: path: "{{ item.src }}" register: out with_community.general.filetree: "{{ output_path }}" when: item.state == 'file' loop_control: label: "{{ item.path }}" - debug: var: reports - debug: var: reports_lists

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM