Add optional automatic check cycle (clear queue -> scan FIPs -> wait -> repeat)

An admin-controlled scenario that repeats what the operator does by hand:
clear the IP queue, scan and enqueue all free Floating IPs, wait until every
queued address reaches a terminal state (so results are in the Registry),
then wait a configurable interval and start over.

- control-api: new auto_cycle singleton table (migration 0008) holding
  enabled/interval/max-run settings and persisted phase state, so the cycle
  survives restarts; engine in orchestrator/autocycle.go driven from the
  existing loop tick with an injectable "now" for deterministic tests.
- Interval (default 1h, min 60s) and max wait (default unlimited, timeout
  outcome) are runtime settings, never hardcoded.
- The periodic fip_scan_interval_seconds scan is skipped while the cycle is
  enabled. An emptied queue mid-cycle counts as finished; stopping during
  the pause keeps the last cycle's outcome.
- API: GET/PUT /api/v1/admin/auto-cycle, POST .../start, POST .../stop.
- admin-dashboard: "Автоматический цикл" panel on /settings and an
  "Автоцикл активен" indicator on /overview.
- Tests for db, orchestrator, httpapi and dashboard; run-local-e2e.sh now
  exercises a full auto cycle; docs updated; bin/ rebuilt with refreshed
  SHA256SUMS.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ayurishchevandClaude Sonnet 5.5 committed 2026-10-01 10:28:53 +03:00
1 parent 008ae1b0db
commit cd37b10f3b
35 files changed
+2205 -16

No files matched your search

+68 -1
View File
@@ -28,6 +28,7 @@ JSON, базовый префикс прикладных методов — `/ap
- [Методы для prober](#методы-для-prober)
- [Служебные и административные методы](#служебные-и-административные-методы)
- [Управление очередью и конфигурацией](#управление-очередью-и-конфигурацией)
- [Автоматический цикл проверок](#автоматический-цикл-проверок)
- [Реестр адресов и история проверок](#реестр-адресов-и-история-проверок)
- [Модель состояний и связь методов с ней](#модель-состояний-и-связь-методов-с-ней)
- [Сквозной пример работы (curl)](#сквозной-пример-работы-curl)
@@ -469,7 +470,73 @@ YAML для этой секции больше не перечитывается
Помимо ручного вызова, сканирование можно включить по расписанию —
`orchestrator.fip_scan_interval_seconds` в `control-api.yaml` (0, по
умолчанию, — только по запросу через эту ручку или кнопку «Сканировать
Floating IP» в дашборде).
Floating IP» в дашборде). Пока включён
[автоматический цикл](#автоматический-цикл-проверок), периодический скан
не выполняется.
## Автоматический цикл проверок
Опциональный повторяющийся сценарий «очистить очередь → просканировать
Floating IP → дождаться завершения всех проверок → пауза → заново» (описание
для оператора — [USAGE.md](USAGE.md#автоматический-цикл-проверок)). По умолчанию
выключен. Состояние и параметры хранятся в базе; перезапуск control-api их
не сбрасывает.
| Метод | Путь | Тело | Успех | Ошибки |
|---|---|---|---|---|
| `GET` | `/api/v1/admin/auto-cycle` | — | `200`, статус | — |
| `PUT` | `/api/v1/admin/auto-cycle` | `{"interval_seconds": N, "max_run_seconds": M}` — любое поле можно опустить | `200`, статус | `400` — `interval_seconds < 60` или `max_run_seconds < 0` (ничего не применяется) |
| `POST` | `/api/v1/admin/auto-cycle/start` | — | `200`, статус | — |
| `POST` | `/api/v1/admin/auto-cycle/stop` | — | `200`, статус | — |
Каждый метод отвечает полным объектом статуса:
```json
{
"enabled": true,
"interval_seconds": 3600,
"max_run_seconds": 0,
"phase": "waiting",
"run_started_at": null,
"next_run_at": "2026-10-01T08:00:12.345Z",
"last_run_started_at": "2026-10-01T07:00:01.100Z",
"last_run_finished_at": "2026-10-01T07:00:12.345Z",
"last_outcome": "completed",
"last_error": "",
"last_scanned_free": 12,
"runs_total": 5
}
```
- `phase` — `idle` (выключен или ещё не стартовал), `running` (идут
проверки), `waiting` (пауза до `next_run_at`).
- `last_outcome` — `completed`, `no_free_ips`, `timeout`, `error` или
`stopped`; пустая строка, пока не завершился ни один цикл. Для `error`
причина — в `last_error`.
- `last_scanned_free` — сколько свободных Floating IP нашёл скан последнего
цикла; `runs_total` — сколько циклов завершилось исходом `completed`.
- `max_run_seconds = 0` — без ограничения времени ожидания проверок.
- Времена — RFC 3339 (UTC), `null`, пока не наступили.
`POST .../start` идемпотентен: у уже включённого цикла ничего не меняется
(идущий цикл не перезапускается). Включённый цикл начинает первый прогон на
ближайшем шаге оркестратора (порядка `orchestrator.poll_interval_seconds`).
`POST .../stop` выключает цикл и переводит его в `idle`; проверки, которые
уже идут, не прерываются. Новое значение `interval_seconds` начинает действовать
со следующей паузы.
События цикла (`auto_cycle_started`, `auto_cycle_completed`,
`auto_cycle_timeout`, `auto_cycle_error`, `auto_cycle_stopped`) попадают в
общий журнал событий; шаги цикла записывают обычные `queue_cleared` и
`fip_scan`.
```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 http://<control-api>:8080/api/v1/admin/auto-cycle
curl -s -X POST http://<control-api>:8080/api/v1/admin/auto-cycle/stop
```
## Реестр адресов и история проверок