Ansible - Compare dictionaries works but will not accept an addition

Viewed 34

I am trying to compare two dictionaries and create a 3rd dictionary if an item has changed or is not present. I have this playbook

---
- hosts: localhost
  connection: local
  gather_facts: no
  vars:
    host_list1:
      host1: 1.1.1.1
      host2: 2.2.2.2
      host3: 3.3.3.3
    host_list2:
       host1: 1.1.1.1
       host2: 2.2.2.2
       host3: 3.3.3.3
  tasks:
    - name: Define Dict
      set_fact: 
        diff_list: {}

    - name: Compare lists
      set_fact:
        diff_list: "{{ diff_list|combine({item: host_list1[item]}) }}"
      loop: "{{ host_list1.keys()|list }}"
      when: host_list1[item] != host_list2[item]

    - name: Display Results
      debug:
        msg: "{{ diff_list }}"

If I change the IP in host_list1 it has the desired result and will update the diff_list.

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

TASK [Define Dict] ************************************************************************************************************************************************************
ok: [localhost]

TASK [Compare lists] **********************************************************************************************************************************************************
ok: [localhost] => (item=host3)
ok: [localhost] => (item=host2)
skipping: [localhost] => (item=host1)

TASK [Display Results] ********************************************************************************************************************************************************
ok: [localhost] => {
    "msg": {
        "host2": "2.2.2.5",
        "host3": "3.3.3.4"
    }
}

PLAY RECAP ********************************************************************************************************************************************************************
localhost                  : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

However if I try and add another host into the host_list1 dictionary I get the following error

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

TASK [Define Dict] ************************************************************************************************************************************************************
ok: [localhost]

TASK [Compare lists] **********************************************************************************************************************************************************
fatal: [localhost]: FAILED! => {"msg": "The conditional check 'host_list1[item] != host_list2[item]' failed. The error was: error while evaluating conditional (host_list1[item] != host_list2[item]): 'dict object' has no attribute u'host4'\n\nThe error appears to be in '/home/playbooks/test.yml': line 20, 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: Compare lists\n      ^ here\n"}

PLAY RECAP ********************************************************************************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0

I have tried updating the when statement

when: host_list1[item] != host_list2[item] or not defined

However this does not work and I am unable to get the new host appended to the diff_list dictionary

2 Answers

The problem is your conditional statement:

when: host_list1[item] != host_list2[item]

If item is key that is present in only host_list1, then host_list2[item] is an error. There are (at least!) two ways of fixing this.

Use a default value

You can use the default filter to provide a default value when the result of an item lookup is undefined:

when: host_list1[item] != host_list2[item]|default(none)

Explicitly check for membership

You can check that the key exists in host_list2 before attempting to get its value:

when: item not in host_list2 or host_list1[item] != host_list2[item]

You can get your result using a single jinja2 expression without using any tasks.

The following playbook:

---
- hosts: localhost
  connection: local
  gather_facts: no
  vars:

    hosts_current:
      host1: 1.1.1.1
      host2: 2.2.2.3
      host3: 3.3.3.4
      host4: 5.5.5.5

    hosts_origin:
       host1: 1.1.1.1
       host2: 2.2.2.2
       host3: 3.3.3.3

    diff_dict: >-
      {{
        hosts_origin | dict2items
        | symmetric_difference(hosts_current | dict2items)
        | items2dict
      }}

  tasks:
    - name: Show the calculated var
      debug:
        var: diff_dict

gives:

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

TASK [Show the calculated var] ************************************************************************************************************************************************************************************************
ok: [localhost] => {
    "diff_dict": {
        "host2": "2.2.2.3",
        "host3": "3.3.3.4",
        "host4": "5.5.5.5"
    }
}

PLAY RECAP ********************************************************************************************************************************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
Related