Отладка Ansible

Отладка Ansible


Недавно я занялся разработкой фичи для одного ansible-модуля. По дороге, разумеется, наделал багов и пришлось разбираться, как их находить и чинить. Тут было всё подряд: от проблем с вложенностью данных (очевидно - распечатай их и подвигай уровни вверх или вниз) до ошибок, связанных с попытками удалить что-то из объекта параметров ansible / его структуры данных. Сами баги были довольно простыми в исправлении, но вот с дебаггером всё оказалось не так просто.

Ansible - это Python, а значит, по сути, мы говорим про pdb. Но как вытащить его из ansible? Лучшая команда ansible из CLI, которую я нашёл для тестирования, выглядит так:

$ ANSIBLE_KEEP_REMOTE_FILES=1 ANSIBLE_DEBUG=True ansible-playbook -vvvv test.yml

В основном это просто даёт безумный объём вывода. Плюс здесь есть опция, которая оставляет артефактный файл, который ansible обычно создаёт и потом убирает - но об этом чуть позже. В целом, это действительно тонны логов… а ведь вроде именно этого мы и хотим, да?

Ну, как бы да - но на самом деле нет. Чего нам действительно хочется, так это возможности провалиться в интерактивный дебаггер или хотя бы логировать переменные в определённых местах. Теоретически для этого можно использовать модуль логирования ansible, но это уже дополнительная логика, которую мне не хотелось тащить ради временного решения (я вообще не хочу оставлять много отладочного кода после того, как закончу). Так что же делать?

А вот тут документация (кстати, довольно хорошая) подсказывает вариант: положить ANSIBLE_ARGS в JSON-файл и запускать модуль вот так:

$ python ~/ansible_collections/community/test/plugins/modules/iptables.py \
  <<< '{"ANSIBLE_MODULE_ARGS": {"do":"save","chain":"INPUT","jump":"RETURN"}}'

Это работает ровно до того момента, пока вам не нужно попасть в дебаггер. А потом - тишина, ничего не происходит. Я предполагаю, что редирект файлового дескриптора для входных данных перехватывает что-то, чем пользуется pdb. Но мы и так уже пытаемся отдебажить одну проблему, так что лезть ещё и во вторую - сомнительная идея. Давайте упростим и сделаем так, как советует полезная документация:

https://docs.ansible.com/ansible/latest/dev_guide/developing_modules_general.html

А именно - положим аргументы в файл. После этого мы можем запустить модуль, например, так:

$ python -pdb ansible_module.py test_parameters.json

Это не слишком полезно, если вам не хочется просто пошагово пройтись по каждому фрейму или вроде того. Гораздо более удобный для меня вариант - вставить вот эту строку в то место, где вы хотите провалиться в дебаггер:

import pdb; pdb.set_trace()

Но вот вы закончили первичную отладку на тестовом коде, создали play, запустили его - и модуль снова падает. Вы смотрите на весь этот объём данных и думаете: «И как теперь передать всё это в ansible, чтобы снова попасть в дебаггер?» Ведь по-прежнему нельзя просто запустить ansible и ожидать, что он отправит вас в интерактивную отладку.

Моей первой мыслью было использовать yq и jq, чтобы собрать нужную структуру (не делайте так - теоретически это должно работать, но серьёзно, не надо):

$ yq -o=json test.yml \
  | jq --argfile file t.json '{ANSIBLE_MODULE_ARGS: .[0].tasks[0]."community.test.iptables"} \
  | .ANSIBLE_MODULE_ARGS.picker_definitions = $file'

Это требует знания структуры ролей и плейбуков, знания самого модуля и к тому же работает только для одного play за раз. Гораздо лучший способ - найти последний ansible-овский «remote»-файл, который вы сохранили, вытащить оттуда данные и просто положить их в файл:

$ grep ANSIBLE_MODULE_ARGS /home/azureuser/.ansible/tmp/ansible-tmp-1659789728.2394295-6951-276310893811636/AnsiballZ_iptables.py \
  | cut -d'=' -f2- \
  | sed -r "s/^ '//" \
  | sed -r "s/' *$//" \
  | jq > t3.json

На самом деле jq здесь не обязателен - я просто использую его как sanity check, прежде чем создавать файл, который собираюсь применять для отладки чего-то ещё. А дальше можно запустить модуль обычной python-командой и получить ровно тот же прогон, как если бы он был собран через роль:

$ python ~/ansible_collections/community/test/plugins/modules/iptables.py t2.json


Report Page