Anexando a listas ou adicionando chaves aos dicionários no Ansible

34

(Relacionado a retornos de chamada ou ganchos e séries reutilizáveis ​​de tarefas, em funções Ansible ):

Existe uma maneira melhor de acrescentar uma lista ou adicionar uma chave a um dicionário no Ansible do que (ab) usando uma expressão de modelo jina2?

Eu sei que você pode fazer algo como:

- name: this is a hack
  shell: echo "{% originalvar.append('x') %}New value of originalvar is {{originalvar}}"

mas não há realmente nenhum tipo de meta tarefa ou auxiliar para fazer isso?

Parece frágil, parece não estar documentado e depende de muitas suposições sobre como as variáveis ​​funcionam no Ansible.

Meu caso de uso são várias funções (extensões de servidor de banco de dados) que precisam fornecer alguma configuração a uma função base (o servidor de banco de dados). Não é tão simples quanto anexar uma linha ao arquivo de configuração do servidor db; cada alteração se aplica à mesma linha , por exemplo, as extensões bdre pg_stat_statementsdeve aparecer na linha de destino:

shared_preload_libaries = 'bdr, pg_stat_statements'

A maneira Ansible de fazer isso é apenas processar o arquivo de configuração várias vezes (uma vez por extensão) com uma regexp que extrai o valor atual, analisa e depois reescreve? Em caso afirmativo, como você torna esse idempotente em várias execuções?

E se a configuração for mais difícil do que isso para analisar e não for tão simples quanto acrescentar outro valor separado por vírgula? Pense nos arquivos de configuração XML.

Craig Ringer
fonte
Sabe o que mais? Eu gosto do corte de seu patíbulo side-effect-ful ☺
DomQ

Respostas:

13

Você pode mesclar duas listas em uma variável com +. Digamos que você tenha um group_varsarquivo com este conteúdo:

---
# group_vars/all
pgsql_extensions:
  - ext1
  - ext2
  - ext3

E é usado em um modelo pgsql.conf.j2como:

# {{ ansible_managed }}
pgsql_extensions={% for item in pgsql_extensions %}{{ item }}, {% endfor %}

Em seguida, você pode anexar extensões aos servidores de banco de dados de teste como este:

---
# group_vars/testing_db
append_exts:
  - ext4
  - ext5
pgsql_extensions: "{{ pgsql_extensions + append_exts }}"

Quando a função é executada em qualquer um dos servidores de teste, as extensões adicionais serão adicionadas.

Não tenho certeza de que isso funcione também para dicionários, e também tenha cuidado com os espaços e deixe uma vírgula pendente no final da linha.

GnP
fonte
Você pode, mas precisa fazer tudo group_vars, as funções não podem cuidar dos detalhes da configuração das extensões. É anexar vários dos papéis que estou procurando particularmente, para que um papel possa ser anexado a um var exposto por outro papel.
Craig Ringer
Sua função de base conhece cada função de extensão? Eu tive um caso semelhante em que fui capaz de deixar a concatenação em uma with_itemsfrase.
GnP 14/07/2015
não, e esse é realmente o problema. Em uma implantação do papel de base pode ser ai
Craig campainha
4
Parece que se você tentar fazer isso para concatenar duas listas, ele achará um modelo infinitamente recursivo, porque o lado esquerdo também está no lado direito. Estou entendendo mal como usar isso?
Ibrahim
2
@spectras Pelo menos a partir do Ansible 2.7, isso NÃO funciona. Como sugeriu Ibrahim, isso causa um erro: "loop recursivo detectado na cadeia de modelo".
Rluba # 25/18
35

Desde o Ansible v2.x, você pode fazer o seguinte:

# use case I: appending to LIST variable:

      - name: my appender
        set_fact:
          my_list_var: '{{my_list_myvar + new_items_list}}'

# use case II: appending to LIST variable one by one:

      - name: my appender
        set_fact:
          my_list_var: '{{my_list_var + [item]}}'
        with_items: '{{my_new_items|list}}'

# use case III: appending more keys DICT variable in a "batch":

      - name: my appender
        set_fact:
          my_dict_var: '{{my_dict_var|combine(my_new_keys_in_a_dict)}}'

# use case IV: appending keys DICT variable one by one from tuples
      - name: setup list of tuples (for 2.4.x and up
        set_fact:
          lot: >
            [('key1', 'value1',), ('key2', 'value2',), ..., ('keyN', 'valueN',)],
      - name: my appender
        set_fact:
          my_dict_var: '{{my_dict_var|combine({item[0]: item[1]})}}'
        with_items: '{{lot}}'
# use case V: appending keys DICT variable one by one from list of dicts (thanks to @ssc)

  - name: add new key / value pairs to dict
    set_fact:
      my_dict_var: "{{ my_dict_var | combine({item.key: item.value}) }}"
    with_items:
    - { key: 'key01', value: 'value 01' }
    - { key: 'key02', value: 'value 03' }
    - { key: 'key03', value: 'value 04' }

tudo isso está documentado em: http://docs.ansible.com/ansible/playbooks_filters.html

Max Kovgan
fonte
1
caso de uso IV apenas anexadou'(': u\"'\"}"
ssc 25/02
1
obrigado, @ssc. Notei que não está funcionando com ansible 2.4.x(fixo)
Max Kovgan
de acordo com USECASE # 4, eu adicionei o valor padrão para lidar com o erro indefinido no meu cenário: set_fact: my_dict_var: '{{my_dict_var|default({})|combine({item[0]: item[1]})}}'. O erro indefinido ocorre quando alguma filtragem é usada ou nenhum resultado é registrado.
SK Venkat
Sr. SK Venkat, o código de exemplo aqui demonstra apenas uma coisa muito específica (adição de itens de dicionário de tuplas). Se você precisar fazer outra coisa, esse código não é sua cópia e colagem.
Max Kovgan 18/09
3

você precisa dividir o loop em 2

--- 
- hosts: localhost
  tarefas: 
    - include_vars: pilhas
    - set_facts: functions = {{stacks.Roles | Dividido(' ')}}
    - inclua: addhost.yml
      with_items: "{{papers}}"

e addhost.yml

- set_facts: groupname = {{item}}
- set_facts: ips = {{stacks [item] | split ('')}}}
- local_action: add_host hostname = {{item}} groupname = {{groupname}}
  with_items: {{ips}}
