Scan floating IPs in the background, page by page, so thousands of addresses work
The "Scan Floating IP" button failed with a client timeout: the project now holds ~6.4k floating IPs and the scan listed them all in one unpaginated, timeout-less Neutron request on the HTTP request context. openstack: ListFreeFloatingIPs reads marker-based pages (fields= keeps them small) with per-page retry/backoff on transport errors, 5xx and 429, and every request now has a timeout (also ends hangs inside the orchestrator tick). orchestrator: the scan is a single-flight background job on the process context with progress (clearing/listing/enqueuing/done/error), dry_run, full discovery before anything is enqueued, then SubmitIPs in chunks of 500 in ascending IP order; a failed read leaves the queue untouched. The auto-cycle gets a "scanning" phase that polls the job, so the control loop and autoCycleMu are never held across OpenStack/DB work; it recovers after a restart and waits for (instead of adopting) a scan started by someone else. db: migration 0009 (indexes), paged ListIPsPage/ListRegistryPage, GROUP BY counters, EXISTS completion check, set-based ClearAllIPs. API: POST /admin/ips/scan -> 202 (dry_run, wait), GET /admin/ips/scan, paging and filters on /admin/ips and /admin/registry (bare arrays without limit), results_by_overall in /admin/status. dashboard: scan progress panel and dry-run button, paginated /ips and /registry with server-side filters, Overview on counters and capped lists with progress/ETA, "select all N by filter", hx-params fix for per-row buttons, real counts in confirmations. Also: docs (API, USAGE, DASHBOARD, README), plan and review under docs/changes/, bin/ rebuilt with new SHA256SUMS. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
debf2afed2
commit
aff8fe38b5
61 files changed
+5833
-536
No files matched your search
+66
-22
@@ -330,15 +330,37 @@ IP на данном проходе". До этого момента control-api
|
||||
{
|
||||
"total_ips": 25,
|
||||
"ips_by_state": {"queued": 10, "checking": 3, "done": 11, "failed": 1},
|
||||
"results_by_overall": {"pass": 8, "partial": 3, "fail": 0, "cancelled": 0},
|
||||
"total_validators": 4
|
||||
}
|
||||
```
|
||||
|
||||
`results_by_overall` — сколько адресов с каким итогом (всегда все четыре ключа). Счётчики считаются
|
||||
запросами `GROUP BY` на стороне БД, а не загрузкой всей очереди, поэтому метод быстрый и при тысячах адресов.
|
||||
|
||||
### `GET /api/v1/admin/ips`
|
||||
|
||||
Полный список всех IP из очереди со всеми полями (см.
|
||||
Список IP из очереди со всеми полями (см.
|
||||
[USAGE.md](USAGE.md#значения-полей-ip) — расшифровка полей и статусов).
|
||||
|
||||
**Без параметров** — как раньше: весь список одним массивом (при тысячах адресов это мегабайты — для больших очередей
|
||||
используйте постраничный режим). **С `limit`** — постраничный режим: ответ — конверт
|
||||
|
||||
```json
|
||||
{"items": [ ... ], "total": 6440, "limit": 50, "offset": 0}
|
||||
```
|
||||
|
||||
| Параметр | Значение |
|
||||
|---|---|
|
||||
| `limit` | размер страницы, `1`…`1000` (иначе `400`); включает постраничный режим |
|
||||
| `offset` | смещение, `>= 0` (без `limit` — `400`) |
|
||||
| `state` | одно или несколько состояний через запятую (`queued`, `assigning_fip`, `awaiting_self_check`, `checking`, `aggregating`, `done`, `failed`, `occupied`) |
|
||||
| `q` | подстрока адреса |
|
||||
| `result` | итог: `pass`, `partial`, `fail`, `cancelled` |
|
||||
| `order` | `sequence` (по умолчанию, порядок очереди) или `aggregated_at_desc` (последние завершённые) |
|
||||
|
||||
`total` — число записей после фильтров. Параметры фильтров без `limit` возвращают отфильтрованный массив.
|
||||
|
||||
### `GET /api/v1/admin/ips/{ip}`
|
||||
|
||||
Детали по одному адресу: сам объект IP, все проверки текущей попытки и
|
||||
@@ -470,33 +492,52 @@ YAML для этой секции больше не перечитывается
|
||||
|
||||
### `POST /api/v1/admin/ips/scan`
|
||||
|
||||
Сканирует текущий проект OpenStack на предмет свободных (не привязанных ни
|
||||
к одному порту) Floating IP и сразу передаёт найденный список в `POST
|
||||
/api/v1/admin/ips` — тот же add/requeue/reorder-вызов, как если бы
|
||||
оператор ввёл эти адреса вручную. Не принимает тело запроса.
|
||||
Запускает **фоновое** сканирование проекта OpenStack: находит все свободные (не привязанные ни к одному порту) Floating IP
|
||||
и ставит их в очередь — тот же add/requeue/reorder, что и `POST /api/v1/admin/ips`. Не принимает тело запроса и **сразу отвечает**
|
||||
`202` со статусом задания; ход сканирования смотрите через `GET /api/v1/admin/ips/scan`.
|
||||
|
||||
Почему в фоне: в проекте может быть тысячи Floating IP (на стенде — около 6,4 тыс.), Neutron отдаёт такой список минуты. Control-api читает
|
||||
его **страницами** (по `openstack.list_page_size`, по умолчанию 200, по `marker`), повторяет страницу при обрыве соединения/5xx/429,
|
||||
сначала обнаруживает **все** адреса и только потом ставит их в очередь кусками по 500 в порядке возрастания IP. Если чтение не удалось
|
||||
(после повторов), в очередь не попадает ничего — очередь остаётся как была, а статус задания — `error`.
|
||||
|
||||
| Параметр | Значение |
|
||||
|---|---|
|
||||
| `dry_run=true` | только найти и посчитать свободные адреса; очередь не меняется (безопасная проверка, итог — в статусе) |
|
||||
| `wait=true` | дождаться окончания и ответить `200` прежним телом `{scanned_free, added[], requeued[], reordered[], skipped_in_progress[]}` (для curl и скриптов; при ошибке `502`) |
|
||||
|
||||
Одновременно идёт одно сканирование: повторный запрос во время работы **присоединяется** к текущему и тоже отвечает `202` с его статусом.
|
||||
|
||||
### `GET /api/v1/admin/ips/scan`
|
||||
|
||||
Статус и прогресс сканирования (admin-токен).
|
||||
|
||||
Ответ (`200`):
|
||||
```json
|
||||
{
|
||||
"scanned_free": 3,
|
||||
"added": ["203.0.113.20"],
|
||||
"requeued": [],
|
||||
"reordered": ["203.0.113.10", "203.0.113.11"],
|
||||
"skipped_in_progress": []
|
||||
"state": "listing",
|
||||
"running": true,
|
||||
"dry_run": false,
|
||||
"pages": 12,
|
||||
"discovered": 2400,
|
||||
"free": 2399,
|
||||
"added": 0,
|
||||
"requeued": 0,
|
||||
"reordered": 0,
|
||||
"skipped_in_progress": 0,
|
||||
"started_at": "2026-10-01T15:47:40.759Z",
|
||||
"finished_at": null,
|
||||
"error": ""
|
||||
}
|
||||
```
|
||||
|
||||
`scanned_free` — сколько свободных Floating IP нашлось в проекте всего
|
||||
(включая уже стоящие в очереди — они попадут в `reordered`, а не
|
||||
`added`). Если свободных адресов нет вообще, это не ошибка: ответ будет
|
||||
`{"scanned_free": 0, "added": [], ...}`.
|
||||
`state`: `idle` (в этом процессе сканирования ещё не было), `clearing` (очистка очереди — только в автоцикле), `listing` (чтение страниц),
|
||||
`enqueuing` (постановка в очередь), `done`, `error` (причина в `error`), `cancelled`. `discovered` — сколько Floating IP прочитано
|
||||
(свободных и занятых), `free` — из них свободных, `added`/`requeued`/`reordered`/`skipped_in_progress` — итог постановки в очередь
|
||||
(как в `POST /admin/ips`). Статус хранится в памяти процесса: после перезапуска control-api он снова `idle`.
|
||||
|
||||
Помимо ручного вызова, сканирование можно включить по расписанию —
|
||||
`orchestrator.fip_scan_interval_seconds` в `control-api.yaml` (0, по
|
||||
умолчанию, — только по запросу через эту ручку или кнопку «Сканировать
|
||||
Floating IP» в дашборде). Пока включён
|
||||
[автоматический цикл](#автоматический-цикл-проверок), периодический скан
|
||||
не выполняется.
|
||||
Помимо ручного вызова, сканирование можно включить по расписанию — `orchestrator.fip_scan_interval_seconds` в `control-api.yaml`
|
||||
(0, по умолчанию, — только по запросу через эту ручку или кнопку «Сканировать Floating IP» в дашборде). Пока включён
|
||||
[автоматический цикл](#автоматический-цикл-проверок), периодический скан не выполняется.
|
||||
|
||||
## Автоматический цикл проверок
|
||||
|
||||
@@ -575,7 +616,10 @@ curl -s -X POST http://<control-api>:8080/api/v1/admin/auto-cycle/stop
|
||||
|
||||
### `GET /api/v1/admin/registry`
|
||||
|
||||
Список всех адресов реестра с краткой сводкой по каждому.
|
||||
Список адресов реестра с краткой сводкой по каждому. Без параметров — все адреса одним массивом; **с `limit`** (`1`…`1000`) —
|
||||
постраничный конверт `{"items": [...], "total": N, "limit": L, "offset": O}`, параметры `offset`, `q` (подстрока адреса) и
|
||||
`last_result` (`pass`/`partial`/`fail`/`cancelled`). Страница и фильтры применяются в SQL до расчёта сводки, поэтому
|
||||
реестр из тысяч адресов отдаётся за доли секунды.
|
||||
|
||||
```json
|
||||
[
|
||||
|
||||
Reference in new issue
Block a user