ansible: archivo de línea para varias líneas?

162

De la misma manera que hay un módulo lineinfilepara agregar una línea en un archivo, ¿hay alguna manera de agregar varias líneas?

No quiero usar una plantilla porque tienes que proporcionar todo el archivo. Solo quiero agregar algo a un archivo existente sin necesariamente saber qué contiene el archivo, por lo que una plantilla no es una opción.

Miguel
fuente
Entiendo que no quieres usar template, pero usar lineinfilees un antipatrón . También es una señal de alerta que "no sabe lo que hay en el archivo", lo que conlleva un riesgo considerable de fallas desconocidas.
tedder42
39
No es un antipatrón. El objetivo de lineinfile es admitir múltiples fuentes que administran el mismo archivo, lo que a veces es inevitable. La mayoría de los archivos de configuración tienen un formato fijo y la lógica para evitar conflictos no suele ser demasiado sustancial.
Doug F
No sé qué hay en la gran mayoría de los archivos en mi PC; ¡no significa que quiera bombardearlos a todos!
DylanYoung

Respuestas:

222

Puedes usar un bucle para hacerlo. Aquí hay un ejemplo usando un with_itemsbucle:

- 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
fuente
ASEGÚRESE de que tiene el argumento para line = y regexp = entre comillas . No lo hice, y seguí recibiendo msg: this module requires key=value arguments. El ejemplo dado tiene esto correcto: simplemente no seguí el ejemplo.
JDS
1
¿Puedo preguntar cómo hacer una sola copia de seguridad antes del primer cambio? tal vez item.backup? : D
tdihp
66
Esto probablemente fue votado antes de Ansible 2.0. Una mejor respuesta es ahora: stackoverflow.com/a/28306576/972128
kkurian
@kkurian ¿Seguramente solo si está insertando, no si está reemplazando?
ndtreviv
77
@kkurian La solución blockinfile no funcionará si, por ejemplo, necesita agregar algunas líneas a un archivo json y no desea ningún marcador. Si bien puede establecer marcadores en "", el archivo de bloqueo ansible seguirá buscando marcadores, no encontrará ninguno, e insertará el bloque nuevamente. Por lo tanto, el archivo de bloque sin marcadores no es idempotente, el archivo de línea con un bucle sí.
absurdo
176

Puedes intentar usar en su blockinfilelugar.

Puedes hacer algo como

- blockinfile: |
    dest=/etc/network/interfaces backup=yes
    content="iface eth0 inet static
        address 192.168.0.1
        netmask 255.255.255.0"
Soichi Hayashi
fuente
8
El blockinfilemódulo ha funcionado maravillosamente cada vez que he elegido usarlo. Especialmente me encanta el comportamiento intuitivo de las insertafter/ insertbeforeopciones.
Jay Taylor
9
La respuesta más votada fue probablemente antes de Ansible 2.0, pero esta es la respuesta más correcta ahora.
Willem van Ketwich
11
Blockinfile requiere marcadores. Esto a veces no es una opción.
ceving
1
¿Podemos sobrescribir el contenido con blockinfile?
pkaramol
1
Es una forma correcta de hacerlo, creo. docs.ansible.com/ansible/blockinfile_module.html
Paulo Victor
20

Si necesita configurar un conjunto de propiedades únicas = líneas de valor, le recomiendo un bucle más conciso. Por ejemplo:

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

Usando un dict como lo sugiere Alix Axel y agregando la eliminación automática de las entradas comentadas coincidentes,

- 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
fuente
2
Si usa with_dict sería más conciso.
Alix Axel
18

Aquí hay una versión libre de ruido de la solución que se debe usar con elementos:

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

Para cada elemento, si el elemento existe en fruits.txt, no se realiza ninguna acción.

Si el elemento no existe, se agregará al final del archivo.

Pan comido.

Rick O'Shea
fuente
Esto no se puede combinar con inserta después.
ceving
Si faltan varias líneas, me gustaría que el artículo aparezca en un pedido. ¿Cómo puedo estar seguro del orden en que se agregan los artículos?
MUY Bélgica
5

No es ideal, pero se le permiten múltiples llamadas lineinfile. Usando eso con insert_after, puede obtener el resultado que desea:

- 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
fuente
55
Sí, pero sigue siendo una línea a la vez. Si tengo 15 líneas, preferiría agregarlas con un solo comando. No parece ser posible.
Michael
1
Gracias. Parece que esta sigue siendo la única forma de hacer varias líneas con inserción después / antes.
TIMSS
5

Pude hacer eso usando \n el parámetro de línea.

Es especialmente útil si el archivo se puede validar, y agregar una sola línea genera un archivo no válido.

En mi caso, estaba agregando AuthorizedKeysCommandy AuthorizedKeysCommandUsera sshd_config , con el siguiente comando:

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

Agregar solo una de las opciones genera un archivo que falla la validación.

Penz
fuente
12
Esto creará la línea una vez más cada vez que se ejecute el libro de jugadas; no reconoce correctamente que la línea ya existe. Al menos, ese es el caso para mí en Ansible 1.7.1
David
1
Informé un error , pero los chicos de Ansible no tienen interés en solucionarlo.
ceving
1
Hay un nuevo módulo blockinfile que debería ser mejor que esa solución ahora. ( docs.ansible.com/ansible/blockinfile_module.html )
Penz
1

Para agregar varias líneas puede usar 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'

Para agregar una línea puede usar 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
fuente
1

Para agregar varias líneas, puede usar el lineinfilemódulo con with_itemstambién variable incluida varsaquí para hacerlo simple :)

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