如何使用带有序列和列表的嵌套循环以及如何在 Ansible 中绕过大括号

How to use nested loops with sequence and a list and how get around of curly braces in Ansible

我有一个使用 ios_config 模块备份配置的代码。我使用 ios_facts 来获取设备的主机名,我想用它来将备份文件放在一个类似命名的文件夹中,并在文件名本身中使用它。

在我的代码的最后一个任务中,我需要遍历两个项目 - 从 0 到 1 的序列(或者我的库存中有多少项目),因为我需要访问结果中的主机名并使用它在备份选项中,还循环浏览我从 csv 文件中提取的设备清单。我知道双花括号的规则,但我不知道如何绕过它。

---
- hosts: localhost
  gather_facts: false

  tasks:
    - name: Block
      block:
          - name: Use CSV

            csv_to_facts:
              src: '{{playbook_dir}}/NEW/Inventory.csv'
              vsheets:
                - INFO:
                    - IP
                    - OS

          - debug:
              msg: '{{item.IP}}'
            loop: '{{INFO}}'

          - name: Create Inventory
            add_host:
              hostname: '{{item.IP}}'
              ansible_network_os: '{{item.OS}}'
              ansible_user: cisco
              ansible_ssh_pass: cisco
              ansible_connection: network_cli
              ansible_become: yes
              ansible_become_method: enable
              groups: group_01
            loop: '{{INFO}}'


          - name: Gather Facts (IOS)
            ios_facts:
            register: ios_facts_loop
            delegate_to: '{{item}}'
            loop: "{{groups['group_01']}}"


          - name: Backup Switch (IOS)
            ios_config:
              backup: yes
              backup_options:
                dir_path:  "tmp/backups/{{ ios_facts_loop.results.{{item[0]}}.ansible_facts.ansible_net_hostname }}"
                filename: "{{ios_facts_loop.results.item{{[0]}}.ansible_facts.ansible_net_hostname}} {{ lookup('pipe','date +%Y-%m-%d@%H:%M:%S')}}"
            register: backup_ios_location
            delegate_to: '{{item[1]}}'
            loop: 
              - with_sequence: "0-{{output|length - 3}}"
              - "{{groups['group_01']}}"

TLDR;对于 vars 符号

您不能像上面的代码那样在双花括号内添加双花括号。您当前的 var 参考:

ios_facts_loop.results.{{item[0]}}.ansible_facts.ansible_net_hostname

应该转为

ios_facts_loop.results[item[0]].ansible_facts.ansible_net_hostname
# or equivalent
ios_facts_loop.results[item.0].ansible_facts.ansible_net_hostname

同时,这只会修复您当前的语法错误(您没有在问题中分享),因为循环中的第一个元素是字符串 'with_sequence: "0-X"',因此没有索引 0。

尝试修复逻辑

如果我理解正确,对于你的最后一个任务,你只需要遍历你的 ios_facts 注册的结果并将任务委托给从中获取它的服务器。幸运的是,您应该已经在 ios_facts_loop.results

中获得了所需的所有信息
  • 这是一个列表,所以你可以直接遍历它
  • 每个元素都应包含一个 item 键,其中包含在注册时在前一个 运行 中使用的实际项目(即您的 groups['group_01'] 元素之一)。

所以我会尝试像这样写你的最后一个任务。 免责声明这纯属猜测,因为我没有看到您的确切数据结构。

- name: Backup Switch (IOS)
  ios_config:
    backup: yes
    backup_options:
      dir_path:  "tmp/backups/{{ item.ansible_facts.ansible_net_hostname }}"
      filename: "{{ item.ansible_facts.ansible_net_hostname}}{{ lookup('pipe','date +%Y-%m-%d@%H:%M:%S')}}"
  register: backup_ios_location
  delegate_to: '{{item.item}}'
  loop: "{{ ios_facts_loop.results }}"

更进一步。

我不是很熟悉 ios_* 模块,但它们应该非常接近我日常使用的其他东西,我认为你可以利用更多的 ansible 功能(例如多重播放)来真正简化你的剧本在剧本中)。我相信以下内容实际上应该可以完成这项工作:

---
- name: Construct inventory from CSV  
  hosts: localhost
  gather_facts: false

  tasks:
    - name: Use CSV
      csv_to_facts:
        src: '{{playbook_dir}}/NEW/Inventory.csv'
        vsheets:
          - INFO:
              - IP
              - OS

    - name: Create Inventory
      add_host:
        hostname: '{{item.IP}}'
        ansible_network_os: '{{item.OS}}'
        ansible_user: cisco
        ansible_ssh_pass: cisco
        ansible_connection: network_cli
        ansible_become: yes
        ansible_become_method: enable
        groups: group_01
      loop: '{{INFO}}'

- name: Backup switches from created inventory
  hosts: group_01
  gather_facts: false

  tasks: 
    - name: Get facts from network os                                                                                            
      ios_facts:                                                                                                  
        gather_subset: all 

    - name: Backup Switch (IOS)
      ios_config:
        backup: yes
        backup_options:
          dir_path:  "tmp/backups/{{ ansible_net_hostname }}"
          filename: "{{ ansible_net_hostname }}{{ lookup('pipe','date +%Y-%m-%d@%H:%M:%S') }}"

有关变量的点和括号表示法的更多背景知识

您基本上可以使用两个等效的符号来导航 yaml 数据结构。

  • 点符号
a_list_var.index_number
a_hasmap_var.keyname
  • 括号表示法
a_list_var[index_number]
a_hashmap_var['key_name']

如果我们举下面的例子:

my_servers:
  hostA:
    ips:
      - x.x.x.x
      - y.y.y.y
    env:
      shell: bash
      home: somewhere
  hostB:
    ips:
      - a.a.a.a
      - b.b.b.b
    env:
      shell: sh
      home: elsewhere

以下符号都是严格等价的:

# all vars of hostA
hostA_vars: "{{ my_servers.hostA }}"
hostA_vars: "{{ my_server['hostA'] }}"
# first IP of hostB
hostB_ip: "{{ my_servers.hostB.0 }}"
hostB_ip: "{{ my_servers.hostB[0] }}"
hostB_ip: "{{ my_servers['hostB'].0 }}"
hostB_ip: "{{ my_servers['hostB'][0] }}"

如您所见,点符号往往不那么冗长且更易读。同时,您不能使用带点符号的变量标识符。因此,如果你想使用可变服务器的主环境,你将不得不使用:

# set a var for server
server: hostA
# all equivalent again
server_home: "{{ my_servers[server].env.home }}"
server_home: "{{ my_servers[server]['env'].home }}"
server_home: "{{ my_servers[server].env['home'] }}"
server_home: "{{ my_servers[server]['env']['home'] }}"