Ansible中如何根据键值合并2JSON

How to merge 2 JSON based on the key value in Ansible

我有 2 个 JSON,如下所示。我想根据子网值比较 JSON1 和 JSON2,如果 subnet in JSON1 & localsubnet 在 JSON2 匹配中,然后我想从 JSON2 复制特定本地子网下的行(useVpn)并将其放在 JSON1 中的子网下。请找到预期的结果。

JSON1:

[
    {
        "ansible_loop_var": "item",
        "cache_control": "no-cache, no-store, max-age=0, must-revalidate",
        "changed": false,
        "connection": "close",
        "content": "[{}]",
        "content_type": "application/json; charset=utf-8",
        "cookies": {},
        "cookies_string": "",
        "date": "Mon, 18 Oct 2021 13:26:25 GMT",
        "elapsed": 1,
        "expires": "Fri, 01 Jan 1990 00:00:00 GMT",
        "failed": false,
        "invocation": {
            "module_args": {
                "attributes": null,
                "body": null,
                "body_format": "raw",
                "ca_path": null,
                "client_cert": null,
                "client_key": null,
                "creates": null,
                "dest": null,
                "follow_redirects": "safe",
                "force": false,
                "force_basic_auth": false,
                "group": null,
                "headers": {
                    "Accept": "application/json",
                    "Content-Type": "application/json"
                },
                "http_agent": "ansible-httpget",
                "method": "GET",
                "mode": null,
                "owner": null,
                "remote_src": false,
                "removes": null,
                "return_content": true,
                "selevel": null,
                "serole": null,
                "setype": null,
                "seuser": null,
                "src": null,
                "status_code": [
                    200
                ],
                "timeout": 30,
                "unix_socket": null,
                "unsafe_writes": false,
                "validate_certs": false
            }
        },
        "item": "555",
        "json": [
            {
                "applianceIp": "11.11.11.12",
                "dhcpBootOptionsEnabled": false,
                "dhcpHandling": "Run a DHCP server",
                "dhcpLeaseTime": "1 day",
                "fixedIpAssignments": {},
                "id": 1,
                "name": "Default",
                "networkId": "888",
                "reservedIpRanges": [],
                "subnet": "11.11.11.11/28"
            }
        ],
        "msg": "OK (unknown bytes)",
        "pragma": "no-cache",
        "redirected": false,
        "server": "nginx/1.21.3",
        "status": 200,
        "strict_transport_security": "max-age=15552000; includeSubDomains",
        "transfer_encoding": "chunked"
    },
    {
        "ansible_loop_var": "item",
        "cache_control": "no-cache, no-store, max-age=0, must-revalidate",
        "changed": false,
        "connection": "close",
        "content": "[{}]",
        "content_type": "application/json; charset=utf-8",
        "cookies": {},
        "cookies_string": "",
        "date": "Mon, 18 Oct 2021 13:26:27 GMT",
        "elapsed": 0,
        "expires": "Fri, 01 Jan 1990 00:00:00 GMT",
        "failed": false,
        "invocation": {
            "module_args": {
                "attributes": null,
                "body": null,
                "body_format": "raw",
                "ca_path": null,
                "client_cert": null,
                "client_key": null,
                "creates": null,
                "dest": null,
                "follow_redirects": "safe",
                "force": false,
                "force_basic_auth": false,
                "group": null,
                "headers": {
                    "Accept": "application/json",
                    "Content-Type": "application/json"
                },
                "http_agent": "ansible-httpget",
                "method": "GET",
                "mode": null,
                "owner": null,
                "remote_src": false,
                "removes": null,
                "return_content": true,
                "selevel": null,
                "serole": null,
                "setype": null,
                "seuser": null,
                "src": null,
                "status_code": [
                    200
                ],
                "timeout": 30,
                "unix_socket": null,
                "unsafe_writes": false
            }
        },
        "item": "555",
        "json": [
            {
                "applianceIp": "10.10.10.11",
                "dhcpBootOptionsEnabled": false,
                "dhcpHandling": "Run a DHCP server",
                "dhcpLeaseTime": "1 day",
                "fixedIpAssignments": {},
                "id": 1,
                "name": "INTERNAL",
                "networkId": "555",
                "reservedIpRanges": [],
                "subnet": "10.10.10.10/28"
            }
        ],
        "msg": "OK (unknown bytes)",
        "pragma": "no-cache",
        "redirected": false,
        "server": "nginx/1.21.3",
        "status": 200,
        "strict_transport_security": "max-age=15552000; includeSubDomains",
        "transfer_encoding": "chunked"
    },
    {
        "ansible_loop_var": "item",
        "cache_control": "no-cache, no-store, max-age=0, must-revalidate",
        "changed": false,
        "connection": "close",
        "content": "[{}]",
        "content_type": "application/json; charset=utf-8",
        "cookies": {},
        "cookies_string": "",
        "date": "Mon, 18 Oct 2021 13:26:29 GMT",
        "elapsed": 0,
        "expires": "Fri, 01 Jan 1990 00:00:00 GMT",
        "failed": false,
        "invocation": {
            "module_args": {
                "attributes": null,
                "body": null,
                "body_format": "raw",
                "ca_path": null,
                "client_cert": null,
                "client_key": null,
                "creates": null,
                "dest": null,
                "follow_redirects": "safe",
                "force": false,
                "force_basic_auth": false,
                "group": null,
                "headers": {
                    "Accept": "application/json",
                    "Content-Type": "application/json"
                },
                "http_agent": "ansible-httpget",
                "method": "GET",
                "mode": null,
                "owner": null,
                "remote_src": false,
                "removes": null,
                "return_content": true,
                "selevel": null,
                "serole": null,
                "setype": null,
                "seuser": null,
                "src": null,
                "status_code": [
                    200
                ],
                "timeout": 30,
                "unix_socket": null,
                "unsafe_writes": false,
                "url_password": null
            }
        },
        "item": "abc",
        "json": [
            {
                "applianceIp": "1.5.6.7",
                "dhcpBootOptionsEnabled": false,
                "dhcpHandling": "Run a DHCP server",
                "dhcpLeaseTime": "1 day",
                "fixedIpAssignments": {},
                "id": 1,
                "name": "Default",
                "networkId": "abc",
                "reservedIpRanges": [],
                "subnet": "1.5.6.7/28"
            },
            {
                "applianceIp": "1.2.3.5",
                "dhcpBootOptionsEnabled": false,
                "dhcpHandling": "Run a DHCP server",
                "dhcpLeaseTime": "1 day",
                "fixedIpAssignments": {},
                "groupPolicyId": "100",
                "id": 123,
                "name": "Test",
                "networkId": "abc",
                "reservedIpRanges": [],
                "subnet": "1.2.3.4/24"
            },
            {
                "applianceIp": "1.1.3.1",
                "dhcpBootOptionsEnabled": false,
                "dhcpHandling": "Run a DHCP server",
                "dhcpLeaseTime": "1 day",
                "dhcpOptions": [],
                "dnsNameservers": "upstream_dns",
                "fixedIpAssignments": {},
                "id": 111,
                "name": "sss",
                "networkId": "123",
                "reservedIpRanges": [],
                "subnet": "1.1.3.0/24"
            }
        ],
        "msg": "OK (unknown bytes)",
        "pragma": "no-cache",
        "redirected": false,
        "server": "nginx/1.21.3",
        "status": 200,
        "strict_transport_security": "max-age=15552000; includeSubDomains",
        "x_xss_protection": "1; mode=block"
    }
]

