Agregar a listas o agregar claves a diccionarios en Ansible

34

(Relacionado con Callbacks o ganchos, y series de tareas reutilizables, en roles Ansible ):

¿Hay alguna manera mejor de agregar a una lista o agregar una clave a un diccionario en Ansible que (ab) usando una expresión de plantilla jina2?

Sé que puedes hacer algo como:

- name: this is a hack
  shell: echo "{% originalvar.append('x') %}New value of originalvar is {{originalvar}}"

pero ¿realmente no hay ningún tipo de metatarea o ayuda para hacer esto?

Se siente frágil, parece no estar documentado y se basa en muchos supuestos sobre cómo funcionan las variables en Ansible.

Mi caso de uso son múltiples roles (extensiones de servidor de base de datos) que cada uno necesita para proporcionar alguna configuración a un rol base (el servidor de base de datos). No es tan simple como agregar una línea al archivo de configuración del servidor db; cada cambio se aplica a la misma línea , por ejemplo, las extensiones bdry pg_stat_statementsambas deben aparecer en una línea de destino:

shared_preload_libaries = 'bdr, pg_stat_statements'

¿Es la forma Ansible de hacer esto para procesar el archivo de configuración varias veces (una vez por extensión) con una expresión regular que extrae el valor actual, lo analiza y luego lo reescribe? Si es así, ¿cómo haces que ese idempotente en múltiples carreras?

¿Qué sucede si la configuración es más difícil de analizar y no es tan simple como agregar otro valor separado por comas? Piensa en archivos de configuración XML.

Craig Ringer
fuente
¿Sabes qué? Me gusta el corte de tu pluma de efectos secundarios ful
DomQ

Respuestas:

13

Puede fusionar dos listas en una variable con +. Digamos que tiene un group_varsarchivo con este contenido:

---
# group_vars/all
pgsql_extensions:
  - ext1
  - ext2
  - ext3

Y se usa en una plantilla pgsql.conf.j2como:

# {{ ansible_managed }}
pgsql_extensions={% for item in pgsql_extensions %}{{ item }}, {% endfor %}

Luego puede agregar extensiones a los servidores de base de datos de prueba de esta manera:

---
# group_vars/testing_db
append_exts:
  - ext4
  - ext5
pgsql_extensions: "{{ pgsql_extensions + append_exts }}"

Cuando el rol se ejecuta en cualquiera de los servidores de prueba, se agregarán las extensiones adicionales.

No estoy seguro de que esto también funcione para los diccionarios, y también tenga cuidado con los espacios y deje una coma colgante al final de la línea.

GnP
fuente
Puede, pero tiene que hacerlo todo group_vars, los roles no pueden ocuparse de los detalles de la configuración de las extensiones. Estoy agregando vars de roles que estoy buscando particularmente, por lo que un rol puede agregarse a una var expuesta por otro rol.
Craig Ringer
¿Su rol base sabe sobre cada rol de extensión? Tuve un caso similar en el que pude dejar la concatenación en una with_itemsoración.
GnP
no, y ese es realmente el problema. En una implementación, el rol base podría ser al
Craig Ringer
44
Parece que si intenta hacer esto para concatenar dos listas, cree que es una plantilla infinitamente recursiva porque el lado izquierdo también está en el lado derecho. ¿Estoy malinterpretando cómo usar esto?
Ibrahim
2
@spectras Al menos a partir de Ansible 2.7 esto NO funciona. Como sugirió Ibrahim, esto causa un error: "bucle recursivo detectado en la cadena de plantilla".
rluba
35

Desde Ansible v2.x puedes hacer esto:

# use case I: appending to LIST variable:

      - name: my appender
        set_fact:
          my_list_var: '{{my_list_myvar + new_items_list}}'

# use case II: appending to LIST variable one by one:

      - name: my appender
        set_fact:
          my_list_var: '{{my_list_var + [item]}}'
        with_items: '{{my_new_items|list}}'

# use case III: appending more keys DICT variable in a "batch":

      - name: my appender
        set_fact:
          my_dict_var: '{{my_dict_var|combine(my_new_keys_in_a_dict)}}'

