Вопрос или проблема
У меня есть рабочая задача по миграции (копированию) определенной части моего домашнего каталога (вторая с конца строка является важной):
- name: "Миграция файлов домашнего каталога модуля my-pc"
copy:
src: "{{ migration_source_path }}{{ item }}"
dest: "{{ migration_destination_path }}{{ item }}"
loop: "{{ migration_paths_my_pc }}"
when:
- not is_migrating_all
- "'{{ migration_source_path }}{{ item }}' существует"
- m_my_pc | bool
Обе переменные ..._path
всегда заканчиваются на косую черту (/
).
Объяснение: Это берет список путей к файлам из migration_paths_my_pc
и копирует их из migration_source_path
в migration_destination_path
). Это случится только если: флаг для этого модуля установлен (m_my_pc
); и путь существует в исходном каталоге; и я не мигрирую все файлы из домашнего каталога в любом случае (is_migrating_all
).
Мое текущее рабочее решение (выше) дает мне предупреждение:
[WARNING]: условные операторы не должны включать в себя разделители шаблона jinja2, такие как {{ }} или {% %}. Обнаружено: '{{ migration_source_path }}{{ item }}' существует
Я понимаю, что мне нужно убрать фигурные скобки {{
из секции when:
, так как условные выражения имеют встроенные фигурные скобки вокруг них. Проблема в том, что я не могу понять, как заставить это работать. Несколько моих неудачных попыток:
- vars["" + migrate_source_path + item] существует
- vars[migrate_source_path + item] существует
- vars[migrate_source_path ~ item] существует
- vars[migrate_source_path]vars[item] существует
- '' + migrate_source_path + item существует
- lookup('/home/k/test/test2/' + item) существует
- "{{ lookup('vars', 'migrate_source_path' + 'item') существует }}"
- "{{ lookup('vars', 'migrate_source_path') + lookup('vars', 'item') существует }}"
- lookup('vars', 'migrate_source_path')lookup('vars', 'item') существует
Предыстория:
Я автоматизирую настройки персональных ПК как по практическим причинам, так и чтобы лучше изучить Ansible и Ansible-playbooks.
Любые другие рекомендации или советы также приветствуются (в комментариях, думаю).
ОБНОВЛЕНИЕ:
Мои цель (функциональные стремления) для миграции заключаются в следующем:
- Я хочу, чтобы стоимость поддержки была низкой, но хотел бы, чтобы при необходимости была доступна информация о том, какие пути были скопированы и какие были пропущены. Поэтому я подумал, что скрипт должен копировать пути, которые существуют в источнике, не останавливаясь и не выдавая ошибку в случае отсутствия пути в источнике. Предупреждения или другие простые сообщения были бы отличны. Мой текущий скрипт делает это приемлемо.
- Было бы неплохо иметь возможность легко переключать, какие пути будут копироваться, а какие нет (например, что-то может измениться в отношении миграции). Делать это в
project/vars/
показалось интуитивным и, следовательно, возможно, лучшим дизайном, чем переключение их внутри задачи. Другие переменные, которые более вероятно будут отключены или изменены, находятся вproject/vars/
. Комментировать что-то, то есть раскомментировать, это одна горячая клавиша обычно в продвинутых текстовых редакторах и IDE. Поэтому это довольно удобно.
Ваши попытки излишне усложнены. Разделители Jinja не должны использоваться в условиях, так как вы уже находитесь в чем-то, что интерпретируется как выражение Jinja, поэтому их просто нужно заменить чем-то, что дает желаемый результат.
В этом случае вы создаете строку, которая является конкатенацией двух переменных, поэтому вы должны заменить это на конкатенацию:
- (migration_source_path ~ item) существует
Вы использовали как migrate_source_path
, так и migration_source_path
в своих примерах, поэтому я произвольно выбрал migration_source_path
как правильный.
Вы были довольно близки с:
- '' + migration_source_path + item существует
но у вас есть ненужная ведущая пустая строка, которую YAML-парсер не приемлет, и как только вы исправите это, правильно закомментировав или удалив ненужную строку:
- "'' + migration_source_path + item существует"
- migration_source_path + item существует
это попытается добавить migration_source_path
и item существует
, так как вы не использовали скобки для корректировки порядка.
Попробуйте переписать это немного по-другому. Вместо того, чтобы проходить по массиву с помощью loop
, укажите директории, которые вы хотите копировать, с with
или with_fileglob
. Если вам действительно нужно иметь список файлов, которые вы хотите копировать из одного местоположения в другое, вы можете записать список в секцию with
. Модуль проверит, существует ли исходный файл, и отобразит ошибку, что в этом случае хорошо, так как иначе ваша миграция будет некорректной.
Помните, что мы используем when
, чтобы сказать Ansible, когда запускать или не запускать какую-то задачу, а не чтобы управлять задачей или её поведением. Для этого мы используем обработчики, регистры и другие ресурсы.
С уважением.
Ответ или решение
Для решения задачи о написании playbook для Ansible, в котором необходимо выполнить условную конкатенацию переменных без использования фигурных скобок Jinja, следует понимать, как Ansible трактует выражения «when» и как правильно использовать синтаксис Jinja внутри них.
Теория (Theory)
Ansible использует язык шаблонов Jinja2, который позволяет динамически формировать данные и логику внутри playbook’ов. Однако, когда дело касается условий, Ansible не требует явного использования фигурных скобок {{ }}
, так как всё, что находится внутри блока when
, уже является выражением Jinja2. Таким образом, фигурные скобки становятся избыточными и могут привести к ошибкам или предупреждениям, как в вашем случае.
Важно также учитывать, что Ansible поддерживает оператор конкатенации строк ~’, который следует использовать для объединения переменных в один путь. Это особенно важно, когда ваша цель — создание динамических строк на основе нескольких переменных, как это происходит с переменными путей
migration_source_pathи
item`.
Пример (Example)
Рассмотрим ваш исходный код и внесём необходимые исправления, чтобы избежать ненужных предупреждений. Мы заменим синтаксис использования фигурных скобок на корректный оператор конкатенации, как указано ниже:
- name: "Migrate my-pc module home files"
copy:
src: "{{ migration_source_path }}{{ item }}"
dest: "{{ migration_destination_path }}{{ item }}"
loop: "{{ migration_paths_my_pc }}"
when:
- not is_migrating_all
- (migration_source_path ~ item) is exists
- m_my_pc | bool
В данной реализации мы применяем оператор ~
для объединения переменных migration_source_path
и item
в одно строковое выражение. Как только это выражение сформировано, оно используется в утверждении is exists
, чтобы проверить наличие файла или каталога.
Применение (Application)
Используя этот подход, мы устраняем предупреждение о неправильном использовании фигурных скобок в условиях и добиваемся более чистой и компактной записи условий. Это позволяет:
-
Снизить количество ошибок в playbook’ах. Исключив явное использование
{{ }}
из условий, мы устраняем частую причину синтаксических ошибок. -
Повысить читаемость и поддерживаемость кода. Простой и ясный синтаксис делает playbook более прозрачным для других разработчиков или администраторов, поддерживающих систему.
-
Обеспечить гибкость и адаптивность. Вы можете легко адаптировать playbook для различных сценариев миграции, просто изменяя значения в соответствующих переменных, не прибегая к значительным изменениям в логике самого playbook.
-
Сохранить текущую логику выполнения задач без прерывания. Если вы хотите сохранить текущие настройки и предупреждения при отсутствии файлов, вы всё равно сможете получать уведомления об этом с помощью встроенных механизмов Ansible, таких как регистры и уведомления (handlers).
Заключение
Использование правильного синтаксиса и понимание, как работает Jinja в Ansible, позволяет создавать более надёжные и поддерживаемые скрипты для автоматизации. Устранение излишних элементов кода способствует не только улучшению производительности, но и облегчает работу с системой, делая её более предсказуемой и последовательной. Ответ на ваш вопрос иллюстрирует необходимость внимательного отношения к деталям при разработке автоматизированных решений на базе Ansible.