pilipeet
pilipeet

Reputation: 47

Parsing a list of strings in ansible

I have this list:

ok: [localhost] => {
 "ansible_facts": {
    "servs": [
        "Is active: Yes\nHost Name: northrend\n Ip Address: 192.168.1.12",
        "Is active: Yes\nHost Name: pandaria \n IP Address: 192.168.1.30"
             ]
},

I've been trying to parse this list to extract the value of "Host name" but I just keep on getting the following error:

fatal: [localhost]: FAILED! => {
 "msg": "Unexpected templating type error occurred on ({{ servs | regex_search('Host Name: (.)+\n'),multiline=True) }}): expected string or buffer"
}

This is what I tried:

    - set_fact:
        output: {{ servs | regex_search('Host Name: (.)+\n'), multiline=True) }}

How do you properly parse the contents of this list?

Upvotes: 2

Views: 1263

Answers (2)

Vladimir Botka
Vladimir Botka

Reputation: 68144

The list servs is nearly valid YAML. Only the indentation of the attribute 'Ip Address' is wrong. One space shifted to the right. If you manage to get rid of this space, e.g.

    servs:
      - "Is active: Yes\nHost Name: northrend\nIp Address: 192.168.1.12"
      - "Is active: Yes\nHost Name: pandaria \nIP Address: 192.168.1.30"

the situation would be trivial. Simply convert the list of strings to a list of dictionaries, e.g.

    - set_fact:
        servs_list: "{{ servs_list|d([]) + [item|from_yaml] }}"
      loop: "{{ servs }}"

gives

  servs_list:
  - Host Name: northrend
    Ip Address: 192.168.1.12
    Is active: true
  - Host Name: pandaria
    IP Address: 192.168.1.30
    Is active: true

If you can't fix the indentation parse the data on your own. The task below gives the same result

    - set_fact:
        servs_list: "{{ servs_list|d([]) + [item|
                                            regex_replace(regex, replace)|
                                            from_yaml] }}"
      loop: "{{ servs }}"
      vars:
        regex: '\n '
        replace: '\n'

Get the output

    - set_fact:
        output: "{{ servs_list|map(attribute='Host Name')|list }}"

gives

  output:
  - northrend
  - Pandaria

Upvotes: 1

toydarian
toydarian

Reputation: 4564

You have multiple errors here:

  • regex_search works on strings, not lists. So you need to apply it to each list element.
  • Your regex creates one group for each character in your hostname. The group should be (.+).
  • You are returning the whole match (including Host Name:) and not just the name itself. You need to specify which matching group to return.

I guess the list is from the output of some script or in some file. It would be a lot better to have it as json or yaml and then use from_json or from_yaml.

If you want to keep it like that, you need to do this:

- hosts: all
  vars:
    servs:
      - |
        Is active: Yes
        Host Name: northrend
        Ip Address: 192.168.1.12
      - |
        Is active: Yes
        Host Name: pandaria
        IP Address: 192.168.1.30
  tasks:

    - debug:
        msg: "{{ servs }}"
    - set_fact:
        output: "{{ servs | map('regex_search', 'Host Name: (.+)\n', '\\1') | list }}"
    - debug:
        msg: "{{ output }}"

This is the output:

TASK [debug] ************
ok: [local] => {
    "msg": [
        "Is active: Yes\nHost Name: northrend\nIp Address: 192.168.1.12\n",
        "Is active: Yes\nHost Name: pandaria\nIP Address: 192.168.1.30\n"
    ]
}

TASK [set_fact] ***************
ok: [local]

TASK [debug] ************
ok: [local] => {
    "msg": [
        [
            "northrend"
        ],
        [
            "pandaria"
        ]
    ]
}

Upvotes: 1

Related Questions