JSON2:

{
    "changed": false,
    "msg": "All items completed",
    "results": [
        {
            "ansible_loop_var": "item",
            "cache_control": "no-cache, no-store, max-age=0, must-revalidate",
            "changed": false,
            "connection": "close",
            "content": "{}",
            "content_type": "application/json; charset=utf-8",
            "cookies": {},
            "cookies_string": "",
            "date": "Mon, 18 Oct 2021 13:26:04 GMT",
            "elapsed": 0,
            "expires": "Fri, 01 Jan 1990 00:00:00 GMT",
            "failed": false,
            "invocation": {
                "module_args": {
                    "attributes": null,
                    "body": null,
                    "body_format": "raw",
                    "ca_path": null,
                    "client_cert": null,
                    "client_key": null,
                    "creates": null,
                    "dest": null,
                    "follow_redirects": "safe",
                    "force": false,
                    "force_basic_auth": false,
                    "group": null,
                    "headers": {
                        "Accept": "application/json",
                        "Content-Type": "application/json"
                    },
                    "http_agent": "ansible-httpget",
                    "method": "GET",
                    "mode": null,
                    "owner": null,
                    "remote_src": false,
                    "removes": null,
                    "return_content": true,
                    "selevel": null,
                    "serole": null,
                    "setype": null,
                    "seuser": null,
                    "src": null,
                    "status_code": [
                        200
                    ],
                    "timeout": 30,
                    "unix_socket": null,
                    "unsafe_writes": false
                }
            },
            "item": "123",
            "json": {
                "hubs": [
                    {
                        "hubId": "123",
                        "useDefaultRoute": true
                    }
                ],
                "mode": "spoke",
                "subnets": [
                    {
                        "localSubnet": "11.11.11.11/28",
                        "useVpn": true
                    },
                    {
                        "localSubnet": "0.0.0.0/0",
                        "useVpn": false
                    }
                ]
            },
            "msg": "OK (unknown bytes)",
            "pragma": "no-cache",
            "redirected": false,
            "server": "nginx/1.21.3",
            "status": 200,
            "x_ua_compatible": "IE=Edge,chrome=1",
            "x_xss_protection": "1; mode=block"
        },
        {
            "ansible_loop_var": "item",
            "cache_control": "no-cache, no-store, max-age=0, must-revalidate",
            "changed": false,
            "connection": "close",
            "content": "{}",
            "content_type": "application/json; charset=utf-8",
            "cookies": {},
            "cookies_string": "",
            "date": "Mon, 18 Oct 2021 13:26:06 GMT",
            "elapsed": 0,
            "expires": "Fri, 01 Jan 1990 00:00:00 GMT",
            "failed": false,
            "invocation": {
                "module_args": {
                    "attributes": null,
                    "body": null,
                    "body_format": "raw",
                    "ca_path": null,
                    "client_cert": null,
                    "client_key": null,
                    "creates": null,
                    "dest": null,
                    "follow_redirects": "safe",
                    "force": false,
                    "force_basic_auth": false,
                    "group": null,
                    "headers": {
                        "Accept": "application/json",
                        "Content-Type": "application/json"
                    },
                    "http_agent": "ansible-httpget",
                    "method": "GET",
                    "mode": null,
                    "owner": null,
                    "remote_src": false,
                    "removes": null,
                    "return_content": true,
                    "selevel": null,
                    "serole": null,
                    "setype": null,
                    "seuser": null,
                    "src": null,
                    "status_code": [
                        200
                    ],
                    "timeout": 30,
                }
            },
            "item": "def",
            "json": {
                "hubs": [
                    {
                        "hubId": "def",
                        "useDefaultRoute": false
                    }
                ],
                "mode": "spoke",
                "subnets": [
                    {
                        "localSubnet": "10.10.10.10/28",
                        "useVpn": true
                    }
                ]
            },
            "msg": "OK (unknown bytes)",
            "pragma": "no-cache",
            "redirected": false,
            "server": "nginx/1.21.3",
            "status": 200,
            "x_xss_protection": "1; mode=block"
        },
        {
            "ansible_loop_var": "item",
            "cache_control": "no-cache, no-store, max-age=0, must-revalidate",
            "changed": false,
            "connection": "close",
            "content": "{}",
            "content_type": "application/json; charset=utf-8",
            "cookies": {},
            "cookies_string": "",
            "date": "Mon, 18 Oct 2021 13:26:07 GMT",
            "elapsed": 0,
            "expires": "Fri, 01 Jan 1990 00:00:00 GMT",
            "failed": false,
            "invocation": {
                "module_args": {
                    "attributes": null,
                    "body": null,
                    "body_format": "raw",
                    "ca_path": null,
                    "client_cert": null,
                    "client_key": null,
                    "creates": null,
                    "dest": null,
                    "follow_redirects": "safe",
                    "force": false,
                    "force_basic_auth": false,
                    "group": null,
                    "headers": {
                        "Accept": "application/json",
                        "Content-Type": "application/json"          
                    },
                    "http_agent": "ansible-httpget",
                    "method": "GET",
                    "mode": null,
                    "status_code": [
                        200
                    ],
                    "timeout": 30,
                    "validate_certs": false
                }
            },
            "item": "abc",
            "json": {
                "hubs": [
                    {
                        "hubId": "abc",
                        "useDefaultRoute": false
                    }
                ],
                "mode": "spoke",
                "subnets": [
                    {
                        "localSubnet": "1.5.6.7/28",
                        "useVpn": true
                    },
                    {
                        "localSubnet": "1.2.3.4/24",
                        "useVpn": false
                    },
                    {
                        "localSubnet": "1.1.3.0/24",
                        "useVpn": false
                    }
                ]
            },
            "msg": "OK (unknown bytes)",
            "x_xss_protection": "1; mode=block"
        }
    ],
    "skipped": false
}

