Перейти до змісту

Ansible - Робота з фільтрами

У цьому розділі ви дізнаєтеся, як перетворювати дані за допомогою фільтрів jinja.


Цілі: В цьому розділі ви дізнаєтеся про:

✔ Перетворення структур даних як словників або списків;
✔ Перетворення змінних.

🏁 ansible, jinja, filters

Знання: ⭐ ⭐ ⭐ Складність: ⭐ ⭐ ⭐ ⭐

Час читання: 20 хвилин


У попередніх розділах ми вже мали можливість використовувати фільтри jinja.

Ці фільтри, написані на Python, дозволяють нам маніпулювати та трансформувати наші змінні ansible.

Примітка

Більше інформації можна знайти тут.

У цьому розділі ми будемо використовувати наступний playbook, щоб перевірити різні представлені фільтри:

- 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

Примітка

Нижче наведено неповний список фільтрів, з якими ви, швидше за все, зіткнетеся. На щастя, є багато інших. Ви навіть можете написати свій!

Playbook відбуватиметься таким чином:

ansible-playbook play-filter.yml

Перетворення даних

Дані можна конвертувати з одного типу в інший.

Щоб дізнатися тип даних (тип на мові python), вам потрібно скористатися фільтром type_debug.

Приклад:

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

що дає нам:

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

Ціле число можна перетворити на рядок:

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

Перетворення рядка в ціле число:

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

або змінну в логічне значення:

- 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

Рядок символів можна перетворити на верхній або нижній регістр:

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

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

що дає нам:

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!"
}

Фільтр replace дозволяє замінювати символи іншими.

Тут ми видаляємо пробіли або навіть замінюємо слово:

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

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

що дає нам:

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 !"
}

Фільтр split розбиває рядок на список на основі символу:

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

Об’єднання елементів списку

Часто доводиться об’єднувати різні елементи в один рядок. Потім ми можемо вказати символ або рядок для вставки між кожним елементом.

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

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

що дає нам:

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"
}

Перетворення словників у списки (і навпаки)

Фільтри dict2items і itemstodict, дещо складніші для реалізації, часто використовуються, особливо в циклах.

Зауважте, що можна вказати назву ключа та значення для використання в перетворенні.

- 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"
    }
}

Робота зі списками

Можна об’єднати або відфільтрувати дані з одного чи кількох списків:

- 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"
    ]
}

Щоб зберегти лише перетин 2 списків (значення, присутні у 2 списках):

- 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"
    ]
}

Або навпаки залишити лише різницю (значення, яких немає у другому списку):

- 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",
    ]
}

Якщо ваш список містить неунікальні значення, їх також можна відфільтрувати за допомогою фільтра unique.

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

Перетворення json/yaml

Можливо, вам доведеться імпортувати дані json (наприклад, з API) або експортувати дані в yaml або 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]"
}

Значення за замовчуванням, додаткові змінні, захистити змінні

Ви швидко зіткнетеся з помилками під час виконання ваших ігор, якщо ви не надасте значення за замовчуванням для ваших змінних або якщо ви не захистите їх.

Значення змінної можна замінити на інше, якщо воно не існує за допомогою фільтра default:

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

Зверніть увагу на наявність апострофа ', який слід захистити, наприклад, якщо ви використовуєте модуль shell:

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

Нарешті, необов’язкову змінну в модулі можна проігнорувати, якщо вона не існує з ключовим словом omit у фільтрі default, що позбавить вас від помилки під час виконання.

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

Прив'язати значення відповідно до іншого (ternary)

Іноді вам потрібно використовувати умову, щоб призначити значення змінній, і в цьому випадку зазвичай потрібно виконати крок set_fact.

Цього можна уникнути за допомогою фільтра 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"
}

Деякі інші фільтри

  • {{ 10000 | random }}: як вказує його назва, дає випадкове значення.
  • {{ my_simple_list | first }}: витягує перший елемент списку.
  • {{ my_simple_list | length }}: дає довжину (списку або рядка).
  • {{ ip_list | ansible.netcommon.ipv4 }}: відображає лише v4 IP. Не зупиняючись на цьому, якщо вам потрібно, є багато фільтрів, присвячених мережі.
  • {{ user_password | password_hash('sha512') }}: генерує хешований пароль у sha512.

Author: Antoine Le Morvan

Contributors: Steven Spencer, Ganna Zhyrnova