ansible: lineinfile для нескольких строк?

162

Точно так же есть модуль lineinfileдля добавления одной строки в файл, есть ли способ добавить несколько строк?

Я не хочу использовать шаблон, потому что вы должны предоставить весь файл. Я просто хочу добавить что-то в существующий файл, не обязательно зная, что файл уже содержит, поэтому шаблон не вариант.

Майкл
источник
Я понимаю, что вы не хотите использовать template, но использование lineinfileявляется антипаттерном . Это также сильный красный флаг, который вы «не знаете, что в файле», что приводит к значительному риску неизвестных сбоев.
tedder42
39
Это не анти-паттерн. Суть lineinfile заключается в поддержке нескольких источников, управляющих одним и тем же файлом, что иногда неизбежно. Большинство файлов конфигурации имеют фиксированный формат и логика, чтобы избежать конфликтов, как правило, не слишком существенна.
Даг Ф
Я не знаю, что находится в подавляющем большинстве файлов на моем компьютере; не значит, что я хочу уничтожить их всех!
DylanYoung

Ответы:

222

Вы можете использовать цикл, чтобы сделать это. Вот пример использования with_itemsцикла:

- name: Set some kernel parameters
  lineinfile:
    dest: /etc/sysctl.conf
    regexp: "{{ item.regexp }}"
    line: "{{ item.line }}"
  with_items:
    - { regexp: '^kernel.shmall', line: 'kernel.shmall = 2097152' }
    - { regexp: '^kernel.shmmax', line: 'kernel.shmmax = 134217728' }
    - { regexp: '^fs.file-max', line: 'fs.file-max = 65536' }
Бен Уэйли
источник
УБЕДИТЕСЬ, что у вас есть аргумент для line = и regexp = в кавычках . Я не сделал, и я продолжал получать msg: this module requires key=value arguments. В приведенном примере это верно - я просто не последовал примеру.
JDS
1
Могу ли я спросить, как сделать одну резервную копию перед первым изменением? может быть item.backup? : D
tdihp
6
Вероятно, за это проголосовали до Ansible 2.0. Лучший ответ сейчас: stackoverflow.com/a/28306576/972128
kkurian
@kkurian Конечно, только если вы вставляете, а не если вы заменяете?
января
7
@kkurian Решение blockinfile не будет работать, если, например, вам нужно добавить несколько строк в файл json и не хотите использовать маркеры. Хотя вы можете установить маркеры на "", ansible blockinfile все равно будет искать маркеры, не найдет их и снова вставит блок. Таким образом, blockinfile без маркеров не является идемпотентом, lineinfile с циклом.
Абсурд
176

Вы можете попробовать использовать blockinfileвместо этого.

Вы можете сделать что-то вроде

- blockinfile: |
    dest=/etc/network/interfaces backup=yes
    content="iface eth0 inet static
        address 192.168.0.1
        netmask 255.255.255.0"
Соичи Хаяси
источник
8
blockinfileМодуль работает чудесно каждый раз , когда я решил использовать его. Мне особенно нравится интуитивное поведение параметров insertafter/ insertbefore.
Джей Тейлор
9
Ответ с наибольшим количеством голосов был, вероятно, до Ansible 2.0, но сейчас это более правильный ответ.
Виллем ван Кетвич
11
Blockinfile требует маркеров. Это иногда не вариант.
выступление
1
Можем ли мы перезаписать контент blockinfile?
Пкарамол
1
Это правильный способ сделать это, я думаю. docs.ansible.com/ansible/blockinfile_module.html
Пауло Виктор
20

Если вам нужно настроить набор уникальных строк свойство = значение, я рекомендую более краткий цикл. Например:

- name: Configure kernel parameters
  lineinfile:
    dest: /etc/sysctl.conf
    regexp: "^{{ item.property | regex_escape() }}="
    line: "{{ item.property }}={{ item.value }}"
  with_items:
    - { property: 'kernel.shmall', value: '2097152' }
    - { property: 'kernel.shmmax', value: '134217728' }
    - { property: 'fs.file-max', value: '65536' }

Используя диктовку, предложенную Аликс Акселем, и добавляя автоматическое удаление соответствующих закомментированных записей,

- name: Configure IPV4 Forwarding
  lineinfile:
    path: /etc/sysctl.conf
    regexp: "^#? *{{ item.key | regex_escape() }}="
    line: "{{ item.key }}={{ item.value }}"
  with_dict:
    'net.ipv4.ip_forward': 1
