diff --git a/readme.md b/readme.md index ad1cf7d..16f14fb 100644 --- a/readme.md +++ b/readme.md @@ -41,3 +41,4 @@ * [8.3. Использование Yandex Cloud](/src/homework/08-ansible/8.3) * [8.4. Работа с Roles](/src/homework/08-ansible/8.4) * [8.5. Тестирование Roles](/src/homework/08-ansible/8.5) +* [8.6. Создание собственных modules](/src/homework/08-ansible/8.6) diff --git a/src/homework/08-ansible/8.6/my_own_module.py b/src/homework/08-ansible/8.6/my_own_module.py new file mode 100644 index 0000000..f89ee88 --- /dev/null +++ b/src/homework/08-ansible/8.6/my_own_module.py @@ -0,0 +1,121 @@ +#!/usr/bin/python + +# Copyright: (c) 2018, Terry Jones +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +DOCUMENTATION = r''' +--- +module: my_own_module + +short_description: Netology devops test module + +# If this is part of a collection, you need to use semantic versioning, +# i.e. the version is of the form "2.5.0" and not "2.4". +version_added: "1.0.0" + +description: Создание тестового файла на удалённом хосте по определённому пути с определённым содержимым + +options: + path: + description: Путь до файла + required: true + type: str + new: + description: Содержимое файла + required: true + type: str +# Specify this value according to your collection +# in format of namespace.collection.doc_fragment_name +extends_documentation_fragment: + - my_namespace.my_collection.my_doc_fragment_name + +author: + - Denis Savosin (@Dannecron) +''' + +EXAMPLES = r''' +# Pass in a message +- name: Test with a message + my_namespace.my_collection.my_test: + name: hello world + +# pass in a message and have changed true +- name: Test with a message and changed output + my_namespace.my_collection.my_test: + name: hello world + new: true + +# fail the module +- name: Test failure of the module + my_namespace.my_collection.my_test: + name: fail me +''' + +RETURN = r''' +''' + +import os + +from ansible.module_utils.basic import AnsibleModule + +def run_module(): + # define available arguments/parameters a user can pass to the module + module_args = dict( + path=dict(type='str', required=True), + content=dict(type='str', required=True) + ) + + # seed the result dict in the object + # we primarily care about changed and state + # changed is if this module effectively modified the target + # state will include any data that you want your module to pass back + # for consumption, for example, in a subsequent task + result = dict(changed=False) + + # the AnsibleModule object will be our abstraction working with Ansible + # this includes instantiation, a couple of common attr would be the + # args/params passed to the execution, as well as if the module + # supports check mode + module = AnsibleModule( + argument_spec=module_args, + supports_check_mode=True + ) + + # if file does not exists then set changed to True + # else get content of file and compare it with content variable + # if contents are not equal, then changed=True + + file_exists = os.path.exists(module.params['path']) + file_content = '' + + if not file_exists: + result['changed'] = True + else: + file = open(module.params['path'], mode='r') + file_content = file.read() + file.close() + + if file_content != module.params['content']: + result['changed'] = True + + if module.check_mode: + module.exit_json(**result) + + if result['changed'] == False: + module.exit_json(**result) + + file = open(module.params['path'], mode='w') + file.write(module.params['content']) + file.close() + + module.exit_json(**result) + + +def main(): + run_module() + + +if __name__ == '__main__': + main() diff --git a/src/homework/08-ansible/8.6/payload.json b/src/homework/08-ansible/8.6/payload.json new file mode 100644 index 0000000..7ec3606 --- /dev/null +++ b/src/homework/08-ansible/8.6/payload.json @@ -0,0 +1,6 @@ +{ + "ANSIBLE_MODULE_ARGS": { + "path": "/tmp/new", + "content": "some content" + } +} diff --git a/src/homework/08-ansible/8.6/readme.md b/src/homework/08-ansible/8.6/readme.md new file mode 100644 index 0000000..45b78b7 --- /dev/null +++ b/src/homework/08-ansible/8.6/readme.md @@ -0,0 +1,184 @@ +Выполнение [домашнего задания](https://github.com/netology-code/mnt-homeworks/blob/MNT-13/08-ansible-06-module/README.md) +по теме "8.6. Создание собственных modules". + +## Q/A + +### Задание 1 + +> Подготовка к выполнению +> 1. Создайте пустой публичных репозиторий в любом своём проекте: `my_own_collection` +> 2. Скачайте репозиторий ansible: `git clone https://github.com/ansible/ansible.git` по любому удобному вам пути +> 3. Зайдите в директорию ansible: `cd ansible` +> 4. Создайте виртуальное окружение: `python3 -m venv venv` +> 5. Активируйте виртуальное окружение: `./venv/bin/activate`. Дальнейшие действия производятся только в виртуальном окружении +> 6. Установите зависимости `pip install -r requirements.txt` +> 7. Запустить настройку окружения `./hacking/env-setup` +> 8. Если все шаги прошли успешно - выйти из виртуального окружения `deactivate` +> 9. Ваше окружение настроено, для того чтобы запустить его, нужно находиться в директории `ansible` и выполнить конструкцию `./venv/bin/activate && ./hacking/env-setup` + +Дополнительные действия: +1. В ubuntu-дистрибутивах модуль `venv` не установлен по умолчанию, поэтому нужно его установить самостоятельно + + ```shell + sudo apt install python3.8-venv + ``` + +2. Напрямую вызвать `./venv/bin/activate` нельзя, он не исполняемый. Внутри файла описан комментарий + + ```text + This file must be used with "source bin/activate" *from bash* + you cannot run it directly + ``` + + Поэтому нужно запустить следующую команду: + + ```shell + source ./venv/bin/activate + ``` + +3. Аналогично с запуском `./hacking/env-setup` + + ```shell + source ./hacking/env-setup + ``` + +### Задание 2 + +> Основная часть +> Наша цель - написать собственный module, который мы можем использовать в своей role, через playbook. +> Всё это должно быть собрано в виде collection и отправлено в наш репозиторий. +> +> 1. В виртуальном окружении создать новый `my_own_module.py` файл +> 2. Наполнить его содержимым из [статьи](https://docs.ansible.com/ansible/latest/dev_guide/developing_modules_general.html#creating-a-module) +> 3. Заполните файл в соответствии с требованиями ansible так, чтобы он выполнял основную задачу: module должен создавать текстовый файл на удалённом хосте по пути, +> определённом в параметре `path`, с содержимым, определённым в параметре `content`. + +Код модуля доступен в файле [my_own_module.py](./my_own_module.py). Внутри репозитория `ansible` его +необходимо положить по пути `lib/ansible/modules`. + +> 4. Проверьте module на исполняемость локально + +Нужно создать в корне репозитория `ansible` файл `payload.json` со следующим содержимым: + +```json +{ + "ANSIBLE_MODULE_ARGS": { + "path": "/tmp/new", + "content": "some content" + } +} +``` + +Затем нужно выполнить следующую команду: + +```shell +python3 -m ansible.modules.my_own_module payload.json +``` + +```json +{"changed": true, "invocation": {"module_args": {"path": "/tmp/new", "content": "some content"}}} +``` + +При первом запуске модуль создаст файл: + +```shell +cat /tmp/new +``` + +```text +some content +``` + +При втором вызове модуля ничего не изменится: + +```shell +python3 -m ansible.modules.my_own_module payload.json +``` + +```text +{"changed": false, "invocation": {"module_args": {"path": "/tmp/new", "content": "some content"}}} +``` + +> 5. Напишите single task playbook и используйте module в нём. +> 6. Проверьте через playbook на идемпотентность +> 7. Выйдите из виртуального окружения + +Playbook будет выглядеть следующим образом: [test_module.yml](./test_module.yml). +Данный файл нужно положить в корень репозитория `ansible` и выполнить следующую команду: + +```shell +ansible-playbook test_module.yml +``` + +```text +[WARNING]: No inventory was parsed, only implicit localhost is available +[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does +not match 'all' + +PLAY [test my own module] ************************************************************************************ +TASK [Gathering Facts] *************************************************************************************** +ok: [localhost] + +TASK [create file] ******************************************************************************************* +changed: [localhost] + +PLAY RECAP *************************************************************************************************** +localhost : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 +``` + +Повторим запуск, чтобы проверить идемпотентность: + +```shell +ansible-playbook test_module.yml +``` + +```text +[WARNING]: No inventory was parsed, only implicit localhost is available +[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does +not match 'all' + +PLAY [test my own module] ************************************************************************************ +TASK [Gathering Facts] *************************************************************************************** +ok: [localhost] + +TASK [create file] ******************************************************************************************* +ok: [localhost] + +PLAY RECAP *************************************************************************************************** +localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 +``` +> 8. Инициализируйте новую collection: `ansible-galaxy collection init my_own_namespace.yandex_cloud_elk` + +// todo + +> 9. В данную collection перенесите свой module в соответствующую директорию. + +// todo + +> 10. Single task playbook преобразуйте в single task role и перенесите в collection. +> У role должны быть default всех параметров module + +// todo + +> 11. Создайте playbook для использования этой role + +// todo + +> 12. Заполните всю документацию по collection, выложите в свой репозиторий, поставьте тег `1.0.0` на этот коммит. + +// todo + +> 13. Создайте .tar.gz этой collection: `ansible-galaxy collection build` в корневой директории collection. + +// todo +> 14. Создайте ещё одну директорию любого наименования, перенесите туда single task playbook и архив c collection. + +// todo + +> 15. Установите collection из локального архива: `ansible-galaxy collection install .tar.gz` + +// todo + +> 16. Запустите playbook, убедитесь, что он работает. + +// todo diff --git a/src/homework/08-ansible/8.6/test_module.yml b/src/homework/08-ansible/8.6/test_module.yml new file mode 100644 index 0000000..5d285ed --- /dev/null +++ b/src/homework/08-ansible/8.6/test_module.yml @@ -0,0 +1,8 @@ +--- +- name: test my own module + hosts: localhost + tasks: + - name: create file + my_own_module: + path: "/tmp/new" + content: "some new content"