【问题标题】:Ansible: Updating values in a list of dictionariesAnsible:更新字典列表中的值
【发布时间】:2017-10-30 14:58:29
【问题描述】:

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

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)和端口。端口的长度应为五个字符,开头为 0(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 变量。

【问题讨论】:

    标签: ansible


    【解决方案1】:

    这似乎是将您的逻辑放入自定义 Ansible 模块的好时机。它不必花哨,例如:

    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
    

    结果如下:

    TASK [debug] *******************************************************************
    ok: [localhost] => {
        "padded.result": [
            {
                "applications": [
                    {
                        "port": "08302", 
                        "protocol": "tcp"
                    }, 
                    {
                        "port": "02000", 
                        "protocol": "udp"
                    }, 
                    {
                        "port": "02000", 
                        "protocol": "tcp"
                    }
                ], 
                "ip_addresses": [
                    "010.010.010.010", 
                    "011.011.011.011"
                ], 
                "name": "name1", 
                "nat_destination_addresses": [
                    {
                        "destination": "013.013.013.013", 
                        "host": "012.012.012.012"
                    }, 
                    {
                        "destination": "015.015.015.015", 
                        "host": "014.014.014.014"
                    }
                ], 
                "nat_source_address": "016.016.016.016"
            }, 
            {
                "ip_addresses": [
                    "017.017.017.017"
                ], 
                "name": "name2"
            }, 
            {
                "ip_addresses": [
                    "018.018.018.018", 
                    "019.019.019.019"
                ], 
                "name": "name3"
            }
        ]
    }
    

    【讨论】:

    • 感谢您的回答!我测试了该解决方案,它运行良好并且非常简单,但不幸的是,我们正试图限制我们创建的 Python 模块的数量(不是我的决定),我将发布我用于参考目的的解决方案,以防有人结束和我有同样的要求。
    【解决方案2】:

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

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

    本地:

    我将myvar 输出到一个yml 文件(文件顶部带有'---' 并确保myvar 仍设置为键。

    使用正则表达式和替换模块,我替换了我想要替换的文件部分。

    在我的所有主机上:

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

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

    【讨论】:

      猜你喜欢
      • 2019-02-21
      • 1970-01-01
      • 2016-08-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多