Search code examples
ansibleansible-toweransible-awx

Undefined variable error 'dict object' on various hosts when running playbook


EDIT: It seems this only occurs when running with the --check parameter. Running this playbook live doesn't throw this error. But it would still be good to know what is causing this.

I'm starting to use Ansible AWX to manage a bunch of servers and haven't used Ansible before, though I've gone through a number of online tutorials and feel quite comfortable with it.

I'm attempting to run a playbook which installs updates to a number of webservers.

It's throwing an error up, which strangely appears on different hosts on different runs. For example, if I run the playbook, the host server3.mydomain.com fails with this error. If I remove that server from the inventory, then I get the same error on server2.mydomain.com, and so on and so on.

The error output doesn't provide enough information for me to figure out why this is failing, even though it isolates it to one small section, and I haven't managed to find the issue through an online search.

This is the playbook (from an template I found online, with some changes):

---
- name: ensure services are up before doing anything
  hosts: webservers
  become: true
  any_errors_fatal: true
  serial: 1
  tasks:

- name: upgrade packages and reboot (if necessary)
  hosts: webservers
  become: true
  serial: 1 
  any_errors_fatal: true
  max_fail_percentage: 0

  tasks: 

    - name: apt-get update
      apt:
        update-cache: yes
      changed_when: 0

    - name: get list of pending upgrades
      command: apt-get --simulate dist-upgrade
      args:
        warn: false 
      register: apt_simulate
      changed_when: 0

    - name: parse apt-get output to get list of changed packages
      set_fact: 
        updates: '{{ apt_simulate.stdout_lines | select("match", "^Inst ") | list | sort }}'
      changed_when: 0

    - name: show pending updates
      debug:
        var: updates
      when: updates.0 is defined

    - name: apt-get autoremove
      command: apt-get -y autoremove
      args:
        warn: false
      when: '"Inst linux-image-" in apt_simulate.stdout'
      changed_when: 0

    - name: apt-get dist-upgrade
      apt:
        upgrade: dist 
      register: upgrade_output

    - name: check if reboot needed
      stat: path=/var/run/reboot-required
      register: file_reboot_required

    - meta: end_play
      when: not file_reboot_required.stat.exists

    - name: reboot node
      shell: sleep 2 && shutdown -r now "Reboot triggered by ansible"
      async: 1
      poll: 0
      ignore_errors: true

    - name: wait for node to finish booting
      wait_for_connection:
          connect_timeout=10
          delay=30
          timeout=120

    - name: wait for ssh to start fully
      pause:
        seconds: 45

And this is the error:

fatal: [server3.mydomain.com]: FAILED! => {
    "msg": "The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'stdout_lines'\n\nThe error appears to have been in '/var/lib/awx/projects/_8__infrastructure_management/projects/infrastructure-management/test/test.yml': line 30, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n    - name: parse apt-get output to get list of changed packages\n      ^ here\n"

So, the error appears to relate to this block, but beyond that I'm stuck:

- name: parse apt-get output to get list of changed packages
  set_fact: 
    updates: '{{ apt_simulate.stdout_lines | select("match", "^Inst ") | list | sort }}'
  changed_when: 0

Can't see what I am missing here.


Solution

  • Running with --check has no way to "dry run" a command: or shell: module, since it cannot predict what is safe to run versus not. Thus, since the command: doesn't run, it doesn't create any stdout_lines in that apt_simulate variable. Interestingly enough, using debug: var=apt_simulate shows that it actually does say apt_simulate.skipped=True as well as apt_simulate.msg="remote module (command) does not support check mode". So you can decide for yourself if you wish to just guard that updates: reference with when: not apt_simulate.skipped or even go so far as when: not {{ansible_check_mode}}

    Thankfully, you can override commands behavior by specifying check_mode: no if you are certain -- as appears to be the case with your command -- that it truly is safe to run the command even in check mode.