Files
cloud-ip-validator/docs/USAGE.md
T
ayurishchevandClaude Sonnet 5 93c79b63ea Skip the check cycle for a Floating IP already occupied by another port
The cloud is live: the address list submitted as "free" (bootstrap config
or POST /api/v1/admin/ips) can drift by the time the orchestrator claims
it, or an operator can queue an already-occupied address by mistake.
Neutron's floating-IP association is a blind "last write wins" PUT with
no conflict error to catch, so associateFIP now checks the FIP's PortID
(already fetched via GetFloatingIPByAddress) before associating, guarded
against the false-positive of the FIP already belonging to this same
validator's own port.

A match routes the address straight to a new terminal ip_queue.state
("occupied", distinct from failed/fail) via db.MarkFIPOccupied — no
retries, since Neutron won't free it on its own and requeuing would let
it be reclaimed again next tick, starving the rest of the queue — plus a
dedicated fip_occupied audit event. Resubmitting the address later (once
the conflict is resolved) resets it to queued via the existing
POST /api/v1/admin/ips resubmit path (CancelIP/ListExpiredLeases updated
to treat occupied as terminal too). admin-dashboard gets its own "занят"
badge, distinct from fail/partial/cancelled.

Rebuilt bin/{control-api,admin-dashboard,prober,validator-agent} and
bin/SHA256SUMS per docs/SETUP.md's documented build recipe, since
control-api and admin-dashboard source changed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NeVbMVEiE7XQAkBd7HQgj6
2026-09-13 23:54:35 +03:00

