Динамический Ansible inventory в zVirt

Динамический inventory в Ansible

Механизм, который позволяет Ansible динамически находить и использовать инвентаризационные данные в реальном времени из внешних источников, таких как API, базы данных и другие источники данных, а не только из INI- YAML-файлов инвентаризации.

В zVirt динамический inventory реализован за счёт обращение к API и сбору информации о хостах, формировании системных переменные и группировке хостов на основе общих метаданных (принадлежность к кластеру или наличие тега).

1. Подготовка

Для того, чтобы можно было работать с динамическим inventory в zVirt, необходимо подготовить файлы ansible.cfg и сам динамический inventory.

динамический inventory обязательно должен иметь название ovirt.yaml, так как Python проводит валидацию названия файла.

В файле ansible.cfg вам необходимо активировать плагин ovirt.ovirt.ovirt (этот плагин отвечает за работу динамического inventory) в секции [inventory].

Пример ниже:

[inventory]
enable_plugins = ovirt.ovirt.ovirt, yaml, ini

По-умолчанию используется следующие форматы: host_list, script, auto, yaml, ini, toml.

Если необходимо использовать иные плагины, помимо указанных в примере ansible.cfg, можно просто добавить их в список.

Далее нужно настроить сам inventory ovirt.yaml:

plugin: ovirt.ovirt.ovirt                                     # Название плагина
ovirt_url: "https://zvirt.example.ru/ovirt-engine/api"        # URL API zVirt
ovirt_cafile: "/root/zvirt/ca.cer"                            # Путь до корневого сертификат zVirt. Файл можно положить в любую директорию, путь рекомендуется указывать абсолютный
ovirt_username: admin@internal                                # Имя пользователя с указанием пространства имён. Локальные пользователи в zVirt находятся в пространстве @internal
ovirt_password: admin                                         # Пароль  учётной записи
ovirt_query_filter:                                           # Указывается запрос для поиска хостов для Inventory
  search: 'name=*'                                            # Запрос 'name=*' выбирает ВМ с любым именем
  # search: 'name=* AND cluster=SecondCluster'                # Пример запроса, в котором выбираются ВМ с любым именем, состоящие в кластере SecondCluster
  case_sensitive: no                                          # Указание необходимости учитывать регистр

После создания файла, необходимо проверить работу. Для этого достаточно воспользоваться командой:

ansible-inventory --graph

Либо можно указать inventory файл прямо в CLI:

ansible-inventory -i /path/to/inventory/ovirt.yaml --graph

Команда должна обнаружить все ВМ:

@all:
  |--@ungrouped:
  |  |--CentOS8Template
  |  |--my-zvirt-vm
  |  |--nginx

2. Группировка хостов

Динамический inventory позволяет также группировать хосты. Пример группировки по кластеру и тегу ниже:

plugin: ovirt.ovirt.ovirt
ovirt_url: "https://zvirt.example.ru/ovirt-engine/api"
ovirt_cafile: "/root/zvirt/ca.cer"
ovirt_username: admin@internal
ovirt_password: admin
ovirt_query_filter:
  search: 'name=*'
  case_sensitive: no
keyed_groups:
  - key: cluster           # Группировка по ключу cluster
    prefix: "cluster"      # Префикс группы. В название группы будет добавлен префикс, указанный здесь. Префикс и значение ключа будут разделены нижним прочерком.
  - key: tags              # Группировка по ключу tags
    prefix: "tag"

Вывод такого inventory:

@all:
  |--@ungrouped:
  |--@cluster_Default:
  |  |--CentOS8Template
  |  |--my-zvirt-vm
  |  |--nginx
  |--@tag_my_zvirt_tag:
  |  |--my-zvirt-vm
  |--@tag_nginx:
  |  |--nginx

Все виртуальные машины попали в группу cluster_Default, ВМ nginx попала в группу tag_nginx, а ВМ my-zvirt-vm попала в группу tag_my_zvirt_tag.

3. Добавление переменной "ansible_host"

Чтобы хостам в inventory автоматически назначался ansible_host IP-адресом, можно воспользоваться compose в inventory-файле.

Для этого создайте следующий inventory:

plugin: ovirt.ovirt.ovirt
ovirt_url: "https://zvirt.example.ru/ovirt-engine/api"
ovirt_cafile: "/root/zvirt/ca.cer"
ovirt_username: admin@internal
ovirt_password: admin
ovirt_query_filter:
  search: 'name=*'
  case_sensitive: no
keyed_groups:
  - key: cluster
    prefix: "cluster"
compose:
  ansible_host: devices["enp1s0"][0]     # Будет произведена композиция переменной ansible_host по ключу devices, в котором выбирается устройство enp1s0 и выбирается первый элемент списка IP-адресов уйстройства

Проверка inventory:

"my-zvirt-vm": {
    "ansible_host": "10.0.88.19",
    "cluster": "Default",
    "devices": {
        "enp1s0": [
            "10.0.88.19",
            "fe80::546f:feff:feaa:2"
        ]
    },
    "name": "my-zvirt-vm",
    "status": "up",
    "tags": [
        "my-zvirt-tag"
    ]
},
"nginx": {
    "ansible_host": "10.0.88.18",
    "cluster": "Default",
    "devices": {
        "enp1s0": [
            "10.0.88.18",
            "fe80::546f:feff:feaa:1"
        ]
    },
    "name": "nginx",
    "status": "up",
    "tags": [
        "nginx"
    ]
}

Вывод выше неполный, так как помимо указанных переменных, есть и иные, однако, для упрощения понимания были выбраны лишь самые важные.

В compose мы указывали выбрать из devices устройство enp1s0 и уже из него выбрать первый элемент списка. В devices обоих хостов входит устройство enp1s0. У устройства есть список, в котором каждый элемент из себя представляет IP-адрес, в данном случае у обоих хостов у устройства enp1s0 сначала идёт IPv4, затем IPv6. Как раз первый элемент списка мы указывали в ovirt.yaml и переменной ansible_host присвоилось нужное значение.

Название устройств может разниться у разных ОС, перед указанием compose рекомендуем изучить вывод ansible-inventory --list без указания compose в inventory.

4. Тестирование

Для проверки был запущен модуль ping для различных групп, составленных с помощью динамического inventory

ansible-inventory --graph -i inventory/ovirt.yaml

Вывод:

@all:
  |--@ungrouped:
  |--@cluster_Default:
  |  |--CentOS8Template
  |  |--my-zvirt-vm
  |  |--nginx
  |--@tag_my_zvirt_tag:
  |  |--my-zvirt-vm
  |--@tag_nginx:
  |  |--nginx
[root@ansible ansible]$ ansible tag_nginx -m ping
nginx | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}
[root@ansible ansible]$ ansible tag_my_zvirt_tag -m ping
my-zvirt-vm | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}
[root@ansible ansible]$ ansible cluster_Default -m ping
nginx | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}
my-zvirt-vm | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}
CentOS8Template | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}