预期结果:

            {
                "applianceIp": "1.1.1.3",
                "subnet": "1.1.1.1/28",
                "useVpn": true
            }

代码:

- name: Combine corresponding UseVpn value to each element in json1 inside the result variable
      vars:
        vpn_value: >-
          {{
            json2.results[0].json.subnets
            | selectattr('localSubnet', '==', item.subnet)
            | map(attribute='useVpn')
            | first
            | default(false, true)
          }}
      set_fact:
        result: >-
          {{
            result | default([])
            +
            [item | combine({'useVpn': vpn_value})]
          }}
      loop:  "{{ json1[0].json }}"

    - debug:
       msg: 
         - "{{ result }}"

这里的人们通常期望您提供一个您已经尝试过的示例实现,如果您想在这里度过一些美好的时光,您应该认真考虑下一个问题(阅读 how to ask and creating an MCVE)。

同时,因为我也知道 ansible 中的数据操作很容易让初学者迷失方向,这里有可能得到你的结果,它会引入几个你需要经历一次或另一次的过滤器和模块。

我强烈建议您阅读以下文档章节以理解下面的示例:

以下剧本:

---
- hosts: localhost
  gather_facts: false

  vars:
    json1: [ { "applianceIp": "1.1.1.3", "subnet": "1.1.1.1/28" } ]
    json2: { "hubs": [ { "hubId": "abs", "useDefaultRoute": true } ], "mode": "spoke", "subnets": [ { "localSubnet": "1.1.1.1/28", "useVpn": true }, { "localSubnet": "0.0.0.0/0", "useVpn": false } ] }

  tasks:
    - name: Combine corresponding UseVpn value to each element in json1 inside the result variable
      vars:
        vpn_value: >-
          {{
            json2.subnets
            | selectattr('localSubnet', '==', item.subnet)
            | map(attribute='useVpn')
            | first
            | default(false, true)
          }}
      set_fact:
        result: >-
          {{
            result | default([])
            +
            [item | combine({'useVpn': vpn_value})]
          }}
      loop: "{{ json1 }}"

    - name: Show the result
      debug:
        var: result

给出:

PLAY [localhost] ***********************************************************************************************************************************************************************************************************************

TASK [Combine corresponding UseVpn value to each element in json1 inside the result variable] *********************************************************************************************************************************************************************
ok: [localhost] => (item={'applianceIp': '1.1.1.3', 'subnet': '1.1.1.1/28'})

TASK [Show the result] *****************************************************************************************************************************************************************************************************************
ok: [localhost] => {
    "result": [
        {
            "applianceIp": "1.1.1.3",
            "subnet": "1.1.1.1/28",
            "useVpn": true
        }
    ]
}

PLAY RECAP *****************************************************************************************************************************************************************************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0