Ansible-如何合并列表属性?

时间:2019-01-11 12:22:59

标签: ansible jinja2 jq ansible-inventory

我有两个单独的列表。第一个是具有基本参数的列表(base_list),第二个是具有特定支架的参数的列表(dev_list)。

"base_list": [
    {
        "name": "kibana",
        "path": "kibana/conf/kibana.xml",
        "src": "/Users/ansible/inventories/_base/group_vars/kibana/conf/kibana.xml"
    },
    {
        "name": "logstash",
        "path": "logstash/conf/logstash.yml",
        "src": "/Users/ansible/inventories/_base/group_vars/logstash/conf/logstash.yml"
    },
    {
        "name": "grafana",
        "path": "grafana/conf/grafana.json",
        "src": "/Users/ansible/inventories/_base/group_vars/grafana/conf/grafana.json"
    },
    {
        "name": "grafana",
        "path": "grafana/conf/nginx.json",
        "src": "/Users/ansible/inventories/_base/group_vars/grafana/conf/nginx.json"
    },
    {
        "name": "grafana",
        "path": "grafana/conf/config.json",
        "src": "/Users/ansible/inventories/_base/group_vars/grafana/conf/config.json"
    },
]

"dev_list": [
    {
        "name": "kibana",
        "path": "kibana/conf/kibana.xml",
        "src": "/Users/ansible/inventories/dev-st/group_vars/kibana/conf/kibana.xml"
    },
    {
        "name": "logstash",
        "path": "logstash/conf/jvm.options",
        "src": "/Users/ansible/inventories/dev-st/group_vars/logstash/conf/jvm.options"
    }
]

我的目标是将这两个列表组合在一起,以获得一个item.name和几个item.path和item.src。看起来像这样的路径:

"end_list": [
        {
            "name": "kibana", 
            "path": "kibana/conf/kibana.xml",
            "src": "/Users/ansible/inventories/dev-st/group_vars/kibana/conf/kibana.xml"
        }, 
        {
            "name": "logstash", 
            "path": [
                "logstash/conf/logstash.yml", 
                "logstash/conf/jvm.options"
            ], 
            "src": [
                "/Users/ansible/inventories/_base/group_vars/logstash/conf/logstash.yml", 
                "/Users/ansible/inventories/dev-st/group_vars/logstash/conf/jvm.options"
            ]
        }, 
        {
            "name": "grafana",
            "path": [
                "grafana/conf/grafana.json",
                "grafana/conf/nginx.json",
                "grafana/conf/config.json"
            ]
            "src": [
                "/Users/ansible/inventories/_base/group_vars/grafana/conf/grafana.json",
                "/Users/ansible/inventories/_base/group_vars/grafana/conf/nginx.json",
                "/Users/ansible/inventories/_base/group_vars/grafana/conf/config.json"
            ]
        },
    ]

什么是最好的方法?

2 个答案:

答案 0 :(得分:1)

使用自定义Python过滤器可能会更容易,但这是使用Ansible内置过滤器的解决方案:

---
- hosts: localhost
  gather_facts: false
  vars:
    "base_list": [
        {
            "name": "kibana",
            "path": "kibana/conf/kibana.xml",
            "src": "/Users/ansible/inventories/_base/group_vars/kibana/conf/kibana.xml"
        },
        {
            "name": "logstash",
            "path": "logstash/conf/logstash.yml",
            "src": "/Users/ansible/inventories/_base/group_vars/logstash/conf/logstash.yml"
        },
        {
            "name": "grafana",
            "path": "grafana/conf/grafana.json",
            "src": "/Users/ansible/inventories/_base/group_vars/grafana/conf/grafana.json"
        },
    ]

    "dev_list": [
        {
            "name": "kibana",
            "path": "kibana/conf/kibana.xml",
            "src": "/Users/ansible/inventories/dev-st/group_vars/kibana/conf/kibana.xml"
        },
        {
            "name": "logstash",
            "path": "logstash/conf/jvm.options",
            "src": "/Users/ansible/inventories/dev-st/group_vars/logstash/conf/jvm.options"
        }
    ]
  tasks:
    - set_fact:
        end_list: >-
          {{ end_list|default([]) + [
          {
          'name': item.0.name,
          'path': item.1.path|ternary([item.0.path, item.1.path], item.0.path),
          'src': item.1.src|ternary([item.0.src, item.1.src], item.1.src)
          }
          ]}}
      loop: >-
        {{ base_list|zip_longest(dev_list,
        fillvalue={'path': false, 'src': false})|list }}

    - debug:
        var: end_list

放在一起有点棘手,所以我将尝试描述各个部分:

循环使用zip_longest过滤器。给定列表list1=[1, 2, 3]list2=[11, 12]list1|zip_longest(list2)将产生[[1,11], [2,12], [3,None]](即默认情况下,zip_longest将使用None作为填充值如果一个列表比另一个列表短)。通过设置fillvalue参数,我们可以使用None以外的其他值。在这种情况下...

loop: >-
  {{ base_list|zip_longest(dev_list,
  fillvalue={'path': false, 'src': false})|list }}

...我们正在将填充值设置为具有pathsrc的存根值的字典,因为这使表达式的其余部分更容易。

解决方案的重点当然是set_fact操作,其简化形式如下:

end_list: "{{ end_list|default([]) + [{...a dictionary...}] }}"

换句话说,对于loop的每次迭代,这都会向end_list附加新的字典。

我们这样创建字典:

{
'name': item.0.name,
'path': item.1.path|ternary([item.0.path, item.1.path], item.0.path),
'src': item.1.src|ternary([item.0.src, item.1.src], item.1.src)
}

我们在这里使用ternary过滤器,该过滤器将其输入评估为布尔值;如果为true,则选择第一个参数,否则选择第二个。这里我们利用传递给fillvalue过滤器的zip_longest的优势:如果dev_list短于base_list,我们将有一些{{1 }}和item.1.pathitem.1.src,从而使三元过滤器选择第二个值(falseitem.0.path)。在其他情况下,我们通过组合item.1.srcbase_list中的每个值来构建列表。

运行此剧本的结果如下:

dev_list

请让我知道是否有帮助,以及是否找到了所需的数据结构。由于您的示例ok: [localhost] => { "end_list": [ { "name": "kibana", "path": [ "kibana/conf/kibana.xml", "kibana/conf/kibana.xml" ], "src": [ "/Users/ansible/inventories/_base/group_vars/kibana/conf/kibana.xml", "/Users/ansible/inventories/dev-st/group_vars/kibana/conf/kibana.xml" ] }, { "name": "logstash", "path": [ "logstash/conf/logstash.yml", "logstash/conf/jvm.options" ], "src": [ "/Users/ansible/inventories/_base/group_vars/logstash/conf/logstash.yml", "/Users/ansible/inventories/dev-st/group_vars/logstash/conf/jvm.options" ] }, { "name": "grafana", "path": "grafana/conf/grafana.json", "src": false } ] } 包含无效的语法,因此我不得不做一些假设,所以我猜测了您想要的是什么。

答案 1 :(得分:0)

假设您具有格式正确的json并且这些是根对象上的属性,则jq非常适合此操作。按名称对数组的内容进行分组,然后生成适当的结果对象。

date = "12/01/2019"  
day = 01
month = 12
year = 2019
$ jq '{
    end_combine: (
        .base_list + .dev_list
          | group_by(.name)
          | map({ name: .[0].name, path: map(.path), src: map(.src) })
    )
}' input.json