Pavel Drexler
Pavel Drexler

Reputation: 41

Ansible - Looping through list and writing into it

I am ansible beginner and I have a problem with ansible playbook that should gather info about system version from multiple servers.

First step was to gather info about server (if it uses Jboss or Tomcat and where it is) - I was able to do it and store it in list like this:

"server_list": [
        {
            "hostname": "tst-24.ph.koop.cz",
            "path": "/srv/ais/test/knz-batch/eap",
            "pid": "14660",
            "type": "wildfly/jboss",
            "version": "",
            "version_cmd": "/srv/ais/test/knz-batch/eap/bin/standalone.sh --version"
        },
        {
            "hostname": "tst-24.ph.koop.cz",
            "path": "/srv/ais/test/knz-eap/eap",
            "pid": "20153",
            "type": "wildfly/jboss",
            "version": "",
            "version_cmd": "/srv/ais/test/knz-eap/eap/bin/standalone.sh --version"
        },
        {
            "hostname": "tst-24.ph.koop.cz",
            "path": "/srv/ais/skoleni/knz-ws-int/eap",
            "pid": "24861",
            "type": "wildfly/jboss",
            "version": "",
            "version_cmd": "/srv/ais/skoleni/knz-ws-int/eap/bin/standalone.sh --version"
        },
        {
            "hostname": "tst-24.ph.koop.cz",
            "path": "/srv/ais/skoleni/knz-ws/wildfly",
            "pid": "25195",
            "type": "wildfly/jboss",
            "version": "",
            "version_cmd": "/srv/ais/skoleni/knz-ws/wildfly/bin/standalone.sh --version"
        },
        {
            "hostname": "tst-24.ph.koop.cz",
            "path": "/srv/ais/skoleni/knz-ws/undertow",
            "pid": "25667",
            "type": "tomcat",
            "version": "",
            "version_cmd": "/srv/ais/skoleni/knz-ws/undertow/bin/version.sh --version"
        },
        {
            "hostname": "tst-24.ph.koop.cz",
            "path": "/srv/ais/skoleni/knz/wildfly",
            "pid": "26446",
            "type": "wildfly/jboss",
            "version": "",
            "version_cmd": "/srv/ais/skoleni/knz/wildfly/bin/standalone.sh --version"
        }
    ]

Now I need to run other shell command (that will use version_cmd) to get that version (with use of GREP).

But I don't know how to write into list when looping through. And second problem is how to make more conditions (to use other regex for wildfly/jboss)

    - name: Get server version
      shell: "{{item.version_cmd}}| grep -Po {{ regexp_tomcat_version }}"
      loop: "{{ server_list }}"
      loop_control:
        label: "Check version for {{ item.path }}"
      when: item.type == "tomcat"
      register: server_list.version
      vars:
        regexp_tomcat_version: 'Server version:\s*([^(\n)]*)'
      ignore_errors: yes

Is this thing even possible in Ansible?

Upvotes: 2

Views: 91

Answers (1)

Vladimir Botka
Vladimir Botka

Reputation: 68004

For example, given the list below for testing

server_list:
  - hostname: srv1
    path: /usr/bin/cc
    type: prod
    version_cmd: cc --version
    version_parse: awk 'FNR == 1 {print $4}'
  - hostname: srv1
    path: /usr/local/bin/python
    type: devel
    version_cmd: python --version
    version_parse: awk 'FNR == 1 {print $2}'
  - hostname: srv2
    path: /usr/bin/cc
    type: prod
    version_cmd: cc --version
    version_parse: awk 'FNR == 1 {print $4}'
  - hostname: srv2
    path: /usr/local/bin/python
    type: devel
    version_cmd: python --version
    version_parse: awk 'FNR == 1 {print $2}'

Create a dynamic group of hostnames in the first play and run it in the second play. In the second play, register the versions into the variable server_list_versions. Then iterate ansible_play_hosts and create the dictionary versions.

- name: Create group server_group
  hosts: localhost
  tasks:
    - add_host:
        name: "{{ item }}"
        groups: server_group
        server_list: "{{ server_list }}"
      loop: "{{ server_list|map(attribute='hostname')|unique }}"

- name: Collect versions
  hosts: server_group
  gather_facts: false
  tasks:
    - shell: "{{ item.version_cmd }}|{{ item.version_parse }}"
      loop: "{{ server_list|selectattr('hostname', 'eq', inventory_hostname) }}"
      loop_control:
        label: "Check version for {{ item.path }}"
      # when: item.type == "devel"
      register: server_list_versions
    - set_fact:
        versions: "{{ versions|d({})|
                      combine({item: hostvars[item].server_list_versions.results|
                                     json_query('[].{path: item.path,
                                                     version: stdout,
                                                     stderr: stderr}')}) }}"
      loop: "{{ ansible_play_hosts }}"
      run_once: true

gives

versions:
  srv1:
    - path: /usr/bin/cc
      stderr: ''
      version: 11.0.1
    - path: /usr/local/bin/python
      stderr: ''
      version: 3.8.12
  srv2:
    - path: /usr/bin/cc
      stderr: ''
      version: 11.0.1
    - path: /usr/local/bin/python
      stderr: '/bin/sh: python: not found'
      version: ''

If you want to update the attribute version in server_list create a dictionary

    - set_fact:
        vers_dict: "{{ vers_dict|d({})|
                       combine({item: hostvars[item].server_list_versions.results|
                                      json_query('[].{path: item.path,
                                                      version: stdout}')|
                                      items2dict(key_name='path',
                                                 value_name='version')}) }}"
      loop: "{{ ansible_play_hosts }}"
      run_once: true

gives

vers_dict:
  srv1:
    /usr/bin/cc: 11.0.1
    /usr/local/bin/python: 3.8.12
  srv2:
    /usr/bin/cc: 11.0.1
    /usr/local/bin/python: ''

Then, use this dictionary to update the attribute version

    - set_fact:
        server_list_new: "{{ server_list_new|d([]) +
                             [item|combine({'version': version})] }}"
      loop: "{{ server_list }}"
      vars:
        version: "{{ vers_dict[item.hostname][item.path] }}"
      run_once: true

gives

server_list_new:
  - hostname: srv1
    path: /usr/bin/cc
    type: prod
    version: 11.0.1
    version_cmd: cc --version
    version_parse: awk 'FNR == 1 {print $4}'
  - hostname: srv1
    path: /usr/local/bin/python
    type: devel
    version: 3.8.12
    version_cmd: python --version
    version_parse: awk 'FNR == 1 {print $2}'
  - hostname: srv2
    path: /usr/bin/cc
    type: prod
    version: 11.0.1
    version_cmd: cc --version
    version_parse: awk 'FNR == 1 {print $4}'
  - hostname: srv2
    path: /usr/local/bin/python
    type: devel
    version: ''
    version_cmd: python --version
    version_parse: awk 'FNR == 1 {print $2}'

Upvotes: 2

Related Questions