Gary Whittington

Using Ansible 2.x To Support Multiple Package Managers (Unix Distributions)

1 January 2016

Ansible version 2 adds a range of new features including better support for multiple package managers with a new package module.

During a recent upgrade of servers - which used various different package management systems (yum and dnf) - we decided to upgrade our Anisble scripts to version 2 (currently in rc3) to simplify support for these different package management systems that we use.

This blog post provides details how this was done including code samples.

Historically Handling Multiple Package Managers with Ansible 1.x

Traditionally, with Ansible 1.x the packages were handled via specific modules to support each of the major package managers. For example, yum for CentOS/Fedora and apt-get for Debian distributions.

For example, the installation a package ntpd can have been written as,


  - name: install ntpd
    yum: name=ntpd state=latest
    when: ansible_distribution == "Fedora"
    
  - name: install ntpd
    apt-get: name=ntpd state=latest
    when: ansible_distribution == 'Ubuntu'

The issue with this code is that it repeat itself with the only difference being the distribution.
This repetition breaks the DRY principle (https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) and leads to a lot of boiler plate repetition.

Anisble 2.x Package Module

Ansible 2.x introduces a new package module which attempts to eliminate this issue (see docs.ansible.com/package_module.htm). Using the module the code can now be written without repetition as the package manager will be selected at run-time by ansible.

For example the above code now becomes,


  - name: install ntpd
    package: name=ntpd state=latest

This is easier to read and makes it more robust by removig the references to the specific distribution's package manager.

For example, this will now work on any distribution that has a ntpd package such as BSD distribution which had not been considered before.

Anisble 2.x Package Module Limitation

Whilst the package module is a significant improvement there is a limitation to its use as the name of the software package being installed needs to have the same on each distributions.

For example, the Apache web server's package is named httpd on CentOS/Fedora and apache2 on Debian distributions.

The recommended way that we resolved this issue is use variable which can be changed depending up this distribution.


- name: install apache 
  package : name={{apachepkg}} state=present

This approach has now created a different issue in that a list of package names needs to be maintained for each distribution that is used.

In practice we have not found this to be a significant issue as many of the package names are the same across the distributions that we use. So this is now a much smaller task than before (as this can be included in a single place in the source) and the DRY principle has been upheld.

Final Code

In the vast majority of our Ansible scripts we successfully did a global search for our existing package managers references (such as yum, apt-get) and replaced them with package references.
There were only a few cases were the package name was different.

In the cases where the package names did not match we created a simple variable at the start of the Ansible code to handle the difference.

For example,


- name: defined var apachepkg (Fedora)
  apachepkg=httpd
  when: ansible_distribution == "Fedora"
    
- name: defined var apachepkg (Ubuntu)
  apachepkg=apache2
  when: ansible_distribution == 'Ubuntu'
    
- name: install apache
  package : name={{apachepkg}} state=latest

If there had been many references that we had to handle an alternative approach we could have used would have been to create distribution specific variable files and to load these.

For example,


  # files to provide distribution specific package names 
  
  - include_vars: "{{item}}"
    with_first_found:
      - "package-names-{{ansible_distribution}}.yml"
      - "package-names-{{ansible_os_family}}.yml"
      - "package-names.yml"
   
  - name: install apache
    package : name={{apachepkg}} state=latest

Conclusion

The upgrade to the Ansible 2.x package was very effective and solved our issue of maintaining duplicate code for different package managers (on different Unix platforms).

However, in the future there is the possibility for the package module to have its own built-in look-up feature to automatically hand the differences in packages names between the various Unix distributions.

Also see more postings in my Blog.