Search code examples
ansiblejinja2jmespath

join multiple arrays in complex data structure with jmespath


I'm trying to transform NFS exports, described in complex data structure, to config option accepted by nfs-server daemon which later be used in ansible.

I have:

    nfs_exports:
    - path: /export/home
      state: present
      options:
      - clients: "192.168.0.0/24"
        permissions:
        - "rw"
        - "sync"
        - "no_root_squash"
        - "fsid=0"
    - path: /export/public
      state: present
      options:
      - clients: "192.168.0.0/24"
        permissions:
        - "rw"
        - "sync"
        - "root_squash"
        - "fsid=0"
      - clients: "*"
        permissions:
        - "ro"
        - "async"
        - "all_squash"
        - "fsid=1"

which must become:

        [
            {
                "options": "192.168.0.0/24(rw,sync,no_root_squash,fsid=0)",
                "path": "/export/home",
                "state": "present"
            },
            {
                "options": "192.168.0.0/24(rw,sync,root_squash,fsid=0) *(ro,async,all_squash,fsid=1)",
                "path": "/export/public",
                "state": "present"
            }
        ]

So far I was able, using {{ nfs_exports | json_query(query) }}

query: "[].{path:path,state:state,options:options.join(` `,[].join(``,[clients,`(`,join(`,`,permissions),`)`]))}"

get

{
        "options": "192.168.0.0/24(rw,sync,no_root_squash,fsid=0)",
        "path": "/export/home",
        "state": "present"
},
{
        "options": "192.168.0.0/24(rw,sync,root_squash,fsid=0)*(ro,async,all_squash,fsid=1)",
        "path": "/export/public",
        "state": "present"
}

It's probably simple but I can't get pass that last options join, space ' ' gets removed. So if someone knows the correct query additional explanation will be much appreciated.


Solution

  • Given the query:

    [].{ path: path, state: state, options: join(' ', options[].join('', [clients, '(', join(',', permissions), ')'])) }
    

    On the JSON

    {
      "nfs_exports": [
        {
          "path": "/export/home",
          "state": "present",
          "options": [
            {
              "clients": "192.168.0.0/24",
              "permissions": [
                "rw",
                "sync",
                "no_root_squash",
                "fsid=0"
              ]
            }
          ]
        },
        {
          "path": "/export/public",
          "state": "present",
          "options": [
            {
              "clients": "192.168.0.0/24",
              "permissions": [
                "rw",
                "sync",
                "root_squash",
                "fsid=0"
              ]
            },
            {
              "clients": "*",
              "permissions": [
                "ro",
                "async",
                "all_squash",
                "fsid=1"
              ]
            }
          ]
        }
      ]
    }
    

    It would give you your expected output:

    [
      {
        "path": "/export/home",
        "state": "present",
        "options": "192.168.0.0/24(rw,sync,no_root_squash,fsid=0)"
      },
      {
        "path": "/export/public",
        "state": "present",
        "options": "192.168.0.0/24(rw,sync,root_squash,fsid=0) *(ro,async,all_squash,fsid=1)"
      }
    ]
    

    Please mind: the string litteral `` wont work on a space character string, because, as pointed in the documentation, it will be parsed as JSON:

    A literal expression is an expression that allows arbitrary JSON objects to be specified

    Source: https://jmespath.org/specification.html#literal-expressions


    This is quite easy when you get to the point of:

    [].{ path: path, state: state, options: options[].join('', [clients, '(', join(',', permissions), ')']) }
    

    Which is something you seems to have achived, that gives

    [
      {
        "path": "/export/home",
        "state": "present",
        "options": [
          "192.168.0.0/24(rw,sync,no_root_squash,fsid=0)"
        ]
      },
      {
        "path": "/export/public",
        "state": "present",
        "options": [
          "192.168.0.0/24(rw,sync,root_squash,fsid=0)",
          "*(ro,async,all_squash,fsid=1)"
        ]
      }
    ]
    

    Because you are just left with joining the whole array in options with a space as glue character.