ansible

Problem with ansible loop list of strings/numbers


Im somewhat at my google-fu end. I can't see a reason why i would get "The conditional check 'XYZ.results[item] is success' failed. The error was: The 'failed' test expects a dictionary". It works with list a of numbers but not a list of strings? If i add prints statement and compare the outputs, the only notable differences are different item names.

Maybe their is a better and correct way to loop over hosts, if host needs to be pve?

This works.

- name: Clone Virtual Machine
  community.general.proxmox_kvm:
    api_host: ANYPVEHOST
    api_user: APIPVEUSER@pve
    api_token_id: PVETOKEN
    api_token_secret: "{{ PVETOKENSECRET }}"

    name: "{{ '%s%02d' | format(hostvars[groups['docker_swarm'][0]]['target_name_prefix'], item | int + 1) }}"
    newid: "{{ '%d' | format(hostvars[groups['docker_swarm'][0]]['first_id'] + item) }}"
    node: PVENODE

    clone: debian12-genericcloud
    storage: local-zfs
    state: present
  register: vm_clone
  loop: "{{ range(0, hostvars[groups['docker_swarm'][0]]['target_count'] | int, 1) | list }}"

- name: Check if VM is present
  when: vm_clone.results[item] is success
  community.general.proxmox_vm_info:
    api_host: ANYPVEHOST
    api_user: APIPVEUSER@pve
    api_token_id: PVETOKEN
    api_token_secret: "{{ PVETOKENSECRET }}"

    node: PVENODE
    vmid: "{{ vm_clone.results[item].vmid }}"
  until: >
    vm_present is defined and
    vm_present.proxmox_vms is defined
  retries: 60
  delay: 1
  register: vm_present
  loop: "{{ range(0, hostvars[groups['docker_swarm'][0]]['target_count'] | int, 1) | list }}"

and also this

- name: Check if VM is present
  community.general.proxmox_vm_info:
    api_host: ANYPVEHOST
    api_user: APIPVEUSER@pve
    api_token_id: PVETOKEN
    api_token_secret: "{{ PVETOKENSECRET }}"

    node: "{{ hostvars[groups['windows'][item]].vars.node }}"
    vmid: "{{ hostvars[groups['windows'][item]].vars.vmid }}"
  until: >
    vm_present is defined and
    vm_present.proxmox_vms is defined
  retries: 60
  delay: 1
  register: vm_present
  loop: "{{ range(0, groups['windows'] | length, 1) | list }}"

- name: Start VM
  when: vm_present.results[item] is success
  community.general.proxmox_kvm:
    api_host: ANYPVEHOST
    api_user: APIPVEUSER@pve
    api_token_id: PVETOKEN
    api_token_secret: "{{ PVETOKENSECRET }}"

    vmid: "{{ hostvars[groups['windows'][item]].vars.vmid }}"
    node: PVENODE
    state: started
  register: vm_started
  loop: "{{ range(0, groups['windows'] | length, 1) | list }}"

But not this

- name: Check if VM is present
  community.general.proxmox_vm_info:
    api_host: ANYPVEHOST
    api_user: APIPVEUSER@pve
    api_token_id: PVETOKEN
    api_token_secret: "{{ PVETOKENSECRET }}"

    node: "{{ hostvars[item].vars.node }}"
    vmid: "{{ hostvars[item].vars.vmid }}"
  until: >
    vm_present is defined and
    vm_present.proxmox_vms is defined
  retries: 60
  delay: 1
  register: vm_present
  loop: "{{ groups.windows }}"

- name: Start VM
  when: vm_present.results[item] is success
  community.general.proxmox_kvm:
    api_host: ANYPVEHOST
    api_user: APIPVEUSER@pve
    api_token_id: PVETOKEN
    api_token_secret: "{{ PVETOKENSECRET }}"

    vmid: "{{ hostvars[item].vars.vmid }}"
    node: PVENODE
    state: started
  register: vm_started
  loop: "{{ groups.windows }}"

Solution

  • Simply debug the content of all your variables to see your mistake. You loop over a range of integers in your first examples which are then used as list indexes in your when clause.

    Your last example loops over machine names in a group as strings which are indeed not list indexes.

    You are taking this the wrong way anyhow. To fix a first common Ansible beginners error, you could loop directly on the results filtering out the unsuccessful ones. Each element contains the original looped value in the item key (the machine name is hence item.item)

    - name: Check if VM is present
      community.general.proxmox_vm_info:
        api_host: ANYPVEHOST
        api_user: APIPVEUSER@pve
        api_token_id: PVETOKEN
        api_token_secret: "{{ PVETOKENSECRET }}"
        node: "{{ hostvars[item].node }}"
        vmid: "{{ hostvars[item].vmid }}"
      until: vm_present.proxmox_vms is defined
      retries: 60
      delay: 1
      register: vm_present
      loop: "{{ groups.windows }}"
    
    - name: Start VM
      community.general.proxmox_kvm:
        api_host: ANYPVEHOST
        api_user: APIPVEUSER@pve
        api_token_id: PVETOKEN
        api_token_secret: "{{ PVETOKENSECRET }}"
        vmid: "{{ hostvars[item.item].vmid }}"
        node: PVENODE
        state: started
      register: vm_started
      loop: "{{ vm_present.results | select('success') }}"
    

    This is still not optimal as you are using unnecessary loops. Your playbook becomes much simpler if you directly target your group and delegate the necessary tasks to localhost. The hosts failing the first task will not make it to the second one.

    ---
    - name: Manage VMS
      hosts: windows
      gather_facts: false
    
      tasks:
        - name: Check if VM is present
          community.general.proxmox_vm_info:
            api_host: ANYPVEHOST
            api_user: APIPVEUSER@pve
            api_token_id: PVETOKEN
            api_token_secret: "{{ PVETOKENSECRET }}"
            node: "{{ node }}"
            vmid: "{{ vmid }}"
          register: vm_present
          until: vm_present.proxmox_vms is defined
          retries: 60
          delay: 1
          delegate_to: localhost
    
        - name: Start VM
          community.general.proxmox_kvm:
            api_host: ANYPVEHOST
            api_user: APIPVEUSER@pve
            api_token_id: PVETOKEN
            api_token_secret: "{{ PVETOKENSECRET }}"
            vmid: "{{ vmid }}"
            node: PVENODE
            state: started
          delegate_to: localhost