Arthur Tsang
fonte
1

Não tenho certeza quando eles adicionaram isso, mas pelo menos para dicionários / hashes (NOT lists / matrizes), você pode definir a variável hash_behaviour , assim: hash_behaviour = mergeno seu ansible.cfg.

Levei algumas horas para acidentalmente tropeçar nessa configuração: S

nozes
fonte
isso é muito útil, mas tenha cuidado para habilitá-lo e2e na base de código existente. pode quebrar alguns ovos.
precisa saber é o seguinte
0

Quase todas as respostas aqui exigem alterações nas tarefas, mas eu precisava mesclar dinamicamente dicionários na definição de vars, não durante a execução.

Por exemplo, eu quero definir alguns vars compartilhados all group_varse depois quero estendê-los em algum outro groupou host_vars. Muito útil ao trabalhar para funções.

Se você tentar usar os filtros combineou unionsubstituindo a variável original nos arquivos var, você terminará em loop infinito durante a modelagem, por isso criei essa solução alternativa (não é solução).

Você pode definir várias variáveis ​​com base em algum padrão de nome e carregá-las automaticamente na função.

group_vars/all.yml

dictionary_of_bla:
  - name: blabla
    value1 : blabla
    value2 : blabla

group_vars/group1.yml

dictionary_of_bla_group1:
  - name: blabla2
    value1 : blabla2
    value2 : blabla2

snippet de código de função

tasks:
  - name: Run for all dictionary_of_bla.* variations
    include_tasks: do_some_stuff.yml
    with_items: "{{ lookup('varnames','dictionary_of_bla.*').split(',') }}"
    loop_control:
      loop_var: _dictionary_of_bla

do_some_stuff.yml

- name: do magic
  magic:
    trick_name: item.name
    trick_value1: item.value1
    trick_value2: item.value2
  with_items: "{{ vars[_dictionary_of_bla] }}"

É apenas um trecho, mas você deve ter uma ideia de como funciona. note: lookup ('varnames', '') está disponível desde o ansible 2.8

Eu acho que também seria possível mesclar todas as variáveis dictionary_of_bla.*em um dicionário durante o tempo de execução usando a mesma pesquisa.

A vantagem dessa abordagem é que você não precisa definir listas exatas de nomes de variáveis, mas apenas o padrão e o usuário podem configurá-lo dinamicamente.

VeselaHouba
fonte
-4

Ansibleé um sistema de automação e, no que diz respeito ao gerenciamento de arquivos de configuração, não é muito diferente apt. A razão pela qual mais e mais softwares oferece o recurso de ler trechos de configuração de umconf.d diretório é permitir que esses sistemas de automação tenham diferentes pacotes / funções que adicionem configuração ao software. Acredito que não é a filosofia de Ansiblefazer o que você tem em mente, mas sim usar o conf.dtruque. Se o software que está sendo configurado não oferecer essa funcionalidade, você poderá estar com problemas.

Como você menciona os arquivos de configuração XML, aproveito a oportunidade para reclamar. Há uma razão para a tradição do Unix de usar arquivos de configuração de texto sem formatação. Os arquivos de configuração binária não se prestam bem à automação do sistema; portanto, qualquer tipo de formato binário causará problemas e provavelmente exigirá que você crie um programa para lidar com a configuração. (Se alguém pensa que XML é um formato de texto sem formatação, deve examinar seu cérebro.)

Agora, sobre o seu PostgreSQLproblema específico . PostgreSQLsuporta o conf.dtruque. Primeiro, eu verificaria se shared_preload_librariespode ser especificado várias vezes. Não encontrei nenhuma dica na documentação que possa, mas ainda a tentaria. Se não puder ser especificado várias vezes, eu explicaria meu problema para os PostgreSQLcaras, caso eles tenham idéias; isso é um PostgreSQLproblema e não um Ansibleproblema. Se não houver solução e eu realmente não puder mesclar as diferentes funções em uma, implementaria um sistema para compilar a configuração no host gerenciado. Neste caso, eu provavelmente criar um script /usr/local/sbin/update_postgresql_configque compile /etc/postgresql/postgresql.conf.jinjaem /etc/postgresql/9.x/main/postgresql.conf. O script lê as bibliotecas compartilhadas de pré-carregamento de /etc/postgresql/shared_preload_libraries.txt, uma biblioteca por linha, e as fornece ao jinja.

Não é incomum os sistemas de automação fazerem isso. Um exemplo é o exim4pacote Debian .

Antonis Christofides
fonte
O PostgreSQL suporta um conf.dmecanismo de inclusão e, felizmente, usa arquivos de texto sem formatação. No entanto, existem algumas opções de configuração em que várias extensões podem ter opiniões sobre isso - por exemplo, "aumente max_wal_senders em 10 do que era antes".
Craig Ringer
4
Parece que você está dizendo que o aplicativo deve ser alterado para solucionar as limitações do sistema de gerenciamento de configuração ou que eu deveria desistir de ter funções reutilizáveis.
Craig Ringer