Search code examples
ansibleansible-2.xjmespathjson-query

Ansible: Looking for a known key with a known value at an arbitrary depth using json_query?


Is it possible to accomplish something like this using json_query? I wasn't able to find anything after quite a bit of searching (neither with json_query nor with jmespath). Everything I was able to find assumed that the structure of the dict/json is known (i.e. the depth of the searched-for key is known).

Sample JSON input:

{
  "changed": false,
  "msg": {
    "Data": {
      "Message": "returned status code doesn't match with the expected success code",
      "Status": "Failed",
      "StatusCode": 409
    },
    "Message": "none",
    "Status": "Failed",
    "StatusCode": 409,
    "error": {
      "error": {
        "@Message.ExtendedInfo": [
          {
            "Message": "Server is already powered OFF.",
            "MessageArgs": [

            ],
            "MessageArgs@odata.count": 0,
            "MessageId": "IDRAC.1.6.PSU502",
            "RelatedProperties": [

            ],
            "RelatedProperties@odata.count": 0,
            "Resolution": "No response action is required.",
            "Severity": "Informational"
          }
        ],
        "code": "Base.1.0.GeneralError",
        "message": "A general error has occurred. See ExtendedInfo for more information"
      }
    },
    "retval": true
  }
}

I would like to check whether a key Message is present with the value Server is already powered OFF. without assuming the depth of the key/structure of the JSON.


Solution

  • With custom filter_plugins

    $ ls -1 filter_plugins/*.py
    filter_plugins/dict_utils.py
    filter_plugins/list_methods.py
    

    the task below

      vars:
        my_key: Message
        my_value: Server is already powered OFF
      tasks:
        - debug:
            msg: "{{ item.key }}: {{ item.value is search(my_value)|
                                     ternary(my_value, 'NOT FOUND') }}"
          loop: "{{ input|dict_flatten|dict2items }}"
          when: item.key.split('.')|list_reverse|first == my_key
    

    gives

    "msg": "msg.Data.Message: NOT FOUND"
    "msg": "msg.error.error.@Message.ExtendedInfo.0.Message: Server is already powered OFF"
    "msg": "msg.Message: NOT FOUND"
    

    Details

    Example of filter dict_flatten from dict_utils below

    vars:
    dict4: {
          "a":{
            "r": 1,
            "s": 2,
            "t": 3
            },
          "b":{
            "u": 1,
            "v": {
                "x": 1,
                "y": 2,
                "z": [ 3, 4, 5 ]
              },
            "w": 3
            }
          }
    tasks:
      - debug:
          var: dict4_flatten
        vars:
          dict4_flatten: "{{ dict4|dict_flatten('.') }}"
    

    gives

    "dict4_flatten": {
        "a.r": 1, 
        "a.s": 2, 
        "a.t": 3, 
        "b.u": 1, 
        "b.v.x": 1, 
        "b.v.y": 2, 
        "b.v.z.0": 3, 
        "b.v.z.1": 4, 
        "b.v.z.2": 5, 
        "b.w": 3
    }