How to do arithmetic operation inside a JMESPath query in Ansible? - Stack Overflow

I'm working with Ansible trying to convert attribute from byte to Gb.The code start with this CSV

I'm working with Ansible trying to convert attribute from byte to Gb.

The code start with this CSV file

Path,Owner,Group,Mode,Last Access,Last Modified,Size,Total Size,Diskspace Quota,Namespace Quota,File and Directory Count
/a/b/c,user,users,16877,1970-01-01T00:00:00.000Z,2025-02-18T10:57:48.428Z,0,0,-1,-1,13,0
/a/b/e,user,users,16877,2025-02-28T11:37:50.800Z,2025-02-19T14:44:54.496Z,20948515,62845545,-1,-1,662,20948515

Then it convert the CSV into a JSON selecting and renaming fields, and convert byte from string to number type

- name: "Formater les données"
  set_fact:
    json_data: "{{ csv.content | community.general.from_csv | community.general.json_query('[].{\"Chemin\": \"Path\", \"Date de dernière modification\": \"Last Modified\", \"Taille en octets\": to_number(\"Size\"), \"Nombre de fichiers et de dossiers\": \"File and Directory Count\"}') }}"

Which gives a list of JSON

"json_data": [
    {
        "Chemin": "/a/b/c",
        "Date de dernière modification": "2024-10-23T13:03:51.187Z",
        "Nombre de fichiers et de dossiers": "6",
        "Taille en octets": 20948515
    },
    {
        "Chemin": "/a/b/e",
        "Date de dernière modification": "2025-02-19T14:45:04.971Z",
        "Nombre de fichiers et de dossiers": "7",
        "Taille en octets": 21948534
    }
]

Is there a way to do arithmetic operation within json_query (or somewhere else)?

to_number(\"Size\")

The solution I found

I simply use this command on the source CSV. Don't ask me how it works ^^

- name: Ajouter la taille en Go
  set_fact:
    dictionnary: >-
      {{ dictionnary + [item | combine({'Taille en Go': (item['Size'] | int / (1024*1024*1024)) | round(2)})] }}
  loop: "{{ csv.content | community.general.from_csv }}"

And then I choose the fields I want to keep this way

- name: "Sélectionner les champs à afficher"
  set_fact:
    result: "{{ dictionnary | community.general.json_query('[].{\"Chemin\": \"Path\", \"Date de dernière modification\": \"Last Modified\", \"Taille en Go\": \"Taille en Go\", \"Nombre de fichiers et de dossiers\": \"File and Directory Count\"}') }}"

The solution I found according to this post

I choose the fields I want to keep this way. It converts the CSV to a list of Json.

- name: "Formater les données"
  set_fact:
    json_data: "{{ 
      csv.content
        | community.general.from_csv
        | community.general.json_query(
          '[].{
            \"Chemin HDFS\": \"Path\",
            \"Date de dernière modification\": \"Last Modified\",
            \"Taille\": to_number(\"Size\"),
            \"Nombre de fichiers et de dossiers\": \"File and Directory Count\"}')
      }}"
  delegate_to: dpn

Then I loop on every Json, create a new field in a list, and combine with the Json looped. The new field is created using human_readable_filter.

- name: "Convertir la taille en Human Readable"
  vars:
    key_to_replace: 'Taille'
  set_fact:
    result: "{{
        result | default([]) +
        [
          item | combine({ key_to_replace: item[key_to_replace] | int | human_readable })
        ]
      }}"
  loop: "{{ json_data }}"

I'm working with Ansible trying to convert attribute from byte to Gb.

The code start with this CSV file

Path,Owner,Group,Mode,Last Access,Last Modified,Size,Total Size,Diskspace Quota,Namespace Quota,File and Directory Count
/a/b/c,user,users,16877,1970-01-01T00:00:00.000Z,2025-02-18T10:57:48.428Z,0,0,-1,-1,13,0
/a/b/e,user,users,16877,2025-02-28T11:37:50.800Z,2025-02-19T14:44:54.496Z,20948515,62845545,-1,-1,662,20948515

Then it convert the CSV into a JSON selecting and renaming fields, and convert byte from string to number type

- name: "Formater les données"
  set_fact:
    json_data: "{{ csv.content | community.general.from_csv | community.general.json_query('[].{\"Chemin\": \"Path\", \"Date de dernière modification\": \"Last Modified\", \"Taille en octets\": to_number(\"Size\"), \"Nombre de fichiers et de dossiers\": \"File and Directory Count\"}') }}"

Which gives a list of JSON

"json_data": [
    {
        "Chemin": "/a/b/c",
        "Date de dernière modification": "2024-10-23T13:03:51.187Z",
        "Nombre de fichiers et de dossiers": "6",
        "Taille en octets": 20948515
    },
    {
        "Chemin": "/a/b/e",
        "Date de dernière modification": "2025-02-19T14:45:04.971Z",
        "Nombre de fichiers et de dossiers": "7",
        "Taille en octets": 21948534
    }
]

Is there a way to do arithmetic operation within json_query (or somewhere else)?

