# План: ros_control — централизованное управление парком MikroTik RouterOS ## Context Нужно приложение для группового администрирования множества устройств MikroTik RouterOS через API и WEB UI. Источник требований — `ros_control.svg` (draw.io): архитектура, список функций, ссылки на API. Из схемы: - **Control Server** связан двусторонне с **Local Metadata DB** (DB transactions), **S3 Bucket** (Yandex Object Storage: `ListObjectsV2`, `GetObject`, `PutObject`, `DeleteObject`) и **Admin Dashboard**. - Dashboard ↔ Control Server: JSON payload, UI Forms (ввод), JSON payload render (вывод). - Control Server ↔ **Endpoint API Server (on device)** = RouterOS REST API (`GET/PATCH/PUT/POST/DELETE`, JSON), N устройств. Функции dashboard: список устройств и управление списком (добавить, изменить, удалить); статус (модель, канал обновлений, версия ROS, версия FW, uptime, время запроса бэкапа); бэкап по каждому устройству (бинарный `.backup` без шифрования + текстовый `.rsc`, загрузка в S3, удаление с устройства, скачивание из бакета, список бэкапов в бакете); выбор канала обновлений; обновление ROS до актуальной версии канала (перезагрузка после скачивания); обновление FW (перезагрузка сразу после обновления). Принятые решения: Python FastAPI + Jinja2/HTMX, SQLite (SQLAlchemy), пароли устройств шифруются (Fernet), вход в UI по логину/паролю, API — Bearer-токен, Docker Compose для развёртывания + `venv` в корне проекта для разработки. ## Артефакты процесса (по CLAUDE.md проекта) - `docs/changes/001-initial-implementation/plan.md` — копия этого плана (создаётся первым шагом реализации) - `docs/changes/001-initial-implementation/summary.md` — итоги по выполненному (создаётся последним шагом) - `README.md` — описание, запуск, конфигурация, ссылки на API; обновляется при каждом изменении - Тесты — минимальный набор (см. Verification) ## Структура проекта ``` app/ main.py # FastAPI, роутинг, lifespan config.py # настройки из env (pydantic-settings) db.py, models.py # SQLAlchemy + SQLite: users, devices, backups, jobs security.py # Fernet для паролей устройств, хэш пароля админа, API-токен ros/client.py # httpx-клиент RouterOS REST (basic auth, verify_tls per device) ros/operations.py # status, backup, update channel/install, firmware upgrade s3.py # boto3: list/get(presigned)/put(presigned)/delete, endpoint Yandex services/ # devices, backups, updates, jobs (фоновые задачи + batch) api/v1.py # JSON API ui/routes.py, templates/, static/ # Jinja2 + HTMX tests/ # минимальный набор Dockerfile, docker-compose.yml, requirements.txt, .env.example, README.md docs/changes/001-initial-implementation/{plan,summary}.md ``` ## Ключевые технические решения 1. **RouterOS REST** (`https:///rest/...`, ROS ≥ 7.1, сервис `www-ssl`). Для самоподписанных сертификатов — флаг `verify_tls` на устройство. Ограничение: только ROS 7. 2. **Статус** — параллельный опрос (`asyncio.gather` + семафор): `/rest/system/resource` (модель, версия ROS, uptime), `/rest/system/routerboard` (current/upgrade firmware), `/rest/system/package/update` (channel, installed/latest version). Недоступное устройство отображается как offline, не ломает список. 3. **Бэкап**: - `POST /rest/system/backup/save` (`dont-encrypt=yes`) и `POST /rest/export` (`.rsc`); - REST не отдаёт содержимое крупных файлов, поэтому **устройство само загружает файлы в S3** через `/tool/fetch` (`http-method=put upload=yes src-path=…`) по **presigned PUT URL**, выданному Control Server. Ключи S3: `backups//.backup|.rsc`; - после успешной загрузки — `DELETE /rest/file/` на устройстве; время запроса пишется в БД; - список — `ListObjectsV2` по префиксу, скачивание — presigned GET (редирект). 4. **Обновление ROS**: `POST /rest/system/package/update/set {channel}` → `check-for-updates` → `install` (устройство скачивает пакет и перезагружается само — соответствует требованию «перезагрузка после скачивания»). 5. **Обновление FW**: `POST /rest/system/routerboard/upgrade` → опрос до `current-firmware == upgrade-firmware` → `POST /rest/system/reboot`. 6. **Долгие операции** — таблица `jobs` + фоновые задачи; UI опрашивает статус через HTMX. Групповые операции: эндпоинты принимают список `device_ids`, выполняются параллельно с ограничением конкурентности. 7. **API v1**: `/api/v1/devices` (CRUD), `/devices/{id}/status`, `/devices/{id}/backups` (POST/GET), `/backups/download`, `/devices/{id}/update/channel` (PUT), `/devices/{id}/update/install`, `/devices/{id}/firmware/upgrade` (POST), `/jobs/{id}`. UI использует те же сервисы. 8. **Безопасность**: пароли устройств не возвращаются в API/UI; ключ шифрования и S3-ключи — только из env; UI — сессия после логина; API — Bearer-токен. ## Этапы реализации 1. Артефакты: `plan.md`, каркас проекта, `venv`, `requirements.txt`, `.env.example`. 2. Модели БД, security (Fernet, логин, токен), конфиг. 3. RouterOS-клиент и операции статуса; CRUD устройств; список со статусами (API + UI). 4. S3-модуль; бэкап (2 формата, загрузка с устройства, очистка), список и скачивание. 5. Каналы обновлений, обновление ROS, обновление FW; jobs и групповые операции. 6. Docker/compose, README, минимальные тесты, `summary.md`. ## Риски / проверить на реальном устройстве - `/tool/fetch` с `http-method=put upload=yes` на целевой версии ROS (основной вариант). Запасной: устройство отправляет файл на эндпоинт Control Server. - Синхронные REST-вызовы (`fetch`, `install`) могут упереться в таймаут — использовать `duration`/асинхронные задачи и опрос состояния. - Сертификат `www-ssl` на устройствах и доступность 443 от Control Server; доступность S3 (`storage.yandexcloud.net`) с устройств. ## Verification - **Автотесты (минимум, ~4)**: шифрование/расшифровка пароля; разбор статуса RouterOS (mock httpx); сценарий бэкапа с мокнутыми RouterOS и S3 (порядок: save → export → fetch → delete file → запись в БД); авторизация API (401 без токена). - **Ручная проверка**: RouterOS CHR (ROS 7) в лаборатории + тестовый бакет Yandex Object Storage: добавить устройство → статус в списке → бэкап (оба файла в бакете, на устройстве удалены) → скачивание → смена канала → обновление ROS → обновление FW. - `docker compose up` — приложение поднимается, UI на `:8000`, `/docs` показывает OpenAPI.