ansible: infile linii dla kilku linii?

162

Tak samo jest z modułem lineinfile do dodawania jednej linii w pliku, czy istnieje sposób na dodanie kilku wierszy?

Nie chcę używać szablonu, ponieważ musisz dostarczyć cały plik. Chcę tylko dodać coś do istniejącego pliku, niekoniecznie wiedząc, co plik już zawiera, więc szablon nie jest opcją.

Michael
źródło
Rozumiem, że nie chcesz używać template, ale używanie lineinfilejest przeciwieństwem . Jest to również silna czerwona flaga, że ​​„nie wiesz, co jest w pliku”, co prowadzi do znacznego ryzyka nieznanych błędów.
przetrząsacz 42
39
To nie jest anty-wzór. Celem pliku lineinfile jest obsługa wielu źródeł zarządzających tym samym plikiem, co czasami jest nieuniknione. Większość plików konfiguracyjnych ma ustalony format i logikę, aby uniknąć konfliktów, zwykle nie jest zbyt istotny.
Doug F
Nie wiem, co znajduje się w zdecydowanej większości plików na moim komputerze; nie oznacza, że ​​chcę ich wszystkich zniszczyć!
DylanYoung

Odpowiedzi:

222

Możesz to zrobić za pomocą pętli . Oto przykład użycia with_itemspętli:

- 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' }
Ben Whaley
źródło
UPEWNIJ SIĘ, że argument line = i regexp = jest umieszczony w cudzysłowie . Nie zrobiłem i wciąż dostawałem msg: this module requires key=value arguments. Podany przykład ma to poprawne - po prostu nie podążałem za przykładem.
JDS
1
Czy mogę zapytać, jak wykonać pojedynczą kopię zapasową przed pierwszą zmianą? może item.backup? : D
tdihp
6
To było prawdopodobnie przegłosowane przed Ansible 2.0. Lepsza odpowiedź brzmi teraz: stackoverflow.com/a/28306576/972128
kkurian
@kkurian Z pewnością tylko wtedy, gdy wstawiasz, a nie, jeśli wymieniasz?
ndtreviv
7
@kkurian Rozwiązanie blockinfile nie zadziała, jeśli np. potrzebujesz dodać kilka wierszy do pliku json i nie chcesz żadnych znaczników. Chociaż możesz ustawić znaczniki na „”, plik ansiblowy blokowy nadal będzie szukał znaczników, nie znajdował żadnych i wstawi blok ponownie. Zatem plik_bloku bez znaczników nie jest idempotentny, natomiast plik_wierszowy z pętlą tak.
absurdalny
176

Możesz spróbować użyć blockinfilezamiast tego.

Możesz zrobić coś takiego

- blockinfile: |
    dest=/etc/network/interfaces backup=yes
    content="iface eth0 inet static
        address 192.168.0.1
        netmask 255.255.255.0"
Soichi Hayashi
źródło
8
blockinfileModuł wypracowała cudownie za każdym razem wybrałem się go używać. Szczególnie podoba mi się intuicyjne zachowanie opcji insertafter/ insertbeforeoptions.
Jay Taylor,
9
Najwięcej głosów otrzymała odpowiedź prawdopodobnie przed Ansible 2.0, ale teraz jest to poprawniejsza odpowiedź.
Willem van Ketwich
11
Blockinfile wymaga znaczników. Czasami nie ma takiej opcji.
ceving
1
Czy możemy nadpisać zawartość blockinfile?
pkaramol
1
Myślę, że to właściwy sposób. docs.ansible.com/ansible/blockinfile_module.html
Paulo Victor,
20

Jeśli chcesz skonfigurować zestaw unikalnych linii właściwość = wartość, polecam bardziej zwięzłą pętlę. Na przykład:

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

Korzystanie z dyktu zgodnie z sugestią Alix Axel i dodawanie automatycznego usuwania pasujących zakomentowanych wpisów,

- 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
Nicholas Sushkin
źródło
2
Jeśli użyjesz with_dict, będzie to bardziej zwięzłe.
Alix Axel
18

Oto wolna od szumów wersja rozwiązania, które ma być używane with_items:

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

Dla każdego elementu, jeśli istnieje on w pliku fruit.txt, nie jest podejmowane żadne działanie.

Jeśli element nie istnieje, zostanie dołączony na końcu pliku.

Bułka z masłem.

Rick O'Shea
źródło
Nie można tego łączyć z wkładką po.
ceving
Jeśli brakuje wielu linii, chciałbym, aby pozycja pojawiła się w zamówieniu. Jak mogę się upewnić, w jakiej kolejności są dołączane pozycje?
MUY Belgium
5

To nie jest idealne rozwiązanie, ale możesz do niego dzwonić lineinfile. Używając tego z insert_after, możesz uzyskać pożądany wynik:

- 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"
Ramon de la Fuente
źródło
5
tak, ale to wciąż jedna linia na raz. Jeśli mam 15 linii, wolałbym dodać je tylko jednym poleceniem. Wydaje się, że nie jest to możliwe.
Michael
1
Dzięki. Wygląda na to, że jest to nadal jedyny sposób na zrobienie wielu wierszy z wstawieniem po / przed.
czas
5

Udało mi się to zrobić za pomocą \n parametru line.

Jest to szczególnie przydatne, jeśli plik może zostać zweryfikowany, a dodanie jednej linii generuje nieprawidłowy plik.

W moim przypadku dodawałem AuthorizedKeysCommandi AuthorizedKeysCommandUserdo sshd_config za pomocą następującego polecenia:

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

Dodanie tylko jednej opcji generuje plik, który nie przejdzie weryfikacji.

Penz
źródło
12
Spowoduje to utworzenie dodatkowego wiersza za każdym razem, gdy zostanie uruchomiony podręcznik - nie rozpoznaje poprawnie, że linia już istnieje. Przynajmniej tak jest w przypadku mnie w Ansible 1.7.1
David
1
Zgłosiłem błąd , ale faceci z Ansible nie chcą go naprawiać.
ceving
1
Jest nowy moduł blockinfile, który powinien być teraz lepszy od tego rozwiązania. ( docs.ansible.com/ansible/blockinfile_module.html )
Penz,
1

Aby dodać wiele linii, możesz użyć pliku blokowego:

- 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'

aby dodać jedną linię, możesz użyć lininfile:

- name: server.example.com in /etc/hosts
  lineinfile:
    path: /etc/hosts
    line: '192.0.2.42 server.example.com server'
    state: present
Ahmed Taha
źródło
1

Aby dodać wiele linii, możesz użyć lineinfilemodułu zawierającego with_itemsrównież zmienną varstutaj, aby było to proste :)

---
- 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
źródło