How can i check software version with Ansible?

I have three linux servers and i created the ansible inventory file:

[web]
192.168.0.155
192.168.0.165
192.168.0.175

And i have playbook.yml:

---
- hosts: web
  tasks:

  - name: Check drinks versions
    shell: "python3.4 {{ item.sw_path }} -v"
    sudo: yes
    with_items:
      - { sw_path: '/home/beer.py' }
      - { sw_path: '/home/vodka.py' }
      - { sw_path: '/home/whisky.py' }

The scripts (beer.py, vodka.py and whisky.py) print their versions in format like: "/home/beer.py 1.0.0". And i need to get this versions, compare it with versions that i store in database (this is the actual versions) and if versions are not equal then copy an actual version from svn (svn paths also stored in database) to the server. How can i do that using ansible capabilities?


Ansible does not have a module to directly check the versions of any program. You have two options, both involving a bash command to extract the version number from the output of your scripts. This should probably do:

$program | rev | cut -d ' ' -f1 | rev

Option 1: Run tasks to fetch the version. Basically what you already had plus the version extraction.

- name: Check drinks versions
  shell: "python3.4 {{ item.sw_path }} -v | rev | cut -d ' ' -f1 | rev"
  sudo: yes
  with_items:
    - { sw_path: '/home/beer.py' }
    - { sw_path: '/home/vodka.py' }
    - { sw_path: '/home/whisky.py' }
  register: versions

Now you have a variable versions registered and in versions.result is a list of dicts which contain the sw_path and the return value of each loop item.

Something like this:

"results": [
  {
    "item": {
      "sw_path": "/home/beer.py"
    },
    "stdout": "1.0.0"
  },
  {
    "item": {
      "sw_path": "/home/vodka.py"
    },
    "stdout": "1.0.0"
  },
  {
    "item": {
      "sw_path": "/home/whiskey.py"
    },
    "stdout": "1.0.0"
  }
}

To see the real content of the registered data use a debug task like so:

- debug: var=versions

Option 2: Use custom facts

You can install a script on the remote hosts (with Ansible of course) which returns the versions. When Ansible connects to these hosts it will automatically run these scripts and use them as facts, just like any other system property.

Here's the docs for local facts.

The script could be as simple as this:

#!/bin/sh
echo [versions]
echo beer=$(python3.4 /home/beer.py -v | rev | cut -d ' ' -f1 | rev)
echo vodka=$(python3.4 /home/vodka.py -v | rev | cut -d ' ' -f1 | rev)
echo whiskey=$(python3.4 /home/whiskey.py -v | rev | cut -d ' ' -f1 | rev)

The output should be looking like this:

[versions]
beer=1.0.0
vodka=1.0.0
whiskey=1.0.0

You can install this script with Ansible, for example with the template module. So you can even make it dynamic based on your list of sw_path items.

After installation you need to reload the facts. You can do this with this task right after your template task:

- setup:
    filter: ansible_local

Now you will be able to directly access the versions as ansible_local.versions.beer etc.

So much for detecting the versions.

You did not mention it but I assume you do know how to get the version from your database for comparison. Otherwise you would need to provide a lot more data. So let's assume you have the "should versions" stored as should["beer"], should["vodka"] and should["whiskey"].

You now can compare the versions with the the version_compare filter.

- subversion: dummy command installing {{ item }}
  with_items:
    - beer
    - vodka
    - whiskey
  when: "{{ ansible_local.versions[item] | version_compare(should[item], '<') }}"

This would only upgrade but never downgrade in case a newer version than referenced in your database is installed. Of course you can directly compare the strings and make sure you always install the exact version.

when: "{{ ansible_local.versions[item] != should[item] }}"