Ansible — объединить два списка словарей

#ansible

#ansible

Вопрос:

Я пытаюсь понять, как объединить два dicts разных размеров. Цель состоит в том, чтобы добавить дополнительных получателей в список получателей в третьем словаре.

 mail_aliases_list:
  - name: mailer-daemon
    recipients:
      - foo
  - name: postmaster
    recipients:
      - bar
  - name: bin
    recipients:
      - baz
  ...
  

и

 mail_aliases_defaults:
  - name: mailer-daemon
    recipients:
      - postmaster
  - name: postmaster
    recipients:
      - root
  - name: bin
    recipients:
      - root
  ...
  

В результате

 mail_aliases:
  - name: mailer-daemon
    recipients:
      - postmaster
      - foo
  - name: postmaster
    recipients:
      - root
      - bar
  - name: bin
    recipients:
      - root
      - baz
  ...
  

Это немного выходит за рамки моего понимания фильтров, и я разочарован тем, что мне даже приходится просить о помощи, но, надеюсь, я чему-нибудь научусь в процессе.

Ответ №1:

Нет простого способа сделать именно то, что вы хотите, а то, как структурированы ваши данные, немного усложняет задачу. Если вы включите mail_aliases_list и mail_aliases_defaults в словари, станет проще достичь желаемого конечного состояния.

Что-то вроде:

 - hosts: localhost
  gather_facts: false
  become: true
  vars:
    mail_aliases_overrides:
      mailer-daemon:
        recipients:
          - foo
      postmaster:
        recipients:
          - bar
      bin:
        recipients:
          - baz
      not_in_defaults:
        recipients:
          - bob
    mail_aliases_defaults:
      mailer-daemon:
        recipients:
          - postmaster
      postmaster:
        recipients:
          - root
      bin:
        recipients:
          - root
      only_in_defaults:
        recipients:
          - alice

  tasks:
    - set_fact:
        mail_aliases: >-
          {{ mail_aliases|default({})|combine({item:
          (mail_aliases_overrides[item]|default({'recipients': []})).recipients  
          (mail_aliases_defaults[item]|default({'recipients': []})).recipients
          })
          }}
      loop: "{{ (mail_aliases_overrides.keys()|list   mail_aliases_defaults.keys()|list)|unique }}"

    - debug:
        var: mail_aliases
  

Это будет сгенерировано в качестве вывода:

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

TASK [set_fact] *******************************************************************************************************************************************************************************
ok: [localhost] => (item=mailer-daemon)
ok: [localhost] => (item=postmaster)
ok: [localhost] => (item=bin)
ok: [localhost] => (item=not_in_defaults)
ok: [localhost] => (item=only_in_defaults)

TASK [debug] **********************************************************************************************************************************************************************************
ok: [localhost] => {
    "mail_aliases": {
        "bin": [
            "baz",
            "root"
        ],
        "mailer-daemon": [
            "foo",
            "postmaster"
        ],
        "not_in_defaults": [
            "bob"
        ],
        "only_in_defaults": [
            "alice"
        ],
        "postmaster": [
            "bar",
            "root"
        ]
    }
}

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

Я убедился, что это работает с ключами, которые существуют только в одном из mail_aliases_defaults or mail_aliases_overrides .

Комментарии:

1. Спасибо, я собираюсь попробовать это и отчитаться. Это выглядит очень многообещающе и на самом деле может быть полезным шаблоном, который я могу использовать для некоторых других ролей. Я читал, что в ansible сложно объединить словари, поэтому я избегал их, но я думаю, что это может быть решением, которое я искал.

2. Это будет использоваться в шаблоне jinja2 для записи псевдонимов почты, поэтому оно должно быть итеративным. Я думал, что способ сделать это — просто установить факт с помощью mail_aliases_overrides.keys() mail_aliases_defaults.keys() , но это не сработает, поскольку является неподдерживаемым операндом для dict_keys . Я думаю объединить два словаря, а затем изменить шаблон, чтобы использовать ключи из полученного объединенного словаря.

3. Обновление: я обновил playbook, чтобы он также работал с python3. Попробуйте. Я также добавил |unique фильтр, который отсутствовал в первом проекте, чтобы вы не получали повторяющиеся результаты.

4. Это выглядит хорошо. Можете ли вы установить результирующий словарь как факт, а затем отладить переменную факта, вместо того, чтобы отлаживать ее напрямую, у меня возникли проблемы с пониманием того, как превратить это в переменную.

5. Смотрите мое обновление, которое теперь создает новую переменную из исходных данных.