Ansible: динамически назначать и перебирать список

#ansible #jinja2 #ansible-template #fortigate

Вопрос:

Я новичок в Ansible и пытаюсь автоматизировать настройку Fortigate с помощью модулей fortinet.fortios.

У меня возникла проблема с fortios_firewall_addrgrp тем, что конкретно не поддерживает добавление адреса брандмауэра в группу.

У меня есть этот набор в моих переменных:

 addresses:
  host_0:
    subnet: 192.168.1.10/24
    group: group_0
  host_1:
    subnet: 192.168.1.11/24
    group: group_0
  host_2:
    subnet: 192.168.10.10/24
    group: group_1
  host_3:
    subnet: 192.168.10.11/24
    group: group_1
 

И я выполняю эту задачу для создания группы адресов.

 - name: Configure IPv4 address groups.
  fortios_firewall_addrgrp:
    vdom: '{{ vdom }}'
    state: present
    firewall_addrgrp:
      name: '{{ item.value.group }}'
      member:
        - name: '{{ item.key }}'
  loop: '{{ addresses | dict2items }}'
 

Он проходит по каждому хосту и генерирует группу адресов, но в результате получается 2 группы, содержащие только последнее имя хоста в списке.

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

 member:
  - name: host_0
  - name: host_1
 

Приведенный выше пример будет работать, но я не могу заранее знать группы и имена хостов в переменной.

Я мог бы сгенерировать или отфильтровать входную переменную в словарь хоста и групп и передать ее участникам. Тем не менее, я не могу понять, как динамически перебирать его.

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

1. Я думаю, что, вероятно, было бы лучше открыть проблему .

Ответ №1:

Проблема, с которой вы столкнулись, связана со структурой данных, которую вы используете для цикла. Как вы упомянули, fortios_firewall_addrgrp модуль ожидает список словарей для members ключа, представляющих каждый хост.

Итак, вам нужно создать новую структуру данных, которая соответствует входным fortios_firewall_addrgrp данным модуля. Вот пример того, как это сделать:

 ---
- hosts: localhost
  gather_facts: 'no'
  vars:
    addresses:
      host_0:
        subnet: 192.168.1.10/24
        group: group_0
      host_1:
        subnet: 192.168.1.11/24
        group: group_0
      host_2:
        subnet: 192.168.10.10/24
        group: group_1
      host_3:
        subnet: 192.168.10.11/24
        group: group_1
  tasks:
    - set_fact:
        addresses_by_group: |
          {{
            addresses_by_group | default({}) | combine({
              item.value.group: (addresses_by_group[item.value.group] | default([]))   [{"name": item.key}]
            })
          }}
      loop: '{{ addresses | dict2items }}'
    - name: Configure IPv4 address groups.
      fortios_firewall_addrgrp:
        vdom: '{{ vdom }}'
        state: present
        firewall_addrgrp:
          name: '{{ item.key }}'
          member: '{{ item.value }}'
      loop: '{{ addresses_by_group | dict2items }}'
 

Мы создаем новую переменную с именем addresses_by_group , в которой будет храниться список хостов для каждой группы. combine Фильтр позволяет объединить два разных словаря, в то время default как фильтр устанавливает значение по умолчанию для неопределенной переменной. Мы используем оператор для объединения двух списков. Если вы отладите значение переменной addresses_by_group , вы увидите следующее:

 TASK [debug] *******************************************************************
ok: [localhost] => 
  addresses_by_group:
    group_0:
    - name: host_0
    - name: host_1
    group_1:
    - name: host_2
    - name: host_3
 

А это как раз то, что нам нужно. Имейте в виду, что мы не касались addresses переменных, чтобы вы могли использовать их позже.

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

1. Отличный ответ, спасибо. Я пытался преобразовать логику для генерации addresses_by_group из Python в Ansible, но как это сделать на самом деле, было выше моего понимания. Фильтры-это ответ!