2026-08-21 07:34:45 +03:00
# Работа со стендом
Этот документ — для оператора, который уже развернул стенд (см.
[SETUP.md ](SETUP.md )) и теперь использует его в повседневной работе:
добавляет адреса на проверку, следит за очередью, разбирается в
результатах и реагирует на проблемы. Прямые вызовы API описаны в
[API.md ](API.md ) — здесь мы используем их только как инструмент, не
углубляясь в протокол.
2026-10-01 11:35:24 +03:00
> **Токен в примерах.** Если на стенде включена аутентификация
> ([SETUP.md](SETUP.md#5-аутентификация-токены-и-пароль-дашборда)), к вызовам
> `/api/v1/admin/*` в примерах `curl` ниже нужно добавлять заголовок
> `-H "Authorization: Bearer $ADMIN_TOKEN"` (значение `CONTROL_API_ADMIN_TOKEN`);
> для краткости он опущен. Дашборд запрашивает логин и пароль.
2026-08-21 07:34:45 +03:00
## Содержание
- [Как устроена работа с системой ](#как-устроена-работа-с-системой )
- [Добавление новых IP в очередь ](#добавление-новых-ip-в-очередь )
2026-09-23 09:52:01 +03:00
- [Сканирование Floating IP из OpenStack ](#сканирование-floating-ip-из-openstack )
2026-10-01 10:28:53 +03:00
- [Автоматический цикл проверок ](#автоматический-цикл-проверок )
2026-08-21 07:34:45 +03:00
- [Наблюдение за очередью ](#наблюдение-за-очередью )
- [Значения полей IP ](#значения-полей-ip )
- [Как читать итоговый результат (pass/partial/fail) ](#как-читать-итоговый-результат-passpartialfail )
- [Просмотр деталей и истории по конкретному адресу ](#просмотр-деталей-и-истории-по-конкретному-адресу )
2026-09-23 09:52:01 +03:00
- [Реестр адресов и глубина истории ](#реестр-адресов-и-глубина-истории )
2026-08-21 07:34:45 +03:00
- [Управление валидаторами ](#управление-валидаторами )
- [Управление площадками (проберами) ](#управление-площадками-проберами )
2026-08-26 19:45:48 +03:00
- [Управление типами проверок пробера ](#управление-типами-проверок-пробера )
2026-08-23 20:39:22 +03:00
- [Управление целями проверки ](#управление-целями-проверки )
2026-08-21 07:34:45 +03:00
- [Повторная проверка адреса ](#повторная-проверка-адреса )
2026-08-23 20:39:22 +03:00
- [Принудительная остановка проверки ](#принудительная-остановка-проверки )
2026-08-23 22:24:55 +03:00
- [Удаление адресов из очереди ](#удаление-адресов-из-очереди )
2026-08-24 10:29:08 +03:00
- [Пауза перед self-check (fip_settle_seconds) ](#пауза-перед-self-check-fip_settle_seconds )
2026-08-21 07:34:45 +03:00
- [Частые проблемы и что с ними делать ](#частые-проблемы-и-что-с-ними-делать )
## Как устроена работа с системой
Оператор не взаимодействует с валидаторами и проберами напрямую — вся
работа идёт через `control-api` . Цикл жизни одного IP-адреса:
1. Адрес встаёт в очередь (`queued` ).
2. Control-api сам находит свободный валидатор, привязывает адрес к нему
как Floating IP.
3. Валидатор проверяет, что действительно вышел в интернет именно через
этот адрес (self-check), затем прогоняет исходящие проверки (HTTPS,
ICMP, опционально SSH до заданных внешних целей).
4. Одновременно три внешние площадки проверяют, что этот адрес доступен
*снаружи* (входящие TCP-подключения на 22/80/443/8080 и ICMP) — это
ловит блокировки/чёрные списки на конкретных внешних сетях.
5. Как только все источники (валидатор + 3 площадки) отчитались — или
истекло время ожидания — control-api подводит итог и освобождает
адрес (отвязывает Floating IP).
Всё это происходит автоматически, без участия оператора. Задача оператора
— положить адреса в очередь и снять с них результат.
## Добавление новых IP в очередь
2026-08-23 20:39:22 +03:00
Основной способ — API, без перезапуска процесса:
2026-08-21 07:34:45 +03:00
2026-08-23 20:39:22 +03:00
```bash
curl -s -X POST http://<control-api>:8080/api/v1/admin/ips \
-d '{"addresses": ["203.0.113.10", "203.0.113.11"]}'
```
2026-08-21 07:34:45 +03:00
2026-08-23 20:39:22 +03:00
```json
{ "added" : [ "203.0.113.10" , "203.0.113.11" ], "requeued" : [], "reordered" : [], "skipped_in_progress" : []}
```
2026-08-21 07:34:45 +03:00
2026-08-23 20:39:22 +03:00
Адреса обрабатываются в порядке, в котором перечислены в `addresses` —
именно в этом порядке они и встанут в очередь друг за другом. Метод
идемпотентен относительно уже идущих проверок: адрес, который сейчас
активно проверяется, в ответе окажется в `skipped_in_progress` и не будет
тронут (см. [«Повторная проверка адреса» ](#повторная-проверка-адреса )
ниже — тот же метод форсирует перепроверку уже завершённых адресов).
Также по-прежнему можно добавить адреса через `ip_addresses` в
`/etc/cloud-ip-validator/control-api.yaml` и перезапустить control-api —
при каждом старте control-api доливает в очередь только новые адреса из
этого списка (уже обработанные ранее не сбрасываются и повторно не
проверяются). Держать `control-api.yaml` под версионным контролем (git)
по-прежнему полезно как журнал того, что изначально ставилось в очередь
при разворачивании стенда — но для повседневного добавления адресов проще
и быстрее пользоваться API выше.
2026-08-21 07:34:45 +03:00
2026-09-23 09:52:01 +03:00
## Сканирование Floating IP из OpenStack
Вместо того чтобы перечислять адреса вручную, можно попросить control-api
самому найти их в облаке:
```bash
curl -s -X POST http://<control-api>:8080/api/v1/admin/ips/scan
```
Сканируются все Floating IP текущего проекта OpenStack, но в очередь
ставятся только **свободные** — те, что не привязаны сейчас ни к одному
порту (это и есть пул адресов, ожидающих проверки перед повторной
выдачей). Уже привязанные к чему-то Floating IP игнорируются. Внутри
вызов делает то же самое, что и обычное добавление — новые адреса
встают в очередь, уже завершённые перезапускаются, активно проверяемые не
трогаются (см. [выше ](#добавление-новых-ip-в-очередь )).
В `admin-dashboard` то же самое — кнопка «Сканировать Floating IP» на
странице `/ips` .
Если хочется, чтобы сканирование происходило само по расписанию, а не
только по запросу — задайте `orchestrator.fip_scan_interval_seconds`
(в секундах) в `control-api.yaml` ; `0` (по умолчанию) оставляет только
2026-10-01 10:28:53 +03:00
ручной запуск через ручку/кнопку выше. Пока включён
[автоматический цикл ](#автоматический-цикл-проверок ), это периодическое
сканирование не выполняется — цикл сам управляет очередью.
## Автоматический цикл проверок
Опциональный режим, который сам повторяет то, что оператор делает руками:
по умолчанию **выключен** , включается и настраивается администратором.
Один цикл — это пять шагов:
1. Очередь очищается целиком — то же, что кнопка «Очистить всё» (см.
[«Удаление адресов из очереди» ](#удаление-адресов-из-очереди )). История
в [реестре ](#реестр-адресов-и-глубина-истории ) при этом сохраняется.
2. Control-api находит все свободные Floating IP и ставит их в очередь —
то же, что «Сканировать Floating IP» (см.
[выше ](#сканирование-floating-ip-из-openstack )).
3. Проверки запускаются сами — как для любого адреса в очереди.
4. Цикл ждёт, пока **все** адреса очереди дойдут до конечного состояния
(`done` , `failed` или `occupied` ). К этому моменту результат каждого
адреса уже записан в реестр.
5. Выдерживается пауза `interval_seconds` , после чего цикл начинается заново
с шага 1. Пауза отсчитывается от **завершения** предыдущего цикла, а не
от его начала.
### Параметры
| Параметр | По умолчанию | Смысл |
|---|---|---|
| `interval_seconds` | `3600` (1 час) | Пауза между циклами. Не меньше `60` : слишком частые сканы нагружают API OpenStack. |
| `max_run_seconds` | `0` (без лимита) | Сколько максимум ждать на шаге 4. По истечении цикл фиксирует `timeout` и переходит к паузе — защита от зависания (нет свободных валидаторов, недоступна площадка). Очередь при этом не трогается: следующий цикл её очистит, а до тех пор видно, что именно не дошло до конца. |
Параметры хранятся в базе и меняются на лету, без перезапуска; в `control-api.yaml`
ничего задавать не нужно. Новый `interval_seconds` применяется к паузе
**со следующего цикла** — уже идущая пауза досчитывается по старому значению.
### Управление
Через API (подробности — в [API.md ](API.md#автоматический-цикл-проверок )):
```bash
# задать параметры (любое из полей можно опустить)
curl -s -X PUT http://<control-api>:8080/api/v1/admin/auto-cycle \
-d '{"interval_seconds": 7200, "max_run_seconds": 1800}'
# включить: первый цикл начнётся сразу
curl -s -X POST http://<control-api>:8080/api/v1/admin/auto-cycle/start
# выключить
curl -s -X POST http://<control-api>:8080/api/v1/admin/auto-cycle/stop
# посмотреть состояние
curl -s http://<control-api>:8080/api/v1/admin/auto-cycle
```
В `admin-dashboard` — панель «Автоматический цикл» на странице `/settings` : поля
«Интервал между циклами» и «Максимальная длительность проверки» (в минутах),
кнопки «Включить»/«Выключить». Пока автоцикл включён, на странице `/overview`
в блоке статистики показывается индикатор «Автоцикл активен» с фазой и временем
следующего запуска.
### Фазы и результат последнего цикла
`phase` показывает, что происходит сейчас: `idle` (автоцикл выключен или ещё не
стартовал), `running` (идут проверки — шаги 3–4) и `waiting` (пауза между циклами,
шаг 5; время следующего запуска — `next_run_at` ). Результат последнего цикла
(`last_outcome` ):
| Значение | Что произошло |
|---|---|
| `completed` | Все адреса дошли до конечного состояния; `runs_total` растёт на 1. |
| `no_free_ips` | Сканирование не нашло свободных Floating IP — ждать нечего, цикл сразу ушёл в паузу. |
| `timeout` | Проверки не уложились в `max_run_seconds` . |
| `error` | Не удалось очистить очередь или просканировать облако; причина — в `last_error` . Повтор — через `interval_seconds` . |
| `stopped` | Автоцикл выключили в момент, когда шёл цикл. Выключение в паузе предыдущий результат не затирает. |
### Что важно знать
- **Выключение не прерывает проверки**, которые уже идут: они закончатся и попадут
в реестр, остановится только повторение.
- Автоцикл **владеет очередью** : каждый цикл начинается с её полной очистки,
поэтому адреса, добавленные вручную, будут удалены (их история в реестре
остаётся). Ручные «Очистить всё» и «Сканировать Floating IP» во время цикла
не ломают его: если очередь опустела, цикл считается завершённым.
- Состояние хранится в базе и **переживает перезапуск** control-api: идущий цикл
продолжит ждать, а пауза — досчитается до прежнего `next_run_at` .
- События цикла (`auto_cycle_started` , `auto_cycle_completed` , `auto_cycle_timeout` ,
`auto_cycle_error` , `auto_cycle_stopped` ) пишутся в журнал событий вместе с
`queue_cleared` и `fip_scan` .
- В реальном OpenStack отвязка Floating IP после очистки очереди может
отразиться с задержкой; если скан сразу после неё не увидел свободных адресов,
цикл завершится с `no_free_ips` и повторится через `interval_seconds` .
2026-09-23 09:52:01 +03:00
2026-08-21 07:34:45 +03:00
## Наблюдение за очередью
Общая сводка:
```bash
curl -s http://<control-api>:8080/api/v1/admin/status | python3 -m json.tool
```
```json
{
"total_ips" : 25 ,
"ips_by_state" : { "queued" : 10 , "awaiting_self_check" : 1 , "checking" : 3 , "done" : 10 , "failed" : 1 },
"total_validators" : 4
}
```
`ips_by_state` — сколько адресов в каждом состоянии прямо сейчас. Если
хотите наблюдать за прогрессом в реальном времени:
```bash
watch -n 2 'curl -s http://<control-api>:8080/api/v1/admin/status | python3 -m json.tool'
```
Полный список всех адресов со всеми полями:
```bash
curl -s http://<control-api>:8080/api/v1/admin/ips | python3 -m json.tool
```
Только финальные результаты (уже готовые адреса), с помощью `jq` :
```bash
curl -s http://<control-api>:8080/api/v1/admin/ips \
| jq '[.[] | select(.State=="done" or .State=="failed") | {IPAddress, State, OverallResult}]'
```
2026-09-23 13:25:31 +03:00
В `admin-dashboard` то же самое — страница `/overview` , с поиском по IP и
фильтром по статусу (`pass` /`partial` /`fail` /`cancelled` ) над обеими
таблицами сразу (см. [DASHBOARD.md ](DASHBOARD.md )).
2026-08-21 07:34:45 +03:00
## Значения полей IP
| Поле | Значение |
|---|---|
| `IPAddress` | Проверяемый адрес |
2026-08-23 20:39:22 +03:00
| `Sequence` | Позиция в очереди (порядок постановки — из конфига при первом старте либо из последнего вызова `POST /api/v1/admin/ips` ) |
2026-09-13 23:54:35 +03:00
| `State` | Текущий этап: `queued` , `assigning_fip` , `awaiting_self_check` , `checking` , `aggregating` , `done` , `failed` , `occupied` (см. ниже) |
2026-08-21 07:34:45 +03:00
| `OwnerValidatorID` | Какой валидатор сейчас (или последним) занимался этим адресом |
| `FIPID` | Идентификатор Floating IP в OpenStack, к которому привязан адрес (пусто, если ещё/уже не привязан) |
| `AttemptNumber` | Номер попытки — растёт при каждом requeue (сбой привязки, сбой self-check, реклейм по таймауту) |
| `RetryCount` | Сколько раз адрес уже переставлялся в очередь заново |
| `EgressComplete` | Валидатор закончил исходящие проверки |
2026-08-23 20:39:22 +03:00
| `OverallResult` | Итог: `pass` , `partial` , `fail` , `cancelled` (принудительно остановлена, см. [«Принудительная остановка проверки» ](#принудительная-остановка-проверки )), либо пусто, пока проверка не завершена |
2026-08-21 07:34:45 +03:00
| `AssignedAt` / `AggregatedAt` / `FIPReleasedAt` | Метки времени соответствующих этапов |
2026-08-26 20:47:54 +03:00
> Завершённость входящих проверок по каждой конкретной площадке в этом
> списке не отображается (площадок теперь может быть сколько угодно, а не
> фиксированные три) — детали по конкретной площадке смотрите в массиве
> `checks` ответа `GET /api/v1/admin/ips/{ip}` (`Source: "inbound-site-N"`).
2026-08-21 07:34:45 +03:00
## Как читать итоговый результат (pass/partial/fail)
2026-08-21 11:25:52 +03:00
- **`pass` ** — прошли все проверки: все исходящие, плюс все площадки по
всем портам и ICMP — если площадки вообще настроены (`sites` в конфиге
control-api может быть пустым, см.
[«Управление площадками» ](#управление-площадками-проберами ) — тогда
учитываются только исходящие). Адрес можно считать пригодным к
повторной выдаче.
2026-08-21 07:34:45 +03:00
- **`partial` ** — часть проверок прошла, часть — нет (например, площадка
site-2 не смогла достучаться по 8080/tcp, но остальное в порядке).
Означает частичную деградацию — например, адрес заблокирован в
отдельном сегменте сети/у отдельного провайдера. Требует решения
оператора: годится ли адрес для данного случая использования.
- **`fail` ** — либо ни одна проверка не прошла, либо адрес вообще не
дошёл до стадии проверок (например, self-check не подтвердился —
трафик валидатора не пошёл через назначенный FIP — и попытки
исчерпались). Смотрите `events` по этому адресу (см. ниже), чтобы
понять, на каком шаге и почему.
2026-08-23 20:39:22 +03:00
- **`cancelled` ** — проверку остановил оператор через `POST
/api/v1/admin/ips/{ip}/cancel` (` State` при этом — ` failed`), а не
система по итогам проверок. Отличать от обычного ` fail` полезно, чтобы
не путать «адрес не прошёл проверку» с «проверку прервали вручную».
2026-08-21 07:34:45 +03:00
2026-09-13 23:54:35 +03:00
Отдельно от ` OverallResult` стоит состояние **` State: "occupied"`** —
облако живое, и список адресов, переданный как «свободные» (из конфига
или через ` POST /api/v1/admin/ips`), мог с тех пор разойтись с
реальностью, либо адрес мог быть передан на проверку по ошибке уже
занятым. Если при попытке привязки Floating IP control-api видит, что тот
уже привязан к чужому порту, адрес переводится в ` occupied` **до начала**
цикла проверки — ` OverallResult` при этом остаётся пустым, это не ` fail`:
` fail` означает «проверка стартовала и не прошла», ` occupied` — «проверка
не стартовала, адрес занят кем-то другим». В ` events` по адресу
появляется строка ` fip_occupied`. Автоматических повторных попыток нет
(Neutron сам не освобождает адрес) — верните адрес в работу вручную через
` POST /api/v1/admin/ips`, когда убедитесь, что конфликт в облаке
разрешился; в дашборде для таких адресов также показывается кнопка
«Перепроверить» вместо «Отменить».
2026-08-21 07:34:45 +03:00
Отсутствие ответа от источника (площадка не прислала результат до
истечения ` checking_window_seconds`) засчитывается как провал — это
управляется настройкой ` aggregation.missing_counts_as_fail` в конфиге
control-api (по умолчанию включено).
## Просмотр деталей и истории по конкретному адресу
` ``bash
curl -s http://<control-api>:8080/api/v1/admin/ips/203.0.113.10 | python3 -m json.tool
` ``
Ответ содержит три части:
- ` ip` — те же поля, что и в списке ` admin/ips`, но для одного адреса;
- ` checks` — все отдельные проверки текущей попытки: кто проверял
(` Source`: ` egress` или ` inbound-site-N`), что именно (` CheckType`,
` Target`), результат (` Success`), задержка (` LatencyMS`), и
человекочитаемая деталь (` Detail`, например текст ошибки при отказе);
- ` events` — журнал аудита по этому адресу в хронологическом порядке
(регистрация, привязка FIP, self-check, агрегация и т.д.) — полезен,
чтобы восстановить точную последовательность событий при разборе
инцидента.
Пример: почему адрес получил ` fail`?
` ``bash
curl -s http://<control-api>:8080/api/v1/admin/ips/203.0.113.10 \
| jq '.checks[] | select(.Success==false)'
` ``
2026-09-23 09:52:01 +03:00
Это — только текущая попытка. Полная история адреса за всё время, включая
предыдущие попытки и даже периоды, когда адрес не стоял в очереди вовсе,
смотрится через реестр — см. следующий раздел.
## Реестр адресов и глубина истории
` GET /api/v1/admin/ips/{ip}` (и ` /ips/{ip}` в дашборде) показывает только
*текущую* попытку проверки. Но если адрес удалили из очереди и позже
добавили заново, это уже новая попытка — а куда девается история старой?
Она не пропадает: control-api ведёт отдельный **реестр** (` ip_registry`) —
запись обо всех адресах, когда-либо поставленных на проверку, вместе с
полной накопленной историей проверок по каждому, независимо от того,
удалялся ли адрес из очереди и добавлялся ли повторно.
` ``bash
# все адреса, когда-либо ставившиеся на проверку, с краткой сводкой
curl -s http://<control-api>:8080/api/v1/admin/registry | python3 -m json.tool
# полная история проверок одного адреса, по всем циклам, не только текущему
curl -s http://<control-api>:8080/api/v1/admin/registry/203.0.113.10 | python3 -m json.tool
` ``
В ` admin-dashboard` — страницы ` /registry` (список) и ` /registry/{ip}`
(история конкретного адреса), со ссылкой туда со страницы ` /ips/{ip}`.
2026-09-23 13:25:31 +03:00
На ` /registry` — тот же поиск по IP и фильтр по статусу, что и на
` /overview`, плюс он отражается в адресной строке (` ?q=&status=`), так что
отфильтрованную ссылку можно сохранить или переслать.
2026-09-23 09:52:01 +03:00
**Глубина хранения.** Чтобы история не росла бесконечно на адресах,
которые перепроверяют очень часто, можно ограничить, сколько последних
циклов проверки хранить на каждый адрес — ` history_retention_cycles` на
странице ` /settings` (или ` PUT /api/v1/admin/config/orchestrator`, см.
[API.md](API.md#настройки-оркестратора-apiv1adminconfigorchestrator)).
` 0` (по умолчанию) — хранить без ограничения. Ограничение действует только
на глубину детальной истории проверок; сама запись в реестре (что адрес
существует, когда впервые встречен, сколько всего было циклов) не
удаляется никогда.
2026-08-21 07:34:45 +03:00
## Управление валидаторами
Список валидаторов и их текущее состояние:
` ``bash
curl -s http://<control-api>:8080/api/v1/admin/validators | python3 -m json.tool
` ``
Состояния валидатора: ` unregistered` (в конфиге есть, агент ещё не
подключался), ` idle` (свободен, готов взять адрес), ` assigned`/` checking`
(занят), ` unreachable` (пропустил heartbeat дольше
` orchestrator.heartbeat_timeout_seconds`).
2026-08-23 20:39:22 +03:00
**Добавление нового валидатора (без перезапуска control-api):**
2026-08-21 07:34:45 +03:00
1. Поднимите новую ВМ в сервисном проекте облака, узнайте её Neutron
` port_id`.
2026-08-23 20:39:22 +03:00
2. Зарегистрируйте валидатора через API:
` ``bash
curl -s -X POST http://<control-api>:8080/api/v1/admin/config/validators \
-d '{"validator_id": "validator_05", "os_port_id": "port-abc123"}'
` ``
2026-08-21 07:34:45 +03:00
3. Разверните и запустите ` validator-agent` на новой ВМ с тем же
` validator_id` в его конфиге (см. [SETUP.md](SETUP.md#развёртывание-validator-agent-на-вм-валидаторах)).
2026-08-23 20:39:22 +03:00
Сменить ` os_port_id` уже существующего валидатора (например, после
пересоздания ВМ) — ` PUT /api/v1/admin/config/validators/{id}` с телом
` {"os_port_id": "новый-port-id"}`.
Полный список зарегистрированных валидаторов — ` GET
/api/v1/admin/config/validators` (в отличие от ` GET
/api/v1/admin/validators`, отдаёт ` snake_case` и без текущего IP —
только конфигурационные поля).
2026-08-21 07:34:45 +03:00
**Вывод валидатора из эксплуатации:** остановите на нём
` validator-agent` (` systemctl stop validator-agent`). Он перестанет
получать новые задания после того, как закончит текущее (если оно было);
если он был убит посреди работы — control-api сам заберёт у него
незавершённый адрес обратно в очередь по истечении
2026-08-23 20:39:22 +03:00
` orchestrator.lease_ttl_seconds`. Удалять регистрацию валидатора не
обязательно — просто выключенный агент не будет ничего забирать. Если всё
же нужно убрать валидатора из системы совсем:
` ``bash
curl -s -X DELETE http://<control-api>:8080/api/v1/admin/config/validators/validator_05
` ``
Возвращает ` 409`, если валидатор прямо сейчас владеет каким-то IP —
дождитесь освобождения (или принудительно остановите его проверку, см.
[«Принудительная остановка проверки»](#принудительная-остановка-проверки))
перед удалением.
> Правки через ` validators[]` в ` control-api.yaml` тоже поддерживаются,
> но только как bootstrap пустой базы данных при самом первом старте — как
> только в БД есть хотя бы один валидатор, YAML для этой секции
> игнорируется при всех последующих рестартах (см.
> [SETUP.md](SETUP.md#развёртывание-control-api)). Для стенда, который уже
> хоть раз запускался, используйте API выше.
2026-08-21 07:34:45 +03:00
## Управление площадками (проберами)
2026-08-26 20:47:54 +03:00
**Входящие (inbound/prober) проверки полностью опциональны, а число
площадок не ограничено.** Список ` sites` в конфиге control-api и есть
переключатель: пусто — inbound-проверки выключены целиком, итоговый
результат считается только по исходящим (egress) проверкам, и агрегация
не ждёт вообще ни одного пробера. Указано N слотов — ждём именно их.
2026-08-21 11:25:52 +03:00
Явного отдельного флага "включить/выключить" нет — самого списка ` sites`
достаточно.
2026-08-21 07:34:45 +03:00
2026-08-23 20:39:22 +03:00
Чтобы добавить площадку (без перезапуска control-api) — назначьте
2026-08-26 20:47:54 +03:00
` site_id` любому свободному слоту (` index` — любое целое ` >= 1`, слотов
может быть сколько угодно) через API:
2026-08-23 20:39:22 +03:00
` ``bash
curl -s -X PUT http://<control-api>:8080/api/v1/admin/config/sites/1 \
-d '{"site_id": "site-1"}'
` ``
и разверните на площадке ` prober` с тем же ` site_id`. Чтобы отключить
конкретную площадку — освободите слот:
` ``bash
curl -s -X DELETE http://<control-api>:8080/api/v1/admin/config/sites/1
` ``
2026-08-21 11:25:52 +03:00
процесс ` prober` на ней можно не останавливать (он просто перестанет
2026-08-23 20:39:22 +03:00
получать назначения — ` POST /api/v1/probers/register` для отвязанного
` site_id` начнёт отвечать ` 400`). Текущее распределение слотов — ` GET
/api/v1/admin/config/sites`.
> Правки через ` sites[]` в ` control-api.yaml` тоже поддерживаются, но
> только как bootstrap пустой базы данных при самом первом старте — как
> только в БД есть хотя бы одна площадка, YAML для этой секции
> игнорируется при всех последующих рестартах. Для стенда, который уже
> хоть раз запускался, используйте API выше.
2026-08-21 11:25:52 +03:00
2026-08-26 20:47:54 +03:00
### Состояния площадки
По аналогии с валидаторами (см. [«Управление
валидаторами»](#управление-валидаторами) выше), у каждой площадки есть
состояние подключения — видно и в ` GET /api/v1/admin/config/sites`
(поля ` hostname`/` state`/` last_heartbeat_at`), и на странице ` /sites`
дашборда бейджем:
- **` unregistered`** — слот сконфигурирован, но процесс ` prober` на этой
площадке ещё ни разу не подключался (не вызывал ` POST
/api/v1/probers/register`).
- **` idle`** — площадка на связи: ` prober` зарегистрирован и присылает
heartbeat (` POST /api/v1/probers/{site_id}/heartbeat`) на каждом опросе.
- **` unreachable`** — площадка пропустила heartbeat дольше
` orchestrator.heartbeat_timeout_seconds` (тот же параметр, что и для
валидаторов) — вероятно, процесс ` prober` упал или потерял сеть до
control-api.
В отличие от валидатора, у площадки нет состояний ` assigned`/` checking`
— пробер не привязан к одному IP, а на каждом опросе обрабатывает сразу
весь активный набор.
2026-08-21 07:34:45 +03:00
2026-08-26 19:45:48 +03:00
## Управление типами проверок пробера
Список TCP-портов и флаг ICMP, которые ` prober` проверяет на каждой
настроенной площадке — единый глобальный набор, общий для всех площадок
сразу (не то же самое, что список ` sites` выше: ` sites` решает, *сколько*
точек его применяют, а этот набор — *что именно* они проверяют).
Посмотреть текущий набор:
` ``bash
curl -s http://<control-api>:8080/api/v1/admin/config/inbound-checks | python3 -m json.tool
` ``
Изменить набор портов и/или ICMP (без перезапуска control-api — новое
значение сразу видно и следующему опросу пробера, и уже идущей агрегации):
` ``bash
curl -s -X PUT http://<control-api>:8080/api/v1/admin/config/inbound-checks \
-d '{"ports": [22, 80, 443, 8080], "icmp": true}'
` ``
Порты должны быть в диапазоне ` 1..65535` и не повторяться — иначе ` 400`.
Пустой список портов вместе с ` "icmp": false` — штатный способ временно
отключить inbound-проверки целиком, не трогая список площадок:
` ``bash
curl -s -X PUT http://<control-api>:8080/api/v1/admin/config/inbound-checks \
-d '{"ports": [], "icmp": false}'
` ``
То же самое — на странице ` /settings` дашборда, второй формой рядом с
паузой перед self-check (см. ниже): текстовое поле с портами через запятую
и чекбокс ICMP.
2026-08-26 23:52:31 +03:00
Порты ` 22` и ` 443` в этом списке трактуются особо: помимо базового
TCP-connect (` tcp-22`/` tcp-443`) пробер дополнительно выполняет настоящий
обмен SSH-банером (` ssh`) и настоящий TLS-хендшейк (` tls-443`) —
голого открытого TCP-порта недостаточно, чтобы считать SSH/HTTPS
рабочими. Отдельного переключателя для этих доп.проверок нет: они
включаются и выключаются вместе с самим портом в ` ports`. Если на порту
22/443 у площадки на самом деле слушает что-то, кроме SSH/HTTPS,
доп.проверка будет закономерно проваливаться — заведите для такого сервиса
другой порт.
2026-08-26 19:45:48 +03:00
> Правки через ` orchestrator.inbound_checks` в ` control-api.yaml` тоже
> поддерживаются, но только как bootstrap пустой базы данных при самом
> первом старте — как только в БД есть эта настройка (а она появляется
> сразу же при первом старте, значение по умолчанию — из YAML), YAML для
> этой секции игнорируется при всех последующих рестартах. Для стенда,
> который уже хоть раз запускался, используйте API выше.
2026-08-23 20:39:22 +03:00
## Управление целями проверки
Набор egress-целей (` targets`) и типов проверок (` check_types`,
привязывающих тип — ` https`/` icmp`/` ssh` — к одной или нескольким группам
целей) управляется через API так же, как валидаторы и площадки —
изменения подхватываются немедленно, следующим же назначением от
оркестратора, без перезапуска.
Посмотреть текущий набор:
` ``bash
curl -s http://<control-api>:8080/api/v1/admin/config/targets | python3 -m json.tool
curl -s http://<control-api>:8080/api/v1/admin/config/check-types | python3 -m json.tool
` ``
Создать/заменить группу целей и включить тип проверки, ссылающийся на
неё:
` ``bash
curl -s -X PUT http://<control-api>:8080/api/v1/admin/config/targets/web \
-d '{"targets": ["https://hub.docker.com", "https://github.com"]}'
curl -s -X PUT http://<control-api>:8080/api/v1/admin/config/check-types/ssh \
-d '{"enabled": true, "targets": ["web"]}'
` ``
Отключить тип проверки, не удаляя его (значения целей сохраняются):
` ``bash
curl -s -X PUT http://<control-api>:8080/api/v1/admin/config/check-types/ssh \
-d '{"enabled": false, "targets": ["web"]}'
` ``
Удалить группу целей можно только если на неё не ссылается ни один
` check_type` (иначе — ` 409`); удалить сам ` check_type` можно в любой
момент (` DELETE /api/v1/admin/config/check-types/{name}`).
**Важное следствие принятого компромисса**: если конфигурация меняется
ровно в момент, когда чей-то IP уже находится в ` checking` (self-check
уже пройден, проверки уже назначены агенту), агрегация этой конкретной
попытки посчитает уже изменённую конфигурацию, а не ту, что была на
момент выдачи задания. На практике это узкое окно в несколько секунд;
деградирует безопасно — через ` aggregation.missing_counts_as_fail` худший
исход для одной попытки — ` partial` вместо ` pass`, самоисправляется на
следующей попытке (в том числе через принудительный повтор, см. ниже).
> Правки через ` targets`/` check_types` в ` control-api.yaml` тоже
> поддерживаются, но только как bootstrap пустой базы данных при самом
> первом старте — см. примечание в разделах выше.
2026-08-21 07:34:45 +03:00
## Повторная проверка адреса
Если адрес завершился с ` failed` или ` partial`, а вы хотите перепроверить
2026-08-23 20:39:22 +03:00
его ещё раз (например, после устранения блокировки на стороне сети) —
отправьте его тем же методом, что используется для постановки новых
адресов в очередь:
2026-08-21 07:34:45 +03:00
2026-08-23 20:39:22 +03:00
` ``bash
curl -s -X POST http://<control-api>:8080/api/v1/admin/ips \
-d '{"addresses": ["203.0.113.10"]}'
` ``
` ``json
{"added": [], "requeued": ["203.0.113.10"], "reordered": [], "skipped_in_progress": []}
` ``
Адрес в ` requeued` означает, что он был в терминальном состоянии
(` done`/` failed`) и его перезапустили: ` AttemptNumber` увеличился,
` RetryCount` обнулён, предыдущий ` OverallResult` сброшен, адрес снова
` queued` и будет обработан на общих основаниях. Никакой особой обработки
для уже проверенных адресов не требуется — тот же вызов безопасно
принимает список из новых и уже проверенных адресов одновременно;
единственное, что метод не сделает — не запустит вторую параллельную
проверку адреса, который прямо сейчас уже проверяется (такой адрес
вернётся в ` skipped_in_progress`, см.
[«Добавление новых IP в очередь»](#добавление-новых-ip-в-очередь)).
## Принудительная остановка проверки
Если проверка адреса зависла дольше ожидаемого либо просто больше не
актуальна, не дожидайтесь истечения ` checking_window_seconds` —
остановите её сразу:
` ``bash
curl -s -X POST http://<control-api>:8080/api/v1/admin/ips/203.0.113.10/cancel
` ``
Работает из любого состояния, кроме уже терминального (` done`/` failed`
вернут ` 409` — отменять нечего). Если на момент отмены был привязан
Floating IP — он отвязывается (best-effort, как и при обычном завершении
проверки), владевший валидатор освобождается и снова становится ` idle`.
Итог записывается как ` OverallResult: "cancelled"` (в ` State: "failed"`),
и виден в истории адреса наравне с обычными результатами:
` ``bash
curl -s http://<control-api>:8080/api/v1/admin/ips/203.0.113.10 | python3 -m json.tool
` ``
Чтобы позже всё же проверить этот адрес — используйте
[«Повторную проверку адреса»](#повторная-проверка-адреса) выше, она
одинаково работает и для отменённых, и для обычно завершённых адресов.
2026-08-21 07:34:45 +03:00
2026-08-23 22:24:55 +03:00
## Удаление адресов из очереди
2026-09-23 09:52:01 +03:00
**Отличие от отмены (` cancel`) выше: удаление безвозвратно убирает адрес
из очереди.** Cancel переводит адрес в ` failed`/` cancelled` и сохраняет
запись как историю — её видно в очереди и в деталях адреса. Delete
физически стирает строку ` ip_queue`: адрес полностью исчезает из
` /ips`/` /ips/{ip}`, восстановить именно эту строку нельзя. Накопленная
история проверок при этом **не теряется** — она остаётся в
[реестре](#реестр-адресов-и-глубина-истории) (` /registry/{ip}`) и видна
там даже после удаления адреса из очереди. Если нужно просто остановить
зависшую проверку, но сохранить её результат прямо в очереди —
используйте
2026-08-23 22:24:55 +03:00
[«Принудительную остановку проверки»](#принудительная-остановка-проверки)
выше, а не удаление.
Удалить один адрес (работает из любого состояния, включая активно
проверяемое — Floating IP при этом отвязывается, а владевший валидатор
освобождается, точно так же, как при cancel):
` ``bash
curl -s -X DELETE http://<control-api>:8080/api/v1/admin/ips/203.0.113.10
` ``
Удалить список адресов одним вызовом (неизвестные адреса просто
попадают в ` not_found`, не считаются ошибкой):
` ``bash
curl -s -X POST http://<control-api>:8080/api/v1/admin/ips/delete \
-d '{"addresses": ["203.0.113.10", "203.0.113.11"]}'
` ``
Полностью очистить очередь — **самая опасная операция**, удаляет вообще
всё, включая адреса, которые прямо сейчас проверяются:
` ``bash
curl -s -X POST http://<control-api>:8080/api/v1/admin/ips/clear
` ``
В ` admin-dashboard` то же самое доступно на странице ` /ips`: чекбоксы у
каждой строки + кнопка «Удалить выбранные» для точечного/массового
удаления, кнопка «Удалить» в каждой строке, и отдельная кнопка «Очистить
всё» — каждая с подтверждением, явно предупреждающим о необратимости
(см. [DASHBOARD.md](DASHBOARD.md)).
2026-08-24 10:29:08 +03:00
## Пауза перед self-check (fip_settle_seconds)
Как только Floating IP привязывается к валидатору, control-api по
умолчанию сразу же позволяет агенту начать self-check — а data plane
OpenStack может не успеть в этот же момент реально начать пропускать
трафик через только что привязанный адрес, из-за чего self-check ложно
проваливается по причине, не связанной с самой привязкой. Если это
наблюдается на вашем стенде, задайте паузу между привязкой FIP и началом
self-check:
` ``bash
curl -s -X PUT http://<control-api>:8080/api/v1/admin/config/orchestrator \
-d '{"fip_settle_seconds": 5}'
` ``
То же самое — на странице ` /settings` дашборда. ` 0` (по умолчанию) — без
паузы. Пока пауза не истекла, адрес уже в состоянии
` awaiting_self_check`, но ` GET /assignment` агенту продолжает отдавать
` 204` (агент просто ждёт следующего опроса, доработок на его стороне не
требуется); в дашборде на ` /ips` такой адрес в это время помечен бейджем
«прогрев FIP» вместо обычного статуса.
Значение обязано оставлять запас внутри лизинга адреса:
` fip_settle_seconds + self_check_timeout_seconds` должно быть **меньше**
` orchestrator.lease_ttl_seconds` — иначе пауза плюс сам self-check не
влезут в лизинг, адрес не успеет пройти self-check до истечения
` lease_ttl_seconds` и будет вечно возвращаться в очередь через
` sweepExpiredLeases`. Попытка задать такое значение отклоняется ` 400`, не
обрезается молча.
2026-08-21 07:34:45 +03:00
## Частые проблемы и что с ними делать
**Валидатор долго висит в ` unreachable`.**
Проверьте сетевую связность ВМ-валидатора до ` control-api` (порт из
` server.listen_addr`) и что процесс ` validator-agent` вообще запущен
(` systemctl status validator-agent`, ` journalctl -u validator-agent`).
2026-08-21 11:04:49 +03:00
**Адрес не выходит из ` awaiting_self_check` (статус не меняется вообще).**
2026-08-24 10:29:08 +03:00
Если это длится всего несколько секунд и на стенде настроена
[пауза перед self-check](#пауза-перед-self-check-fip_settle_seconds)
(` fip_settle_seconds`) — это ожидаемое поведение, не сбой: адрес
сознательно придерживается, прежде чем агенту разрешат начать проверку.
Проблема — если статус не меняется значительно дольше этой паузы. Тогда
дело обычно в self-check: он запрашивает внешние (вне облака) сервисы из
2026-08-21 11:04:49 +03:00
` self_check.ip_echo_urls` в конфиге валидатора (по умолчанию
` api.ipify.org`, ` ifconfig.me`) — если у ВМ-валидатора нет исходящего
доступа в интернет к этим адресам, запрос не проходит вообще, и агент
даже не может *сообщить* результат control-api (ни успешный, ни
неуспешный) — тогда статус реально зависает до истечения
` orchestrator.lease_ttl_seconds`, после чего адрес возвращается в
` queued` и цикл повторяется. Проверьте связность до
` self_check.ip_echo_urls` прямо с ВМ-валидатора (` curl -s
https://api.ipify.org`) и логи ` journalctl -u validator-agent` на предмет
` ip echo request failed`.
**Адрес постоянно проваливает self-check (не зависает, а именно
возвращается в очередь снова и снова).**
2026-08-21 07:34:45 +03:00
Смотрите ` events` по адресу (` GET /api/v1/admin/ips/{ip}`) — в детали
события ` self_check_result` будет указан обнаруженный исходящий адрес.
Если он не совпадает с ожидаемым — вероятно, на ВМ-валидаторе есть другой
маршрут наружу (не через назначенный Floating IP), либо привязка FIP на
стороне OpenStack не применилась. Проверьте вручную в OpenStack
(` openstack floating ip show <адрес>`), что ` port_id` совпадает с портом
2026-08-21 11:04:49 +03:00
валидатора. Обратите внимание: адрес для сравнения обязан быть **вне**
облака (см. ` self_check.ip_echo_urls`) — запрос к чему-либо внутри
проекта (в том числе к самому control-api, если он в той же внутренней
сети) покажет приватный адрес валидатора независимо от того, правильно
ли привязан FIP, и всегда будет давать ложный провал.
2026-08-21 07:34:45 +03:00
2026-08-26 20:47:54 +03:00
**Площадка (` site-N`) никогда не отчитывается по конкретному IP.**
Сперва проверьте статус самой площадки — ` GET
/api/v1/admin/config/sites`, поле ` state`. ` unregistered` или
` unreachable` означает, что ` prober` на этой площадке вообще не на связи
с control-api (см. [«Состояния площадки»](#состояния-площадки) выше) —
проверьте, что процесс запущен и его ` site_id` в конфиге совпадает с
` site_id` в конфиге control-api, и что площадка имеет сетевой доступ до
` control-api`. Если статус ` idle` (площадка на связи), а конкретный IP
всё равно не получает отметку о завершении — проверьте отдельно
связность до проверяемого адреса (входящий трафик на 22/80/443/8080 +
ICMP — это отдельная связность от связи с control-api, см.
2026-08-21 07:34:45 +03:00
[SETUP.md](SETUP.md#сетевые-доступы)).
**Много адресов зависло в ` checking` дольше ожидаемого.**
Это нормально, если ещё не истёк ` orchestrator.checking_window_seconds` —
агрегация ждёт либо полного набора ответов, либо истечения окна. Если
адрес завис заметно дольше окна — проверьте, что фоновый цикл control-api
вообще работает (смотрите ` journalctl -u control-api` на предмет ошибок
в ` sweep checking window`).
**Нужно посмотреть на данные "из первых рук", в обход API.**
` control-api` использует SQLite, файл — по пути ` database.path` из
конфига. Можно (только для чтения, на **той же машине**, где крутится
control-api) открыть его ` sqlite3` в режиме WAL — это безопасно для
чтения параллельно с работающим процессом:
` ``bash
sqlite3 /var/lib/cloud-ip-validator/control-api.db \
"select ip_address, state, overall_result from ip_queue order by sequence"
` ``
Не редактируйте эту базу вручную во время работы control-api — это может
рассинхронизировать состояние с реальными привязками Floating IP в
OpenStack.