Пишем свои фильтры для Ansible

Ansible "из коробки" имеет множество встроенных фильтров, как реализованных в jinja2, так и собственных. Подробное описание существующих фильтров можно найти на странице документации. Однако, бывают ситуации, в которых подходящего дефолтного фильтра нет (а очень хочется).  В этой короткой статье мы научимcя писать свои плагины-фильтры для ansible.

Задача...

Задача следующая: написать плагин, который будет преобразовывать переменную, представляющую собой дату и время в строковом формате в другой произвольный формат.

Например, имея на входе дату в строке вида "2017-10-14T15:39:22Z" хочу получить "14.10.2017".

... и ее решение

Подготовим следующую структуру:

- inventory
- filter_plugins
-- filters.py
- test.yml

В плейбуке test.yml опишем вызов нашего будущего фильтра:

---
- hosts: localhost
  gather_facts: no
  tasks:
    - name: Print formatted time
      debug:
        msg: "{{'2017-10-14T15:39:22Z'|format_time}}"

В файле filters.py опишем сам фильтр:

#!/usr/bin/python
import time

class FilterModule(object):
    def filters(self):
        return {
            'format_time': self.format_time
        }

    def format_time(self, time_string):
        result = time.strftime('%d.%m.%Y', time.strptime(time_string, '%Y-%m-%dT%H:%M:%SZ'))
        return result

Пробуем запустить плейбук:

$ ansible-playbook -i inventory/test test.yml

PLAY [localhost] ***************************************************************************************************

TASK [Print formatted time] ****************************************************************************************************
ok: [localhost] => {
    "msg": "14.10.2017"
}

Неплохо, но мы хотим большего - задавать любой входной и выходной форматы. Немного доработаем filters.py:

#!/usr/bin/python
import time

class FilterModule(object):
    def filters(self):
        return {
            'format_time': self.format_time
        }

    def format_time(self, time_string, in_format, out_format):
        if time_string == '':
            return ''
        else:
            return time.strftime(out_format, time.strptime(time_string, in_format))

Кроме входных параметров я добавил обработку пустого входного time_string, чтобы не возникало ошибки, если фильтр применен к не заданной переменной.

И плейбук:

---
- hosts: localhost
  gather_facts: no
  tasks:
    - name: Print formatted time
      debug:
        msg: "{{'2017-10-14T15:39:22Z'|format_time('%Y-%m-%dT%H:%M:%SZ','%d.%m.%Y')}}"

Проверяем...работает!

Подход в целом хорош, но наш фильтр в директори filter_plugins не будет работать в других плейбуках (находящихся в других директориях), если такая необходимость возникнет.

Чтобы решить эту проблему, можно добавить в ansible.cfg путь к filters.py в значение переменной filter_plugins.