Files
cloud-ip-validator/docs/USAGE.md
T

598 lines
42 KiB
Markdown
Raw Normal View History

2026-08-21 07:34:45 +03:00
# Работа со стендом
Этот документ — для оператора, который уже развернул стенд (см.
[SETUP.md](SETUP.md)) и теперь использует его в повседневной работе:
добавляет адреса на проверку, следит за очередью, разбирается в
результатах и реагирует на проблемы. Прямые вызовы API описаны в
[API.md](API.md) — здесь мы используем их только как инструмент, не
углубляясь в протокол.
## Содержание
- [Как устроена работа с системой](#как-устроена-работа-с-системой)
- [Добавление новых IP в очередь](#добавление-новых-ip-в-очередь)
- [Наблюдение за очередью](#наблюдение-за-очередью)
- [Значения полей IP](#значения-полей-ip)
- [Как читать итоговый результат (pass/partial/fail)](#как-читать-итоговый-результат-passpartialfail)
- [Просмотр деталей и истории по конкретному адресу](#просмотр-деталей-и-истории-по-конкретному-адресу)
- [Управление валидаторами](#управление-валидаторами)
- [Управление площадками (проберами)](#управление-площадками-проберами)
- [Управление типами проверок пробера](#управление-типами-проверок-пробера)
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
## Наблюдение за очередью
Общая сводка:
```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}]'
```
## Значения полей IP
| Поле | Значение |
|---|---|
| `IPAddress` | Проверяемый адрес |
2026-08-23 20:39:22 +03:00
| `Sequence` | Позиция в очереди (порядок постановки — из конфига при первом старте либо из последнего вызова `POST /api/v1/admin/ips`) |
| `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` | Метки времени соответствующих этапов |
> Завершённость входящих проверок по каждой конкретной площадке в этом
> списке не отображается (площадок теперь может быть сколько угодно, а не
> фиксированные три) — детали по конкретной площадке смотрите в массиве
> `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
Отдельно от `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)'
```
## Управление валидаторами
Список валидаторов и их текущее состояние:
```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
## Управление площадками (проберами)
**Входящие (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) — назначьте
`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
### Состояния площадки
По аналогии с валидаторами (см. [«Управление
валидаторами»](#управление-валидаторами) выше), у каждой площадки есть
состояние подключения — видно и в `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
## Управление типами проверок пробера
Список 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.
Порты `22` и `443` в этом списке трактуются особо: помимо базового
TCP-connect (`tcp-22`/`tcp-443`) пробер дополнительно выполняет настоящий
обмен SSH-банером (`ssh`) и настоящий TLS-хендшейк (`tls-443`) —
голого открытого TCP-порта недостаточно, чтобы считать SSH/HTTPS
рабочими. Отдельного переключателя для этих доп.проверок нет: они
включаются и выключаются вместе с самим портом в `ports`. Если на порту
22/443 у площадки на самом деле слушает что-то, кроме SSH/HTTPS,
доп.проверка будет закономерно проваливаться — заведите для такого сервиса
другой порт.
> Правки через `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
## Удаление адресов из очереди
**Отличие от отмены (`cancel`) выше: удаление безвозвратно.** Cancel
переводит адрес в `failed`/`cancelled` и сохраняет запись как историю —
её видно в очереди и в деталях адреса. Delete физически стирает строку
`ip_queue` и всю её историю проверок и событий: адрес полностью исчезает,
восстановить его нельзя. Если нужно просто остановить зависшую проверку,
но сохранить её результат в истории — используйте
[«Принудительную остановку проверки»](#принудительная-остановка-проверки)
выше, а не удаление.
Удалить один адрес (работает из любого состояния, включая активно
проверяемое — 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
**Площадка (`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.