599 lines
42 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Работа со стендом
Этот документ — для оператора, который уже развернул стенд (см.
[SETUP.md](SETUP.md)) и теперь использует его в повседневной работе:
добавляет адреса на проверку, следит за очередью, разбирается в
результатах и реагирует на проблемы. Прямые вызовы API описаны в
[API.md](API.md) — здесь мы используем их только как инструмент, не
углубляясь в протокол.
## Содержание
- [Как устроена работа с системой](#как-устроена-работа-с-системой)
- [Добавление новых IP в очередь](#добавление-новых-ip-в-очередь)
- [Наблюдение за очередью](#наблюдение-за-очередью)
- [Значения полей IP](#значения-полей-ip)
- [Как читать итоговый результат (pass/partial/fail)](#как-читать-итоговый-результат-passpartialfail)
- [Просмотр деталей и истории по конкретному адресу](#просмотр-деталей-и-истории-по-конкретному-адресу)
- [Управление валидаторами](#управление-валидаторами)
- [Управление площадками (проберами)](#управление-площадками-проберами)
- [Управление типами проверок пробера](#управление-типами-проверок-пробера)
- [Управление целями проверки](#управление-целями-проверки)
- [Повторная проверка адреса](#повторная-проверка-адреса)
- [Принудительная остановка проверки](#принудительная-остановка-проверки)
- [Удаление адресов из очереди](#удаление-адресов-из-очереди)
- [Пауза перед self-check (fip_settle_seconds)](#пауза-перед-self-check-fip_settle_seconds)
- [Частые проблемы и что с ними делать](#частые-проблемы-и-что-с-ними-делать)
## Как устроена работа с системой
Оператор не взаимодействует с валидаторами и проберами напрямую — вся
работа идёт через `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 в очередь
Основной способ — API, без перезапуска процесса:
```bash
curl -s -X POST http://<control-api>:8080/api/v1/admin/ips \
-d '{"addresses": ["203.0.113.10", "203.0.113.11"]}'
```
```json
{"added": ["203.0.113.10", "203.0.113.11"], "requeued": [], "reordered": [], "skipped_in_progress": []}
```
Адреса обрабатываются в порядке, в котором перечислены в `addresses` —
именно в этом порядке они и встанут в очередь друг за другом. Метод
идемпотентен относительно уже идущих проверок: адрес, который сейчас
активно проверяется, в ответе окажется в `skipped_in_progress` и не будет
тронут (см. [«Повторная проверка адреса»](#повторная-проверка-адреса)
ниже — тот же метод форсирует перепроверку уже завершённых адресов).
Также по-прежнему можно добавить адреса через `ip_addresses` в
`/etc/cloud-ip-validator/control-api.yaml` и перезапустить control-api —
при каждом старте control-api доливает в очередь только новые адреса из
этого списка (уже обработанные ранее не сбрасываются и повторно не
проверяются). Держать `control-api.yaml` под версионным контролем (git)
по-прежнему полезно как журнал того, что изначально ставилось в очередь
при разворачивании стенда — но для повседневного добавления адресов проще
и быстрее пользоваться API выше.
## Наблюдение за очередью
Общая сводка:
```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` | Проверяемый адрес |
| `Sequence` | Позиция в очереди (порядок постановки — из конфига при первом старте либо из последнего вызова `POST /api/v1/admin/ips`) |
| `State` | Текущий этап: `queued`, `assigning_fip`, `awaiting_self_check`, `checking`, `aggregating`, `done`, `failed`, `occupied` (см. ниже) |
| `OwnerValidatorID` | Какой валидатор сейчас (или последним) занимался этим адресом |
| `FIPID` | Идентификатор Floating IP в OpenStack, к которому привязан адрес (пусто, если ещё/уже не привязан) |
| `AttemptNumber` | Номер попытки — растёт при каждом requeue (сбой привязки, сбой self-check, реклейм по таймауту) |
| `RetryCount` | Сколько раз адрес уже переставлялся в очередь заново |
| `EgressComplete` | Валидатор закончил исходящие проверки |
| `OverallResult` | Итог: `pass`, `partial`, `fail`, `cancelled` (принудительно остановлена, см. [«Принудительная остановка проверки»](#принудительная-остановка-проверки)), либо пусто, пока проверка не завершена |
| `AssignedAt` / `AggregatedAt` / `FIPReleasedAt` | Метки времени соответствующих этапов |
> Завершённость входящих проверок по каждой конкретной площадке в этом
> списке не отображается (площадок теперь может быть сколько угодно, а не
> фиксированные три) — детали по конкретной площадке смотрите в массиве
> `checks` ответа `GET /api/v1/admin/ips/{ip}` (`Source: "inbound-site-N"`).
## Как читать итоговый результат (pass/partial/fail)
- **`pass`** — прошли все проверки: все исходящие, плюс все площадки по
всем портам и ICMP — если площадки вообще настроены (`sites` в конфиге
control-api может быть пустым, см.
[«Управление площадками»](#управление-площадками-проберами) — тогда
учитываются только исходящие). Адрес можно считать пригодным к
повторной выдаче.
- **`partial`** — часть проверок прошла, часть — нет (например, площадка
site-2 не смогла достучаться по 8080/tcp, но остальное в порядке).
Означает частичную деградацию — например, адрес заблокирован в
отдельном сегменте сети/у отдельного провайдера. Требует решения
оператора: годится ли адрес для данного случая использования.
- **`fail`** — либо ни одна проверка не прошла, либо адрес вообще не
дошёл до стадии проверок (например, self-check не подтвердился —
трафик валидатора не пошёл через назначенный FIP — и попытки
исчерпались). Смотрите `events` по этому адресу (см. ниже), чтобы
понять, на каком шаге и почему.
- **`cancelled`** — проверку остановил оператор через `POST
/api/v1/admin/ips/{ip}/cancel` (`State` при этом — `failed`), а не
система по итогам проверок. Отличать от обычного `fail` полезно, чтобы
не путать «адрес не прошёл проверку» с «проверку прервали вручную».
Отдельно от `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`, когда убедитесь, что конфликт в облаке
разрешился; в дашборде для таких адресов также показывается кнопка
«Перепроверить» вместо «Отменить».
Отсутствие ответа от источника (площадка не прислала результат до
истечения `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`).
**Добавление нового валидатора (без перезапуска control-api):**
1. Поднимите новую ВМ в сервисном проекте облака, узнайте её Neutron
`port_id`.
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"}'
```
3. Разверните и запустите `validator-agent` на новой ВМ с тем же
`validator_id` в его конфиге (см. [SETUP.md](SETUP.md#развёртывание-validator-agent-на-вм-валидаторах)).
Сменить `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 —
только конфигурационные поля).
**Вывод валидатора из эксплуатации:** остановите на нём
`validator-agent` (`systemctl stop validator-agent`). Он перестанет
получать новые задания после того, как закончит текущее (если оно было);
если он был убит посреди работы — control-api сам заберёт у него
незавершённый адрес обратно в очередь по истечении
`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 выше.
## Управление площадками (проберами)
**Входящие (inbound/prober) проверки полностью опциональны, а число
площадок не ограничено.** Список `sites` в конфиге control-api и есть
переключатель: пусто — inbound-проверки выключены целиком, итоговый
результат считается только по исходящим (egress) проверкам, и агрегация
не ждёт вообще ни одного пробера. Указано N слотов — ждём именно их.
Явного отдельного флага "включить/выключить" нет — самого списка `sites`
достаточно.
Чтобы добавить площадку (без перезапуска control-api) — назначьте
`site_id` любому свободному слоту (`index` — любое целое `>= 1`, слотов
может быть сколько угодно) через API:
```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
```
процесс `prober` на ней можно не останавливать (он просто перестанет
получать назначения — `POST /api/v1/probers/register` для отвязанного
`site_id` начнёт отвечать `400`). Текущее распределение слотов — `GET
/api/v1/admin/config/sites`.
> Правки через `sites[]` в `control-api.yaml` тоже поддерживаются, но
> только как bootstrap пустой базы данных при самом первом старте — как
> только в БД есть хотя бы одна площадка, YAML для этой секции
> игнорируется при всех последующих рестартах. Для стенда, который уже
> хоть раз запускался, используйте API выше.
### Состояния площадки
По аналогии с валидаторами (см. [«Управление
валидаторами»](#управление-валидаторами) выше), у каждой площадки есть
состояние подключения — видно и в `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, а на каждом опросе обрабатывает сразу
весь активный набор.
## Управление типами проверок пробера
Список 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 выше.
## Управление целями проверки
Набор 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 пустой базы данных при самом
> первом старте — см. примечание в разделах выше.
## Повторная проверка адреса
Если адрес завершился с `failed` или `partial`, а вы хотите перепроверить
его ещё раз (например, после устранения блокировки на стороне сети) —
отправьте его тем же методом, что используется для постановки новых
адресов в очередь:
```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
```
Чтобы позже всё же проверить этот адрес — используйте
[«Повторную проверку адреса»](#повторная-проверка-адреса) выше, она
одинаково работает и для отменённых, и для обычно завершённых адресов.
## Удаление адресов из очереди
**Отличие от отмены (`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)).
## Пауза перед 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`, не
обрезается молча.
## Частые проблемы и что с ними делать
**Валидатор долго висит в `unreachable`.**
Проверьте сетевую связность ВМ-валидатора до `control-api` (порт из
`server.listen_addr`) и что процесс `validator-agent` вообще запущен
(`systemctl status validator-agent`, `journalctl -u validator-agent`).
**Адрес не выходит из `awaiting_self_check` (статус не меняется вообще).**
Если это длится всего несколько секунд и на стенде настроена
[пауза перед self-check](#пауза-перед-self-check-fip_settle_seconds)
(`fip_settle_seconds`) — это ожидаемое поведение, не сбой: адрес
сознательно придерживается, прежде чем агенту разрешат начать проверку.
Проблема — если статус не меняется значительно дольше этой паузы. Тогда
дело обычно в self-check: он запрашивает внешние (вне облака) сервисы из
`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 (не зависает, а именно
возвращается в очередь снова и снова).**
Смотрите `events` по адресу (`GET /api/v1/admin/ips/{ip}`) — в детали
события `self_check_result` будет указан обнаруженный исходящий адрес.
Если он не совпадает с ожидаемым — вероятно, на ВМ-валидаторе есть другой
маршрут наружу (не через назначенный Floating IP), либо привязка FIP на
стороне OpenStack не применилась. Проверьте вручную в OpenStack
(`openstack floating ip show <адрес>`), что `port_id` совпадает с портом
валидатора. Обратите внимание: адрес для сравнения обязан быть **вне**
облака (см. `self_check.ip_echo_urls`) — запрос к чему-либо внутри
проекта (в том числе к самому control-api, если он в той же внутренней
сети) покажет приватный адрес валидатора независимо от того, правильно
ли привязан FIP, и всегда будет давать ложный провал.
**Площадка (`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, см.
[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.