Saltar a contenido

Ansible - Trabajar con filtros

En este capítulo aprenderá a transformar datos mediante filtros de Jinja.


Objetivos : En este capítulo aprenderá a:

✔ Transformar estructuras de datos como diccionarios o listas;
✔ Transformar variables;

🏁 ansible, jinja, filtros

Conocimiento: ⭐ ⭐ ⭐
Complejidad: ⭐ ⭐ ⭐ ⭐

Tiempo de lectura: 20 minutos


Ya hemos tenido la oportunidad, durante los capítulos anteriores, de utilizar los filtros de Jinja.

Estos filtros, escritos en python, nos permiten manipular y transformar nuestras variables de ansible.

Note

Puede encontrar más información aquí.

A lo largo de este capítulo, utilizaremos el siguiente playbook para probar los diferentes filtros presentados:

- name: Manipulating the data
  hosts: localhost
  gather_facts: false
  vars:
    zero: 0
    zero_string: "0"
    non_zero: 4
    true_booleen: True
    true_non_booleen: "True"
    false_boolean: False
    false_non_boolean: "False"
    whatever: "It's false!"
    user_name: antoine
    my_dictionary:
      key1: value1
      key2: value2
    my_simple_list:
      - value_list_1
      - value_list_2
      - value_list_3
    my_simple_list_2:
      - value_list_3
      - value_list_4
      - value_list_5
    my_list:
      - element: element1
        value: value1
      - element: element2
        value: value2

  tasks:
    - name: Print an integer
      debug:
        var: zero

Note

La siguiente es una lista no exhaustiva de los filtros que probablemente se encontrará o necesitará mientras trabaje con Ansible. Afortunadamente, hay muchos otros. ¡Incluso podría escribir el suyo propio!

El playbook se ejecutará de la siguiente manera:

ansible-playbook play-filter.yml

Convertir datos

Los datos se pueden convertir de un tipo a otro.

Para conocer el tipo de un dato (el tipo en lenguaje python), hay que utilizar el filtro type_debug.

Ejemplo:

- name: Display the type of a variable
  debug:
    var: true_boolean|type_debug

esto produce:

TASK [Display the type of a variable] ******************************************************************
ok: [localhost] => {
    "true_boolean|type_debug": "bool"
}

Es posible transformar un entero en una cadena:

- name: Transforming a variable type
  debug:
    var: zero|string
TASK [Transforming a variable type] ***************************************************************
ok: [localhost] => {
    "zero|string": "0"
}

Convertir una cadena en un entero:

- name: Transforming a variable type
  debug:
    var: zero_string|int

o una variable en un booleano:

- name: Display an integer as a boolean
  debug:
    var: non_zero | bool

- name: Display a string as a boolean
  debug:
    var: true_non_boolean | bool

- name: Display a string as a boolean
  debug:
    var: false_non_boolean | bool

- name: Display a string as a boolean
  debug:
    var: whatever | bool

Una cadena de caracteres se puede transformar a mayúsculas o minúsculas:

- name: Lowercase a string of characters
  debug:
    var: whatever | lower

- name: Upercase a string of characters
  debug:
    var: whatever | upper

esto produce:

TASK [Lowercase a string of characters] *****************************************************
ok: [localhost] => {
    "whatever | lower": "it's false!"
}

TASK [Upercase a string of characters] *****************************************************
ok: [localhost] => {
    "whatever | upper": "IT'S FALSE!"
}

El filtro reemplazar le permite sustituir unos caracteres por otros.

En este ejemplo, eliminamos espacios o incluso sustituimos una palabra:

- name: Replace a character in a string
  debug:
    var: whatever | replace(" ", "")

- name: Replace a word in a string
  debug:
    var: whatever | replace("false", "true")

esto produce:

TASK [Replace a character in a string] *****************************************************
ok: [localhost] => {
    "whatever | replace(\" \", \"\")": "It'sfalse!"
}

TASK [Replace a word in a string] *****************************************************
ok: [localhost] => {
    "whatever | replace(\"false\", \"true\")": "It's true !"
}

El filtro split le permite dividir una cadena en una lista basada en un carácter:

- name: Cutting a string of characters
  debug:
    var: whatever | split(" ", "")
TASK [Cutting a string of characters] *****************************************************
ok: [localhost] => {
    "whatever | split(\" \")": [
        "It's",
        "false!"
    ]
}

Unir los elementos de una lista

Es frecuente tener que unir los diferentes elementos de una lista en una única cadena. A continuación, podemos especificar un carácter o una cadena para insertar entre cada elemento.

- name: Joining elements of a list
  debug:
    var: my_simple_list|join(",")

- name: Joining elements of a list
  debug:
    var: my_simple_list|join(" | ")

esto produce:

TASK [Joining elements of a list] *****************************************************************
ok: [localhost] => {
    "my_simple_list|join(\",\")": "value_list_1,value_list_2,value_list_3"
}

TASK [Joining elements of a list] *****************************************************************
ok: [localhost] => {
    "my_simple_list|join(\" | \")": "value_list_1 | value_list_2 | value_list_3"
}

Transformar diccionarios en listas (y viceversa)

Los filtros dict2items y itemstodict, son filtros más complejos de implementar y se utilizan frecuentemente, especialmente en bucles.

Observe que es posible especificar el nombre de la clave y del valor a utilizar en la transformación.

- name: Display a dictionary
  debug:
    var: my_dictionary

- name: Transforming a dictionary into a list
  debug:
    var: my_dictionary | dict2items

- name: Transforming a dictionary into a list
  debug:
    var: my_dictionary | dict2items(key_name='key', value_name='value')

