Ansible - Как объединить атрибуты списка? - PullRequest
0 голосов
/ 11 января 2019

У меня есть два отдельных списка. Первый - это список (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 голосов
/ 21 января 2019

Если у вас правильно сформированный 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"
      ]
    }
  ]
}
0 голосов
/ 11 января 2019

Вероятно, это было бы проще с пользовательским фильтром 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 }}

... Мы устанавливаем значение заполнения для словаря со значениями-заглушками для path и src, поскольку это облегчает оставшуюся часть выражения.

Мясо решения - это, конечно, действие 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, у нас будет несколько элементов, для которых item.1.path и item.1.src равны false , заставляя троичный фильтр выбирать второе значение (item.0.path или item.1.src). В других случаях мы строим список, комбинируя значения из каждого из base_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
        }
    ]
}

Дайте мне знать, если это поможет, и является ли полученная структура данных тем, что вы искали. Мне пришлось сделать несколько предположений, поскольку ваш пример end_list содержал неверный синтаксис, поэтому я решил, что вы хотите.

...