Acceda al valor de la clave del diccionario especificada dentro de una lista de diccionarios en Ansible

Acceda al valor de la clave del diccionario especificada dentro de una lista de diccionarios en Ansible

Estoy instalando una lista de paquetes en la variable de registro ansible y la estoy generando con debug:

  community.general.homebrew:
    name: "{{ package }}"
    state: present
  register: package_install
  until: package_install is succeeded
  loop:
    - pam-reattach
    - pinentry-mac
    - jorgelbg/tap/pinentry-touchid
  loop_control:
    loop_var: package

- debug:
    msg: "{{ package_install }}"

La salida se ve así:
msg:
  changed: true
  msg: All items completed
  results:
  - ansible_loop_var: package
    attempts: 1
    changed: false
    changed_pkgs: []
    failed: false
    invocation:
      module_args:
        install_options: []
        name:
        - pam-reattach
        path: /usr/local/bin:/opt/homebrew/bin:/home/linuxbrew/.linuxbrew/bin
        state: present
        update_homebrew: false
        upgrade_all: false
        upgrade_options: []
    msg: 'Package already installed: pam-reattach'
    package: pam-reattach
    unchanged_pkgs:
    - pam-reattach
  - ansible_loop_var: package
    attempts: 1
    changed: true
    changed_pkgs:
    - pinentry-mac
    failed: false
    invocation:
      module_args:
        install_options: []
        name:
        - pinentry-mac
        path: /usr/local/bin:/opt/homebrew/bin:/home/linuxbrew/.linuxbrew/bin
        state: present
        update_homebrew: false
        upgrade_all: false
        upgrade_options: []
    msg: 'Package installed: pinentry-mac'
    package: pinentry-mac
    unchanged_pkgs: []
  - ansible_loop_var: package
    attempts: 1
    changed: true
    changed_pkgs:
    - jorgelbg/tap/pinentry-touchid
    failed: false
    invocation:
      module_args:
        install_options: []
        name:
        - jorgelbg/tap/pinentry-touchid
        path: /usr/local/bin:/opt/homebrew/bin:/home/linuxbrew/.linuxbrew/bin
        state: present
        update_homebrew: false
        upgrade_all: false
        upgrade_options: []
    msg: 'Package installed: jorgelbg/tap/pinentry-touchid'
    package: jorgelbg/tap/pinentry-touchid
    unchanged_pkgs: []
  skipped: false

La var registrada package_install.resultscontiene una lista de diccionarios (o mapas/hashes; corríjame si me equivoco) con los datos relacionados con cada packageinstalación.

Necesito verificar si alguno pinentry-macde pinentry-touchidlos paquetes se instaló durante la tarea anterior (el valor de changedla clave dentro de cada elemento es igual truea o false) y, en caso afirmativo, ejecutar un comando específico, por ejemplo:

- command: <command>
  when: >
    `pinentry-mac` item's attribute `changed` is `True` within `package_install.results` \
    OR \
    `pinentry-touchid` item's attribute `changed` is `True` within `package_install.results`

¿Cómo haría esto?

Ahora mismo hago lo siguiente:

  - command: <command>
    when: "'pinentry' in item.package and item.changed"
    loop: "{{ macterm_package_install.results }}"

Pero en este caso el comando se ejecutará dos veces si ambos paquetes se instalaron durante el paso anterior, aunque el comando debe ejecutarse sólo una vez.

¿Hay alguna manera de hacerlo correctamente? Cualquier idea es muy apreciada.

ACTUALIZAR

La "mejor" forma que pude encontrar es esta (en dos pasos):

  - name: Check if any of the pinentry packages were installed during previous tasks
    set_fact:
      pinentry_changed: True
    when: "'pinentry-' in item.package and item.changed"
    loop: "{{ macterm_package_install.results }}"

  - command: <command>
    when: pinentry_changed | default(false)

Pero realmente, ¿existe alguna forma más elegante de solucionar esto?

Respuesta1

Hay muchas opciones. Elija el que mejor se adapte a su caso de uso.

  1. Crear el diccionario
  package_changed: "{{ package_install.results|
                       items2dict(key_name='package', value_name='changed') }}"

da

  package_changed:
    jorgelbg/tap/pinentry-touchid: true
    pam-reattach: false
    pinentry-mac: true

Entonces las condiciones son triviales.

    - command: <command>
      when:  package_changed['pinentry-mac'] or
             package_changed['jorgelbg/tap/pinentry-touchid']
  1. Crear la lista de paquetes modificados.
  changed_pkgs: "{{ package_install.results|
                    map(attribute='changed_pkgs')|flatten }}"

da

  changed_pkgs:
  - pinentry-mac
  - jorgelbg/tap/pinentry-touchid

Pruebe cada paquete

    - command: <command>
      when:  ('pinentry-mac' in changed_pkgs) or
             ('jorgelbg/tap/pinentry-touchid' in changed_pkgs)

, o intersecar las listas si puede colocar los paquetes probados en una lista

    - command: <command>
      when:  changed_pkgs|intersect(test_pkgs)|length > 0
      vars:
        test_pkgs: [pinentry-mac, jorgelbg/tap/pinentry-touchid]
  1. Crea la lista y asigna el nombre base.
  changed_pkgs: "{{ package_install.results|
                    map(attribute='changed_pkgs')|flatten|
                    map('basename')|list }}"

da

  changed_pkgs:
  - pinentry-mac
  - pinentry-touchid

Utilice únicamente los nombres de los paquetes.

    - command: <command>
      when:  ('pinentry-mac' in changed_pkgs) or
             ('pinentry-touchid' in changed_pkgs)

información relacionada