- name: Transforming a list into a dictionary
  debug:
    var: my_list | items2dict(key_name='element', value_name='value')
TASK [Display a dictionary] *************************************************************************
ok: [localhost] => {
    "my_dictionary": {
        "key1": "value1",
        "key2": "value2"
    }
}

TASK [Transforming a dictionary into a list] *************************************************************
ok: [localhost] => {
    "my_dictionary | dict2items": [
        {
            "key": "key1",
            "value": "value1"
        },
        {
            "key": "key2",
            "value": "value2"
        }
    ]
}

TASK [Transforming a dictionary into a list] *************************************************************
ok: [localhost] => {
    "my_dictionary | dict2items (key_name = 'key', value_name = 'value')": [
        {
            "key": "key1",
            "value": "value1"
        },
        {
            "key": "key2",
            "value": "value2"
        }
    ]
}

TASK [Transforming a list into a dictionary] ************************************************************
ok: [localhost] => {
    "my_list | items2dict(key_name='element', value_name='value')": {
        "element1": "value1",
        "element2": "value2"
    }
}

Trabajar con listas

Es posible fusionar o filtrar datos de una o varias listas:

- name: Merger of two lists
  debug:
    var: my_simple_list | union(my_simple_list_2)
ok: [localhost] => {
    "my_simple_list | union(my_simple_list_2)": [
        "value_list_1",
        "value_list_2",
        "value_list_3",
        "value_list_4",
        "value_list_5"
    ]
}

Para mantener únicamente la intersección de las 2 listas (los valores presentes en las 2 listas):

- name: Merger of two lists
  debug:
    var: my_simple_list | intersect(my_simple_list_2)
TASK [Merger of two lists] *******************************************************************************
ok: [localhost] => {
    "my_simple_list | intersect(my_simple_list_2)": [
        "value_list_3"
    ]
}

O, por el contrario, mantener sólo la diferencia (los valores que no existen en la segunda lista):

- name: Merger of two lists
  debug:
    var: my_simple_list | difference(my_simple_list_2)
TASK [Merger of two lists] *******************************************************************************
ok: [localhost] => {
    "my_simple_list | difference(my_simple_list_2)": [
        "value_list_1",
        "value_list_2",
    ]
}

Si su lista contiene valores que no son únicos, también es posible filtrarlos mediante el filtro unique.

- name: Unique value in a list
  debug:
    var: my_simple_list | unique

Transformación json/yaml

Es posible que tenga que importar datos en formato json (desde una API, por ejemplo) o exportar datos en formato yaml o json.

- name: Display a variable in yaml
  debug:
    var: my_list | to_nice_yaml(indent=4)

- name: Display a variable in json
  debug:
    var: my_list | to_nice_json(indent=4)
TASK [Display a variable in yaml] ********************************************************************
ok: [localhost] => {
    "my_list | to_nice_yaml(indent=4)": "-   element: element1\n    value: value1\n-   element: element2\n    value: value2\n"
}

TASK [Display a variable in json] ********************************************************************
ok: [localhost] => {
    "my_list | to_nice_json(indent=4)": "[\n    {\n        \"element\": \"element1\",\n        \"value\": \"value1\"\n    },\n    {\n        \"element\": \"element2\",\n        \"value\": \"value2\"\n    }\n]"
}

Valores por defecto, variables opcionales, variables protegidas

Se encontrará rápidamente con errores en la ejecución de sus playbooks si no proporciona valores por defecto para sus variables, o si no las proteges.

Si no existe el valor de una variable, se puede ser sustituir por otro valor mediante el filtro default:

- name: Default value
  debug:
    var: variablethatdoesnotexists | default(whatever)
TASK [Default value] ********************************************************************************
ok: [localhost] => {
    "variablethatdoesnotexists | default(whatever)": "It's false!"
}

Observe la presencia del apóstrofe ' que debería estar protegido, por ejemplo, si utiliza el modulo shell:

- name: Default value
  debug:
    var: variablethatdoesnotexists | default(whatever| quote)
TASK [Default value] ********************************************************************************
ok: [localhost] => {
    "variablethatdoesnotexists | default(whatever|quote)": "'It'\"'\"'s false!'"
}

Finalmente, si no existe una variable opcional en un módulo se puede ignorar utilizando con la palabra clave omit en el filtro default, lo que le ahorrará un error en tiempo de ejecución.

- name: Add a new user
  ansible.builtin.user:
    name: "{{ user_name }}"
    comment: "{{ user_comment | default(omit) }}"

Asociar un valor en función de otro (ternary)

A veces es necesario utilizar una condición para asignar un valor a una variable, en cuyo caso es común pasar por un paso set_fact.

Esto se puede evitar utilizando el filtro ternary:

- name: Default value
  debug:
    var: (user_name == 'antoine') | ternary('admin', 'normal_user')
TASK [Default value] ********************************************************************************
ok: [localhost] => {
    "(user_name == 'antoine') | ternary('admin', 'normal_user')": "admin"
}

Otros filtros

  • {{ 10000 | random }}: Como su nombre indica, da un valor aleatorio.
  • {{ my_simple_list | first }}: Extrae el primer elemento de la lista.
  • {{ my_simple_list | length }}: Da la longitud (de una lista o una cadena).
  • {{ ip_list | ansible.netcommon.ipv4 }}: Sólo muestra las IPs v4. Sin insistir en esto, si lo necesita, hay muchos filtros dedicados a la red.
  • {{ user_password | password_hash('sha512') }}: Genera una contraseña con hash en sha512.

Author: Antoine Le Morvan

Contributors: Steven Spencer