Skip to content
Advertisement

Encountering ‘dict object’ has no attribute ‘results’ with Ansible despite previous checks indicating it does exist

I am using Ansible and I am encountering an issue that I do not understand or know how to solve. Hopefully someone can help me.

The problem occurs with a failed_when conditional:

fatal: [localhost]: FAILED! => {
    "msg": "The conditional check ''must not contain special characters or whitespace' not in result.results[0].json['localizedMessage']' failed. The error was: error while evaluating conditional ('must not contain special characters or whitespace' not in result.results[0].json['localizedMessage']): 'dict object' has no attribute 'results'"
}

My task is:

  - name: Create instance - must not contain special characters or whitespace
    uri:
     url: "https://{{PROXY_ADDRESS}}/api/sol005/etsi/nslcm/v2/ns_instances"
     status_code: 400
     validate_certs: no
     body_format: json
     method: POST
     headers:
      Content-type: application/json
      Authorization: "{{'Bearer'+' '+access_token}}"
      Version: "2.0.0"
      Accept: "*/*"
     body: >
           { "nsdId": "instance::int::1.0",
             "nsName": "{{ item }}",
             "nsDescription": "Test instance with name containing invalid characters."
           }
    register: result
#  - set_fact:
#      localizedMessage: "{{ result.results[0].json['localizedMessage'] }}"
    with_items:
      - "caesar:test"
    failed_when: "'must not contain special characters or whitespace' not in result.results[0].json['localizedMessage']"

In attempting to debug, I have written the following:

 - debug:
      msg:
        - "============ Printing result output ============"
        - "Showing result type: {{ result | type_debug }}"
        - "Showing result contents: {{ result}}"
        - "Showing result.keys(): {{ item }}"
    with_items: "{{ result.keys() }}"
  - debug:
      msg:
        - "============ Printing result.results output ============"
        - "Showing result.results type: {{ result.results | type_debug }}"
        - "Showing result.results contents: {{ result.results }}"
  - debug:
      msg:
        - "============ Printing result.results[0] output ============"
        - "Showing result.results[0].type: {{ result.results[0] | type_debug }}"
        - "Showing result.results[0].contents: {{ result.results[0] }}"
        - "Showing result.results[0].keys(): {{ item }}"
    with_items: "{{ result.results[0].keys() }}"
  - debug:
      msg:
        - "============ Printing result.results[0].json output ============"
        - "Showing result.results[0].json type: {{ result.results[0].json | type_debug }}"
        - "Showing result.results[0].json contents: {{ result.results[0].json }}"
        - "Showing result.results[0].json.keys(): {{ item }}"
    with_items: "{{ result.results[0].json.keys() }}"
  - debug:
      msg:
        - "============ Printing result.results[0].json.localizedMessage output ============"
        - "Showing result.results[0].json.localizedMessage type: {{ result.results[0].json.localizedMessage | type_debug }}"
        - "Showing result.results[0].json.localizedMessage contents: {{ result.results[0].json.localizedMessage }}"

where, I think, the important information here is:

        "============ Printing result output ============",
        "Showing result type: dict",
        "Showing result.keys(): dict_keys(['results', 'msg', 'changed'])"

       "============ Printing result.results output ============",
        "Showing result.results type: list",

        "============ Printing result.results[0] output ============",
        "Showing result.results[0].type: dict",
        "Showing result.results[0].keys(): dict_keys(['redirected', 'url', 'status', 'x_content_type_options', 'x_xss_protection', 'cache_control', 'pragma', 'expires', 'strict_transport_security', 'x_frame_options', 'vary', 'content_type', 'transfer_encoding', 'date', 'connection', 'server', 'msg', 'elapsed', 'changed', 'json', 'invocation', 'failed', 'item', 'ansible_loop_var'])"

        "============ Printing result.results[0].json output ============",
        "Showing result.results[0].json type: dict",
        "Showing result.results[0].json.keys(): dict_keys(['url', 'localizedMessage', 'details'])"

        "============ Printing result.results[0].json.localizedMessage output ============",
        "Showing result.results[0].json.localizedMessage type: AnsibleUnsafeText",
        "Showing result.results[0].json.localizedMessage contents: A INVALID_REQUEST error has occurred: name must not contain special characters or whitespace (excluding: '-', '_')"

