ansible-cmdb — Структуризация и визуализация ansible facts

ansible-cmdb — Структуризация и визуализация ansible facts

Приветствую!

ansible-cmdb — программа на Python, которая структуризирует собранные Ansible facts и визуализирует их с помощью HTML с красивым форматированием.

Ansible facts — это данные о серверах (хостах Ansible), которые собираются с помощью модуля setup. Они представляют собой «факты» о системе, например данные об ОС, сетевых интерфейсах, блочных устройствах и других компонентах системы.

С помощью ansible-cmdb также можно экспортировать структуризированные данные в файлы следующих форматов: json, csv, sql, txt_table. Такие файлы удобно использовать для анализа, хранения или дальнейшей обработки. Но это тема для отдельной статьи😉.

В этой заметке я расскажу📋:

  • Как установить и использовать ansible-cmdb для генерации HTML;
  • Как добавить кастомные факты;
  • Как использовать HTML файл в качестве отдельной страницы по сабурлу в Nginx;
  • Как автоматизировать обновление базы фактов.
Подписывайтесь на наш телеграм @r4ven_me📱, чтобы не пропустить новые публикации на сайте😉. А если есть вопросы или желание пообщаться по тематике — заглядывайте в Вороний чат @r4ven_me_chat🧐.

Предполагается, что вы знаете, что такое Ansible и умеете им пользоваться🙄. Если нет, то рекомендую к прочтению мои статьи:

Все примеры из статьи выполнялись в среде дистрибутива Linux Mint Debian Edition 6🍃 (Debian 12). Во всех остальных дистрибутивах процесс выглядит +- аналогично.

Установка ansible-cmdb

Первым делом переходим в рабочую директорию Ansible, где у вас находятся файл инвентаризации и конфигурации (у меня это ~/ansible):

cd ~/ansible/

Как уже говорил ранее, ansible-cmdb, как и сам Ansible, написан на языке программирования Python🐍. Поэтому рекомендуется создать виртуальное окружение venv📦 и уже внутри него выполнять установку ansible-cmdb:

Способ #1 ‐ venv, если у вас версия Python ниже 3.12

Выполняем в терминале:

mkdir -p ./cmdb/venv

python3 -m venv ./cmdb/venv/ansible-cmdb

./cmdb/venv/ansible-cmdb/bin/pip install ansible-cmdb

Способ #2 — docker, если у вас версия Python 3.12 и выше

К сожалению ansible-cmdb давненько не обновлялся, а в Python 3.12 библиотека imp стала deprecated. В таком случае для запуска ansible-cmdb необходимо установить более раннюю версию Python.

Универсальным способом сделать это является Docker:

mkdir -p ./cmdb/venv

docker run -u $(id -u):$(id -g) -v ./cmdb:/cmdb python:3.11-alpine python -m venv /cmdb/venv/ansible-cmdb

docker run -u $(id -u):$(id -g) -v ./cmdb:/cmdb python:3.11-alpine /cmdb/venv/ansible-cmdb/bin/pip install ansible-cmdb

В конце установки должно быть подобное сообщение:

Installing collected packages: ushlex, jsonxs, pyyaml, MarkupSafe, mako, ansible-cmdb
Successfully installed MarkupSafe-3.0.2 ansible-cmdb-1.31 jsonxs-0.6 mako-1.3.10 pyyaml-6.0.2 ushlex-0.99.1

Чтобы в будущем каждый раз не активировать venv (или не заходить в docker) для работы с ansible-cmdb создадим небольшой скрипт.

