Search code examples
arraysjsonansibleruntime-errorredhat

Unable to get array element from JSON file using Ansible 2.10 version on RedHat


Below is my JSON 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.host1.bank.com"
          }
        ]
      },
      {
        "server": [
          {
            "name": "myserv1"
          },
          {
            "ssl": [
              {
                "name": "myserv1"
              },
              {
                "login-timeout-millis": "25000"
              }
            ]
          },
          {
            "listen-port": "22421"
          }
        ]
      },
      {
        "server": [
          {
            "name": "myserv2"
          },
          {
            "ssl": {
              "name": "myserv2"
            }
          },
          {
            "reverse-dns-allowed": "false"
          },
          {
            "log": [
              {
                "name": "myserv2"
              },
              {
                "file-name": "/web/bea_logs/domains/mydom/myserv2/myserv2.log"
              }
            ]
          },
          {
            "listen-port": "12401"
          }
        ]
      }
    ]
  }
]

I wish to get the listen-port printed while keeping in mind that the position of listen-port element may change in the array.

I was able to get the listen port on latest ansible version 2.12.2 using the below play

 - name: display Listen Port
   debug:
     msg: "{{ myserver.0.name }} -> {{ cpath[0]['listen-port'] }}"
   loop: "{{ jsondata[1].domain }}"
   vars:
     myserver: "{{ item.server | selectattr('name', 'defined') | list }}"
     cpath:  "{{ item.server | selectattr('listen-port', 'defined') | list }}"
   when: item.server is defined and (item.server | selectattr('listen-port', 'defined') | list ) != []

However, this play does not work on redhat OS where ansible version is 2.10 the latest offering.

Below is the error i recieve:

TASK [create YML for server name with Listen port] ************************************************************
Wednesday 16 March 2022  08:41:06 -0500 (0:00:00.171)       0:00:05.917 *******
skipping: [localhost] => (item={'name': 'mydom'})
skipping: [localhost] => (item={'domain-version': '12.2.1.3.0'})
failed: [localhost] (item={'server': [{'name': 'AdminServer'}, {'ssl': {'name': 'AdminServer'}}, {'listen-port': '12400'}, {'listen-address': 'mydom.host1.bank.com'}]}) => {"ansible_loop_var": "item", "changed": true, "cmd": "echo AdminServer_httpport: 12400>>/web/aes/admin/playbooks/Migrator/wlsdatadump.yml", "delta": "0:00:00.006786", "end": "2022-03-16 08:41:07.175709", "item": {"server": [{"name": "AdminServer"}, {"ssl": {"name": "AdminServer"}}, {"listen-port": "12400"}, {"listen-address": "mydom.host1.bank.com"}]}, "msg": "non-zero return code", "rc": 1, "start": "2022-03-16 08:41:07.168923", "stderr": "/bin/sh: 12400: Bad file descriptor", "stderr_lines": ["/bin/sh: 12400: Bad file descriptor"], "stdout": "", "stdout_lines": []}
skipping: [localhost] => (item={'server': [{'name': 'myserv1'}, {'ssl': [{'name': 'myserv1'}, {'login-timeout-millis': '25000'}]}, {'log': [{'name': 'myserv1'}, {'file-name': '/web/bea_logs/domains/mydom/myserv1/myserv1.log'}]}]})
skipping: [localhost] => (item={'server': [{'name': 'myserv2'}, {'ssl': {'name': 'myserv2'}}, {'reverse-dns-allowed': 'false'}, {'log': [{'name': 'myserv2'}, {'file-name': '/web/bea_logs/domains/mydom/myserv2/myserv2.log'}]}]})

Can you please suggest any other solution?


Solution

  • i suggest you to create a custom filter to avoid multiple choices:

    you create a file myfilter.py in a folder filter_plugins (same level your playbook), i have named the plugin customfilter:

    #!/usr/bin/python
    class FilterModule(object):
        def filters(self):
            return {
                'customfilter': self.customfilter
            }
     
        def customfilter(self, json):
            result = []
            
            for obj in json[1]['domain']:
                for server in obj:
                    ob = {}
                    if server == 'server':                 
                        for k in obj[server]:
                            for x in k:
                                ob.update({x: k[x]})
                        result.append(ob)
                                  
            #print(result)
            
            return result
    

    Playbook:

    - hosts: localhost
      gather_facts: no
      vars:
        json: "{{ lookup('file', './file.json') | from_json | customfilter }}"
      tasks:
        - name: display
          debug:
            msg: "server name: {{ server }} -> filename: {{ filename }} -> listenport: {{ listenport }}"
          loop: "{{ json }}"
          vars:
            server: "{{ item.name }}"
            filename:  "{{ item.log[1]['file-name'] | d('')}}"
            listenport: "{{ item['listen-port'] | d('') }}"
    

    result:

    ok: [localhost] => (item={'name': 'AdminServer', 'ssl': {'name': 'AdminServer'}, 'listen-port': '12400', 'listen-address': 'mydom.host1.bank.com'}) => {
        "msg": "server name: AdminServer -> filename:  -> listenport: 12400"
    }
    ok: [localhost] => (item={'name': 'myserv1', 'ssl': [{'name': 'myserv1'}, {'login-timeout-millis': '25000'}], 'listen-port': '22421'}) => {
        "msg": "server name: myserv1 -> filename:  -> listenport: 22421"
    }
    ok: [localhost] => (item={'name': 'myserv2', 'ssl': {'name': 'myserv2'}, 'reverse-dns-allowed': 'false', 'log': [{'name': 'myserv2'}, {'file-name': '/web/bea_logs/domains/mydom/myserv2/myserv2.log'}], 'listen-port': '12401'}) => {
        "msg": "server name: myserv2 -> filename: /web/bea_logs/domains/mydom/myserv2/myserv2.log -> listenport: 12401"
    }
    

    the plugin role is to group all dictionaries of each record server in one dictionary and simplify the datas you want to use.

    [
      {
        "name": "AdminServer",
        "ssl": {
          "name": "AdminServer"
        },
        "listen-port": "12400",
        "listen-address": "mydom.host1.bank.com"
      },
      {
        "name": "myserv1",
        "ssl": [
          {
            "name": "myserv1"
          },
          {
            "login-timeout-millis": "25000"
          }
        ],
        "listen-port": "22421"
      },
      {
        "name": "myserv2",
        "ssl": {
          "name": "myserv2"
        },
        "reverse-dns-allowed": "false",
        "log": [
          {
            "name": "myserv2"
          },
          {
            "file-name": "/web/bea_logs/domains/mydom/myserv2/myserv2.log"
          }
        ],
        "listen-port": "12401"
      }
    ]