Search code examples
ansibleansible-factsjson-query

Ansible: Task giving error when trying to parse 'ansible_facts'


I am fairly new to Ansible and am trying to create a playbook that will look at my Docker Swarm machines, check what NFS shares are already mounted, mount specified NFS shares from a list if they are not already there and unmount any NFS shares that should not be on that type of machine.

Currently when I run this with --check I get an error, that if I am reading correctly, is saying that the JSON query of the ansible_facts does not exist. I am sure I am doing something wrong but not sure what. I have confirmed that there is at least one NFS mount in the ansible_facts dictionary that has "fstype"="nfs4".

The actual error is below

fatal: [DockerSwarmManager01]: FAILED! =>
  msg: 'Unexpected templating type error occurred on ({{ ansible_facts | community.general.json_query(query) | list }}): ''NoneType'' object is not iterable. ''NoneType'' object is not iterable'

Playbook code below

---
- name: mount NFS shares to docker swarm members
  hosts: "docker_swarm"
  become: yes
  vars:
    nfsmounts:
      - {  path: "/srv/docker/paperless-consume", src: "192.168.100.5:/mnt/scale01-corvette/paperless", where: "worker" }
      - {  path: "/srv/docker/syncthing", src: "192.168.100.5:/mnt/scale01-corvette/docker/syncthing", where: "worker" }
      - {  path: "/srv/docker/paperless", src: "192.168.100.5:/mnt/scale01-corvette/docker/paperless", where: "worker" }
      - {  path: "/srv/docker/cloudflared", src: "192.168.100.5:/mnt/scale01-corvette/docker/cloudflared", where: "worker" }
      - {  path: "/srv/docker/portainer", src: "192.168.100.5:/mnt/scale01-corvette/docker/portainer", where: "all" }
      - {  path: "/srv/docker/gitlab", src: "192.168.100.5:/mnt/scale01-corvette/docker/gitlab", where: "worker" }
      - {  path: "/srv/docker/traefik", src: "192.168.100.5:/mnt/scale01-corvette/docker/traefik", where: "manager" }
      - {  path: "/srv/docker/flame", src: "192.168.100.5:/mnt/scale01-corvette/docker/flame", where: "worker" }

  tasks:

    - name: gather mounted nfs shares as ansible_fact
      ansible.builtin.set_fact:
        nfs_mounts_list: "{{ ansible_facts | json_query(query) | list }}"
      vars:
        query: "ansible_facts.ansible_mounts[?fstype=='nfs4'].mount"

    - name: mount the nfsshare in client side docker worker
      ansible.posix.mount:
        fstype: nfs
        opts: rw,rsize=8192,wsize=8192,timeo=14,proto=tcp
        state: mounted
        src: "{{ item.src }}"
        path: "{{ item.path }}"
      loop: "{{ nfsmounts | flatten(level=1) }}"
      when: (item.path not in nfs_mounts_list) and (inventory_hostname in groups['docker_swarm_workers']) and (( {{ item.where }} == "worker" or ( {{ item.where }} == "all" ))

    - name: ensure unwanted nfsshares are not present in client side docker worker
      ansible.posix.mount:
        fstype: nfs
        opts: rw,rsize=8192,wsize=8192,timeo=14,proto=tcp
        state: absent
        src: "{{ item.src }}"
        path: "{{ item.path }}"
      loop: "{{ nfsmounts | flatten(level=1) }}"
      when: (item.path in nfs_mounts_list) and (inventory_hostname in groups['docker_swarm_workers']) and (( {{ item.where }} != "worker" and ( {{ item.where }} != "all" ))

    - name: mount the nfsshare in client side docker manager
      ansible.posix.mount:
        fstype: nfs
        opts: rw,rsize=8192,wsize=8192,timeo=14,proto=tcp
        state: mounted
        src: "{{ item.src }}"
        path: "{{ item.path }}"
      loop: "{{ nfsmounts | flatten(level=1) }}"
      when: (item.path not in nfs_mounts_list) and (inventory_hostname in groups['docker_swarm_managers']) and (( {{ item.where }} == "manager" or ( {{ item.where }} == "all" ))

    - name: ensure unwanted nfsshares are not present in client side docker manager
      ansible.posix.mount:
        fstype: nfs
        opts: rw,rsize=8192,wsize=8192,timeo=14,proto=tcp
        state: absent
        src: "{{ item.src }}"
        path: "{{ item.path }}"
      loop: "{{ nfsmounts | flatten(level=1) }}"
      when: (item.path in nfs_mounts_list) and (inventory_hostname in groups['docker_swarm_managers']) and (( {{ item.where }} != "manager" and ( {{ item.where }} != "all" ))

Update I have changed the relevant task to be

- name: gather mounted nfs shares from ansible_facts
      ansible.builtin.set_fact:
        nfs_mounts_list: "{{ ansible_facts | community.general.json_query(query) | list }}"
      vars:
        query: "ansible_mounts[?fstype==`nfs4`].mount"

and still get the same output.

Taking a sample of the ansible_facts as seen below

{
    "ansible_facts": 
    {
        "ansible_mounts": 
        [
            {
                "mount": "/srv/docker/paperless-consume", 
                "device": "192.168.100.5:/mnt/scale01-corvette/paperless", 
                "fstype": "nfs4", 
                "options": "rw,relatime,vers=4.2,rsize=8192,wsize=8192,namlen=255,hard,proto=tcp,timeo=14,retrans=2,sec=sys,clientaddr=192.168.100.26,local_lock=none,addr=192.168.100.5", 
                "size_total": 3800493850624, 
                "size_available": 3800493588480, 
                "block_size": 8192, 
                "block_total": 463927472, 
                "block_available": 463927440, 
                "block_used": 32, 
                "inode_total": 7422839117, 
                "inode_available": 7422839109, 
                "inode_used": 8, 
                "uuid": "N/A"
            },
            {
                "mount": "/srv/docker/portainer", 
                "device": "192.168.100.5:/mnt/scale01-corvette/docker/portainer", 
                "fstype": "nfs", 
                "options": "rw,relatime,vers=3,rsize=8192,wsize=8192,namlen=255,hard,proto=tcp,timeo=14,retrans=2,sec=sys,mountaddr=192.168.100.5,mountvers=3,mountport=47071,mountproto=tcp,local_lock=none,addr=192.168.100.5", 
                "size_total": 3800494243840, 
                "size_available": 3800493588480, 
                "block_size": 8192, 
                "block_total": 463927520, 
                "block_available": 463927440, 
                "block_used": 80, 
                "inode_total": 7422839236, 
                "inode_available": 7422839109, 
                "inode_used": 127, 
                "uuid": "N/A"
            }
        ]
    }
}

Full section error message

TASK [gather mounted nfs shares from ansible_facts] **********************************************************************************************************************************************
task path: /home/travis/playbooks/one_time/mount_nfs_share.yml:22
fatal: [DockerSwarmManager01]: FAILED! =>
  msg: 'Unexpected templating type error occurred on ({{ ansible_facts | community.general.json_query(query) | list }}): ''NoneType'' object is not iterable. ''NoneType'' object is not iterable'
fatal: [DockerSwarmManager02]: FAILED! =>
  msg: 'Unexpected templating type error occurred on ({{ ansible_facts | community.general.json_query(query) | list }}): ''NoneType'' object is not iterable. ''NoneType'' object is not iterable'
fatal: [DockerSwarmManager03]: FAILED! =>
  msg: 'Unexpected templating type error occurred on ({{ ansible_facts | community.general.json_query(query) | list }}): ''NoneType'' object is not iterable. ''NoneType'' object is not iterable'
fatal: [DockerSwarmWorker01]: FAILED! =>
  msg: 'Unexpected templating type error occurred on ({{ ansible_facts | community.general.json_query(query) | list }}): ''NoneType'' object is not iterable. ''NoneType'' object is not iterable'
fatal: [DockerSwarmWorker02]: FAILED! =>
  msg: 'Unexpected templating type error occurred on ({{ ansible_facts | community.general.json_query(query) | list }}): ''NoneType'' object is not iterable. ''NoneType'' object is not iterable'
fatal: [DockerSwarmWorker03]: FAILED! =>
  msg: 'Unexpected templating type error occurred on ({{ ansible_facts | community.general.json_query(query) | list }}): ''NoneType'' object is not iterable. ''NoneType'' object is not iterable'

I got it working with the following code

---
- name: mount nfs shares to docker swarm members
  hosts: "docker_swarm"
  become: yes
  vars:
    nfsmounts:
      - {  path: "/srv/docker/paperless-consume", src: "192.168.100.5:/mnt/scale01-corvette/paperless", where: "worker" }
      - {  path: "/srv/docker/syncthing", src: "192.168.100.5:/mnt/scale01-corvette/docker/syncthing", where: "worker" }
      - {  path: "/srv/docker/paperless", src: "192.168.100.5:/mnt/scale01-corvette/docker/paperless", where: "worker" }
      - {  path: "/srv/docker/cloudflared", src: "192.168.100.5:/mnt/scale01-corvette/docker/cloudflared", where: "worker" }
      - {  path: "/srv/docker/portainer", src: "192.168.100.5:/mnt/scale01-corvette/docker/portainer", where: "all" }
      - {  path: "/srv/docker/gitlab", src: "192.168.100.5:/mnt/scale01-corvette/docker/gitlab", where: "worker" }
      - {  path: "/srv/docker/traefik", src: "192.168.100.5:/mnt/scale01-corvette/docker/traefik", where: "manager" }
      - {  path: "/srv/docker/flame", src: "192.168.100.5:/mnt/scale01-corvette/docker/flame", where: "worker" }

  tasks:

    - name: gather mounted nfs shares from ansible_facts
      ansible.builtin.set_fact:
        nfs_mounts_list: "{{ ansible_facts | to_json | from_json | community.general.json_query(query) | list }}"
      vars:
        query: mounts[?contains(fstype, `nfs`)].mount

    - name: mount the nfsshare in client side docker worker
      vars:
        where_to_mount: "{{item.where}}"
      ansible.posix.mount:
        fstype: nfs
        opts: rw,rsize=8192,wsize=8192,timeo=14,proto=tcp
        state: mounted
        src: "{{ item.src }}"
        path: "{{ item.path }}"
      loop: "{{ nfsmounts | flatten(1) }}"
      when: (item.path not in nfs_mounts_list) and (inventory_hostname in groups['docker_swarm_worker']) and (( item.where == "worker") or ( item.where == "all" ))

    - name: ensure unwanted nfsshares are not present in client side docker worker
      ansible.posix.mount:
        fstype: nfs
        opts: rw,rsize=8192,wsize=8192,timeo=14,proto=tcp
        state: absent
        src: "{{ item.src }}"
        path: "{{ item.path }}"
      loop: "{{ nfsmounts | flatten(1) }}"
      when: (item.path in nfs_mounts_list) and (inventory_hostname in groups['docker_swarm_worker']) and (( item.where != "worker") and ( item.where != "all" ))

    - name: mount the nfsshare in client side docker manager
      ansible.posix.mount:
        fstype: nfs
        opts: rw,rsize=8192,wsize=8192,timeo=14,proto=tcp
        state: mounted
        src: "{{ item.src }}"
        path: "{{ item.path }}"
      loop: "{{ nfsmounts | flatten(1) }}"
      when: (item.path not in nfs_mounts_list) and (inventory_hostname in groups['docker_swarm_manager']) and (( item.where == "manager") or ( item.where == "all" ))

    - name: ensure unwanted nfsshares are not present in client side docker manager
      ansible.posix.mount:
        fstype: nfs
        opts: rw,rsize=8192,wsize=8192,timeo=14,proto=tcp
        state: absent
        src: "{{ item.src }}"
        path: "{{ item.path }}"
      loop: "{{ nfsmounts | flatten(1) }}"
      when: (item.path in nfs_mounts_list) and (inventory_hostname in groups['docker_swarm_manager']) and (( item.where != "manager") and ( item.where != "all" ))

where I've mainly changed the JSON query from ansible_facts.ansible_mounts

    - name: gather mounted nfs shares as ansible_fact
      ansible.builtin.set_fact:
        nfs_mounts_list: "{{ ansible_facts | json_query(query) | list }}"
      vars:
        query: "ansible_facts.ansible_mounts[?fstype=='nfs4'].mount"

to mounts

    - name: gather mounted nfs shares from ansible_facts
      ansible.builtin.set_fact:
        nfs_mounts_list: "{{ ansible_facts | to_json | from_json | community.general.json_query(query) | list }}"
      vars:
        query: mounts[?contains(fstype, `nfs`)].mount

Solution

  • As you have already mentioned within your comments

    I got it figured out. It seems that the Ansible facts JSON output shows the ansible_mounts section name but it can not be referenced with that rather you have to leave the ansible_ off and only use mounts.

    you have to use either ansible_facts.mounts or ansible_mounts.

    Very detailed background information can be found within a Q&A about Why should I remove the ansible_ prefix when referring to an Ansible fact?.