ООП на Ansible? Да ладно!

ansible-oop-miniatureВ одной из последних статей я знатно набросил на Puppet  в пользу Ansible. В этой же статье попробую продемонстрировать, от чего я был в особенности в восторге при знакомстве с Ansible — от функций, а именно, от возможности объединять куски последовательностей действий (tasks) в некое подобие функций и даже классов. В общем, адепт ООП везде объекты найдёт. 🙂 Перейдем к примеру…

 

Предположим вполне реальный пример — мы хотим спрятать в некую роль знания о том, какие порты брандмауэра и для каких протоколов должны быть открыты, либо закрыты.

В принципе, мы можем в сценарий этой роли напихать действий вида:

(источник)

Возможно для системного администратора это действительно выход. Программисту же, возможно, не захочется перегружать своё сознание деталями реализации, тем более не захочется Copy&Paste’ить эти кишки реализации по коду.

Намного удобнее и безопаснее (в контексте возможных дальнейших модификаций и поддержки кода) было бы использовать что-нибудь вида:

Не спорю, очень близкий результат можно получить, написав соответствующий модуль для Ansible на Python. Я же воспользовался средствами самого Ansible и, написав отдельную Ansible роль firewall, получил в результате что-то весьма близкое к желаемому:

(исходник роли)

Кроме приличного внешнего вида обрёл в добавок:

  1. Свободу в выборе реализации — она скрыта за ../../firewall/tasks/do.yml
  2. Возможность использовать один и тот же код в различных ролях
  3. Возможность, не изменяя вызывающего кода, изменять поведение вызываемого
  4. И, наконец, корявый внешне ../../firewall/tasks/do.yml. (Кто-нибудь знает, как сделать это попристойнее?)

Конечно, не во всех случаях достаточно просто include‘а. Например, в нашем примере этот include может не работать без инсталляции и настройки определённого софта (iptables? ufw?) и специальных обработчиков (handlers — firewall restart).

Порефлексируем над следующей картинкой:

ansible-composition

Тут WebApplication — некий объект, составленный-соостоящий (Композиция) из других объектов, и Firewall в их числе.

В классах (ролях) этих объектов определены конструкторы (в нашем примере firewall/tasks/main.yml) и методы (в нашем примере firewall/tasks/do.yml). Конструкторы отвечают за первоначальную инициализацию объектов. В случае с Firewall, в конструкторе может устанавливаться соответствующее ПО и определяться обработчики (handlers/main.yml).

Класс (роль) WebApplication, описывающий Композицию, так же имеет конструктор (webapplication/tasks/main.yml), и этот конструктор неявно дополняется вызовами конструкторов из классов Firewall & Co (tasks/main.yml ролей перечисленных в webapplication/meta/main.yml — dependencies). При завершении выполнения конструктора WebApplication получаем готовое Web-приложение со всеми его частями.

 

При таком подходе к работе с Ansible, элементарные роли объединяются в более сложные структуры, а те в свою очередь в ещё более сложные.. Как тут не вспомнить Кена Уилбера с его пресловутыми холонами 🙂

А вы говорите Ansible.

 

Полезные ссылки: