Я ищу способ изменить файл конфигурации с сохранением локальных изменений. Формат файла конфигурации похож на этот:
entry1: This is a local
entry2: modification.
entry3:
Файл состоит из переменного количества ключей (запись1, запись2, запись3 и, возможно, более поздняя запись4 и т. Д. - может быть до 100, а в будущем, возможно, даже до 2000 или около того), которые должны быть настроены Ansible. , за которыми следуют дополнительные параметры, которые другая программа добавит в файл конфигурации.
Я хочу, чтобы Ansible мог добавлять новые ключи в файл, сохраняя при этом любые существующие локальные модификации.
Самым естественным вариантом кажется lineinfile, но, к сожалению, он дает мне только возможность либо сохранение локальных изменений (используя backref = yes), или добавление новых ключей (backref = yes не добавит новых строк).
Есть ли хороший способ выполнить то, что мне нужно?
Я могу изменить существующие записи с включенными lineinfile и backref:
- lineinfile:
path: myfile.conf
regexp: "^{{ item }}: (.*)"
backref: yes
line: "{{ item }}: \1"
with_items:
- entry1
- entry2
- entry3
- entry4
Но это не добавит entry4 в мой файл.
Или я могу использовать backref: no
- lineinfile:
path: myfile.conf
regexp: "^{{ item }}: (.*)"
backref: no
line: "{{ item }}:"
with_items:
- entry1
- entry2
- entry3
- entry4
Но это приведет к сбою локальных изменений для entry1, entry2 и entry3.
Или я мог бы изменить регулярное выражение:
- lineinfile:
path: myfile.conf
regexp: "^"
backref: no
line: "{{ item }}:"
with_items:
- entry1
- entry2
- entry3
- entry4
Но это, конечно, добавит каждый ключ при каждом запуске.
Я также рассмотрел возможность использования шаблона (но не нашел простого способа использовать его для управления существующим файлом).
Конечно, я могу написать свой собственный модуль Python, но должен ли быть более простой способ сделать это?
Если вы изменяете хорошо известные файлы конфигурации, вы можете использовать Augeas с этим доступным плагином augeas: https://github.com/paluh/ansible-augeas
Это один из примеров использования augeas:
- name: Force password on sudo group
action: augeas path=/files/etc/sudoers/spec[user=\"sudo\"]/host_group/command/tag value=PASSWD
Другой вариант - использовать команду lineinfile, чтобы убедиться, что нужная строка доступна в файле: http://docs.ansible.com/ansible/latest/lineinfile_module.html
пример:
- lineinfile:
path: /etc/selinux/config
regexp: '^SELINUX='
line: 'SELINUX=enforcing'
Я нашел способ как это сделать. Хитрость заключается в том, чтобы сначала использовать grep, чтобы найти только недостающие записи.
grep выдаст ошибку, если файл не существует, поэтому сначала я создаю файл:
copy:
dest: myfile.conf
content: ""
force: no
# set mode, owner, group to taste
Теперь используйте grep, чтобы найти только недостающие элементы. Grep вернет 0, если запись уже существует, или 1, если ее нет. Обычно код возврата 1 означает отказ Ansible. failed_when: True меняет это. Поскольку это только получение информации и ничего не меняет, он никогда не должен сообщать как "измененный", поэтому также необходимо установить changed_when.
command: 'grep "^{{ item }}" myfile.conf'
with_items:
- entry1
- entry2
- entry3
- entry4
failed_when: False
changed_when: False
register: grep_output
Grep произведет вывод в зарегистрированную переменную grep_output. При использовании в цикле grep_output будет содержать массив с именем results, который содержит один хэш для каждого элемента из цикла. В этом массиве мы находим всю необходимую информацию: код возврата (называемый rc) и исходный элемент из цикла (называемый элементом).
Теперь мы можем добавить только недостающие записи, проверив rc. Я не уверен, требуется ли здесь регулярное выражение.
lineinfile:
path: myfile.conf
regexp: "^{{ item.item }} *(.*)"
insertafter: EOF
line: '{{ item.item }}'
state: present
when: "item.rc > 0"
with_items: "{{ grep_output.results }}"
lineinfile: path: myfile.conf regexp: "^{{ item }}: (.*)" backref: no line: "{{ item }}:" with_items: - entry1 - entry2 - entry3 - entry4