Ansible, with_nested, как назначить динамические переменные в цикле

Вопрос или проблема

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

Думаю, я делаю что-то не так.

Как можно присвоить переменной значение конца последовательности, которое представляет количество текущего массива?

ansible-playbook 2.9.6

run.yml:

---
- hosts: localhost

  tasks:
  - name: Импорт конфигурации
    include_vars: 
      file: ./config.yml

  - name: ОТЛАДКА
    debug: msg="{{ item[0].team_name }}: {{ item[0].applications.name }}: индекс: {{ item[1] }}"
    with_nested:
      - "{{ teams }}"
      - "{{ lookup('sequence', 'start=1 end='+(item[0].applications|length))|string }}"

config.yml:

teams:
  - team_name: Название-Команды_A
    applications:
      - name: имя_приложения_a
      - name: имя_приложения_b
  - team_name: Название-Команды_B
    applications:
      - name: имя_приложения_c
      - name: имя_приложения_d

Выполнение:

ansible-playbook run.yml

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

TASK [Сбор фактов] **************************************************************************************************************************************************************
ok: [localhost]

TASK [Импорт конфигурации] ********************************************************************************************************************************************************
ok: [localhost]

TASK [ОТЛАДКА] ************************************************************************************************************************************************************************
fatal: [localhost]: FAILED! => {"msg": "'item' is undefined"}

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

Желаемый результат:

msg: 'Название-Команды_A: имя_приложения_a: индекс: 1' 
msg: 'Название-Команды_A: имя_приложения_b: индекс: 2' 
msg: 'Название-Команды_B: имя_приложения_c: индекс: 1' 
msg: 'Название-Команды_B: имя_приложения_d: индекс: 2'

Ответ: Например, задача ниже

- debug:
    msg: "{{ item.0.team_name }}: {{ item.1.name }}: {{ index1|int + 1 }}"
  with_subelements:
    - "{{ teams }}"
    - applications
  vars:
    team_names: "{{ teams|
                    map(attribute="team_name")|
                    list }}"
    index0: "{{ team_names.index(item.0.team_name) }}"
    applications: "{{ teams|
                      map(attribute="applications")|
                      list }}"
    application_names: "{{ applications[index0|int]|
                           map(attribute="name")|
                           list }}"
    index1: "{{ application_names.index(item.1.name) }}"

дает

  msg: 'Название-Команды_A: имя_приложения_a: 1'
  msg: 'Название-Команды_A: имя_приложения_b: 2'
  msg: 'Название-Команды_B: имя_приложения_c: 1'
  msg: 'Название-Команды_B: имя_приложения_d: 2'

Это решение ограничено уникальными списками.


Лучшей структурой для этого случая является словарь. Например, задача ниже дает те же результаты

- debug:
    msg: "{{ item.0.key }}: {{ item.1 }}: {{ teams[item.0.key].index(item.1) + 1 }}"
  with_subelements:
    - "{{ teams|dict2items }}"
    - value
  vars:
    teams:
      Название-Команды_A:
        - имя_приложения_a
        - имя_приложения_b
      Название-Команды_B:
        - имя_приложения_c
        - имя_приложения_d

Списки должны быть уникальными. Метод index найдет первое совпадение.

Ответ или решение

Для решения вашей задачи в Ansible с использованием динамических переменных внутри цикла with_nested, вы можете использовать другой подход, который позволит избежать проблем с неопределенными переменными. Ваша текущая структура с with_nested создает трудности при доступе к item внутри блока, поэтому рекомендуется использовать with_subelements.

Вот исправленный пример вашего файла run.yml, который выводит желаемый результат:

---
- hosts: localhost
  tasks:
    - name: Импорт конфигурации
      include_vars:
        file: ./config.yml

    - name: DEBUG
      debug:
        msg: "{{ item.0.team_name }}: {{ item.1.name }}: {{ index1 | int + 1 }}"
      with_subelements:
        - "{{ teams }}"
        - applications
      vars:
        team_names: "{{ teams | map(attribute='team_name') | list }}"
        index0: "{{ team_names.index(item.0.team_name) }}"
        applications: "{{ teams | map(attribute='applications') | list }}"
        application_names: "{{ applications[index0 | int] | map(attribute='name') | list }}"
        index1: "{{ application_names.index(item.1.name) }}"

Объяснение:

  1. with_subelements: Этот метод позволяет вам итерироваться по элементам из вложенного списка, что в вашем случае — это applications для каждой команды.
  2. Индексация: Мы используем переменные index0 и index1 для получения индексов команды и приложения соответственно. Это достигается с помощью метода index, который возвращает позицию первого найденного элемента в списке.
  3. Форматирование вывода: Сообщение, которое вы хотите вывести, формируется с помощью f-string, что упрощает отображение имен команды и приложения с их индексами.

Вывод:
Этот подход гарантирует, что вы получите следующий вывод, который вы ожидаете:

msg: 'Name-of-Team_A: app_name_a: 1'
msg: 'Name-of-Team_A: app_name_b: 2'
msg: 'Name-of-Team_B: app_name_c: 1'
msg: 'Name-of-Team_B: app_name_d: 2'

Альтернативный подход:

Если хотите улучшить структуру данных, рассмотрите возможность использования словаря вместо списка. Например, вы можете изменить вашу конфигурацию в config.yml на следующий формат:

teams:
  Name-of-Team_A:
    - app_name_a
    - app_name_b
  Name-of-Team_B:
    - app_name_c
    - app_name_d

Тогда ваш run.yml будет выглядеть так:

---
- hosts: localhost
  tasks:
    - name: Импорт конфигурации
      include_vars:
        file: ./config.yml

    - name: DEBUG
      debug:
        msg: "{{ item.0.key }}: {{ item.1 }}: {{ teams[item.0.key].index(item.1) + 1 }}"
      with_subelements:
        - "{{ teams | dict2items }}"
        - value

Этот подход более эффективен для манипуляций с уникальными элементами и упрощает понимание структуры данных.

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

Оцените материал
Добавить комментарий

Капча загружается...