Ansible:更新词典列表中的值

时间:2017-10-30 14:58:29

标签: ansible

在我的有效载荷中,我有一个变量实际上是一个字典列表,例如这个:

myvar:
  - name: name1
    ip_addresses:
      - 10.10.10.10
      - 11.11.11.11
    nat_destination_addresses:
      - host: 12.12.12.12
        destination: 13.13.13.13
      - host: 14.14.14.14
        destination: 15.15.15.15
    nat_source_address: 16.16.16.16
    applications:
      - protocol: tcp
        port: 8302
      - protocol: udp
        port: 2000
      - protocol: tcp
        port: 2000-5600

  - name: name2
    ip_addresses:
      - 17.17.17.17

  - name: name3
    ip_addresses:
      - 18.18.18.18
      - 19.19.19.19

myvar中每个元素的所有值都是可选的,但名称除外,这是必需的。

我正在尝试填充IP地址(ip_addressesnat_destination_addressesnat_source_address)和端口。端口的长度应为五个字符,开头为零(2000变为020002000-5600变为02000-05600),每个子节点的IP地址应为三个字符(18.18.18.18变为018.018.018.018)。

我遇到的问题是我无法仅更改myvar的子部分。

我在这里已经阅读了其他问题,例如:

merging dictionaries in ansible

Using set_facts and with_items together in Ansible

但无济于事。无论我做什么,我都无法保留原始字典,如果我使用第二个StackOverflow链接中的combine过滤器,我最终会得到一个ip_addresses列表。

预期结果是具有更新的IP地址和端口的原始myvar变量。

2 个答案:

答案 0 :(得分:2)

这似乎是将逻辑投入自定义Ansible模块的好时机。它不一定是任何花哨的东西,例如:

 vm.dtIsHidden = whatever;

如果我将上述内容放入from ansible.module_utils.basic import AnsibleModule def pad_addr(addr): return '.'.join('%03d' % int(x) for x in addr.split('.')) def main(): module_args = dict( data=dict(type='list', required=True), ) module = AnsibleModule( argument_spec=module_args, supports_check_mode=True ) data = module.params['data'] for d in data: if 'ip_addresses' in d: d['ip_addresses'] = [pad_addr(x) for x in d['ip_addresses']] if 'nat_destination_addresses' in d: for dest in d['nat_destination_addresses']: dest['host'] = pad_addr(dest['host']) dest['destination'] = pad_addr(dest['destination']) if 'nat_source_address' in d: d['nat_source_address'] = pad_addr(d['nat_source_address']) if 'applications' in d: for service in d['applications']: service['port'] = '%05d' % service['port'] module.exit_json(changed=False, result=data) if __name__ == '__main__': main() ,然后运行以下剧本:

library/pad_data.py

我得到了结果:

- hosts: localhost
  gather_facts: false

  vars:
    myvar:
      - name: name1
        ip_addresses:
          - 10.10.10.10
          - 11.11.11.11
        nat_destination_addresses:
          - host: 12.12.12.12
            destination: 13.13.13.13
          - host: 14.14.14.14
            destination: 15.15.15.15
        nat_source_address: 16.16.16.16
        applications:
          - protocol: tcp
            port: 8302
          - protocol: udp
            port: 2000
          - protocol: tcp
            port: 2000

      - name: name2
        ip_addresses:
          - 17.17.17.17

      - name: name3
        ip_addresses:
          - 18.18.18.18
          - 19.19.19.19

  tasks:

    - pad_data:
        data: "{{ myvar }}"
      register: padded

    - debug:
        var: padded.result

答案 1 :(得分:0)

Larsks的回答很明确,可能是大多数人的最佳解决方案,但我的要求是限制使用Python为这个项目创建的模块数量,所以这是我的解决方法以供参考。

基本上,我在这个样本中所做的是:

<强>本地:

我接受myvar,我将其输出到yml文件(文件顶部带有“---”,并确保myvar仍然设置为密钥。

使用regexp和替换模块,我替换了要替换的文件部分。

在我的所有主机上:

我重新加载(现在)格式正确的myvar并使用myvar替换旧的include_vars变量

---

- name: Customer {{ customer_id }} - Format the ip addresses and ports
  hosts: localhost
  gather_facts: no
  connection: local
  tags: [format_vars]

  tasks:
    - name: Copy the 'myvar' content to a local file to allow ip addresses 
and ports formatting
      copy:
        content: "---\n{{ { 'myvar': myvar} | to_nice_yaml(indent=2) }}"
        dest: "{{ formatted_myvar_file }}"

    - name: Pad all ip addresses parts with two zeroes to ensure that all parts have at least three numbers
      replace:
        path: "{{ formatted_myvar_file }}"
        regexp: '(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})'
        replace: '00\1.00\2.00\3.00\4'

    - name: Remove extra zeroes from ip addresses to ensure that all of their parts have exactly three numbers
      replace:
        path: "{{ formatted_myvar_file }}"
        regexp: '\d{0,2}(\d{3})\.\d{0,2}(\d{3})\.\d{0,2}(\d{3})\.\d{0,2}(\d{3})'
        replace: '\1.\2.\3.\4'

    - name: Pad all ports with four zeroes to ensure that they all have at least five numbers
      replace:
        path: "{{ formatted_myvar_file }}"
        regexp: 'port: (\d{1,5})'
        replace: 'port: 0000\1'

    - name: Remove extra zeroes from ports to ensure that they all have exactly five numbers
      replace:
        path: "{{ formatted_myvar_file }}"
        regexp: 'port: \d{0,4}(\d{5})'
        replace: 'port: \1'

    - name: Pad all second parts of port ranges with four zeroes to ensure that they all have at least five numbers
      replace:
        path: "{{ formatted_myvar_file }}"
        regexp: 'port: (\d{5})-(\d{1,5})'
        replace: 'port: \1-0000\2'

    - name: Remove extra zeroes from second parts of port ranges to ensure that they all have exactly five numbers
      replace:
        path: "{{ formatted_myvar_file }}"
        regexp: 'port: (\d{5})-\d{0,4}(\d{5})'
        replace: 'port: \1-\2'

- name: Customer {{ customer_id }} - Load the properly formatted ip addresses and ports
  hosts: localhost:all-n7k:srx-clu:all-mx80:all-vsrx
  gather_facts: no
  connection: local
  tags: [format_vars]

  tasks:
    - include_vars:
        file: "{{ formatted_myvar_file }}"
        ignore_errors: yes