# use case IV: appending keys DICT variable one by one from tuples
      - name: setup list of tuples (for 2.4.x and up
        set_fact:
          lot: >
            [('key1', 'value1',), ('key2', 'value2',), ..., ('keyN', 'valueN',)],
      - name: my appender
        set_fact:
          my_dict_var: '{{my_dict_var|combine({item[0]: item[1]})}}'
        with_items: '{{lot}}'
# use case V: appending keys DICT variable one by one from list of dicts (thanks to @ssc)

  - name: add new key / value pairs to dict
    set_fact:
      my_dict_var: "{{ my_dict_var | combine({item.key: item.value}) }}"
    with_items:
    - { key: 'key01', value: 'value 01' }
    - { key: 'key02', value: 'value 03' }
    - { key: 'key03', value: 'value 04' }

todo lo anterior está documentado en: http://docs.ansible.com/ansible/playbooks_filters.html

Max Kovgan
fuente
1
el caso de uso IV simplemente se agregau'(': u\"'\"}"
ssc
1
gracias, @ssc. Noté que no funciona con ansible 2.4.x(FIJO)
Max Kovgan
de acuerdo con USECASE # 4, he añadido el valor por defecto para manejar el error no definido en mi escenario: set_fact: my_dict_var: '{{my_dict_var|default({})|combine({item[0]: item[1]})}}'. El error indefinido se produce cuando se utiliza algún filtrado o no se registran resultados.
SK Venkat
Sr. SK Venkat, el código de muestra aquí solo demuestra algo muy específico (agregar elementos de diccionario de tuplas). Si necesita hacer algo más, este código no es su copiar y pegar.
Max Kovgan
3

necesitas dividir el ciclo en 2

--- 
- hosts: localhost
  Tareas: 
    - include_vars: pilas
    - set_facts: roles = {{stacks.Roles | dividir ('')}}
    - Incluye: addhost.yml
      with_items: "{{roles}}"

y addhost.yml

- set_facts: groupname = {{item}}
- set_facts: ips = {{stacks [item] | split ('')}}
- local_action: add_host hostname = {{item}} groupname = {{groupname}}
  with_items: {{ips}}
Arthur Tsang
fuente
1

No estoy seguro de cuándo agregaron esto, pero al menos para los diccionarios / hashes (NO listas / matrices), puede configurar la variable hash_behaviour , así: hash_behaviour = mergeen su ansible.cfg.

Me tomó bastantes horas tropezar accidentalmente con esta configuración: S

nueces
fuente
Esto es muy útil, pero tenga cuidado de habilitarlo e2e en la base de código existente. Puede romper algunos huevos.
Max Kovgan
0

Casi todas las respuestas aquí requieren cambios en las tareas, pero necesitaba fusionar dinámicamente los diccionarios en la definición de vars, no durante la ejecución.

Por ejemplo, quiero definir algunos vars compartidos all group_varsy luego quiero extenderlos en otro groupo host_vars. Muy útil cuando se trabaja para roles.

Si intenta usar los filtros combineo unionsobrescribiendo la variable original en los archivos var, terminará en un bucle infinito durante la creación de plantillas, así que creé esta solución (no es la solución).

Puede definir múltiples variables basadas en algún patrón de nombre y luego cargarlas automáticamente en función.

group_vars/all.yml

dictionary_of_bla:
  - name: blabla
    value1 : blabla
    value2 : blabla

group_vars/group1.yml

dictionary_of_bla_group1:
  - name: blabla2
    value1 : blabla2
    value2 : blabla2

fragmento de código de rol

tasks:
  - name: Run for all dictionary_of_bla.* variations
    include_tasks: do_some_stuff.yml
    with_items: "{{ lookup('varnames','dictionary_of_bla.*').split(',') }}"
    loop_control:
      loop_var: _dictionary_of_bla

do_some_stuff.yml

- name: do magic
  magic:
    trick_name: item.name
    trick_value1: item.value1
    trick_value2: item.value2
  with_items: "{{ vars[_dictionary_of_bla] }}"

Es solo un fragmento, pero debes hacerte una idea de cómo funciona. nota: la búsqueda ('varnames', '') está disponible desde ansible 2.8

Supongo que también sería posible fusionar todas las variables dictionary_of_bla.*en un diccionario durante el tiempo de ejecución usando la misma búsqueda.

La ventaja de este enfoque es que no necesita establecer listas exactas de nombres de variables, sino que solo el patrón y el usuario pueden configurarlo dinámicamente.

VeselaHouba
fuente
-4

Ansiblees un sistema de automatización y, con respecto a la gestión de archivos de configuración, no es muy diferente de apt. La razón por la que cada vez más software ofrece la función de leer fragmentos de configuración de un conf.ddirectorio es permitir que dichos sistemas de automatización tengan diferentes paquetes / roles para agregar configuración al software. Creo que no es la filosofía de Ansiblehacer lo que tienes en mente, sino utilizar el conf.dtruco. Si el software que se está configurando no ofrece esta funcionalidad, puede estar en problemas.

Como menciona los archivos de configuración XML, aprovecho la oportunidad para quejarme un poco. Hay una razón para la tradición de Unix de usar archivos de configuración de texto sin formato. Los archivos de configuración binarios no se prestan bien a la automatización del sistema, por lo que cualquier tipo de formato binario le dará problemas y probablemente requerirá que cree un programa para manejar la configuración. (Si alguien piensa que XML es un formato de texto sin formato, debe examinar sus cerebros).

Ahora, sobre tu PostgreSQLproblema específico . PostgreSQLapoya el conf.dtruco. Primero, comprobaría si shared_preload_librariesse puede especificar varias veces. No encontré ninguna pista en la documentación que pueda, pero igual lo intentaría. Si no se puede especificar varias veces, explicaría mi problema a los PostgreSQLchicos en caso de que tengan ideas; Esto es un PostgreSQLproblema y no un Ansibleproblema. Si no hay una solución y realmente no podría fusionar los diferentes roles en uno, implementaría un sistema para compilar la configuración en el host administrado. En este caso, probablemente crear un script /usr/local/sbin/update_postgresql_configque compile /etc/postgresql/postgresql.conf.jinjaen /etc/postgresql/9.x/main/postgresql.conf. El script leería las bibliotecas de precarga compartidas /etc/postgresql/shared_preload_libraries.txt, una biblioteca por línea, y se las proporcionaría a jinja.

No es raro que los sistemas de automatización hagan esto. Un ejemplo es el exim4paquete Debian .

Antonis Christofides
fuente
PostgreSQL admite un conf.dmecanismo de inclusión y, afortunadamente, utiliza archivos de texto sin formato. Sin embargo, hay algunas opciones de configuración en las que varias extensiones pueden tener opiniones al respecto, por ejemplo, "aumentar max_wal_senders en 10 de lo que era antes".
Craig Ringer
44
Parece que estás diciendo que la aplicación debería cambiarse para evitar las limitaciones en el sistema de gestión de la configuración, o debería renunciar a tener roles reutilizables.
Craig Ringer