ansible: lineinfile para várias linhas?

162

Da mesma forma que existe um módulo lineinfilepara adicionar uma linha em um arquivo, existe uma maneira de adicionar várias linhas?

Eu não quero usar um modelo porque você precisa fornecer o arquivo inteiro. Eu só quero adicionar algo a um arquivo existente sem necessariamente saber o que o arquivo já contém, para que um modelo não seja uma opção.

Michael
fonte
Entendo que você não deseja usar template, mas usar lineinfileé um antipadrão . Também é uma forte bandeira vermelha que você "não sabe o que está no arquivo", o que leva a um risco substancial de falhas desconhecidas.
tedder42
39
Não é um anti-padrão. O objetivo do lineinfile é oferecer suporte a várias fontes que gerenciam o mesmo arquivo, o que às vezes é inevitável. A maioria dos arquivos de configuração tem um formato fixo e lógica para evitar conflitos geralmente não é muito substancial.
Doug F
Não sei o que há na grande maioria dos arquivos no meu PC; não significa que eu quero destruir todos eles!
DylanYoung

Respostas:

222

Você pode usar um loop para fazer isso. Aqui está um exemplo usando um 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' }
Ben Whaley
fonte
Verifique se você tem o argumento para alinhar = e regexp = entre aspas . Eu não fiz, e continuei recebendo msg: this module requires key=value arguments. O exemplo dado tem isso correto - simplesmente não segui o exemplo.
JDS #
1
Posso perguntar como fazer um único backup antes da primeira alteração? talvez item.backup? : D
tdihp 27/10
6
Provavelmente foi votado antes do Ansible 2.0. Uma resposta melhor é agora: stackoverflow.com/a/28306576/972128
kkurian
@kkurian Certamente apenas se você estiver inserindo, não se estiver substituindo?
Ndtreviv 01/06
7
@kkurian A solução blockinfile não funcionará se você, por exemplo, precisar adicionar algumas linhas a um arquivo json e não desejar marcadores. Enquanto você pode definir marcadores como "", o arquivo de bloqueio ansible ainda procurará marcadores, não encontrará nenhum e inserirá o bloco novamente. Assim, blockinfile sem marcadores não é idempotente, lineinfile com um loop é.
absurdo
176

Você pode tentar usar blockinfile.

Você pode fazer 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
fonte
8
O blockinfilemódulo funcionou maravilhosamente toda vez que eu escolhi usá-lo. Eu amo especialmente o comportamento intuitivo das opções insertafter/ insertbefore.
Jay Taylor
9
A resposta mais votada provavelmente foi anterior à Ansible 2.0, mas esta é a resposta mais correta agora.
Willem van Ketwich
11
Blockinfile requer marcadores. Às vezes isso não é uma opção.
ceving
1
Podemos sobrescrever o conteúdo blockinfile?
Pkaramol
1
É uma maneira certa de fazer isso, eu acho. docs.ansible.com/ansible/blockinfile_module.html
Paulo Victor
20

Se você precisar configurar um conjunto de propriedades exclusivas = linhas de valor, recomendo um loop mais conciso. Por exemplo:

- 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 um ditado conforme sugerido por Alix Axel e adicionando a remoção automática de entradas comentadas correspondentes,

- 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
fonte
2
Se você usar with_dict, seria mais conciso.
Alix Axel
18

Aqui está uma versão sem ruído da solução que deve ser usada com os itens:

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

Para cada item, se o item existir em fruits.txt, nenhuma ação será tomada.

Se o item não existir, ele será anexado ao final do arquivo.

Mole-mole.

Rick O'Shea
fonte
Isto não pode ser combinado com o insertafter.
ceving
Se várias linhas estiverem faltando, gostaria que o item fosse exibido em um pedido. Como posso ter certeza da ordem em que os itens são anexados?
MUY Belgium
5

Não é o ideal, mas você pode fazer várias chamadas lineinfile. Usando isso insert_after, você pode obter o resultado desejado:

- 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
fonte
5
sim, mas ainda é uma linha de cada vez. Se eu tiver 15 linhas, prefiro adicioná-las com apenas um comando. Não parece ser possível.
Michael
1
Obrigado. Parece que essa ainda é a única maneira de fazer várias linhas com inserção depois / antes.
timss 19/05/19
5

Consegui fazer isso usando \no parâmetro line.

É especialmente útil se o arquivo puder ser validado e a adição de uma única linha gera um arquivo inválido.

No meu caso, eu estava adicionando AuthorizedKeysCommande AuthorizedKeysCommandUserao sshd_config , com o seguinte comando:

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

Adicionar apenas uma das opções gera um arquivo que falha na validação.

Penz
fonte
12
Isso criará a linha mais uma vez cada vez que o manual for executado - ele não reconhece corretamente que a linha já existe. Pelo menos, esse é o meu caso no Ansible 1.7.1
David
1
Eu relatei um bug , mas o pessoal do Ansible não tem interesse em corrigi-lo.
ceving
1
Há um novo módulo blockinfile que deve ser melhor do que essa solução agora. ( docs.ansible.com/ansible/blockinfile_module.html )
Penz
1

Para adicionar várias linhas, você pode usar o arquivo de bloco:

- 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 adicionar uma linha, você pode 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
fonte
1

Para adicionar várias linhas, você pode usar o lineinfilemódulo with_itemsincluindo também a variável varsaqui para simplificar :)

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