Николай Сушкин
источник
2
Если вы используете with_dict, это будет более кратким.
Аликс Аксель
18

Вот бесшумная версия решения, которое должно использовать with_items:

- name: add lines
  lineinfile: 
    dest: fruits.txt
    line: '{{ item }}'
  with_items:
    - 'Orange'
    - 'Apple'
    - 'Banana' 

Для каждого элемента, если элемент существует в fruits.txt, никаких действий не предпринимается.

Если элемент не существует, он будет добавлен в конец файла.

Очень просто.

Рик О'Ши
источник
Это не может быть объединено с inserttafter.
выступление
Если пропущено несколько строк, я бы хотел, чтобы элемент появился в заказе. Как я могу быть уверен в порядке добавления элементов?
MUY Бельгия
5

Это не идеально, но вам разрешено несколько звонков lineinfile. Используя это с insert_after, вы можете получить желаемый результат:

- name: Set first line at EOF (1/3)
  lineinfile: dest=/path/to/file regexp="^string 1" line="string 1"
- name: Set second line after first (2/3)
  lineinfile: dest=/path/to/file regexp="^string 2" line="string 2" insertafter="^string 1"
- name: Set third line after second (3/3)
  lineinfile: dest=/path/to/file regexp="^string 3" line="string 3" insertafter="^string 2"
Рамон де ла Фуэнте
источник
5
да, но это все еще одна строка за раз. Если у меня 15 строк, я бы предпочел добавить их только одной командой. Кажется, это невозможно.
Майкл
1
Спасибо. Кажется, что это все еще единственный способ сделать несколько строк с помощью вставки после / до.
Timss
5

Я был в состоянии сделать это с помощью \nпараметра линии.

Это особенно полезно, если файл может быть проверен, и добавление одной строки создает неверный файл.

В моем случае я добавил AuthorizedKeysCommandи AuthorizedKeysCommandUserв sshd_config , используя следующую команду:

- lineinfile: dest=/etc/ssh/sshd_config line='AuthorizedKeysCommand /etc/ssh/ldap-keys\nAuthorizedKeysCommandUser nobody' validate='/usr/sbin/sshd -T -f %s'

Добавление только одного из параметров создает файл, который не проходит проверку.

Penz
источник
12
Это создаст линию дополнительно каждый раз при запуске playbook - он не будет правильно распознавать, что линия уже существует. По крайней мере, это касается меня в Ansible 1.7.1
Дэвид
1
Я сообщил об ошибке , но ребята из Ansible не заинтересованы ее исправлять.
выступление
1
Теперь есть новый модуль blockinfile, который должен быть лучше, чем это решение. ( docs.ansible.com/ansible/blockinfile_module.html )
Penz
1

Чтобы добавить несколько строк, вы можете использовать blockfile:

- name: Add mappings to /etc/hosts
  blockinfile:
    path: /etc/hosts
    block: |
      '10.10.10.10  server.example.com'
      '10.10.10.11  server1.example.com'

Для добавления одной строки вы можете использовать lininfile:

- name: server.example.com in /etc/hosts
  lineinfile:
    path: /etc/hosts
    line: '192.0.2.42 server.example.com server'
    state: present
Ахмед Таха
источник
1

Чтобы добавить несколько строк, вы можете использовать lineinfileмодуль с with_itemsвключением переменной varsздесь, чтобы сделать это просто :)

---
- hosts: localhost  #change Host group as par inventory
  gather_facts: no
  become: yes
  vars:
    test_server: "10.168.1.1"
    test_server_name: "test-server"
    file_dest: "/etc/test/test_agentd.conf"

  - name: configuring test.conf
    lineinfile:
      dest: "{{ item.dest }}"
      regexp: "{{ item.regexp }}"
      line: "{{ item.line }}"
    with_items:
      - { dest: '"{{ file_dest }}"', regexp: 'Server=', line: 'Server="{{test_server}}"' }
      - { dest: '"{{ file_dest }}"', regexp: 'ServerActive=', line: 'ServerActive="{{test_server}}"' }
      - { dest: '"{{ file_dest }}"', regexp: 'Hostname=', line: 'Hostname="{{test_server_name}}"' }
mail2sandeepd
источник