web-dev-qa-db-fra.com

Comment boucler ce dictionnaire dans Ansible?

Dis que j'ai ce dictionnaire

war_files:
  server1:
  - file1.war
  - file2.war
  server2:
  - file1.war
  - file2.war
  - file3.war

et pour l'instant je veux juste faire une boucle sur chaque élément (clé), puis sur chaque élément de la clé (valeur). j'ai fait ça

- name: Loop over the dictionary
  debug: msg="Key={{ item.key }} value={{ item.value }}"
  with_dict: "{{ war_files }}"

Et je comprends ça. C'est bien sûr correct, mais ce n'est PAS ce que je veux.

ok: [localhost] => (item={'value': [u'file1.war', u'file2.war'], 'key': u'server1'}) => {
    "item": {
        "key": "server1", 
        "value": [
            "file1.war", 
            "file2.war"
        ]
    }, 
    "msg": "Server=server1, WAR=[u'file1.war', u'file2.war']"
}
ok: [localhost] => (item={'value': [u'file1.war', u'file2.war', u'file3.war'], 'key': u'server2'}) => {
    "item": {
        "key": "server2", 
        "value": [
            "file1.war", 
            "file2.war", 
            "file3.war"
        ]
    }, 
    "msg": "Server=server2, WAR=[u'file1.war', u'file2.war', u'file3.war']"
}

Je veux obtenir une sortie qui dit

"msg": "Server=server1, WAR=file1.war"
"msg": "Server=server1, WAR=file2.war"
"msg": "Server=server2, WAR=file1.war"
"msg": "Server=server2, WAR=file2.war"
"msg": "Server=server2, WAR=file3.war"

OIE, comment puis-je écrire une tâche pour effectuer une itération sur le dictionnaire afin qu’elle passe par chaque clé, puis les éléments de chaque clé? En substance, j'ai un tableau imbriqué et que je veux parcourir par-dessus?

9
Chris F

Eh bien, je ne pouvais pas trouver un moyen très facile de le faire, cependant, avec un peu de jinja2, nous pouvons réaliser quelque chose de ce genre:

/tmp ❯❯❯ cat example.yml
---
- hosts: 127.0.0.1
  vars:
    war_files:
      server1:
      - file1.war
      - file2.war
      server2:
      - file1.war
      - file2.war
      - file3.war
  tasks:
  - set_fact:
      war_files_list_of_dicts: |
          {% set res = [] -%}
          {% for key in war_files.keys() -%}
             {% for value in war_files[key] -%}
              {% set ignored = res.extend([{'Server': key, 'WAR':value}]) -%}
             {%- endfor %}
          {%- endfor %}
          {{ res }}

  - name: let's debug the crap out of this
    debug: var=war_files_list_of_dicts

  - name: Servers and their WARs!!!
    debug:
       msg: "Server={{ item.Server }}, WAR={{ item.WAR }}"
    with_items: "{{ war_files_list_of_dicts }}"

Et quand le playbook est lancé:

/tmp ❯❯❯ ansible-playbook example.yml
 [WARNING]: provided hosts list is empty, only localhost is available


PLAY [127.0.0.1] ***************************************************************

TASK [setup] *******************************************************************
ok: [127.0.0.1]

TASK [set_fact] ****************************************************************
ok: [127.0.0.1]

