Ansible для детского сада. Часть 5. Приделываем костыли
У меня постоянное ощущение того, что я описываю не велосипед с костылями, а велосипед, который давно изобретен, на котором все катались лет 15 назад, если не 20. Что-то типа «введение в линукс и все вокруг для 10 класса». Что на информатике учат.
Для лиги лени: много примеров, как делать не надо, и как точно не работает
Ansible для детского сада в скольки то частях. Часть 1.Про все сразу
Ansible для детского сада в скольки то частях. Часть 2. Костылируем жалкое подобие WSUS - Linux Server Update Services (LSUS)
Ansible для детского сада. Часть 3. Настраиваем подобие безопасности и все остальное
Подготовка Git
Ansible для детского сада. Часть 4. Первичная настройка конечного клиента
4.1 Теоретически все просто
4.2 Сначала готовим открытую часть сертификата без пароля через Putty в Windows и еще раз вспоминаем что генерирует Putty в Windows, а что нужно в ssh
4.3 Детский скрипт, и как не надо делать
4.4 Отлаживаем SSH, снова и опять
4.5 Проблемы с переносом строки и не только
4.6 Проверяем, что получилось
4.7 Почти переходим к второму Ansible плейбуку
4.8 Воюем с пробелами в Anisble и восстанавливаем пример из первой статьи
4.9 Возвращаемся к основной задаче
Ansible для детского сада в скольки то частях. Часть 5. Приделываем костыли
5.1 Теоретическое обоснование костылей, или «зачем».
5.2 Выбор dataflow
5.3 Собираем Ansible facts без плейбука
5.4 Разбираем полученную выгрузку
Ansible для детского сада в скольки то частях. Часть 5. Приделываем костыли
5.1 Теоретическое обоснование костылей, или «зачем».
С какого-то возраста появился вопрос: «Зачем?» Вот раньше тебе звонил приятель, например, говорил: «Слушай, я познакомился с двумя девушками, у них отдельная квартира в Отрадном, я выпить купил. Поехали!» И ты сразу ехал. Если бы тебя спросили: «А зачем?», ты бы сказал: «Ну как, зачем? Ты че, дурак? Две девушки, отдельная квартира! Посидим, выпьем, ну...» А сейчас...
Перед тем, как идти дальше, необходимо принять решение про архитектуру Linux Server Update Services (LSUS) на первом этапе. Поскольку сейчас я хочу только собирать и показывать статистику в нужном мне виде, с нужной детализацией. Часть информации я могу получать, не изобретая новую систему учета, а взяв имеющуюся, и переформатировав отчет из нее. Но насколько это будет сложно и долго? Нужен ли мне будет настоящий разработчик для такой задачи, потому что разработчик сделает ее быстрее, но сделает ли так, как мне надо? Учитывая, что я сам не знаю, как мне надо, значит, остается только попробовать сделать.
5.2 Выбор dataflow
Про что это? В результате обработки (входных данных) я получу (обработанные входные данные). Затем мне надо понять:
Где я буду обрабатывать сырые данные, на хосте с ansible или сделаю файл «как есть», положу его в общую папку (или как-то еще ?), заберу с хоста с Windows и там обработаю. Мне все равно, под что писать простой обработчик, под Windows или под Linux, и на чем писать – могу на баш (много секса, мало смысла), могу на питоне, могу на powershell. При малых объемах нет принципиальной разницы, обработается ли файл на 100 – 1.000 – 10.000 хостов за минуту, или за две.
Куда я помещу обработанные входные данные, в csv, xlsx, xml, или в web\html.
Если в web, то на каком веб-сервере? Апач, нжинкс, IIS, или даже MiniWeb и Small HTTP Server.
Нужно ли хранить исторические данные ?
Все это, и даже больше, я писал во второй части –
С целевой структурой данных ситуация сложнее. Для своего предпоследнего пет проекта под похожие задачи я просто развернул базу данных (Postgre), и туда клал разное. Нужно ли на первом шаге такое решение? Не знаю, мне не нужно, мне и бинарной таблицы хватит. Но что туда класть? Очевидно, туда должны попасть: FQDN, IP, дистрибутив, версия дистрибутива, ядро сейчас, последние дата и время доступности, аптайм. Должно ли туда попадать предыдущее состояние объекта, и какие-то еще настройки? Не очень важно, всегда можно расширить схему данных, добавить к объекту еще пару свойств.
Пока получается так, что сначала надо разобрать входной объект (файл, выгрузку), собрать из него данные в удобный мне массив объектов (или в список объектов, но это вопрос выбора алгоритма и понимания разницы работы с массивом и работы со списком). Когда будет массив данных в нужном мне виде, из него можно собрать уже то, что нужно мне.
5.3 Собираем Ansible facts без плейбука
Во второй части факты собирались одной командой,
ansible proxmox -m setup -a "filter=ansible_uptime_seconds" --ask-pass
В предыдущей (4-й) части я остановился на выполнении плейбука
ansible-playbook report23.yml --inventory /home/user/second_inventory.ini --private-key /home/ansible1/.ssh/ansible_key
Нужно ли делать плейбук именно для сбора фактов? Получается, что нет. Достаточно еще раз прочитать про ключи к ansible, и сделать:
[-i INVENTORY] [--list-hosts] [-l SUBSET] [--flush-cache]
[--private-key PRIVATE_KEY_FILE] [-u REMOTE_USER]
То есть выполнить все в одну команду:
ansible all --module-name setup --inventory /home/user/second_inventory.ini --private-key /home/ansible1/.ssh/ansible_key
не забывайте про all, даже если указываете --inventory /home/user/second_inventory.ini.
на выводе вы получите текст ужасающей длины, в формате с {}, [], и еще чем-то. 150 килобайт на мои 3 (три) тестовые виртуальные машины.
Из этого огромного массива представляет интерес:
FQDN, IP, дистрибутив, версия дистрибутива, ядро сейчас, последние дата и время доступности, аптайм. То есть, в нужный мне объект должны попасть:
Удачность соединения: 192.168.1111.2222 | SUCCESS => {
FQDN: "ansible_fqdn"
Hostname: "ansible_hostname"
IP: "ansible_all_ipv4_addresses"
IPv6: "ansible_all_ipv6_addresses":
тип: "ansible_board_name": "Virtual Machine",
Текущие сессии: "SSH_CONNECTION":
Текущий пользователь "USER": "ansible1",. С этим параметром надо поработать, а то есть любители забыть закрыть сессию.
MAC "macaddress": ". Нужен, но не для сетевых настроек, а для сверки, не сделал ли кто-то, случайно, клонирование с совпадающим MAC адресом. Бывает и такое легаси.
"ansible_kernel": "6.1.0-40-amd64",
"ansible_kernel_version": "#1 SMP PREEMPT_DYNAMIC Debian 6.1.153-1 (2025-09-20)",
"ansible_lsb": {
"codename": "bookworm",
"description": "Debian GNU/Linux 12 (bookworm)",
"id": "Debian",
"major_release": "12",
"release": "12"
Все вместе выглядит или как начало поиска готового решения «как свернуть xml в набор объектов», или как задача первого (для начинающих) уровня сложности с AlgoMap.io \ Codility \ LeetCode \ итд, свертывание xml в объект со свойствами. Не взяв xml.etree.ElementTree, а все сам, все руками. Дело не сложное, но надо вспоминать лаго алгоритм
Но, у меня сейчас дел вдруг привалило, что я даже не знаю, когда дописать цикл. Новый проект, новые задачи.
Поэтому сделаю быстро, грязно, на powershell под windows, и так делать, вообще говоря, не нужно.
Еще придется не забыть сразу сделать выгрузку с примером ошибки недоступности хоста, и что-то с этим делать.
5.4 Разбираем полученную выгрузку
Поскольку все сделано в непревзойденном стиле «давайте не будем спрашивать нейросеть», то не удивляйтесь, что написано не просто плохо, а отвратительно плохо. Ирония не в том, могу ли я сделать задачу «сверните xml в массив вложенных объектов», как на интервью для джуна, а в том, хочу ли я потом искать, в какой путь это свернулось.
Поэтому вот вам еще один образец, как делать плохо и неправильно. Еще и неполный образец. Еще и на Powershell. Короче, сплошной позор.
# Ansible facts parser
Write-Host "Start 01 ================="
$Version = "AFP001"
$ScriptPath = $PSScriptRoot
$DataFromFile001 = Get-Content ($ScriptPath + "\" + "resultv01.txt")
Write-Host 'Read from file total' $DataFromFile001.count
Write-Host 'Read from file first' $DataFromFile001[0]
Write-Host 'Read from file last' $DataFromFile001[$DataFromFile001.count -1]
$SplitStartMark = '| SUCCESS => {'
Write-Host "Start 02 ================="
for ($Mark = 0; $Mark -lt $DataFromFile001.count; $Mark++){
if ($DataFromFile001[$Mark] -like "*$SplitStartMark") {Write-Host "Mark" $Mark ';' $DataFromFile001[$Mark]
}}
Class Split{
[int]$Position
[string]$String
}
Write-Host "Start 03 ================="
$SplitStartMarkPosition = @()
for ($Mark = 0; $Mark -lt $DataFromFile001.count; $Mark++){
if ($DataFromFile001[$Mark] -like "*$SplitStartMark") {
Write-Host "Mark" $Mark ';' $DataFromFile001[$Mark]
$NewHost = [Split]::new()
$NewHost.Position = $Mark
$NewHost.String = $DataFromFile001[$Mark]
$SplitStartMarkPosition += $NewHost
Remove-Variable NewHost
}}
Write-Host "Total" $SplitStartMarkPosition.Count
Write-Host "Start 04 ================="
Class AnsibleFacts01{
[string]$IsSUCCESS
[string]$FQDN
[string]$IP
[string]$ansible_kernel_version}
$NewHost1 = [AnsibleFacts01]::new()
$NewHost1.IsSUCCESS = $DataFromFile001[0]
for ($Mark = 0; $Mark -lt $SplitStartMarkPosition[1].Position; $Mark++){
if ($DataFromFile001[$Mark] -like '*ansible_fqdn*') {$NewHost1.FQDN = $DataFromFile001[$Mark]}
if ($DataFromFile001[$Mark] -like '*ansible_all_ipv4_addresses*') {$NewHost1.IP = $DataFromFile001[$Mark+1]}
if ($DataFromFile001[$Mark] -like '*ansible_kernel_version*') {$NewHost1.ansible_kernel_version = $DataFromFile001[$Mark]}
}
$NewHost1 | fl
Write-Host "Start 05 ================="
$NewHost2 = [AnsibleFacts01]::new()
$NewHost2.IsSUCCESS = $DataFromFile001[$SplitStartMarkPosition[1].Position] -replace "[^a-z][^A-Z]" -replace "{",""
for ($Mark = $SplitStartMarkPosition[1].Position; $Mark -lt $SplitStartMarkPosition[2].Position; $Mark++){
if ($DataFromFile001[$Mark] -like '*ansible_fqdn*') {$NewHost2.FQDN = $DataFromFile001[$Mark] -replace '"ansible_fqdn":',"" -replace " ", "" -replace ",","" -replace '"',""}
if ($DataFromFile001[$Mark] -like '*ansible_all_ipv4_addresses*') {$NewHost2.IP = ($DataFromFile001[$Mark+1] -replace '"',"").TrimStart()}
$kv = ''
if ($DataFromFile001[$Mark] -like '*ansible_kernel_version*') {$NewHost2.ansible_kernel_version = ($DataFromFile001[$Mark] -replace '"ansible_kernel_version":', "" -replace "," -replace '"',"").TrimStart()}
}
$NewHost2 | fl
Что делать дальше – понятно. Тут и обработка ошибок, и нормальное формирование объекта, и что делать, если хост не отвечает, и сравнение состояния «как есть» и «как надо».
Заключение
На написание 5 заметок ушло где-то 10-15 дней, по часу в день. Не каждый день. Хотя один выходной я потратил, с обеда до ужина.
На запуск такого костыля «чтобы посмотреть, что там в простой инфраструктуре», зная как, и куда смотреть, ушло бы примерно 3 рабочих дня для 1 джуна. Даже если в инфраструктуре вообще ничего нет. Остальные работы, доведение выгрузки до читаемого вида, сбор образцов, итд, заняли бы еще пару дней с кофе.
Поэтому, если где-то нет такой выгрузки, то я не знаю, чем там занимается и отдел ИТ, и отдел имитации безопасности.
Литература
ahuffman / ansible-sudoers
Ansible docs: ansible
Microsoft: Windows PowerShell - Build User-Friendly XML Interfaces with Windows PowerShell
Microsoft: Add-Member Module: Microsoft.PowerShell.Utility Module
Microsoft: Everything you wanted to know about PSCustomObject
Microsoft: about_Regular_Expressions
Лига Сисадминов
2.4K пост18.9K подписчиков
Правила сообщества
Мы здесь рады любым постам связанным с рабочими буднями специалистов нашей сферы деятельности.