Home > database >  Unable to read json data in ansible play
Unable to read json data in ansible play

Time:02-22

I have the below json data file:

[
   {
      "?xml": {
         "attributes": {
            "encoding": "UTF-8",
            "version": "1.0"
         }
      }
   },
   {
      "domain": [

         {
            "name": "mydom"
         },
         {
            "domain-version": "12.2.1.3.0"
         },

         {
            "server": [
               {
                  "name": "AdminServer"
               },
               {
                  "ssl": {
                     "name": "AdminServer"
                  }
               },
               {
                  "listen-port": "12400"
               },
               {
                  "listen-address": "mydom.myserver1.mybank.com"
               }
            ]
         },
         {
            "server": [
               {
                  "name": "SERV01"
               },
               {
                  "log": [
                     {
                        "name": "SERV01"
                     },
                     {
                        "file-name": "/web/bea_logs/domains/mydom/SERV01/SERV01.log"
                     }
                  ]
               },
               {
                  "listen-port": "12401"
               },

               {
                  "listen-address": "mydom.myserver1.mybank.com"
               },

               {
                  "server-start": [
                     {
                        "java-vendor": "Sun"
                     },
                     {
                        "java-home": "/web/bea/platform1221/jdk"
                     }
                  ]
               }
            ]
         },
         {
            "server": [
               {
                  "name": "SERV02"
               },
               {
                  "log": [
                     {
                        "name": "SERV02"
                     },
                     {
                        "file-name": "/web/bea_logs/domains/mydom/SERV02/SERV02.log"
                     }
                  ]
               },
               {
                  "listen-port": "12401"
               },

               {
                  "listen-address": "mydom.myhost2.mybank.com"
               },
               {
                  "server-start": [

                     {
                        "java-home": "/web/bea/platform1221/jdk"
                     }                  ]
               }
            ]
         }

      ]
   }
]

I wish to display all the server names and their respective port numbers.

Below is my failed attempt to display all the server names viz

AdminServer
SERV01
SERV02

My playbook:

tasks:

  - name: Read the JSON file content in a variable
    shell: "cat {{ playbook_dir }}/tmpfiles/{{ Latest_Build_Number }}/testme.json"
    register: result

  - name: Server Names
    set_fact:
      servernames:  "{{ jsondata | json_query(jmesquery) }}"
    vars:
      jmesquery: '*.domain.server[*].name'

  - name: Server Names and Ports
    set_fact:
      serverinfo:  "{{ jsondata | json_query(jmesquery) }}"
    vars:
      jmesquery: '*.server[*].[name, port]'


  - name: Print all server names
    debug:
      msg: "{{ item}}"
    with_items:
      - "{{ servernames }}"

I also tried the below:

      jmesquery: 'domain.server[*].name'

There is no error but no data in the output as well. Output below:

TASK [Print all server names] *********************************************************************************
Monday 21 February 2022  03:07:47 -0600 (0:00:00.129)       0:00:03.590 *******
ok: [localhost] => (item=) => {
    "msg": ""
}

Can you please suggest how can I get the desired data?

CodePudding user response:

lot of solutions, one with jmespath, you could try this:

  tasks:

    - name: Read the JSON file content in a variable
      shell: "cat testme.json"
      register: result

    - name: jsondata
      set_fact:
        jsondata:  "{{ result.stdout | from_json }}"
       

    - name: Server Names
      set_fact:
        servernames:  "{{ servernames | default([])   [dict(name=item[0], port=item[1])] }}"
      loop: "{{ jsondata | json_query(jmesquery0) | zip(jsondata | json_query(jmesquery1)) | list }}"
      vars:
        jmesquery0: '[].domain[].server[].name'
        jmesquery1: '[].domain[].server[]."listen-port"'

    - name: debug result
      debug:
        msg: "{{ servernames }}"

result:

ok: [localhost] => {
    "msg": [
        {
            "name": "AdminServer",
            "port": "12400"
        },
        {
            "name": "SERV01",
            "port": "12401"
        },
        {
            "name": "SERV02",
            "port": "12401"
        }
    ]
}

CodePudding user response:

Due to the nature of your data being in lists, you'll have to resort to conditionals in order to get rid of the empty objects and single item lists that would otherwise pollute your data:

  • [].domain[?server].server, to get the objects having a property server
  • [?name].name | [0] to get the name
  • [?"listen-port"]."listen-port" | [0] to get the port

So, a valid JMESPath query on your data would be

[].domain[?server]
  .server[]
  .{
     name: [?name].name | [0], 
     port: [?"listen-port"]."listen-port" | [0]
  }

And in Ansible, with that single JMESPath query, given that the file is on the controller:

- debug:
    var: >-
      lookup(
        'file', 
        playbook_dir ~ '/tmpfiles/' ~ Latest_Build_Number ~ '/testme.json'
      )
      | from_json
      | json_query('
          [].domain[?server]
            .server[]
            .{
              name: [?name].name | [0], 
              port: [?"listen-port"]."listen-port" | [0]
            }
      ')
  vars: 
    Latest_Build_Number: 1

This would yield

TASK [debug] *************************************************************************
ok: [localhost] => 
  ? |-
    lookup(
      'file',
      playbook_dir ~ '/tmpfiles/' ~ Latest_Build_Number ~ '/testme.json'
    ) | from_json | json_query('
        [].domain[?server]
          .server[]
          .{
            name: [?name].name | [0],
            port: [?"listen-port"]."listen-port" | [0]
          }
    ')
  : - name: AdminServer
      port: '12400'
    - name: SERV01
      port: '12401'
    - name: SERV02
      port: '12401'

If the file is on the nodes and not on the controller, then, you can either slurp the files first or resort to cat, as you did before applying the same JMESPath query.

  • Related