YAML using json_query to print specific value array with filter string contained dot

Viewed 37

I have problem to filter array from output that i've got from some module. So this below is the output of the module that i have put it to some variable, and then i want to show/print only specific value from this variable. Maybe you guys can help me?

{
  "extraConfig": [
    {
      "_vimtype": "vim.option.OptionValue",
      "key": "svga.guestBackedPrimaryAware",
      "value": "TRUE"
    },
    {
      "_vimtype": "vim.option.OptionValue",
      "key": "guestOS.detailed.data",
      "value": "bitness='64' distroName='Red Hat Enterprise Linux' distroVersion='8.2' familyName='Linux' kernelVersion='4.18.0-193.el8.x86_64' prettyName='Red Hat Enterprise Linux 8.2 (Ootpa)'"
    }
  ]
}

This above is the value of variable summary, then i want to get only prettyName='Red Hat Enterprise Linux 8.2 (Ootpa)', is it possible?

I have tried using ?contains but still got empty value, then i tried this below but got error:

fatal: [localhost]: FAILED! => {"msg": "Error in jmespath.search in json_query filter plugin:\n'method' object is not iterable"}

- debug:  
    msg: "{{ vmhost_info | json_query(query) }}"
  vars:
    keyvar: 'guestOS.detailed.data'
    query: "instance.config.extraConfig[?key == '{{ keyvar }}'].value"
2 Answers

It looks like your existing query is pretty close; it returns the string value:

bitness='64' distroName='Red Hat Enterprise Linux' distroVersion='8.2' familyName='Linux' kernelVersion='4.18.0-193.el8.x86_64' prettyName='Red Hat Enterprise Linux 8.2 (Ootpa)'

There are a couple of ways of getting at the value of prettyName. One option is to first split the string into multiple lines. If we split the string on ' , each line begins with the key name. That is, this task:

- debug:
    msg: >-
      {{
        (vmhost_info | json_query(query))  |
        first |
        split("' ")
      }}
  vars:
    keyvar: 'guestOS.detailed.data'
    query: "instance.config.extraConfig[?key == '{{ keyvar }}'].value"

Results in:

TASK [debug] ********************************************************************************************
ok: [localhost] => {
    "msg": [
        "bitness='64",
        "distroName='Red Hat Enterprise Linux",
        "distroVersion='8.2",
        "familyName='Linux",
        "kernelVersion='4.18.0-193.el8.x86_64",
        "prettyName='Red Hat Enterprise Linux 8.2 (Ootpa)'"
    ]
}

Now that we have a list of items, each one beginning with the key name, we can use the select filter to extract the prettyName value:

- debug:
    msg: >-
      {{
        (vmhost_info | json_query(query))  | first |
        split("' ") |
        select('match', '^prettyName') | first |
        split('=')
        | last | replace("'", "")
      }}
  vars:
    keyvar: 'guestOS.detailed.data'
    query: "instance.config.extraConfig[?key == '{{ keyvar }}'].value"

Which yields:

TASK [debug] ********************************************************************************************
ok: [localhost] => {
    "msg": "Red Hat Enterprise Linux 8.2 (Ootpa)"
}

We use the select filter to select only those items in the list that start with prettyName, which gives us a single item list. The first filter gives us the first item from the list, which we then split on = to get a (key, value) tuple. The last filter gives us the value, and then strip the single quotes using replace.

Select the item from the list

  keyvar: "guestOS.detailed.data"
  data: "{{ vmhost_info.instance.config.extraConfig|
            selectattr('key', 'eq', keyvar)|first }}"

Replace the delimiting spaces with newlines and equal signs with colons. Create the dictionary params

  params: "{{ data.value|replace(pattern1, newline)|
                         replace(pattern2, colon)|
                         from_yaml }}"
  pattern1: "' "
  pattern2: "="
  newline: "'\n"
  colon: ": "

gives

  params:
    bitness: '64'
    distroName: Red Hat Enterprise Linux
    distroVersion: '8.2'
    familyName: Linux
    kernelVersion: 4.18.0-193.el8.x86_64
    prettyName: Red Hat Enterprise Linux 8.2 (Ootpa)

Example of a complete playbook for testing

- hosts: localhost

  vars:

    vmhost_info:
      instance:
        config:
          extraConfig:
            - _vimtype: vim.option.OptionValue
              key: svga.guestBackedPrimaryAware
              value: 'TRUE'
            - _vimtype: vim.option.OptionValue
              key: guestOS.detailed.data
              value: >-
                bitness='64' distroName='Red Hat Enterprise Linux' distroVersion='8.2'
                familyName='Linux' kernelVersion='4.18.0-193.el8.x86_64'
                prettyName='Red Hat Enterprise Linux 8.2 (Ootpa)'

    keyvar: "guestOS.detailed.data"
    data: "{{ vmhost_info.instance.config.extraConfig|
              selectattr('key', 'eq', keyvar)|first }}"

    params: "{{ data.value|replace(pattern1, newline)|
                           replace(pattern2, colon)|
                           from_yaml }}"
    pattern1: "' "
    pattern2: "="
    newline: "'\n"
    colon: ": "
            
  tasks:

    - debug:
        var: params
    - debug:
        var: params.prettyName

gives the value of the attribute prettyName

  params.prettyName: Red Hat Enterprise Linux 8.2 (Ootpa)

Q: "There is no simple way I guess?"

A: The filter community.general.jc seems to be a candidate to make it simpler. But, unfortunately, it

  • works with lines only

  • doesn't preserve the case of the keys, and

  • doesn't remove the quotation as promised

    params: "{{ data.value|replace(pattern1, newline)|
                           community.general.jc('ini') }}"
    pattern1: "' "
    newline: "'\n"

gives

  params:
    bitness: '''64'''
    distroname: '''Red Hat Enterprise Linux'''
    distroversion: '''8.2'''
    familyname: '''Linux'''
    kernelversion: '''4.18.0-193.el8.x86_64'''
    prettyname: '''Red Hat Enterprise Linux 8.2 (Ootpa)'''
Related