【问题标题】:Ansible - How to combine list attributes?Ansible - 如何组合列表属性?
【发布时间】:2019-01-11 12:22:58
【问题描述】:

我有两个单独的列表。第一个是带有基本参数的列表(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"
            ]
        },
    ]

最好的方法是什么?

【问题讨论】:

  • 这个Q有[jq]标签但是“预期输出”不是有效的JSON,所以我想知道JSON输出是否可以接受。
  • 你说得对,我的“预期输出”不正确。 @larsks 做出了正确的 JSON 输出。

标签: ansible jinja2 jq ansible-inventory


【解决方案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_listbase_list 短,我们将有一些item.1.pathitem.1.srcfalse 的项目,导致三元过滤器选择第二个值(item.0.pathitem.1.src)。在其他情况下,我们通过组合base_listdev_list 中的每个值来构建一个列表。

运行这个 playbook 的结果如下:

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

让我知道这是否有帮助,以及生成的数据结构是否是您正在寻找的。由于您的示例 end_list 包含无效语法,因此我不得不做出一些假设,因此我猜测您想要什么。

【讨论】:

  • 感谢您的回复和评论。但这并不是我想要收到的。事实证明,您只需将第一个列表的元素与第二个列表的元素进行比较并将它们分组。如果第一个列表中的元素与第二个列表中的元素组合,则仅从第二个列表(dev_list)中获取元素。
【解决方案2】:

假设你有格式良好的 json 并且这些是根对象的属性,jq 非常适合这个。按名称对数组的内容进行分组,然后生成适当的结果对象。

$ jq '{
    end_combine: (
        .base_list + .dev_list
          | group_by(.name)
          | map({ name: .[0].name, path: map(.path), src: map(.src) })
    )
}' input.json
{
  "end_combine": [
    {
      "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"
      ]
    },
    {
      "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"
      ]
    }
  ]
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多