В предыдущей статье удалось заставить ansible работать со стандартными модулями, в частности устанавливать пакеты через менеджер pacman. Однако в archlinux одним pacman сыт не будешь, большинство "вкусных" пакетов находится в пользовательском репозитории пакетов AUR. А разработчики дистрибутива заняли принципиальную позицию, что никогда не будет официального способа ставить неофициальные пакеты, мол если уж решились - то качайте и ставьте самостоятельно. Сообщество выпустило несколько альтернативных менеджеров для работы с AUR, одним из самых популярных считается yaourt. Я это всё к тому, что из ansible просто необходимо уметь вызывать yaourt, а такого модуля у них нет. В принципе это даже хорошо, поскольку даёт повод немного глубже окунуться в ansible и написать такой модуль самостоятельно.
Начинаем как ни странно с документации. Ещё не лишним будет иметь перед глазами то, как написан модуль pacman, исходники ведь открытые, нужно пользоваться. Приведу сразу результат того, что получилось (файл "~/.config/ansible/library/yaourt.py"):
Как видно, это полностью автономный python модуль, его можно запустить даже без ansible и он сделает свою работу. Управляющие команды в него передаются через параметры командной строки, а результат он в виде json выдаёт на stdout. Это замедляет работу, но такое решение объяснимо - разработчики хотели дать возможность писать расширения на любом языке, но не уверен, что много кто этим пользуется
Требования к такому модулю почти полностью отсутствуют. Регламентируется лишь, что входные будут передаваться в формате "key=value". А для выходные значение требуется выдавать в виде json с некоторыми обязательными полями, говорящих насколько успешно удалось справится с работой, я их описывать не буду, проще посмотреть в документации. Обращу внимание, что нельзя печатать что-то лишнее в stdout, поскольку результирующий json нужно будет отдавать туда же, ansible может не разобрать ответ. Для python они предлагают небольшую библиотеку, которая поможет разобрать входные параметры и сформировать ответ, но конечно же можно легко обойтись и без неё. Да и ещё убедительно просят писать документацию и примеры использования.
Описывать что твориться в модуле, я думаю смысла не имеет - там только запуск yaourt и разбор результатов. По интерфейсу получился аналог стандартного модуля pacman, правда умеет он лишь устанавливать пакеты и обновлять кеш, но для текущих нужд достаточно и этого. Заменим в playbook из прошлой статьи название модуля "pacman" на "yaourt":
И запустим:
Поведение и результаты точно такие же как и в оригинальном модуле, но теперь нам стали доступны любые пакеты из AUR.
Промежуточные итоги: разработать свой модуль - оказалось проще простого, документация, открытый код и простая архитектура сделали своё грязное дело. Хотя именно для данного примера bash оказался бы сильно проще - вообще ничего писать бы не пришлось, но в общем случае понятно, что в такой модуль можно засунуть куда больше, чем простой вызов одной команды bash.
Посмотрим, что будет дальше.
Начинаем как ни странно с документации. Ещё не лишним будет иметь перед глазами то, как написан модуль pacman, исходники ведь открытые, нужно пользоваться. Приведу сразу результат того, что получилось (файл "~/.config/ansible/library/yaourt.py"):
#!/usr/bin/python2 # -*- coding: utf-8 -*- DOCUMENTATION = ''' --- module: yaourt short_description: Manage packages with I(yaourt) description: - Manage packages with the I(yaourt) package manager, which is used by Arch Linux and its variants. version_added: "1.0" author: - "'ReanGD (@novovladimir)'" notes: [] requirements: [] options: name: description: - Name of the package to install. required: false default: null state: description: - Desired state of the package. required: false default: "present" choices: ["present"] update_cache: description: - Whether or not to refresh the master package lists. This can be run as part of a package installation or as a separate step. required: false default: "no" choices: ["yes", "no"] ''' EXAMPLES = ''' # Install package foo - yaourt: name=foo state=present # Run the equivalent of "yaourt -Sya" as a separate step - yaourt: update_cache=yes ''' import os.path from ansible.module_utils.basic import * YAOURT_PATH = "/usr/bin/yaourt" def query_package(module, name): lcmd = "pacman -Qi %s" % (name) lrc, lstdout, lstderr = module.run_command(lcmd, check_rc=False) return lrc == 0 def update_package_db(module): cmd = "yaourt -Sya" rc, stdout, stderr = module.run_command(cmd, check_rc=False) if rc == 0: return True else: module.fail_json(msg="could not update package db") def install_packages(module, packages): install_c = 0 for i, package in enumerate(packages): installed = query_package(module, package) if not installed: cmd = "yaourt -S %s --noconfirm" % (package) rc, stdout, stderr = module.run_command(cmd, check_rc=False) if rc != 0: module.fail_json(msg="failed to install %s" % (package)) install_c += 1 if install_c != 0: msg = "installed %s package(s)" % (install_c) module.exit_json(changed=True, msg=msg) module.exit_json(changed=False, msg="package(s) already installed") def check_packages(module, packages): would_be_changed = [] for package in packages: if not query_package(module, package): would_be_changed.append(package) if would_be_changed: msg = "%s package(s) would be installed" % (len(would_be_changed)) module.exit_json(changed=True, msg=msg) else: module.exit_json(change=False, msg="package(s) already installed") def main(): module = AnsibleModule( argument_spec=dict( name=dict(aliases=['pkg']), state=dict(default='present', choices=['present']), update_cache=dict(default='no', aliases=['update-cache'], choices=BOOLEANS, type='bool')), required_one_of=[['name', 'update_cache']], supports_check_mode=True) if not os.path.exists(YAOURT_PATH): msg = "cannot find yaourt, looking for %s" % (YAOURT_PATH) module.fail_json(msg=msg) p = module.params if p["update_cache"] and not module.check_mode: update_package_db(module) if not p['name']: module.exit_json(changed=True, msg='updated the package master lists') if p['update_cache'] and module.check_mode and not p['name']: module.exit_json(changed=True, msg='Would have updated the package cache') if p['name']: pkgs = p['name'].split(',') if module.check_mode: check_packages(module, pkgs) if p['state'] in ['present']: install_packages(module, pkgs) if __name__ == '__main__': main()
Требования к такому модулю почти полностью отсутствуют. Регламентируется лишь, что входные будут передаваться в формате "key=value". А для выходные значение требуется выдавать в виде json с некоторыми обязательными полями, говорящих насколько успешно удалось справится с работой, я их описывать не буду, проще посмотреть в документации. Обращу внимание, что нельзя печатать что-то лишнее в stdout, поскольку результирующий json нужно будет отдавать туда же, ansible может не разобрать ответ. Для python они предлагают небольшую библиотеку, которая поможет разобрать входные параметры и сформировать ответ, но конечно же можно легко обойтись и без неё. Да и ещё убедительно просят писать документацию и примеры использования.
Описывать что твориться в модуле, я думаю смысла не имеет - там только запуск yaourt и разбор результатов. По интерфейсу получился аналог стандартного модуля pacman, правда умеет он лишь устанавливать пакеты и обновлять кеш, но для текущих нужд достаточно и этого. Заменим в playbook из прошлой статьи название модуля "pacman" на "yaourt":
- hosts: all tasks: - name: Refresh package list yaourt: update_cache=yes - name: Install python packages yaourt: name={{ item }} state=present with_items: - python2-idna - python2-notify
$ ansible-playbook ~/.config/ansible/test.yml -i ~/.config/ansible/hosts PLAY [all] ******************************************************************** GATHERING FACTS *************************************************************** ok: [home] TASK: [Refresh package list] ************************************************** changed: [home] TASK: [Install python packages] *********************************************** changed: [home] => (item=python2-idna) changed: [home] => (item=python2-notify) PLAY RECAP ******************************************************************** home : ok=3 changed=2 unreachable=0 failed=0
Промежуточные итоги: разработать свой модуль - оказалось проще простого, документация, открытый код и простая архитектура сделали своё грязное дело. Хотя именно для данного примера bash оказался бы сильно проще - вообще ничего писать бы не пришлось, но в общем случае понятно, что в такой модуль можно засунуть куда больше, чем простой вызов одной команды bash.
Посмотрим, что будет дальше.
Комментариев нет:
Отправить комментарий