- Python 81.7%
- Shell 18.3%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| inventory | ||
| LICENSE | ||
| README.md | ||
Dynamic inventory for Ansible
Генерирует hosts.ini для Ansible из YAML или INI-инвентаря.
Проверяет доступность портов, ведёт кэш last_online, поддерживает
фильтры по имени.
flowchart TB
A["📄 Инвентарь<br/>YAML / INI"] --> B["① extract"]
B --> C["② check_ports"]
C --> D["③ filter_records"]
D --> E["④ compose"]
E --> F["📦 hosts.ini"]
B --> B2["парсинг · диапазоны · exclude-ips<br/>unresolvable → forced"]
C --> C2["TCP :22 · PTR · online_no_ptr.txt"]
D --> D2["--include · --exclude<br/>--exclude-ips-hosts"]
E --> E2["last_online · prune 90d<br/>группировка · дедуп"]
B -.->|targets.txt · hints.json| C
C -.->|resolver.jsonl| D
D -.->|filtered.jsonl| E
E2 -.->|cache.json| E
classDef input fill:#e3f2fd,stroke:#1976d2,color:#000
classDef module fill:#f3e5f5,stroke:#7b1fa2,color:#000
classDef detail fill:#fafafa,stroke:#9e9e9e,color:#555
classDef output fill:#c8e6c9,stroke:#2e7d32,stroke-width:2px,color:#000
class A input
class B,C,D,E module
class B2,C2,D2,E2 detail
class F output
Структура
inventory/
│
├─ bin/
│ └─ gen-inventory.sh # Оркестратор
│
├─ lib/
│ ├─ extract_hosts.py # Модуль 1
│ ├─ check_ports.py # Модуль 2
│ ├─ filter_records.py # Модуль 3
│ └─ compose_inventory.py # Модуль 4
│
├─ etc/
│ ├─ exclude-ips.txt # Чёрный список IP
│ └─ unresolvable.txt # хосты без DNS, но доступные по SSH
│
├─ run/ # Промежуточные файлы
│ ├─ targets.txt # Список целей
│ ├─ hints.json # Карта target
│ ├─ resolver.log # Ошибки DNS и прочие warning
│ ├─ resolver.jsonl # JSONL от check_ports
│ └─ online_no_ptr.txt # IP с открытым портом, но без PTR
│
├─ hosts.yml # Входной YAML-инвентарь (редактируется вручную)
├─ hosts.ini # Альтернативный входной INI (по --input)
|
├─ hosts.ini # Выходной INI-инвентарь (генерируется)
|
├─ hosts.ini.cache.json # Кэш состояний
└─ hosts.ini.lock # Lock (если /var/lock недоступен)
Требования и установка
- python3 (3.8+)
- pyyaml —
pip install pyyamlили системный пакетpython3-yaml
chmod +x bin/gen-inventory.sh lib/*.py
Один хост в нескольких группах
Если в YAML хост входит в несколько групп (например, через children),
в hints.json он получит массив entries со всеми группами и переменными.
В hosts.ini он появится во всех своих группах. Ansible исполнит задачи
для него один раз, несмотря на несколько секций. Дедуп внутри одной группы
сохраняется (по hostname).
Как работают фильтры
Фильтры применяются в модуле filter_records.py, к итоговому имени хоста
(DNS из PTR или сам IP, если PTR не сработал).
все хосты из инвентаря → --include → --exclude → --exclude-ips-hosts
→ filtered.jsonl → compose → hosts.ini
--include REGEX— если задан хотя бы один, остаются только совпавшие.--exclude REGEX— применяется после include.--exclude-ips-hosts REGEX— чёрный список по имени: если итоговое DNS-имя совпало, IP исключается полностью. Работает и для forced-хостов изunresolvable.txt.
Каждый шаблон компилируется отдельно, поэтому inline-флаги вроде (?i)
работают в любом из них:
--include '(?i)^adn' # любой регистр
--include '^(adn07|ADN07)' # ровно два варианта
Файл run/online_no_ptr.txt
После каждого запуска в run/ появляется список IP, у которых порт открыт,
но PTR не сработал. Эти IP попадают в hosts.ini под своим IP-адресом
(ansible_host=IP). Их можно:
- добавить в
etc/exclude-ips.txt— если это оборудование без SSH; - добавить в
etc/unresolvable.txt— если хост нужен под понятным именем.
Чёрный список IP по именам
Пример: убрать принтеры, свитчи, IoT:
--exclude-ips-hosts '^printer-' \
--exclude-ips-hosts '(?i)^switch-' \
--exclude-ips-hosts '^iot-'
Пример запуска
./bin/gen-inventory.sh \
--input /etc/ansible/hosts.yml \
--output /etc/ansible/hosts.ini \
--cache /var/cache/inventory.cache.json \
--run-dir /tmp/inv-run \
--include '(?i)^adn' \
--exclude-ips-hosts '^printer-' \
--exclude-ips-hosts '(?i)^switch-' \
--exclude-ips-hosts '^iot-' \
--exclude-ips-file /etc/ansible/exclude-ips.txt \
--unresolvable-file /etc/ansible/unresolvable.txt
Про регистр
Regex регистрозависим по умолчанию. ^adn07 поймает adn07-web, но
не поймает ADN07-web.
Параметры
| Параметр | Описание | По умолчанию |
|---|---|---|
--input PATH |
Входной инвентарь (YAML или INI) | <BASE>/hosts.yml |
--output PATH |
Выходной INI-файл | <BASE>/hosts.ini |
--cache PATH |
Файл кэша состояний | <output>.cache.json |
--run-dir PATH |
Каталог промежуточных файлов | <BASE>/run |
--exclude-ips-file |
Чёрный список IP | <BASE>/etc/exclude-ips.txt |
--unresolvable-file |
Файл «имя IP» для хостов без DNS | <BASE>/etc/unresolvable.txt |
--include REGEX |
Оставить только совпадающие имена | — |
--exclude REGEX |
Исключить совпадающие имена | — |
--exclude-ips-hosts |
Исключить IP по совпадению DNS-имени | — |
--port N |
TCP-порт проверки | 22 |
--timeout N |
Таймаут TCP-connect, сек | 5 |
--workers N |
Параллельных потоков | 40 |
--delete-older N |
Хранить офлайн-хосты N дней | 90 |
--no-ptr |
Не делать PTR для IP-целей | — |
--backup |
Бэкап предыдущего hosts.ini с меткой времени |
— |
-v |
Подробный вывод | — |
Статусы проверки порта
| TCP-результат | Статус | Что значит |
|---|---|---|
| open | online | Порт открыт, SSH работает |
| refused (RST) | online | Хост жив, порт закрыт или firewall |
| timeout | offline | Тишина: хост выключен или DROP |
| error | unknown | Ошибка сети, DNS-провал |
Запуск по расписанию через systemd
Создать /etc/systemd/system/gen-inventory.service
[Service]
Type=oneshot
User=ansible
Group=ansible
WorkingDirectory=/path/to/inventory
Environment=PATH=/usr/local/bin:/usr/bin:/bin
ExecStart=/path/to/inventory/bin/gen-inventory.sh \
--input /path/to/inventory/hosts.yml \
--output /path/to/inventory/hosts.ini \
--delete-older 90 \
--backup
StandardOutput=journal
StandardError=journal
Создать /etc/systemd/system/gen-inventory.timer
[Unit]
Description=Run inventory generator twice a day
[Timer]
OnCalendar=*-*-* 02:00:00
OnCalendar=*-*-* 14:00:00
RandomizedDelaySec=300
Persistent=true
[Install]
WantedBy=timers.target
Активировать:
sudo systemctl daemon-reload
sudo systemctl enable --now gen-inventory.timer
systemctl list-timers gen-inventory.timer
Запуск через Ansible (add_host)
Если нужно, чтобы этот инвентарь сразу использовался в плейбуке:
# playbooks/refresh-inventory.yml
- hosts: localhost
gather_facts: no
tasks:
- name: Generate inventory
ansible.builtin.command:
cmd: /path/to/inventory/bin/gen-inventory.sh --delete-older 90
register: gen
changed_when: false
- name: Show summary
ansible.builtin.debug:
msg: "{{ gen.stderr_lines | select('search', 'compose:') | list }}"
И затем в основном плейбуке -i inventory/hosts.ini