python-3.x 附加for循环时,CSV标头选项是可能的

evrscar2  于 2023-01-27  发布在  Python
关注(0)|答案(1)|浏览(144)

我试图收集网络设备数据使用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*$'

当使用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(',') }},

因此,我不知道配置了多少SNMP主机,所以我在想是否有一些更好的方法来实现这一点,或者有一个动态选项来生成报头,或者有一个选项来确保每个值{{ ansible_net_hostname|default('N/A') }},首先成为某些指定的值,然后删除任何空列。
我有一点时间限制,所以在这里寻求帮助。

6l7fqoea

6l7fqoea1#

给定测试清单

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

首先创建字典。在 * 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(',') }}

在没有收集任何事实的情况下,

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

写入文件

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

给予

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
  • 编写报告的完整行动手册示例
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 }}"
  • 阅读报告的完整行动手册示例
shell> cat read_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
    - community.general.read_csv:
        path: "{{ output_path }}/{{ filename }}"
      register: report
    - debug:
        var: report.list

创建或收集事实。例如,创建 * 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
  • 将报告写入 * 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
  • 你看一下档案
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
  • 从 * test_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

问:***"由于某些主机配置了5个SNMP,而某些主机仅配置了2个,因此每次都会覆盖。"***
答:如果您将报告写入远程主机上的单独文件中,则不会发生这种情况。将小时-分钟-秒%H-%M-%S添加到CSV文件的名称中

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

然后,每次运行行动手册时都会创建一个新文件。例如,

shell> ansible-playbook write_report.yml

将创建两个文件

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
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

下一个选项是将所有文件写入控制器(localhost)。

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

并写入文件

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

然后,每次运行行动手册时,都会在控制器上创建新文件

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

2 directories, 2 files

您可以轻松地从控制器的文件中读取报告。例如,将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

阅读文件

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

声明以下变量

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(@)]')) }}"

Give
一个22块一个
用于在控制器上读取报告的完整行动手册示例

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

相关问题