ansible: lineinfile per più righe?


162

Allo stesso modo esiste un modulo lineinfileper aggiungere una riga in un file, c'è un modo per aggiungere più righe?

Non voglio usare un modello perché devi fornire l'intero file. Voglio solo aggiungere qualcosa a un file esistente senza necessariamente sapere cosa contiene già il file, quindi un modello non è un'opzione.


Capisco che non vuoi usare template, ma usare lineinfileè un antipattern . È anche una forte bandiera rossa che "non sai cosa c'è nel file", il che comporta un rischio sostanziale di guasti sconosciuti.
tedder42

39
Non è un anti-pattern. Il punto di lineinfile è supportare più fonti gestendo lo stesso file, che a volte è inevitabile. La maggior parte dei file di configurazione ha un formato fisso e la logica per evitare conflitti non è in genere troppo sostanziale.
Doug F,

Non so cosa sia nella stragrande maggioranza dei file sul mio PC; non significa che voglio eliminarli tutti!
DylanYoung,

Risposte:


222

Puoi usare un loop per farlo. Ecco un esempio usando un with_itemsloop:

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

ASSICURARSI di avere l'argomento line = e regexp = tra virgolette . Non l'ho fatto e ho continuato a ricevere msg: this module requires key=value arguments. L'esempio fornito ha questo corretto: non ho seguito l'esempio.
JDS,

1
Posso chiedere come eseguire un singolo backup prima della prima modifica? forse item.backup? : D
tdihp,

6
Questo è stato probabilmente votato prima di Ansible 2.0. Una risposta migliore è ora: stackoverflow.com/a/28306576/972128
kkurian

@kkurian Sicuramente solo se stai inserendo, non se stai sostituendo?
ndtreviv,

7
@kkurian La soluzione blockinfile non funzionerà se, ad esempio, è necessario aggiungere alcune righe a un file json e non si desidera alcun marker. Mentre è possibile impostare i marker su "", il file di risposta risponde cercherà comunque i marker, non ne troverà nessuno e inserirà nuovamente il blocco. Pertanto, blockinfile senza marcatori non è idempotente, lo è lineinfile con un ciclo.
assurdo

176

Puoi provare a usare blockinfileinvece.

Puoi fare qualcosa del genere

- blockinfile: |
    dest=/etc/network/interfaces backup=yes
    content="iface eth0 inet static
        address 192.168.0.1
        netmask 255.255.255.0"

8
Il blockinfilemodulo ha funzionato meravigliosamente ogni volta che ho scelto di usarlo. In particolare, adoro il comportamento intuitivo delle opzioni insertafter/ insertbefore.
Jay Taylor,

9
La risposta più votata era probabilmente prima di Ansible 2.0, ma questa è la risposta più corretta ora.
Willem van Ketwich,

11
Blockinfile richiede marcatori. Questa a volte non è un'opzione.
ceving

1
Siamo in grado di sovrascrivere i contenuti con blockinfile?
pkaramol,

1
È un modo giusto per farlo, penso. docs.ansible.com/ansible/blockinfile_module.html
Paulo Victor,

20

Se è necessario configurare un set di righe proprietà = valore uniche, consiglio un ciclo più conciso. Per esempio:

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

Utilizzando un dict come suggerito da Alix Axel e aggiungendo la rimozione automatica delle voci commentate corrispondenti,

- 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
Se usi with_dict sarebbe più conciso.
Alix Axel,

18

Ecco una versione senza rumore della soluzione che deve essere utilizzata con with_items:

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

Per ogni oggetto, se l'oggetto esiste in fruits.txt non viene intrapresa alcuna azione.

Se l'elemento non esiste, verrà aggiunto alla fine del file.

Vai tranquillo.


Questo non può essere combinato con insertafter.
ceving

Se mancano più righe, vorrei che l'articolo fosse visualizzato in un ordine. Come posso essere sicuro dell'ordine in cui gli articoli vengono aggiunti?
MUY Belgio,

5

Non è l'ideale, ma ti sono consentite più chiamate a lineinfile. Usandolo con insert_after, puoi ottenere il risultato che desideri:

- 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
si ma è ancora una riga alla volta. Se ho 15 righe, preferirei aggiungerle con un solo comando. Non sembra possibile.
Michael,

1
Grazie. Sembra che questo sia ancora l'unico modo per fare più righe con insert after / before.
timss

5

Sono stato in grado di farlo utilizzando \nil parametro line.

È particolarmente utile se il file può essere convalidato e l'aggiunta di una singola riga genera un file non valido.

Nel mio caso, ero l'aggiunta AuthorizedKeysCommande AuthorizedKeysCommandUserper sshd_config , con il seguente comando:

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

L'aggiunta di una sola delle opzioni genera un file che non supera la convalida.


12
Questo creerà la linea un'ulteriore volta ogni volta che il playbook viene eseguito - non riconosce correttamente che la linea esiste già. Almeno, questo è il caso per me su Ansible 1.7.1
David

1
Ho segnalato un bug , ma i ragazzi Ansible non hanno interesse a risolverlo.
ceving il

1
Esiste un nuovo modulo blockinfile che dovrebbe essere migliore di quella soluzione ora. ( docs.ansible.com/ansible/blockinfile_module.html )
Penz

1

Per aggiungere più righe puoi usare 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'

per aggiungere una riga puoi usare lininfile:

- name: server.example.com in /etc/hosts
  lineinfile:
    path: /etc/hosts
    line: '192.0.2.42 server.example.com server'
    state: present

1

Per aggiungere più righe puoi usare il lineinfilemodulo with_itemsincludendo anche la variabile varsqui per renderlo semplice :)

---
- 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}}"' }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.