to_number(\"Size\")

The solution I found

I simply use this command on the source CSV. Don't ask me how it works ^^

- name: Ajouter la taille en Go
  set_fact:
    dictionnary: >-
      {{ dictionnary + [item | combine({'Taille en Go': (item['Size'] | int / (1024*1024*1024)) | round(2)})] }}
  loop: "{{ csv.content | community.general.from_csv }}"

And then I choose the fields I want to keep this way

- name: "Sélectionner les champs à afficher"
  set_fact:
    result: "{{ dictionnary | community.general.json_query('[].{\"Chemin\": \"Path\", \"Date de dernière modification\": \"Last Modified\", \"Taille en Go\": \"Taille en Go\", \"Nombre de fichiers et de dossiers\": \"File and Directory Count\"}') }}"

The solution I found according to this post

I choose the fields I want to keep this way. It converts the CSV to a list of Json.

- name: "Formater les données"
  set_fact:
    json_data: "{{ 
      csv.content
        | community.general.from_csv
        | community.general.json_query(
          '[].{
            \"Chemin HDFS\": \"Path\",
            \"Date de dernière modification\": \"Last Modified\",
            \"Taille\": to_number(\"Size\"),
            \"Nombre de fichiers et de dossiers\": \"File and Directory Count\"}')
      }}"
  delegate_to: dpn

Then I loop on every Json, create a new field in a list, and combine with the Json looped. The new field is created using human_readable_filter.

- name: "Convertir la taille en Human Readable"
  vars:
    key_to_replace: 'Taille'
  set_fact:
    result: "{{
        result | default([]) +
        [
          item | combine({ key_to_replace: item[key_to_replace] | int | human_readable })
        ]
      }}"
  loop: "{{ json_data }}"
Share Improve this question edited Mar 17 at 7:23 Mike HUMPHREYS asked Mar 12 at 14:35 Mike HUMPHREYSMike HUMPHREYS 231 silver badge6 bronze badges 2
  • 1 You should provide your input (aka the CSV). In short, no JMESPath does not allow arithmetic. But I also doubt you really. need JMESPath to achieve what you want here. – β.εηοιτ.βε Commented Mar 12 at 20:05
  • I've edit my question for to be more specific @β.εηοιτ.βε – Mike HUMPHREYS Commented Mar 13 at 7:49
Add a comment  | 

1 Answer 1

Reset to default 2

I'm working with Ansible trying to convert attribute from byte to Gb.

Generally, human_readable filter is responsible for this.

Don't ask me how it works ^^

It works like this:

  • we can't refer to the iterator when using map filter, thus we need a loop where we can use the item loop var;
  • for each dict within a list of dicts, combine filter allows us to replace the value of a dict key when we are combining that dict with another dict consisting of one key of the same name.

This solution could be developed further to make the code more understandable and maintainable:

  • JMESPath query could be written in multiple lines;
  • default value could be set for the resulting variable (your current example doesn't work because of this);
  • arithmetic operation could be replaced with human_readable filter:
---
- name: Test
  hosts: localhost
  connection: local
  gather_facts: false
  vars:
    csv
      content: |
        Path,Owner,Group,Mode,Last Access,Last Modified,Size,Total Size,Diskspace Quota,Namespace Quota,File and Directory Count
        /a/b/c,user,users,16877,1970-01-01T00:00:00.000Z,2025-02-18T10:57:48.428Z,0,0,-1,-1,13,0
        /a/b/e,user,users,16877,2025-02-28T11:37:50.800Z,2025-02-19T14:44:54.496Z,20948515,62845545
  tasks:
    - name: "Formater les données"
      set_fact:
        json_data: "{{ 
          csv.content
            | community.general.from_csv
            | community.general.json_query(
              '[].{
                \"Chemin\": \"Path\",
                \"Date de dernière modification\": \"Last Modified\",
                \"Taille en octets\": to_number(\"Size\"),
                \"Nombre de fichiers et de dossiers\": \"File and Directory Count\"}')
          }}"

    - name: Replace the value
      vars:
        key_to_replace: 'Taille en octets'
      set_fact:
        result: "{{
            result | default([]) +
            [
              item | combine({ key_to_replace: item[key_to_replace] | int | human_readable(unit='G') })
            ]
          }}"

    - name: Log the result
      debug:
        var: result

This will produce the following:


"result": [
    {
        "Chemin": "/a/b/c",
        "Date de dernière modification": "2024-10-23T13:03:51.187Z",
        "Nombre de fichiers et de dossiers": "6",
        "Taille en octets": "0.00 GB"
    },
    {
        "Chemin": "/a/b/e",
        "Date de dernière modification": "2025-02-19T14:45:04.971Z",
        "Nombre de fichiers et de dossiers": "7",
        "Taille en octets": "0.02 GB"
    }
]

If you need a numeric representation, the solution would depend on the goal:

  • if you're fine to simply have the converted value but want it to be a number, you can apply | split | first | float chain of filters;
  • if you want to perform calculations, there's also human_to_bytes filter that is the exact opposite to human_readable.

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744747123a4591368.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信