Если устанавливали через venv (Способ #1):

cat << EOF > "${PWD}"/cmdb/ansible_cmdb.sh
#!/bin/bash
"${PWD}/cmdb/venv/ansible-cmdb/bin/python" "${PWD}/cmdb/venv/ansible-cmdb/lib/ansiblecmdb/ansible-cmdb.py" "\$@"
EOF

Если устанавливали в Docker (Способ #2):

cat << EOF > "${PWD}"/cmdb/ansible_cmdb.sh
#!/bin/bash
docker run -v "${PWD}/cmdb":"/cmdb" python:3.11-alpine /cmdb/venv/ansible-cmdb/bin/python /cmdb/venv/ansible-cmdb/lib/ansiblecmdb/ansible-cmdb.py "\$@"
EOF

Сделаем его исполняемым и добавим символьную ссылку на него в директории ~/.local/bin для удобства:

chmod +x ./cmdb/ansible_cmdb.sh

ln -s "${PWD}"/cmdb/ansible_cmdb.sh ~/.local/bin/ansible-cmdb

☝️Для корректной работы директория ~/.local/bin должна быть в списке переменной окружения $PATH.

Проверяем:

ansible-cmdb --help
Usage: ansible-cmdb.py [option] <dir> > output.html

Options:
  --version             show program's version number and exit
  -h, --help            show this help message and exit
  -t TEMPLATE, --template=TEMPLATE
                        Template to use. Default is 'html_fancy'
  -i INVENTORY, --inventory=INVENTORY
                        Inventory to read extra info from
  -f, --fact-cache      <dir> contains fact-cache files
  -p PARAMS, --params=PARAMS
                        Params to send to template
  -d, --debug           Show debug output
  -q, --quiet           Don't report warnings
  -c COLUMNS, --columns=COLUMNS
                        Show only given columns
  -C CUST_COLS, --cust-cols=CUST_COLS
                        Path to a custom columns definition file
  -l LIMIT, --limit=LIMIT
                        Limit hosts to pattern
  --exclude-cols=EXCLUDE_COLUMNS
                        Exclude cols from output

Пример работы ansible-cmdb

Создаем директорию для базовых фактов и запускаем их сбор с помощью ansible и модуля setup:

mkdir -p ./cmdb/facts/basic

ansible -m setup --tree ./cmdb/facts/basic all &> /dev/null

Теперь с помощью ansible-cmdb генерируем HTML файл на основе собранных данных:

ansible-cmdb -p collapsed=1 ./cmdb/facts/basic > ./cmdb/cmdb.html

💡Параметр (-p) collapsed=1 будет отображать данные о хостах в свернутом виде.

Теперь просто открываем полученный файл в браузере:

Аккуратно и удобно. На странице есть интерактивные элементы: поиск, фильтрация и пр. Очень удобно😏.

Для генерации json | csv | sql | txt_table | markdown команда будет примерно такой:

ansible-cmdb -t csv ./cmdb/facts/basic > ./cmdb/cmdb.csv
"Name","OS","IP","Arch","Mem","MemFree","MemUsed","CPUs","Virt","Disk avail"
"host1","Debian 12.7","10.1.1.10","x86_64/x86_64","8g","0g","1g","4","kvm/guest","8.3g, 0.2g"
"host2","Debian 12.10","0.1.1.11","x86_64/x86_64","8g","2g","2g",6","kvm/guest","7.8g, 0.3g"
"host3","Debian 12.8","0.1.1.12","x86_64/x86_64","8g","0g","3g","4","kvm/guest","1.9g"
"host4","Debian 12.6","0.1.1.13","x86_64/x86_64","8g","0g","1g","8","kvm/guest","8.7g, 0.2g"
"host5","Debian 12.7","0.1.1.14","x86_64/x86_64","8g","0g","1g","2","kvm/guest","9.0g, 0.2g"
"host6","Debian 12.6","0.1.1.15","x86_64/x86_64","8g","0g","1g","2","kvm/guest","0.8g, 3.7g, 3.7g, 0.4g"

Стоит предупредить, что полный список фактов доступен только в шаблоне JSON. Остальные форматы имеют ограниченное количество полей, опеределенных в шаблонах, которые расположены по пути: ./cmdb/venv/ansible-cmdb/lib/ansiblecmdb/data/tpl. При необходимости их можно кастомизировать под себя.

Добавление кастомных фактов

ansible-cmdb предоставляет гибкие возможности по кастомизации итогового файла👨‍🎨.

Покажу на примере добавления своего факта в виде списка маршрутов хоста, который Ansible по умолчанию не собирает🤷‍♂️.

⚠️Факты Ansible должны быть представлены в формате JSON.

Моя реализация выполнена с помощью скрипта на Bash, который в выводе дает список маршрутов хоста в JSON формате📃.

Приступим👨‍🏭. Создаем файл скрипта:

nvim ./cmdb/routes_to_json.sh

Наполняем его таким содержимым:

#!/bin/bash

# Получаем маршруты в массив
mapfile -t routes < <(ip r)

# Начало JSON
RESULT='{"custom_facts": {"IP Routes": ['

# Добавляем маршруты
for i in "${!routes[@]}"; do
    # Экранируем спецсимволы
    line="${routes[$i]//\\/\\\\}"
    line="${line//\"/\\\"}"
    
    # Добавляем маршрут как отдельный объект
    RESULT+="{\"#$((i+1))\": \"$line\"}"
    
    # Добавляем запятую между элементами (кроме последнего)
    [ $i -lt $((${#routes[@]} - 1)) ] && RESULT+=","
done

# Завершаем JSON
RESULT+=']}}'

# Выводим результат
echo "$RESULT"

Делаем исполняемым:

chmod +x ./cmdb/routes_to_json.sh

Проверяем:

./cmdb/routes_to_json.sh | python3 -m json.tool

Вывод должен быть примерно таким:

{
    "custom_facts": {
        "IP Routes": [
            {
                "#1": "default dev vpn0 proto static scope link metric 50 "
            },
            {
                "#2": "default via 10.10.10.1 dev eth0 proto dhcp src 10.10.10.30 metric 600 "
            },
            {
                "#3": "10.11.11.0/24 dev vpn0 proto kernel scope link src 10.11.11.15 metric 50 "
            }
        ]
    }
}

Теперь создаем новый плейбук, который будет собирать наш кастомный факт (запуск скрипта выше на удалённых хостах и сбор вывода в локальные файлы):

nvim ./cmdb/get_custom_facts.yml

Наполняем:

---

- name: Get custom facts
  hosts: all
  gather_facts: false

  tasks:

    - name: Run script to get info about ip routes
      script: "~/ansible/cmdb/routes_to_json.sh"
      register: script_output

    - name: Create local output files with ip routes info
      local_action: copy content="{{ script_output.stdout }}" dest="~/ansible/cmdb/facts/custom/{{ inventory_hostname }}"

☝️Не забудьте скорректировать пути до вашей рабочей директории Ansible.

Теперь создаем отдельную директорию под кастомные факты, собираем их с помощью ansible-playbook и генерируем обновленный HTML с помощью ansible-cmdb:

mkdir -p ./cmdb/facts/custom

ansible-playbook ./cmdb/get_custom_facts.yml

ansible-cmdb -p collapsed=1 ./cmdb/facts/{basic,custom} > ./cmdb/cmdb.html

В итоговом HTML появится новый раздел «Custom facts». Как по мне выглядит неплохо😌:

ansible-cmdb позволяет настраивать свои столбцы в HTML и много чего еще. Рекомендую подробнее изучить документацию📚 (ссылки в конце статьи).

Доступ к cmdb.html по урлу в Nginx

Если вам необходимо предоставлять данную информацию кому-то еще, то можно добавить итоговый HTML в виде отдельной страницы на вашем веб сервере, например Nginx🌐.

Если веб сервер работает от ограниченного в правах пользователя, проще всего будет создать хардлинк на ваш файл в корне сайта Nginx (условно корень /var/www/html:

sudo ln "${PWD}"/cmdb/cmdb.html /var/www/html/

А в конфиге Nginx, добавьте, например, такой location:

server {
    listen 80;
    server_name localhost;

    root /var/www/html;
    index index.html index.htm;

    location / {
        try_files $uri $uri.html $uri/ =404;
    }
}

Как то так:

Автоматизация сбора/обновления фактов

Без автоматизации в мире администрирования никуда🙂‍↔️. Напишем скрипт сбора/обновления фактов и настроим его ежедневный запуск в 01:00 по таймеру systemd в пространстве нашего пользователя (--user).

Создаем файл скрипта:

nvim ./cmdb/update_cmdb.sh

Наполняем:

#!/usr/bin/env bash

set -Eeuo pipefail

export PATH="$PATH:$HOME/.local/bin"

WORK_DIR="$HOME/ansible/"

echo "Script started"

cd "$WORK_DIR"

echo "Get basic facts"
ansible -m setup --tree ./cmdb/facts/basic all &> /dev/null || true

echo "Get custom facts"
ansible-playbook ./cmdb/get_custom_facts.yml &> /dev/null || true

echo "Generate cmdb html file"
ansible-cmdb -p collapsed=1 ./cmdb/facts/{basic,custom} > ./cmdb/cmdb.html

echo "Script done"
echo "--------------------------------------"

☝️Не забудьте указать свой WORK_DIR.

Сохраняем, делаем файл исполняемым, создаем линк в директорию ~/.local/bin для удобства и проверяем:

chmod +x ./cmdb/update_cmdb.sh

ln -s "${PWD}"/cmdb/update_cmdb.sh ~/.local/bin/update-cmdb

update-cmdb

# время изменения
stat -c '%y' ./cmdb/cmdb.html

Далее создаем systemd юнит сервиса:

systemctl --user edit --full --force update-cmdb.service

Наполняем:

[Unit]
Description=Update ansible cmdb

[Service]
WorkingDirectory=%h/ansible
ExecStart=%h/.local/bin/update-cmdb

☝️Также не забывает указать свой WorkingDirectory.

Теперь создаем юнит таймера:

systemctl --user edit --full --force update-cmdb.timer

Наполняем:

[Unit]
Description=Update ansible cmdb daily at 01:00

[Timer]
OnCalendar=*-*-* 01:00:00
Persistent=true

[Install]
WantedBy=timers.target

Активируем таймер:

systemctl --user enable --now update-cmdb.timer

Проверяем:

systemctl --user status update-cmdb.timer

systemctl --user status update-cmdb.service

journalctl --user -fu update-cmdb

💡Для принудительно запуска задания выполните:

systemctl --user start update-cmdb.service

Теперь наш HTML файл будет всегда содержать актуальные данные по хостам Ansible👍.

Не забывайте про нашу телегу📱и чат💬
Всех благ✌️

That should be it. If not, check the logs 🙂

Используемые материалы

Обсуждение закрыто.