TASK [let's debug the crap out of this] ****************************************
ok: [127.0.0.1] => {
    "war_files_list_of_dicts": [
        {
            "Server": "server1", 
            "WAR": "file1.war"
        }, 
        {
            "Server": "server1", 
            "WAR": "file2.war"
        }, 
        {
            "Server": "server2", 
            "WAR": "file1.war"
        }, 
        {
            "Server": "server2", 
            "WAR": "file2.war"
        }, 
        {
            "Server": "server2", 
            "WAR": "file3.war"
        }
    ]
}

TASK [Servers and their WARs!!!] ***********************************************
ok: [127.0.0.1] => (item={'WAR': u'file1.war', 'Server': u'server1'}) => {
    "item": {
        "Server": "server1", 
        "WAR": "file1.war"
    }, 
    "msg": "Server=server1, WAR=file1.war"
}
ok: [127.0.0.1] => (item={'WAR': u'file2.war', 'Server': u'server1'}) => {
    "item": {
        "Server": "server1", 
        "WAR": "file2.war"
    }, 
    "msg": "Server=server1, WAR=file2.war"
}
ok: [127.0.0.1] => (item={'WAR': u'file1.war', 'Server': u'server2'}) => {
    "item": {
        "Server": "server2", 
        "WAR": "file1.war"
    }, 
    "msg": "Server=server2, WAR=file1.war"
}
ok: [127.0.0.1] => (item={'WAR': u'file2.war', 'Server': u'server2'}) => {
    "item": {
        "Server": "server2", 
        "WAR": "file2.war"
    }, 
    "msg": "Server=server2, WAR=file2.war"
}
ok: [127.0.0.1] => (item={'WAR': u'file3.war', 'Server': u'server2'}) => {
    "item": {
        "Server": "server2", 
        "WAR": "file3.war"
    }, 
    "msg": "Server=server2, WAR=file3.war"
}

PLAY RECAP *********************************************************************
127.0.0.1                  : ok=4    changed=0    unreachable=0    failed=0   
5
Nehal J Wani

Comment c'est

- hosts: localhost
  vars:
    war_files:
      server1:
      - file1.war
      - file2.war
      server2:
      - file1.war
      - file2.war
      - file3.war
  tasks:
    - name: Loop over subelements of the dictionary
      debug:
        msg: "Key={{ item.0.key }} value={{ item.1 }}"
      loop: "{{ war_files | dict2items | subelements('value') }}"

Les filtres dict2items, subelements arrivent dans Ansible 2.6. 

Pour votre information, si un filtre pour votre objectif n'existe pas, vous pouvez écrire votre propre filtre en python sans avoir à recourir à jinja2. Ansible est facilement extensible; les filtres dans filter_plugins/*.py sont recherchés par défaut en regard de vos jeux/rôles et sont automatiquement inclus - voir Développement de plugins pour plus de détails.

8
tmoschou

Maintenant, Ansible permet cela 

- name: add several users
  user:
    name: "{{ item.name }}"
    state: present
    groups: "{{ item.groups }}"
  with_items:
    - { name: 'testuser1', groups: 'wheel' }
    - { name: 'testuser2', groups: 'root' }
6
Artem Bernatskyi

dict2items

Je me suis retrouvé à vouloir parcourir un ensemble hétérogène de clés et leurs valeurs associées et à utiliser la paire clé-valeur dans une tâche. Le filtre dict2items est le moyen le moins pénible que j'ai trouvé. Vous pouvez trouver dict2items dans Ansible 2.6

Exemple de dictée

systemsetup:
  remotelogin: "On"
  timezone: "Europe/Oslo"
  usingnetworktime: "On"
  sleep: 0
  computersleep: 0
  displaysleep: 0
  harddisksleep: 0
  allowpowerbuttontosleepcomputer: "Off"
  wakeonnetworkaccess: "On"
  restartfreeze: "On"
  restartpowerfailure: "On"

Exemple de tâche

---
- debug:
    msg: "KEY: {{ item.key }}, VALUE: {{ item.value }}"
  loop: "{{ systemsetup | dict2items }}"
0

Voici la méthode que je préfère pour parcourir les dictionnaires:

input_data.yml contient les éléments suivants:

----
input_data:
  item_1:
    id: 1
    info: "Info field number 1"
  item_2:
    id: 2
    info: "Info field number 2"

J'utilise ensuite une structure de données comme celle ci-dessus dans un jeu utilisant la fonction keys () et itère sur les données avec with_items

---
- hosts: localhost
  gather_facts: false
  connection: local
  tasks:
    - name: Include dictionary data
      include_vars:
        file: data.yml

    - name: Show info field from data.yml
      debug:
        msg: "Id: {{ input_data[item]['id'] }} - info: {{ input_data[item]['info'] }}"
      with_items: "{{ input_data.keys() }}"

Le playbook ci-dessus produit la sortie suivante:

PLAY [localhost] ***********************************************************

TASK [Include dictionary data] *********************************************
ok: [localhost]

TASK [Show info field from data.yml] ***************************************
ok: [localhost] => (item=item_2) => {
    "msg": "Id: 2 - info: Info field item 2"
}
ok: [localhost] => (item=item_3) => {
    "msg": "Id: 3 - info: Info field item 3"
}
ok: [localhost] => (item=item_1) => {
    "msg": "Id: 1 - info: Info field item 1"
}

PLAY RECAP *****************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0
0
M_dk