I am new to Ansible, perhaps that is why I am not seeing it, but with the wording 'dict object' has no attribute 'results' it suggests that result, which is a dict, does not have the attribute results, which my debugging suggests it does. I have been able to traverse to result.results[0].json & result.results[0].json.localizedMessage so it looks OK to me.

Can anyone advise? I would like to understand where I am going wrong, alternatively I’ll take suggestions on performing the failed_when check using some other approach.

Advertisement

Answer

Your issue is coming from the fact that Ansible is creating results in a very peculiar way:

  1. The items are registered as if you where using no loop at all, and you can reference the previous element via your registered variable.
  2. When you exist the loop, then the results key is created in the dictionary and then populated from all the results.

This is described in a really short fashioned way in this sentence and example:

During iteration, the result of the current item will be placed in the variable:

- name: Place the result of the current item in the variable
  shell: echo "{{ item }}"
  loop:
    - one
    - two
  register: echo
  changed_when: echo.stdout != "one"

Source: https://docs.ansible.com/ansible/2.9/user_guide/playbooks_loops.html#registering-variables-with-a-loop


So if you want a self registered task that check for its own items status you will just have to do something like:

failed_when: "'must not contain special characters or whitespace' not in result.json.localizedMessage"

Here are two example playbooks:

  1. The playbook
    - hosts: localhost
      gather_facts: no
    
      tasks:
        - uri:
            url: "{{ item }}"
          loop:
            - https://httpbin.org/get
            - https://httpbin.org/anything
          register: result
          failed_when: "result.json.url == 'https://httpbin.org/get'"
    
    That yields the recap:
    PLAY [localhost] ***************************************************************************************************
    
    TASK [uri] *********************************************************************************************************
    failed: [localhost] (item=https://httpbin.org/get) => {"access_control_allow_credentials": "true", "access_control_allow_origin": "*", "ansible_loop_var": "item", "changed": false, "connection": "close", "content_length": "274", "content_type": "application/json", "cookies": {}, "cookies_string": "", "date": "Fri, 18 Dec 2020 18:17:12 GMT", "elapsed": 0, "failed_when_result": true, "item": "https://httpbin.org/get", "json": {"args": {}, "headers": {"Accept-Encoding": "identity", "Host": "httpbin.org", "User-Agent": "ansible-httpget", "X-Amzn-Trace-Id": "Root=1-5fdcf228-559950431b1315ce1ea53e71"}, "url": "https://httpbin.org/get"}, "msg": "OK (274 bytes)", "redirected": false, "server": "gunicorn/19.9.0", "status": 200, "url": "https://httpbin.org/get"}
    ok: [localhost] => (item=https://httpbin.org/anything)
    
    PLAY RECAP *********************************************************************************************************
    localhost                  : ok=0    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   
    
  2. The playbook
    - hosts: localhost
      gather_facts: no
    
      tasks:
        - uri:
            url: "{{ item }}"
          loop:
            - https://httpbin.org/get
            - https://httpbin.org/anything
          register: result
          failed_when: "result.json.url == 'https://httpbin.org/anything'"
    
    That yields the recap:
    PLAY [localhost] ***************************************************************************************************
    
    TASK [uri] *********************************************************************************************************
    ok: [localhost] => (item=https://httpbin.org/get)
    failed: [localhost] (item=https://httpbin.org/anything) => {"access_control_allow_credentials": "true", "access_control_allow_origin": "*", "ansible_loop_var": "item", "changed": false, "connection": "close", "content_length": "362", "content_type": "application/json", "cookies": {}, "cookies_string": "", "date": "Fri, 18 Dec 2020 18:18:40 GMT", "elapsed": 0, "failed_when_result": true, "item": "https://httpbin.org/anything", "json": {"args": {}, "data": "", "files": {}, "form": {}, "headers": {"Accept-Encoding": "identity", "Host": "httpbin.org", "User-Agent": "ansible-httpget", "X-Amzn-Trace-Id": "Root=1-5fdcf280-71b7cca020b6fda0055b4603"}, "json": null, "method": "GET", "origin": "127.0.0.1", "url": "https://httpbin.org/anything"}, "msg": "OK (362 bytes)", "redirected": false, "server": "gunicorn/19.9.0", "status": 200, "url": "https://httpbin.org/anything"}
    
    PLAY RECAP *********************************************************************************************************
    localhost                  : ok=0    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   
    
Advertisement